From ced45de5984f1d89c238f70337cc0da2a150383f Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 3 Aug 2012 18:44:56 -0700 Subject: [PATCH] Adds ability to inherit wsgi extensions Needed for blueprint disable-server-extensions It is possible to create a wsgi resource that extends another resource by using the same controller, but any extensions that have been applied to the original resource will have no affect. This adds a new parameter to ResourceExtension that allows it to inherit extensions from another resource. This is necessary because we are moving key funcionality of the /servers resource into extensions, and some other resources (like create-server-ext) extend that functionality. This way we can keep the functionality of the other extensions. Change-Id: I21b4c2569c35d59c1f466642355564084a277aea --- nova/api/openstack/__init__.py | 8 ++++++- nova/api/openstack/extensions.py | 5 ++-- nova/api/openstack/wsgi.py | 21 +++++++++++++++- .../api/openstack/compute/test_extensions.py | 24 +++++++++++++++++++ 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index e84353df9965..6baa6be911d7 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -132,7 +132,13 @@ class APIRouter(base_wsgi.Router): LOG.debug(_('Extended resource: %s'), resource.collection) - wsgi_resource = wsgi.Resource(resource.controller) + inherits = None + if resource.inherits: + inherits = self.resources.get(resource.inherits) + if not resource.controller: + resource.controller = inherits.controller + wsgi_resource = wsgi.Resource(resource.controller, + inherits=inherits) self.resources[resource.collection] = wsgi_resource kargs = dict( controller=wsgi_resource, diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index da2157ad8cd7..d8a3edb4b7ca 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -289,9 +289,9 @@ class ControllerExtension(object): class ResourceExtension(object): """Add top level resources to the OpenStack API in nova.""" - def __init__(self, collection, controller, parent=None, + def __init__(self, collection, controller=None, parent=None, collection_actions=None, member_actions=None, - custom_routes_fn=None): + custom_routes_fn=None, inherits=None): if not collection_actions: collection_actions = {} if not member_actions: @@ -302,6 +302,7 @@ class ResourceExtension(object): self.collection_actions = collection_actions self.member_actions = member_actions self.custom_routes_fn = custom_routes_fn + self.inherits = inherits def wrap_errors(fn): diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index 229783d54447..3ea7c1c7c6d4 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -660,11 +660,16 @@ class Resource(wsgi.Application): """ - def __init__(self, controller, action_peek=None, **deserializers): + def __init__(self, controller, action_peek=None, inherits=None, + **deserializers): """ :param controller: object that implement methods created by routes lib :param action_peek: dictionary of routines for peeking into an action request body to determine the desired action + :param inherits: another resource object that this resource should + inherit extensions from. Any action extensions that + are applied to the parent resource will also apply + to this resource. """ self.controller = controller @@ -689,6 +694,7 @@ class Resource(wsgi.Application): # Save a mapping of extensions self.wsgi_extensions = {} self.wsgi_action_extensions = {} + self.inherits = inherits def register_actions(self, controller): """Registers controller actions with this resource.""" @@ -944,6 +950,19 @@ class Resource(wsgi.Application): return response def get_method(self, request, action, content_type, body): + meth, extensions = self._get_method(request, + action, + content_type, + body) + if self.inherits: + _meth, parent_ext = self.inherits.get_method(request, + action, + content_type, + body) + extensions.extend(parent_ext) + return meth, extensions + + def _get_method(self, request, action, content_type, body): """Look up the action-specific method and its extensions.""" # Look up the method diff --git a/nova/tests/api/openstack/compute/test_extensions.py b/nova/tests/api/openstack/compute/test_extensions.py index 32533936bad4..ebecb3ce60ca 100644 --- a/nova/tests/api/openstack/compute/test_extensions.py +++ b/nova/tests/api/openstack/compute/test_extensions.py @@ -109,11 +109,14 @@ class StubExtensionManager(object): self.action_ext = action_ext self.request_ext = request_ext self.controller_ext = controller_ext + self.extra_resource_ext = None def get_resources(self): resource_exts = [] if self.resource_ext: resource_exts.append(self.resource_ext) + if self.extra_resource_ext: + resource_exts.append(self.extra_resource_ext) return resource_exts def get_actions(self): @@ -511,6 +514,27 @@ class ControllerExtensionTest(ExtensionTestCase): self.assertEqual(200, response.status_int) self.assertEqual(extension_body, response.body) + def test_controller_extension_late_inherited_resource(self): + # Need a dict for the body to convert to a ResponseObject + controller = StubController(dict(foo=response_body)) + parent_ext = base_extensions.ResourceExtension('tweedles', controller) + + ext_controller = StubLateExtensionController(extension_body) + extension = StubControllerExtension() + cont_ext = base_extensions.ControllerExtension(extension, 'tweedles', + ext_controller) + + manager = StubExtensionManager(resource_ext=parent_ext, + controller_ext=cont_ext) + child_ext = base_extensions.ResourceExtension('beetles', controller, + inherits='tweedles') + manager.extra_resource_ext = child_ext + app = compute.APIRouter(manager) + request = webob.Request.blank("/fake/beetles") + response = request.get_response(app) + self.assertEqual(200, response.status_int) + self.assertEqual(extension_body, response.body) + def test_controller_action_extension_early(self): controller = StubActionController(response_body) actions = dict(action='POST')