From: Salvatore Orlando Date: Fri, 31 Jul 2015 22:17:11 +0000 (-0700) Subject: Fix hooks for dealing with member actions X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=bcf369b4d099c1cd3a39d47dc015e9ff280a8dfb;p=openstack-build%2Fneutron-build.git Fix hooks for dealing with member actions This patch ensures that operations like add_router_interface are correctly dispatched to the plugin. It does so by introducing fairly ugly hacks in the resource_identifier, and attribute_population pecan hooks. This is however the best possible solution wiht the current infrastructure which basically relies on hooks for request dispatching. Change-Id: I34880d1ce796cfd6f81d16fc1b9fd8387b5db056 --- diff --git a/neutron/newapi/controllers/root.py b/neutron/newapi/controllers/root.py index 3426d9c38..1e2fb553d 100644 --- a/neutron/newapi/controllers/root.py +++ b/neutron/newapi/controllers/root.py @@ -131,6 +131,11 @@ class ItemController(object): @when(index, method='PUT') def put(self, *args, **kwargs): + if request.member_action: + member_action_method = getattr(request.plugin, + request.member_action) + return member_action_method(request.context, self.item, + request.prepared_data) # TODO(kevinbenton): bulk? updater = getattr(request.plugin, 'update_%s' % request.resource_type) return updater(request.context, self.item, request.prepared_data) diff --git a/neutron/newapi/hooks/attribute_population.py b/neutron/newapi/hooks/attribute_population.py index 938e40b4a..cb95cbce7 100644 --- a/neutron/newapi/hooks/attribute_population.py +++ b/neutron/newapi/hooks/attribute_population.py @@ -32,12 +32,20 @@ class AttributePopulationHook(hooks.PecanHook): resource = state.request.resource_type if not resource: return - state.request.prepared_data = v2base.Controller.prepare_request_body( - state.request.context, state.request.json, is_create, resource, - _attributes_for_resource(resource)) + if state.request.member_action: + # Neutron currently does not describe request bodies for member + # actions in meh. prepare_request_body should not be called for + # member actions, and the body should be passed as it is. The + # plugin will do the validation (yuck). + state.request.prepared_data = state.request.json + else: + state.request.prepared_data = ( + v2base.Controller.prepare_request_body( + state.request.context, state.request.json, is_create, + resource, _attributes_for_resource(resource))) state.request.resources = _extract_resources_from_state(state) # make the original object available: - if not is_create: + if not is_create and not state.request.member_action: obj_id = _pull_id_from_request(state.request) attrs = _attributes_for_resource(resource) field_list = [name for (name, value) in attrs.items() diff --git a/neutron/newapi/hooks/resource_identifier.py b/neutron/newapi/hooks/resource_identifier.py index 91000f4f0..889e20a17 100644 --- a/neutron/newapi/hooks/resource_identifier.py +++ b/neutron/newapi/hooks/resource_identifier.py @@ -30,19 +30,54 @@ class ResourceIdentifierHook(hooks.PecanHook): def before(self, state): # TODO(kevinbenton): find a better way to look this up. maybe something # in the pecan internals somewhere? + # TODO(salv-orlando): try and leverage _lookup to this aim. Also remove + # the "special" code path for "actions" state.request.resource_type = None try: - url_type = state.request.path.split('/')[2].rsplit('.', 1)[0] + # TODO(blogan): remove this dirty hack and do a better solution + # needs to work with /v2.0, /v2.0/ports, and /v2.0/ports.json + uri = state.request.path + if not uri.endswith('.json'): + uri += '.json' + # Remove the format suffix if any + uri = uri.rsplit('.', 1)[0].split('/')[2:] + if not uri: + # there's nothing to process in the URI + return except IndexError: return - if url_type == 'extensions': + resource_type = uri[0] + if resource_type == 'extensions': return for plural, single in attributes.PLURALS.items(): - if plural == url_type: + if plural == resource_type: state.request.resource_type = single state.request.plugin = self._plugin_for_resource(single) + state.request.member_action = self._parse_action( + single, plural, uri[1:]) return - abort(404, detail='Resource: %s' % url_type) + abort(404, detail='Resource: %s' % resource_type) + + def _parse_action(self, resource, collection, remainder): + # NOTE(salv-orlando): This check is revolting and makes me + # puke, but avoids silly failures when dealing with API actions + # such as "add_router_interface". + if len(remainder) > 1: + action = remainder[1] + else: + return + ext_mgr = extensions.PluginAwareExtensionManager.get_instance() + resource_exts = ext_mgr.get_resources() + for ext in resource_exts: + if (ext.collection == collection and + action in ext.member_actions): + return action + # Action or resource extension not found + if action: + abort(404, detail="Action %(action)s for resource " + "%(resource)s undefined" % + {'action': action, + 'resource': resource}) def _plugin_for_resource(self, resource): # NOTE(kevinbenton): memoizing the responses to this had no useful