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
# 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)

View File

@ -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)

View File

@ -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

View File

@ -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)