From f55dbc2f599ed56fb59c7f7a94cd81d3fd82c8dd Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 18:01:29 -0500 Subject: Rework how routing is done in ec2 endpoint. --- nova/api/ec2/__init__.py | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index aa3bfaeb4..a5810479e 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -29,6 +29,7 @@ import webob.exc from nova import context from nova import exception from nova import flags +from nova import utils from nova import wsgi from nova.api.ec2 import apirequest from nova.api.ec2 import admin @@ -157,6 +158,31 @@ class Authenticate(wsgi.Middleware): return self.application +class Requestify(wsgi.Middleware): + + def __init__(self, app, controller_name): + super(Requestify, self).__init__(app) + self.controller = utils.import_class(controller_name)() + + @webob.dec.wsgify + def __call__(self, req): + non_args = ['Action', 'Signature', 'AWSAccessKeyId', 'SignatureMethod', + 'SignatureVersion', 'Version', 'Timestamp'] + args = dict(req.params) + try: + # Raise KeyError if omitted + action = req.params['Action'] + for non_arg in non_args: + # Remove, but raise KeyError if omitted + args.pop(non_arg) + except: + raise webob.exc.HTTPBadRequest() + api_request = apirequest.APIRequest(self.controller, action) + req.environ['ec2.request'] = api_request + req.environ['ec2.action_args'] = args + return self.application + + class Router(wsgi.Middleware): """Add ec2.'controller', .'action', and .'action_args' to WSGI environ.""" @@ -256,10 +282,9 @@ class Authorizer(wsgi.Middleware): @webob.dec.wsgify def __call__(self, req): context = req.environ['ec2.context'] - controller_name = req.environ['ec2.controller'].__class__.__name__ - action = req.environ['ec2.action'] - allowed_roles = self.action_roles[controller_name].get(action, - ['none']) + controller = req.environ['ec2.request'].controller.__class__.__name__ + action = req.environ['ec2.request'].action + allowed_roles = self.action_roles[controller].get(action, ['none']) if self._matches_any_role(context, allowed_roles): return self.application else: @@ -289,11 +314,8 @@ class Executor(wsgi.Application): @webob.dec.wsgify def __call__(self, req): context = req.environ['ec2.context'] - controller = req.environ['ec2.controller'] - action = req.environ['ec2.action'] args = req.environ['ec2.action_args'] - - api_request = apirequest.APIRequest(controller, action) + api_request = req.environ['ec2.request'] result = None try: result = api_request.send(context, **args) @@ -369,3 +391,8 @@ def executor_factory(global_args, **local_args): def versions_factory(global_args, **local_args): return Versions() + +def requestify_factory(global_args, **local_args): + def requestifier(app): + return Requestify(app, local_args['controller']) + return requestifier -- cgit From 2491c2484f025cb3f061fcc6a5c6915006feb47b Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 18:16:16 -0500 Subject: Make paste the default api pattern. * get rid of the --use_lockout flag since it will be specified in paste config (Example line is commented out in etc/nova-api.conf, factory is in place) * remove old nova-api binary and promote nova-api-paste * change how we store ec2 parameters to bin the the ApiRequest * get rid of Router, since paste.urlmap is equally effective (Requestify now gets passed the name of the controller requests are to.) --- nova/api/ec2/__init__.py | 74 +++++----------------------------------------- nova/api/ec2/apirequest.py | 7 +++-- 2 files changed, 11 insertions(+), 70 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index a5810479e..2f8f4f272 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -21,7 +21,6 @@ Starting point for routing EC2 requests. """ import logging -import routes import webob import webob.dec import webob.exc @@ -32,8 +31,6 @@ from nova import flags from nova import utils from nova import wsgi from nova.api.ec2 import apirequest -from nova.api.ec2 import admin -from nova.api.ec2 import cloud from nova.auth import manager @@ -41,8 +38,6 @@ FLAGS = flags.FLAGS flags.DEFINE_boolean('use_forwarded_for', False, 'Treat X-Forwarded-For as the canonical remote address. ' 'Only enable this if you have a sanitizing proxy.') -flags.DEFINE_boolean('use_lockout', False, - 'Whether or not to use lockout middleware.') flags.DEFINE_integer('lockout_attempts', 5, 'Number of failed auths before lockout.') flags.DEFINE_integer('lockout_minutes', 15, @@ -57,15 +52,6 @@ _log = logging.getLogger("api") _log.setLevel(logging.DEBUG) -class API(wsgi.Middleware): - """Routing for all EC2 API requests.""" - - def __init__(self): - self.application = Authenticate(Router(Authorizer(Executor()))) - if FLAGS.use_lockout: - self.application = Lockout(self.application) - - class Lockout(wsgi.Middleware): """Lockout for x minutes on y failed auths in a z minute period. @@ -177,55 +163,12 @@ class Requestify(wsgi.Middleware): args.pop(non_arg) except: raise webob.exc.HTTPBadRequest() - api_request = apirequest.APIRequest(self.controller, action) + api_request = apirequest.APIRequest(self.controller, action, args) req.environ['ec2.request'] = api_request req.environ['ec2.action_args'] = args return self.application -class Router(wsgi.Middleware): - - """Add ec2.'controller', .'action', and .'action_args' to WSGI environ.""" - - def __init__(self, application): - super(Router, self).__init__(application) - self.map = routes.Mapper() - self.map.connect("/{controller_name}/") - self.controllers = dict(Cloud=cloud.CloudController(), - Admin=admin.AdminController()) - - @webob.dec.wsgify - def __call__(self, req): - # Obtain the appropriate controller and action for this request. - try: - match = self.map.match(req.path_info) - controller_name = match['controller_name'] - controller = self.controllers[controller_name] - except: - raise webob.exc.HTTPNotFound() - non_args = ['Action', 'Signature', 'AWSAccessKeyId', 'SignatureMethod', - 'SignatureVersion', 'Version', 'Timestamp'] - args = dict(req.params) - try: - # Raise KeyError if omitted - action = req.params['Action'] - for non_arg in non_args: - # Remove, but raise KeyError if omitted - args.pop(non_arg) - except: - raise webob.exc.HTTPBadRequest() - - _log.debug(_('action: %s') % action) - for key, value in args.items(): - _log.debug(_('arg: %s\t\tval: %s') % (key, value)) - - # Success! - req.environ['ec2.controller'] = controller - req.environ['ec2.action'] = action - req.environ['ec2.action_args'] = args - return self.application - - class Authorizer(wsgi.Middleware): """Authorize an EC2 API request. @@ -314,13 +257,11 @@ class Executor(wsgi.Application): @webob.dec.wsgify def __call__(self, req): context = req.environ['ec2.context'] - args = req.environ['ec2.action_args'] api_request = req.environ['ec2.request'] result = None try: - result = api_request.send(context, **args) + result = api_request.invoke(context) except exception.ApiError as ex: - if ex.code: return self._error(req, ex.code, ex.message) else: @@ -373,12 +314,6 @@ def authenticate_factory(global_args, **local_args): return authenticator -def router_factory(global_args, **local_args): - def router(app): - return Router(app) - return router - - def authorizer_factory(global_args, **local_args): def authorizer(app): return Authorizer(app) @@ -396,3 +331,8 @@ def requestify_factory(global_args, **local_args): def requestifier(app): return Requestify(app, local_args['controller']) return requestifier + +def lockout_factory(global_args, **local_args): + def locksmith(app): + return Lockout(app) + return locksmith diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py index a90fbeb0c..8a1dd3978 100644 --- a/nova/api/ec2/apirequest.py +++ b/nova/api/ec2/apirequest.py @@ -83,11 +83,12 @@ def _try_convert(value): class APIRequest(object): - def __init__(self, controller, action): + def __init__(self, controller, action, args): self.controller = controller self.action = action + self.args = args - def send(self, context, **kwargs): + def invoke(self, context): try: method = getattr(self.controller, _camelcase_to_underscore(self.action)) @@ -100,7 +101,7 @@ class APIRequest(object): raise Exception(_error) args = {} - for key, value in kwargs.items(): + for key, value in self.args.items(): parts = key.split(".") key = _camelcase_to_underscore(parts[0]) if isinstance(value, str) or isinstance(value, unicode): -- cgit From 6dc2e665b5b6f690882e6029984a11dc7063b437 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 18:33:17 -0500 Subject: remove unused nova/api/__init__.py --- nova/api/__init__.py | 111 --------------------------------------------------- 1 file changed, 111 deletions(-) delete mode 100644 nova/api/__init__.py (limited to 'nova/api') diff --git a/nova/api/__init__.py b/nova/api/__init__.py deleted file mode 100644 index 26fed847b..000000000 --- a/nova/api/__init__.py +++ /dev/null @@ -1,111 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -""" -Root WSGI middleware for all API controllers. - -**Related Flags** - -:osapi_subdomain: subdomain running the OpenStack API (default: api) -:ec2api_subdomain: subdomain running the EC2 API (default: ec2) - -""" -import logging - -import routes -import webob.dec - -from nova import flags -from nova import wsgi -from nova.api import ec2 -from nova.api import openstack -from nova.api.ec2 import metadatarequesthandler - - -flags.DEFINE_string('osapi_subdomain', 'api', - 'subdomain running the OpenStack API') -flags.DEFINE_string('ec2api_subdomain', 'ec2', - 'subdomain running the EC2 API') - -FLAGS = flags.FLAGS - - -class API(wsgi.Router): - """Routes top-level requests to the appropriate controller.""" - - def __init__(self, default_api): - osapi_subdomain = {'sub_domain': [FLAGS.osapi_subdomain]} - ec2api_subdomain = {'sub_domain': [FLAGS.ec2api_subdomain]} - if default_api == 'os': - osapi_subdomain = {} - elif default_api == 'ec2': - ec2api_subdomain = {} - mapper = routes.Mapper() - mapper.sub_domains = True - - mapper.connect("/", controller=self.osapi_versions, - conditions=osapi_subdomain) - mapper.connect("/v1.0/{path_info:.*}", controller=openstack.API(), - conditions=osapi_subdomain) - - mapper.connect("/", controller=self.ec2api_versions, - conditions=ec2api_subdomain) - mapper.connect("/services/{path_info:.*}", controller=ec2.API(), - conditions=ec2api_subdomain) - mrh = metadatarequesthandler.MetadataRequestHandler() - for s in ['/latest', - '/2009-04-04', - '/2008-09-01', - '/2008-02-01', - '/2007-12-15', - '/2007-10-10', - '/2007-08-29', - '/2007-03-01', - '/2007-01-19', - '/1.0']: - mapper.connect('%s/{path_info:.*}' % s, controller=mrh, - conditions=ec2api_subdomain) - - super(API, self).__init__(mapper) - - @webob.dec.wsgify - def osapi_versions(self, req): - """Respond to a request for all OpenStack API versions.""" - response = { - "versions": [ - dict(status="CURRENT", id="v1.0")]} - metadata = { - "application/xml": { - "attributes": dict(version=["status", "id"])}} - return wsgi.Serializer(req.environ, metadata).to_content_type(response) - - @webob.dec.wsgify - def ec2api_versions(self, req): - """Respond to a request for all EC2 versions.""" - # available api versions - versions = [ - '1.0', - '2007-01-19', - '2007-03-01', - '2007-08-29', - '2007-10-10', - '2007-12-15', - '2008-02-01', - '2008-09-01', - '2009-04-04', - ] - return ''.join('%s\n' % v for v in versions) -- cgit From 406c8cdf027b13636ab3c8fa609aabe929057d6f Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 18:46:12 -0500 Subject: Remove flags and unused API class from openstack api, since such things are specified in paste config now. --- nova/api/openstack/__init__.py | 29 ----------------------------- 1 file changed, 29 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index a1430caed..c5de03a09 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -43,40 +43,11 @@ from nova.api.openstack import sharedipgroups FLAGS = flags.FLAGS -flags.DEFINE_string('os_api_auth', - 'nova.api.openstack.auth.AuthMiddleware', - 'The auth mechanism to use for the OpenStack API implemenation') - -flags.DEFINE_string('os_api_ratelimiting', - 'nova.api.openstack.ratelimiting.RateLimitingMiddleware', - 'Default ratelimiting implementation for the Openstack API') - flags.DEFINE_bool('allow_admin_api', False, 'When True, this API service will accept admin operations.') -class API(wsgi.Middleware): - """WSGI entry point for all OpenStack API requests.""" - - def __init__(self): - auth_middleware = utils.import_class(FLAGS.os_api_auth) - ratelimiting_middleware = \ - utils.import_class(FLAGS.os_api_ratelimiting) - app = auth_middleware(ratelimiting_middleware(APIRouter())) - super(API, self).__init__(app) - - @webob.dec.wsgify - def __call__(self, req): - try: - return req.get_response(self.application) - except Exception as ex: - logging.warn(_("Caught error: %s") % str(ex)) - logging.error(traceback.format_exc()) - exc = webob.exc.HTTPInternalServerError(explanation=str(ex)) - return faults.Fault(exc) - - class APIRouter(wsgi.Router): """ Routes requests on the OpenStack API to the appropriate controller -- cgit From 8926f33d4da9def15dde68a5a15fd9477aee6452 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 4 Jan 2011 19:02:56 -0500 Subject: pep8 fixes. --- nova/api/ec2/__init__.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'nova/api') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 2f8f4f272..dfa919e07 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -327,11 +327,13 @@ def executor_factory(global_args, **local_args): def versions_factory(global_args, **local_args): return Versions() + def requestify_factory(global_args, **local_args): def requestifier(app): return Requestify(app, local_args['controller']) return requestifier + def lockout_factory(global_args, **local_args): def locksmith(app): return Lockout(app) -- cgit From bccec6c8bac90517a972a5eb8bb91a82b3a13065 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 6 Jan 2011 02:08:01 -0500 Subject: Fix openstack api tests and add a FaultWrapper to turn exceptions to faults. --- nova/api/openstack/__init__.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index c5de03a09..21b9b1d7d 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -20,8 +20,6 @@ WSGI middleware for OpenStack API controllers. """ -import time - import logging import routes import traceback @@ -29,15 +27,12 @@ import webob.dec import webob.exc import webob -from nova import context from nova import flags -from nova import utils from nova import wsgi from nova.api.openstack import faults from nova.api.openstack import backup_schedules from nova.api.openstack import flavors from nova.api.openstack import images -from nova.api.openstack import ratelimiting from nova.api.openstack import servers from nova.api.openstack import sharedipgroups @@ -48,6 +43,19 @@ flags.DEFINE_bool('allow_admin_api', 'When True, this API service will accept admin operations.') +class FaultWrapper(wsgi.Middleware): + """Calls down the middleware stack, making exceptions into faults.""" + + @webob.dec.wsgify + def __call__(self, req): + try: + return req.get_response(self.application) + except Exception as ex: + logging.warn(_("Caught error: %s") % str(ex)) + logging.error(traceback.format_exc()) + exc = webob.exc.HTTPInternalServerError(explanation=str(ex)) + return faults.Fault(exc) + class APIRouter(wsgi.Router): """ Routes requests on the OpenStack API to the appropriate controller @@ -105,3 +113,9 @@ def router_factory(global_cof, **local_conf): def versions_factory(global_conf, **local_conf): return Versions() + + +def fault_wrapper_factory(global_conf, **local_conf): + def fwrap(app): + return FaultWrapper(app) + return fwrap -- cgit From 787631f7b3d882b6743ed52dc948301fdbca471e Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 6 Jan 2011 13:17:28 -0500 Subject: Add blank __init__ file for fixing importability. The stale .pyc masked this error locally. --- nova/api/__init__.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 nova/api/__init__.py (limited to 'nova/api') diff --git a/nova/api/__init__.py b/nova/api/__init__.py new file mode 100644 index 000000000..0fedbbfad --- /dev/null +++ b/nova/api/__init__.py @@ -0,0 +1,19 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""No-op __init__ for directory full of api goodies.""" -- cgit From 8003dd2f5b027491f4e171f92ccd2a1cf2946315 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 6 Jan 2011 14:22:11 -0500 Subject: Pep8 --- nova/api/openstack/__init__.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 21b9b1d7d..452290505 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -56,6 +56,7 @@ class FaultWrapper(wsgi.Middleware): exc = webob.exc.HTTPInternalServerError(explanation=str(ex)) return faults.Fault(exc) + class APIRouter(wsgi.Router): """ Routes requests on the OpenStack API to the appropriate controller -- cgit