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
This commit is contained in:
Michael Basnight 2012-03-28 15:03:43 -05:00
parent f635542677
commit 80413fc4b3
4 changed files with 111 additions and 74 deletions

View File

@ -16,62 +16,73 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import routes
import webob.dec
import logging
from reddwarf.openstack.common import extensions from reddwarf.openstack.common import extensions
from reddwarf.common import wsgi
LOG = logging.getLogger(__name__)
ExtensionsDescriptor = extensions.ExtensionDescriptor ExtensionsDescriptor = extensions.ExtensionDescriptor
ResourceExtension = extensions.ResourceExtension 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): def factory(global_config, **local_config):
"""Paste factory.""" """Paste factory."""
def _factory(app): def _factory(app):
extensions.DEFAULT_XMLNS = "http://docs.openstack.org/reddwarf" extensions.DEFAULT_XMLNS = "http://docs.openstack.org/reddwarf"
ext_mgr = TenantExtensionManager( ext_mgr = extensions.ExtensionManager(
global_config.get('api_extensions_path', '')) global_config.get('api_extensions_path', ''))
return extensions.ExtensionMiddleware(app, global_config, ext_mgr) return ReddwarfExtensionMiddleware(app, global_config, ext_mgr)
return _factory 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)

View File

@ -35,6 +35,8 @@ Server = openstack_wsgi.Server
Debug = openstack_wsgi.Debug Debug = openstack_wsgi.Debug
Middleware = openstack_wsgi.Middleware Middleware = openstack_wsgi.Middleware
JSONDictSerializer = openstack_wsgi.JSONDictSerializer JSONDictSerializer = openstack_wsgi.JSONDictSerializer
XMLDictSerializer = openstack_wsgi.XMLDictSerializer
RequestDeserializer = openstack_wsgi.RequestDeserializer
eventlet.patcher.monkey_patch(all=False, socket=True) eventlet.patcher.monkey_patch(all=False, socket=True)
@ -60,7 +62,6 @@ class VersionedURLMap(object):
app = self.urlmap.get(version, Fault(http_exc)) app = self.urlmap.get(version, Fault(http_exc))
else: else:
app = self.urlmap app = self.urlmap
return app(environ, start_response) return app(environ, start_response)

View File

@ -18,6 +18,7 @@
import logging import logging
from reddwarf.common import extensions from reddwarf.common import extensions
from reddwarf.common import wsgi
from reddwarf.extensions.mysql import service from reddwarf.extensions.mysql import service
@ -43,23 +44,34 @@ class Mysql(extensions.ExtensionsDescriptor):
def get_resources(self): def get_resources(self):
resources = [] resources = []
serializer = wsgi.ReddwarfResponseSerializer(
body_serializers={'application/xml':
wsgi.ReddwarfXMLDictSerializer()})
resource = extensions.ResourceExtension( resource = extensions.ResourceExtension(
'databases', 'databases',
service.SchemaController(), service.SchemaController(),
parent={'member_name': 'instance', parent={'member_name': 'instance',
'collection_name': '{tenant_id}/instances'}) 'collection_name': '{tenant_id}/instances'},
deserializer=wsgi.RequestDeserializer(),
serializer=serializer)
resources.append(resource) resources.append(resource)
resource = extensions.ResourceExtension( resource = extensions.ResourceExtension(
'users', 'users',
service.UserController(), service.UserController(),
parent={'member_name': 'instance', 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) resources.append(resource)
resource = extensions.ResourceExtension( resource = extensions.ResourceExtension(
'root', 'root',
service.RootController(), service.RootController(),
parent={'member_name': 'instance', parent={'member_name': 'instance',
'collection_name': '{tenant_id}/instances'}) 'collection_name': '{tenant_id}/instances'},
deserializer=wsgi.RequestDeserializer(),
serializer=serializer)
resources.append(resource) resources.append(resource)
return resources return resources

View File

@ -31,6 +31,29 @@ LOG = logging.getLogger(__name__)
class BaseController(wsgi.Controller): class BaseController(wsgi.Controller):
"""Base controller class.""" """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): class RootController(BaseController):
"""Controller for instance functionality""" """Controller for instance functionality"""
@ -44,7 +67,7 @@ class RootController(BaseController):
auth_tok=req.headers["X-Auth-Token"], auth_tok=req.headers["X-Auth-Token"],
tenant=tenant_id) tenant=tenant_id)
is_root_enabled = models.Root.load(context, instance_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): def create(self, req, body, tenant_id, instance_id):
""" Enable the root user for the db instance """ """ Enable the root user for the db instance """
@ -54,7 +77,7 @@ class RootController(BaseController):
auth_tok=req.headers["X-Auth-Token"], auth_tok=req.headers["X-Auth-Token"],
tenant=tenant_id) tenant=tenant_id)
root = models.Root.create(context, instance_id) root = models.Root.create(context, instance_id)
return views.RootCreatedView(root).data() return wsgi.Result(views.RootCreatedView(root).data(), 201)
class UserController(BaseController): class UserController(BaseController):
@ -67,15 +90,12 @@ class UserController(BaseController):
raise exception.BadRequest("The request contains an empty body") raise exception.BadRequest("The request contains an empty body")
if not body.get('users', ''): if not body.get('users', ''):
raise exception.BadRequest("Required element/key 'users' was not " raise exception.BadRequest(key='users')
"specified")
for user in body.get('users'): for user in body.get('users'):
if not user.get('name'): if not user.get('name'):
raise exception.BadRequest("Required attribute/key 'name' was " raise exception.BadRequest(key='name')
"not specified")
if not user.get('password'): if not user.get('password'):
raise exception.BadRequest("Required attribute/key 'password' " raise exception.BadRequest(key='password')
"was not specified")
def index(self, req, tenant_id, instance_id): def index(self, req, tenant_id, instance_id):
"""Return all users.""" """Return all users."""
@ -85,8 +105,7 @@ class UserController(BaseController):
auth_tok=req.headers["X-Auth-Token"], auth_tok=req.headers["X-Auth-Token"],
tenant=tenant_id) tenant=tenant_id)
users = models.Users.load(context, instance_id) users = models.Users.load(context, instance_id)
# Not exactly sure why we cant return a wsgi.Result() here return wsgi.Result(views.UsersView(users).data(), 201)
return views.UsersView(users).data()
def create(self, req, body, tenant_id, instance_id): def create(self, req, body, tenant_id, instance_id):
"""Creates a set of users""" """Creates a set of users"""
@ -96,14 +115,11 @@ class UserController(BaseController):
context = rd_context.ReddwarfContext( context = rd_context.ReddwarfContext(
auth_tok=req.headers["X-Auth-Token"], auth_tok=req.headers["X-Auth-Token"],
tenant=tenant_id) tenant=tenant_id)
try: self.validate(body)
self.validate(body)
except exception.BadRequest as br:
return webob.exc.HTTPBadRequest(br)
users = body['users'] users = body['users']
model_users = models.populate_users(users) model_users = models.populate_users(users)
models.User.create(context, instance_id, model_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): def delete(self, req, tenant_id, instance_id, id):
LOG.info("Deleting user for instance '%s'" % instance_id) LOG.info("Deleting user for instance '%s'" % instance_id)
@ -114,7 +130,7 @@ class UserController(BaseController):
user = guest_models.MySQLUser() user = guest_models.MySQLUser()
user.name = id user.name = id
models.User.delete(context, instance_id, user.serialize()) models.User.delete(context, instance_id, user.serialize())
return webob.exc.HTTPAccepted() return wsgi.Result(202)
class SchemaController(BaseController): class SchemaController(BaseController):
@ -140,7 +156,7 @@ class SchemaController(BaseController):
tenant=tenant_id) tenant=tenant_id)
schemas = models.Schemas.load(context, instance_id) schemas = models.Schemas.load(context, instance_id)
# Not exactly sure why we cant return a wsgi.Result() here # 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): def create(self, req, body, tenant_id, instance_id):
"""Creates a set of schemas""" """Creates a set of schemas"""
@ -150,14 +166,11 @@ class SchemaController(BaseController):
context = rd_context.ReddwarfContext( context = rd_context.ReddwarfContext(
auth_tok=req.headers["X-Auth-Token"], auth_tok=req.headers["X-Auth-Token"],
tenant=tenant_id) tenant=tenant_id)
try: self.validate(body)
self.validate(body)
except exception.BadRequest as br:
return webob.exc.HTTPBadRequest(br)
schemas = body['databases'] schemas = body['databases']
model_schemas = models.populate_databases(schemas) model_schemas = models.populate_databases(schemas)
models.Schema.create(context, instance_id, model_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): def delete(self, req, tenant_id, instance_id, id):
LOG.info("Deleting schema for instance '%s'" % instance_id) LOG.info("Deleting schema for instance '%s'" % instance_id)
@ -168,4 +181,4 @@ class SchemaController(BaseController):
schema = guest_models.MySQLDatabase() schema = guest_models.MySQLDatabase()
schema.name = id schema.name = id
models.Schema.delete(context, instance_id, schema.serialize()) models.Schema.delete(context, instance_id, schema.serialize())
return webob.exc.HTTPAccepted() return wsgi.Result(202)