From 80413fc4b310b4cae205bf7eac326bcc18553968 Mon Sep 17 00:00:00 2001 From: Michael Basnight Date: Wed, 28 Mar 2012 15:03:43 -0500 Subject: [PATCH] Added validation to the users/databases/root calls. * Added a custom extension manager to make extension behavior normal * Added proper serializer/deserializer to the user/database/root calls * Added exception map for processing exceptions * Fixed all the returns to return wsgi.Result * Added validation method for create user/database/root --- reddwarf/common/extensions.py | 105 +++++++++++++++------------ reddwarf/common/wsgi.py | 3 +- reddwarf/extensions/mysql.py | 18 ++++- reddwarf/extensions/mysql/service.py | 59 +++++++++------ 4 files changed, 111 insertions(+), 74 deletions(-) diff --git a/reddwarf/common/extensions.py b/reddwarf/common/extensions.py index fa8d6ad91f..b96db18eec 100644 --- a/reddwarf/common/extensions.py +++ b/reddwarf/common/extensions.py @@ -16,62 +16,73 @@ # License for the specific language governing permissions and limitations # under the License. +import routes +import webob.dec +import logging + from reddwarf.openstack.common import extensions +from reddwarf.common import wsgi + +LOG = logging.getLogger(__name__) ExtensionsDescriptor = extensions.ExtensionDescriptor ResourceExtension = extensions.ResourceExtension +class ReddwarfExtensionMiddleware(extensions.ExtensionMiddleware): + + def __init__(self, application, config, ext_mgr=None): + ext_mgr = ext_mgr or ExtensionManager( + config['api_extensions_path']) + mapper = routes.Mapper() + + # extended resources + for resource_ext in ext_mgr.get_resources(): + LOG.debug(_('Extended resource: %s'), resource_ext.collection) + LOG.debug(resource_ext.deserializer) + # The only difference here is that we are using our common + # wsgi.Resource instead of the openstack common wsgi.Resource + controller_resource = wsgi.Resource(resource_ext.controller, + resource_ext.deserializer, + resource_ext.serializer) + + self._map_custom_collection_actions(resource_ext, mapper, + controller_resource) + kargs = dict(controller=controller_resource, + collection=resource_ext.collection_actions, + member=resource_ext.member_actions) + if resource_ext.parent: + kargs['parent_resource'] = resource_ext.parent + mapper.resource(resource_ext.collection, + resource_ext.collection, **kargs) + + # extended actions + action_resources = self._action_ext_resources(application, ext_mgr, + mapper) + for action in ext_mgr.get_actions(): + LOG.debug(_('Extended action: %s'), action.action_name) + resource = action_resources[action.collection] + resource.add_action(action.action_name, action.handler) + + # extended requests + req_controllers = self._request_ext_resources(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) + + super(extensions.ExtensionMiddleware, self).__init__(application) + + def factory(global_config, **local_config): """Paste factory.""" def _factory(app): extensions.DEFAULT_XMLNS = "http://docs.openstack.org/reddwarf" - ext_mgr = TenantExtensionManager( + ext_mgr = extensions.ExtensionManager( global_config.get('api_extensions_path', '')) - return extensions.ExtensionMiddleware(app, global_config, ext_mgr) + return ReddwarfExtensionMiddleware(app, global_config, ext_mgr) return _factory - - -# Not sure if this is the way we should do it. -# Might need to make openstack common more extensible for tenants -# or any random values in the routes methods (index, show, etc...) -class TenantExtensionManager(extensions.ExtensionManager): - - def __init__(self, path): - super(TenantExtensionManager, self).__init__(path) - - #TODO(hub-cap): fix openstack-common.extensions to work with tenant ids - def get_resources(self): - """Returns a list of ResourceExtension objects.""" - resources = [] - extension_resource = TenantExtensionsResource(self) - res_ext = extensions.ResourceExtension('{tenant_id}/extensions', - extension_resource, - serializer=extension_resource.serializer) - resources.append(res_ext) - for alias, ext in self.extensions.iteritems(): - try: - resources.extend(ext.get_resources()) - except AttributeError: - # NOTE(dprince): Extension aren't required to have resource - # extensions - pass - return resources - - -class TenantExtensionsResource(extensions.ExtensionsResource): - - def __init__(self, extension_manager): - super(TenantExtensionsResource, self).__init__(extension_manager) - - def index(self, req, tenant_id): - return super(TenantExtensionsResource, self).index(req) - - def show(self, req, id, tenant_id): - return super(TenantExtensionsResource, self).show(req, id) - - def delete(self, req, id, tenant_id): - return super(TenantExtensionsResource, self).delete(req, id) - - def create(self, req, tenant_id): - return super(TenantExtensionsResource, self).create(req) diff --git a/reddwarf/common/wsgi.py b/reddwarf/common/wsgi.py index df7fdda9ca..0c1e5a96f3 100644 --- a/reddwarf/common/wsgi.py +++ b/reddwarf/common/wsgi.py @@ -35,6 +35,8 @@ Server = openstack_wsgi.Server Debug = openstack_wsgi.Debug Middleware = openstack_wsgi.Middleware JSONDictSerializer = openstack_wsgi.JSONDictSerializer +XMLDictSerializer = openstack_wsgi.XMLDictSerializer +RequestDeserializer = openstack_wsgi.RequestDeserializer eventlet.patcher.monkey_patch(all=False, socket=True) @@ -60,7 +62,6 @@ class VersionedURLMap(object): app = self.urlmap.get(version, Fault(http_exc)) else: app = self.urlmap - return app(environ, start_response) diff --git a/reddwarf/extensions/mysql.py b/reddwarf/extensions/mysql.py index dd1844c44d..2b41eb5524 100644 --- a/reddwarf/extensions/mysql.py +++ b/reddwarf/extensions/mysql.py @@ -18,6 +18,7 @@ import logging from reddwarf.common import extensions +from reddwarf.common import wsgi from reddwarf.extensions.mysql import service @@ -43,23 +44,34 @@ class Mysql(extensions.ExtensionsDescriptor): def get_resources(self): resources = [] + serializer = wsgi.ReddwarfResponseSerializer( + body_serializers={'application/xml': + wsgi.ReddwarfXMLDictSerializer()}) resource = extensions.ResourceExtension( 'databases', service.SchemaController(), parent={'member_name': 'instance', - 'collection_name': '{tenant_id}/instances'}) + 'collection_name': '{tenant_id}/instances'}, + deserializer=wsgi.RequestDeserializer(), + serializer=serializer) resources.append(resource) resource = extensions.ResourceExtension( 'users', service.UserController(), parent={'member_name': 'instance', - 'collection_name': '{tenant_id}/instances'}) + 'collection_name': '{tenant_id}/instances'}, + # deserializer=extensions.ExtensionsXMLSerializer() + deserializer=wsgi.RequestDeserializer(), + serializer=serializer) resources.append(resource) resource = extensions.ResourceExtension( 'root', service.RootController(), parent={'member_name': 'instance', - 'collection_name': '{tenant_id}/instances'}) + 'collection_name': '{tenant_id}/instances'}, + deserializer=wsgi.RequestDeserializer(), + serializer=serializer) + resources.append(resource) return resources diff --git a/reddwarf/extensions/mysql/service.py b/reddwarf/extensions/mysql/service.py index d81ba59405..0d10713cd4 100644 --- a/reddwarf/extensions/mysql/service.py +++ b/reddwarf/extensions/mysql/service.py @@ -31,6 +31,29 @@ LOG = logging.getLogger(__name__) class BaseController(wsgi.Controller): """Base controller class.""" + exclude_attr = [] + exception_map = { + webob.exc.HTTPUnprocessableEntity: [ + ], + webob.exc.HTTPBadRequest: [ + exception.BadRequest, + ], + webob.exc.HTTPNotFound: [ + exception.NotFound, + ], + webob.exc.HTTPConflict: [ + ], + } + + def __init__(self): + pass + + def _extract_required_params(self, params, model_name): + params = params or {} + model_params = params.get(model_name, {}) + return utils.stringify_keys(utils.exclude(model_params, + *self.exclude_attr)) + class RootController(BaseController): """Controller for instance functionality""" @@ -44,7 +67,7 @@ class RootController(BaseController): auth_tok=req.headers["X-Auth-Token"], tenant=tenant_id) is_root_enabled = models.Root.load(context, instance_id) - return views.RootEnabledView(is_root_enabled).data() + return wsgi.Result(views.RootEnabledView(is_root_enabled).data(), 201) def create(self, req, body, tenant_id, instance_id): """ Enable the root user for the db instance """ @@ -54,7 +77,7 @@ class RootController(BaseController): auth_tok=req.headers["X-Auth-Token"], tenant=tenant_id) root = models.Root.create(context, instance_id) - return views.RootCreatedView(root).data() + return wsgi.Result(views.RootCreatedView(root).data(), 201) class UserController(BaseController): @@ -67,15 +90,12 @@ class UserController(BaseController): raise exception.BadRequest("The request contains an empty body") if not body.get('users', ''): - raise exception.BadRequest("Required element/key 'users' was not " - "specified") + raise exception.BadRequest(key='users') for user in body.get('users'): if not user.get('name'): - raise exception.BadRequest("Required attribute/key 'name' was " - "not specified") + raise exception.BadRequest(key='name') if not user.get('password'): - raise exception.BadRequest("Required attribute/key 'password' " - "was not specified") + raise exception.BadRequest(key='password') def index(self, req, tenant_id, instance_id): """Return all users.""" @@ -85,8 +105,7 @@ class UserController(BaseController): auth_tok=req.headers["X-Auth-Token"], tenant=tenant_id) users = models.Users.load(context, instance_id) - # Not exactly sure why we cant return a wsgi.Result() here - return views.UsersView(users).data() + return wsgi.Result(views.UsersView(users).data(), 201) def create(self, req, body, tenant_id, instance_id): """Creates a set of users""" @@ -96,14 +115,11 @@ class UserController(BaseController): context = rd_context.ReddwarfContext( auth_tok=req.headers["X-Auth-Token"], tenant=tenant_id) - try: - self.validate(body) - except exception.BadRequest as br: - return webob.exc.HTTPBadRequest(br) + self.validate(body) users = body['users'] model_users = models.populate_users(users) models.User.create(context, instance_id, model_users) - return webob.exc.HTTPAccepted() + return wsgi.Result(202) def delete(self, req, tenant_id, instance_id, id): LOG.info("Deleting user for instance '%s'" % instance_id) @@ -114,7 +130,7 @@ class UserController(BaseController): user = guest_models.MySQLUser() user.name = id models.User.delete(context, instance_id, user.serialize()) - return webob.exc.HTTPAccepted() + return wsgi.Result(202) class SchemaController(BaseController): @@ -140,7 +156,7 @@ class SchemaController(BaseController): tenant=tenant_id) schemas = models.Schemas.load(context, instance_id) # Not exactly sure why we cant return a wsgi.Result() here - return views.SchemasView(schemas).data() + return wsgi.Result(views.SchemasView(schemas).data(), 201) def create(self, req, body, tenant_id, instance_id): """Creates a set of schemas""" @@ -150,14 +166,11 @@ class SchemaController(BaseController): context = rd_context.ReddwarfContext( auth_tok=req.headers["X-Auth-Token"], tenant=tenant_id) - try: - self.validate(body) - except exception.BadRequest as br: - return webob.exc.HTTPBadRequest(br) + self.validate(body) schemas = body['databases'] model_schemas = models.populate_databases(schemas) models.Schema.create(context, instance_id, model_schemas) - return webob.exc.HTTPAccepted() + return wsgi.Result(202) def delete(self, req, tenant_id, instance_id, id): LOG.info("Deleting schema for instance '%s'" % instance_id) @@ -168,4 +181,4 @@ class SchemaController(BaseController): schema = guest_models.MySQLDatabase() schema.name = id models.Schema.delete(context, instance_id, schema.serialize()) - return webob.exc.HTTPAccepted() + return wsgi.Result(202)