summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Prince <dan.prince@rackspace.com>2011-05-12 10:55:04 -0400
committerDan Prince <dan.prince@rackspace.com>2011-05-12 10:55:04 -0400
commit22c33d80ce040f09c9bcd7584cf1165cf769e192 (patch)
treea4df237208211a70b021a95768ea05bd97fa57e9
parent5f2bfe56cf12d8f45ae24a5c9dd0c99e6c4d0310 (diff)
downloadnova-22c33d80ce040f09c9bcd7584cf1165cf769e192.tar.gz
nova-22c33d80ce040f09c9bcd7584cf1165cf769e192.tar.xz
nova-22c33d80ce040f09c9bcd7584cf1165cf769e192.zip
Initial work on request extensions.
-rw-r--r--nova/api/openstack/extensions.py78
-rw-r--r--nova/tests/api/openstack/extensions/foxinsocks.py3
-rw-r--r--nova/tests/api/openstack/test_extensions.py47
3 files changed, 127 insertions, 1 deletions
diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py
index 7ea7afef6..e6dd228ec 100644
--- a/nova/api/openstack/extensions.py
+++ b/nova/api/openstack/extensions.py
@@ -165,6 +165,34 @@ class ResponseExtensionController(common.OpenstackController):
return res
+class RequestExtensionController(common.OpenstackController):
+
+ def __init__(self, application):
+ self.application = application
+ self.handlers = []
+
+ def add_handler(self, handler):
+ self.handlers.append(handler)
+
+ def process(self, req, *args, **kwargs):
+ res = req.get_response(self.application)
+ content_type = req.best_match_content_type()
+ # currently response handlers are un-ordered
+ for handler in self.handlers:
+ res = handler(req, res)
+ try:
+ body = res.body
+ headers = res.headers
+ except AttributeError:
+ default_xmlns = None
+ body = self._serialize(res, content_type, default_xmlns)
+ headers = {"Content-Type": content_type}
+ res = webob.Response()
+ res.body = body
+ res.headers = headers
+ return res
+
+
class ExtensionController(common.OpenstackController):
def __init__(self, extension_manager):
@@ -245,6 +273,25 @@ class ExtensionMiddleware(wsgi.Middleware):
return response_ext_controllers
+ def _request_ext_controllers(self, application, ext_mgr, mapper):
+ """Returns a dict of RequestExtensionController-s by collection."""
+ request_ext_controllers = {}
+ for req_ext in ext_mgr.get_request_extensions():
+ if not req_ext.key in request_ext_controllers.keys():
+ controller = RequestExtensionController(application)
+ mapper.connect(req_ext.url_route + '.:(format)',
+ action='process',
+ controller=controller,
+ conditions=req_ext.conditions)
+
+ mapper.connect(req_ext.url_route,
+ action='process',
+ controller=controller,
+ conditions=req_ext.conditions)
+ request_ext_controllers[req_ext.key] = controller
+
+ return request_ext_controllers
+
def __init__(self, application, ext_mgr=None):
if ext_mgr is None:
@@ -279,6 +326,14 @@ class ExtensionMiddleware(wsgi.Middleware):
controller = resp_controllers[response_ext.key]
controller.add_handler(response_ext.handler)
+ # extended requests
+ req_controllers = self._request_ext_controllers(application, ext_mgr,
+ mapper)
+ for request_ext in ext_mgr.get_request_extensions():
+ LOG.debug(_('Extended request: %s'), request_ext.key)
+ controller = req_controllers[request_ext.key]
+ controller.add_handler(request_ext.handler)
+
self._router = routes.middleware.RoutesMiddleware(self._dispatch,
mapper)
@@ -359,6 +414,18 @@ class ExtensionManager(object):
pass
return response_exts
+ def get_request_extensions(self):
+ """Returns a list of RequestExtension objects."""
+ request_exts = []
+ for alias, ext in self.extensions.iteritems():
+ try:
+ request_exts.extend(ext.get_request_extensions())
+ except AttributeError:
+ # NOTE(dprince): Extension aren't required to have request
+ # extensions
+ pass
+ return request_exts
+
def _check_extension(self, extension):
"""Checks for required methods in extension objects."""
try:
@@ -431,6 +498,17 @@ class ResponseExtension(object):
self.key = "%s-%s" % (method, url_route)
+class RequestExtension(object):
+ """Provide a way to handle custom request data that is sent to core
+ nova OpenStack API controllers.
+ """
+ def __init__(self, method, url_route, handler):
+ self.url_route = url_route
+ self.handler = handler
+ self.conditions = dict(method=[method])
+ self.key = "%s-%s" % (method, url_route)
+
+
class ActionExtension(object):
"""Add custom actions to core nova OpenStack API controllers."""
diff --git a/nova/tests/api/openstack/extensions/foxinsocks.py b/nova/tests/api/openstack/extensions/foxinsocks.py
index 0860b51ac..7699ffb56 100644
--- a/nova/tests/api/openstack/extensions/foxinsocks.py
+++ b/nova/tests/api/openstack/extensions/foxinsocks.py
@@ -89,6 +89,9 @@ class Foxinsocks(object):
response_exts.append(resp_ext2)
return response_exts
+ def get_request_extensions(self):
+ return []
+
def _add_tweedle(self, input_dict, req, id):
return "Tweedle Beetle Added."
diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py
index 481d34ed1..7fadb5b69 100644
--- a/nova/tests/api/openstack/test_extensions.py
+++ b/nova/tests/api/openstack/test_extensions.py
@@ -45,10 +45,12 @@ class StubController(nova.wsgi.Controller):
class StubExtensionManager(object):
- def __init__(self, resource_ext=None, action_ext=None, response_ext=None):
+ def __init__(self, resource_ext=None, action_ext=None, response_ext=None,
+ request_ext=None):
self.resource_ext = resource_ext
self.action_ext = action_ext
self.response_ext = response_ext
+ self.request_ext = request_ext
def get_name(self):
return "Tweedle Beetle Extension"
@@ -77,6 +79,12 @@ class StubExtensionManager(object):
response_exts.append(self.response_ext)
return response_exts
+ def get_request_extensions(self):
+ request_extensions = []
+ if self.request_ext:
+ request_extensions.append(self.request_ext)
+ return request_extensions
+
class ExtensionControllerTest(unittest.TestCase):
@@ -234,3 +242,40 @@ class ResponseExtensionTest(unittest.TestCase):
response_data = json.loads(response.body)
self.assertEqual(test_resp, response_data['flavor']['googoose'])
self.assertEqual("Pig Bands!", response_data['big_bands'])
+
+
+class RequestExtensionTest(unittest.TestCase):
+
+ def setUp(self):
+ super(RequestExtensionTest, self).setUp()
+ self.stubs = stubout.StubOutForTesting()
+ fakes.FakeAuthManager.reset_fake_data()
+ fakes.FakeAuthDatabase.data = {}
+ fakes.stub_out_auth(self.stubs)
+ self.context = context.get_admin_context()
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ super(RequestExtensionTest, self).tearDown()
+
+ def test_post_request_extension_with_stub_mgr(self):
+
+ def _req_handler(req, res):
+ # only handle JSON responses
+ data = json.loads(res.body)
+ data['flavor']['googoose'] = req.GET.get('test_param')
+ return data
+
+ resp_ext = extensions.RequestExtension('GET',
+ '/v1.1/flavors/:(id)',
+ _req_handler)
+
+ manager = StubExtensionManager(None, None, None, resp_ext)
+ app = fakes.wsgi_app()
+ ext_midware = extensions.ExtensionMiddleware(app, manager)
+ request = webob.Request.blank("/v1.1/flavors/1?test_param=foo")
+ request.environ['api.version'] = '1.1'
+ response = request.get_response(ext_midware)
+ self.assertEqual(200, response.status_int)
+ response_data = json.loads(response.body)
+ self.assertEqual('foo', response_data['flavor']['googoose'])