[openstack-dev] [horizon] REST and Django

Tihomir Trifonov t.trifonov at gmail.com
Fri Dec 12 12:51:35 UTC 2014


>
> Here's an example: Admin user Joe has an Domain open and stares at it for
> 15 minutes while he updates the description. Admin user Bob is asked to go
> ahead and enable it. He opens the record, edits it, and then saves it. Joe
> finished perfecting the description and saves it. Doing this action would
> mean that the Domain is enabled and the description gets updated. Last man
> in still wins if he updates the same fields, but if they update different
> fields then both of their changes will take affect without them stomping on
> each other. Whether that is good or bad may depend on the situation…



That's a great example. I believe that all of the Openstack APIs support
PATCH updates of arbitrary fields. This way - the frontend(AngularJS) can
detect which fields are being modified, and to submit only these fields for
update. If we however use a form with POST, although we will load the
object before updating it, the middleware cannot find which fields are
actually modified, and will update them all, which is more likely what PUT
should do. Thus having full control in the frontend part, we can submit
only changed fields. If however a service API doesn't support PATCH, it is
actually a problem in the API and not in the client...



The service API documentation almost always lags (although, helped by specs
> now) and the service team takes on the burden of exposing a programmatic
> way to access the API.  This is tested and easily consumable via the
> python clients, which removes some guesswork from using the service.


True. But what if the service team modifies a method signature from let's
say:

def add_something(self, request,
> ​ field1, field2):
>
>
to

def add_something(self, request,
> ​ field1, field2, field3):
>
>
and in the middleware we have the old signature:

​def add_something(self, request,
> ​ field1, field2):
>

we still need to modify the middleware to add the new field. If however the
middleware is transparent and just passes **kwargs, it will pass through
whatever the frontend sends. So we just need to update the frontend, which
can be done using custom views, and not necessary going through an upstream
change. My point is why do we need to hide some features of the backend
service API behind a "firewall" what the middleware in fact is?







On Fri, Dec 12, 2014 at 8:08 AM, Tripp, Travis S <travis.tripp at hp.com>
wrote:
>
> I just re-read and I apologize for the hastily written email I previously
> sent. I’ll try to salvage it with a bit of a revision below (please ignore
> the previous email).
>
> On 12/11/14, 7:02 PM, "Tripp, Travis S" <travis.tripp at hp.com> wrote
> (REVISED):
>
> >Tihomir,
> >
> >Your comments in the patch were very helpful for me to understand your
> >concerns about the ease of customizing without requiring upstream
> >changes. It also reminded me that I’ve also previously questioned the
> >python middleman.
> >
> >However, here are a couple of bullet points for Devil’s Advocate
> >consideration.
> >
> >
> >  *   Will we take on auto-discovery of API extensions in two spots
> >(python for legacy and JS for new)?
> >  *   The Horizon team will have to keep an even closer eye on every
> >single project and be ready to react if there are changes to the API that
> >break things. Right now in Glance, for example, they are working on some
> >fixes to the v2 API (soon to become v2.3) that will allow them to
> >deprecate v1 somewhat transparently to users of the client library.
> >  *   The service API documentation almost always lags (although, helped
> >by specs now) and the service team takes on the burden of exposing a
> >programmatic way to access the API.  This is tested and easily consumable
> >via the python clients, which removes some guesswork from using the
> >service.
> >  *   This is going to be an incremental approach with legacy support
> >requirements anyway.  So, incorporating python side changes won’t just go
> >away.
> >  *   Which approach would be better if we introduce a server side
> >caching mechanism or a new source of data such as elastic search to
> >improve performance? Would the client side code have to be changed
> >dramatically to take advantage of those improvements or could it be done
> >transparently on the server side if we own the exposed API?
> >
> >I’m not sure I fully understood your example about Cinder.  Was it the
> >cinder client that held up delivery of horizon support, the cinder API or
> >both?  If the API isn’t in, then it would hold up delivery of the feature
> >in any case. There still would be timing pressures to react and build a
> >new view that supports it. For customization, with Richard’s approach new
> >views could be supported by just dropping in a new REST API decorated
> >module with the APIs you want, including direct pass through support if
> >desired to new APIs. Downstream customizations / Upstream changes to
> >views seem a bit like a bit of a related, but different issue to me as
> >long as their is an easy way to drop in new API support.
> >
> >Finally, regarding the client making two calls to do an update:
> >
> >​>>Do we really need the lines:​
> >
> >>> project = api.keystone.tenant_get(request, id)
> >>> kwargs = _tenant_kwargs_from_DATA(request.DATA, enabled=None)
> >​
> >I agree that if you already have all the data it may be bad to have to do
> >another call. I do think there is room for discussing the reasoning,
> >though.
> >As far as I can tell, they do this so that if you are updating an entity,
> >you have to be very specific about the fields you are changing. I
> >actually see this as potentially a protectionary measure against data
> >loss and sometimes a very nice to have feature. It perhaps was intended
> >to *help* guard against race conditions (no locking and no transactions
> >with many users simultaneously accessing the data).
> >
> >Here's an example: Admin user Joe has a Domain open and stares at it for
> >15 minutes while he updates just the description. Admin user Bob is asked
> >to go ahead and enable it. He opens the record, edits it, and then saves
> >it. Joe finished perfecting the description and saves it. They could in
> >effect both edit the same domain independently. Last man in still wins if
> >he updates the same fields, but if they update different fields then both
> >of their changes will take affect without them stomping on each other. Or
> >maybe it is intended to encourage client users to compare their current
> >and previous to see if they should issue a warning if the data changed
> >between getting and updating the data. Or maybe like you said, it is just
> >overhead API calls.
>
>
>
> >
> >From: Tihomir Trifonov <t.trifonov at gmail.com<mailto:t.trifonov at gmail.com
> >>
> >Reply-To: OpenStack List
> ><openstack-dev at lists.openstack.org<mailto:
> openstack-dev at lists.openstack.or
> >g>>
> >Date: Thursday, December 11, 2014 at 7:53 AM
> >To: OpenStack List
> ><openstack-dev at lists.openstack.org<mailto:
> openstack-dev at lists.openstack.or
> >g>>
> >Subject: Re: [openstack-dev] [horizon] REST and Django
> >
> >​​
> >Client just needs to know which URL to hit in order to invoke a certain
> >API, and does not need to know the procedure name or parameters ordering.
> >
> >
> >​That's where the difference is. I think the client has to know the
> >procedure name and parameters. Otherwise​ we have a translation factory
> >pattern, that converts one naming convention to another. And you won't be
> >able to call any service API if there is no code in the middleware to
> >translate it to the service API procedure name and parameters. To avoid
> >this - we can use a transparent proxy model - direct mapping of a client
> >call to service API naming, which can be done if the client invokes the
> >methods with the names in the service API, so that the middleware will
> >just pass parameters, and will not translate. Instead of:
> >
> >
> >updating user data:
> >
> >    <client: POST /user/ >   =>    <middleware: convert to
> >/keystone/update/ >   =>   <keystone: update>
> >
> >we may use:
> >
> >    <client: POST /keystone/{ver:=x.0}/{method:=update} >   =>
> ><middleware: just forward to clients[ver].getattr("method")(**kwargs) >
> >=>   <keystone: update>
> >
> >
> >​The idea here is that if we have keystone 4.0 client, ​we will have to
> >just add it to the clients [] list and nothing more is required at the
> >middleware level. Just create the frontend code to use the new Keystone
> >4.0 methods. Otherwise we will have to add all new/different signatures
> >of 4.0 against 2.0/3.0 in the middleware in order to use Keystone 4.0.
> >
> >There is also a great example of using a pluggable/new feature in
> >Horizon. Do you remember the volume types support patch? The patch was
> >pending in Gerrit for few months - first waiting the cinder support for
> >volume types to go upstream, then waiting few more weeks for review. I am
> >not sure, but as far as I remember, the Horizon patch even missed a
> >release milestone and was introduced in the next release.
> >
> >If we have a transparent middleware - this will be no more an issue. As
> >long as someone has written the frontend modules(which should be easy to
> >add and customize), and they install the required version of the service
> >API - they will not need updated Horizon to start using the feature.
> >Maybe I am not the right person to give examples here, but how many of
> >you had some kind of Horizon customization being locally merged/patched
> >in your local distros/setups, until the patch is being pushed upstream?
> >
> >I will say it again. Nova, Keystone, Cinder, Glance etc. already have
> >stable public APIs. Why do we want to add the translation middleware and
> >to introduce another level of REST API? This layer will often hide new
> >features, added to the service APIs and will delay their appearance in
> >Horizon. That's simply not needed. I believe it is possible to just wrap
> >the authentication in the middleware REST, but not to translate anything
> >as RPC methods/parameters.
> >
> >
> >​And one more example:
> >
> >​@rest_utils.ajax()
> >def put(self, request, id):
> >    """Update a single project.
> >
> >        The POST data should be an application/json object containing the
> >        parameters to update: "name" (string),  "description" (string),
> >        "domain_id" (string) and "enabled" (boolean, defaults to true).
> >        Additional, undefined parameters may also be provided, but you'll
> >have
> >        to look deep into keystone to figure out what they might be.
> >
> >        This method returns HTTP 204 (no content) on success.
> >        """
> >        project = api.keystone.tenant_get(request, id)
> >        kwargs = _tenant_kwargs_from_DATA(request.DATA, enabled=None)
> >        api.keystone.tenant_update(request, project, **kwargs)
> >
> >​Do we really need the lines:​
> >
> >project = api.keystone.tenant_get(request, id)
> >kwargs = _tenant_kwargs_from_DATA(request.DATA, enabled=None)
> >​
> >? ​Since we update the project on the client, it is obvious that we
> >already fetched the project data. So we can simply send:
> >
> >
> >POST /keystone/3.0/tenant_update
> >
> >Content-Type: application/json
> >
> >{"id": cached.id<http://cached.id>, "domain_id": cached.domain_id,
> >"name": "new name", "description": "new description", "enabled":
> >cached.enabled}
> >
> >Fewer requests, faster application.
> >
> >
>
> _______________________________________________
> OpenStack-dev mailing list
> OpenStack-dev at lists.openstack.org
> http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev
>


-- 
Regards,
Tihomir Trifonov
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openstack.org/pipermail/openstack-dev/attachments/20141212/8606c5f7/attachment.html>


More information about the OpenStack-dev mailing list