summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCole Robinson <crobinso@redhat.com>2012-01-23 11:29:24 -0500
committerCole Robinson <crobinso@redhat.com>2012-01-24 13:30:23 -0500
commitfefb88877c6d6f00626df747eb0172484c16f0ec (patch)
treece132ed4c2f86517d8caccd907e4f01968ae124d
parent3ad3292efd7fcba7b58bc9c8b1cb84e8b00a10fa (diff)
downloadnova-fefb88877c6d6f00626df747eb0172484c16f0ec.tar.gz
nova-fefb88877c6d6f00626df747eb0172484c16f0ec.tar.xz
nova-fefb88877c6d6f00626df747eb0172484c16f0ec.zip
extensions: Allow registering actions for create + delete
This allows an extension to add new actions for create and delete operations to an existing collection. Currently when extending an existing collection (API namespace), an extension can register brand new 'actions', and extend any preexisting API functionality, but unfortunately create and delete are special cases that don't fall under the 'actions' classification, meaning the infrastructure can't handle them unless extending an existing impl. Stubbing out the create/delete methods in the original resource with the equiv of a NotImplementedError doesn't work, since 'extend'ing requires the original implementation to exit correctly. Whitelist 'create' and 'delete' and handle them appropriately. v2: Add myself to Authors Update wsgi.action docs Add a test case v3: Actually update Authors Change-Id: I550ae93c5e200f18644042ac81656bc76dbe8955
-rw-r--r--Authors1
-rw-r--r--nova/api/openstack/wsgi.py15
-rw-r--r--nova/tests/api/openstack/test_wsgi.py29
3 files changed, 41 insertions, 4 deletions
diff --git a/Authors b/Authors
index 6a80ade28..cb08b5b67 100644
--- a/Authors
+++ b/Authors
@@ -30,6 +30,7 @@ Chris Behrens <cbehrens@codestud.com>
Christian Berendt <berendt@b1-systems.de>
Christopher MacGown <chris@pistoncloud.com>
Chuck Short <zulcss@ubuntu.com>
+Cole Robinson <crobinso@redhat.com>
Cor Cornelisse <cor@hyves.nl>
Cory Wright <corywright@gmail.com>
Dan Prince <dan.prince@rackspace.com>
diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py
index bcb64c98a..8a7a077b8 100644
--- a/nova/api/openstack/wsgi.py
+++ b/nova/api/openstack/wsgi.py
@@ -884,15 +884,19 @@ class Resource(wsgi.Application):
else:
meth = getattr(self.controller, action)
except AttributeError as ex:
- if action != 'action' or not self.wsgi_actions:
+ if (not self.wsgi_actions or
+ action not in ['action', 'create', 'delete']):
# Propagate the error
raise
else:
return meth, self.wsgi_extensions.get(action, [])
- # OK, it's an action; figure out which action...
- mtype = _MEDIA_TYPE_MAP.get(content_type)
- action_name = self.action_peek[mtype](body)
+ if action == 'action':
+ # OK, it's an action; figure out which action...
+ mtype = _MEDIA_TYPE_MAP.get(content_type)
+ action_name = self.action_peek[mtype](body)
+ else:
+ action_name = action
# Look up the action method
return (self.wsgi_actions[action_name],
@@ -908,6 +912,9 @@ def action(name):
"""Mark a function as an action.
The given name will be taken as the action key in the body.
+
+ This is also overloaded to allow extensions to provide
+ non-extending definitions of create and delete operations.
"""
def decorator(func):
diff --git a/nova/tests/api/openstack/test_wsgi.py b/nova/tests/api/openstack/test_wsgi.py
index 90d048443..3dfcb2388 100644
--- a/nova/tests/api/openstack/test_wsgi.py
+++ b/nova/tests/api/openstack/test_wsgi.py
@@ -516,6 +516,35 @@ class ResourceTest(test.TestCase):
self.assertEqual(method, controller._action_foo)
self.assertEqual(extensions, [extended._action_foo])
+ def test_get_method_action_whitelist_extensions(self):
+ class Controller(wsgi.Controller):
+ def index(self, req, pants=None):
+ return pants
+
+ class ControllerExtended(wsgi.Controller):
+ @wsgi.action('create')
+ def _create(self, req, body):
+ pass
+
+ @wsgi.action('delete')
+ def _delete(self, req, id):
+ pass
+
+ controller = Controller()
+ extended = ControllerExtended()
+ resource = wsgi.Resource(controller)
+ resource.register_actions(extended)
+
+ method, extensions = resource.get_method(None, 'create',
+ 'application/json',
+ '{"create": true}')
+ self.assertEqual(method, extended._create)
+ self.assertEqual(extensions, [])
+
+ method, extensions = resource.get_method(None, 'delete', None, None)
+ self.assertEqual(method, extended._delete)
+ self.assertEqual(extensions, [])
+
def test_pre_process_extensions_regular(self):
class Controller(object):
def index(self, req, pants=None):