summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/direct.py12
-rw-r--r--nova/api/openstack/common.py7
-rw-r--r--nova/api/openstack/consoles.py3
-rw-r--r--nova/api/openstack/contrib/volumes.py23
-rw-r--r--nova/api/openstack/extensions.py97
-rw-r--r--nova/api/openstack/faults.py39
-rw-r--r--nova/api/openstack/image_metadata.py1
-rw-r--r--nova/api/openstack/images.py11
-rw-r--r--nova/api/openstack/versions.py44
-rw-r--r--nova/api/openstack/wsgi.py23
10 files changed, 141 insertions, 119 deletions
diff --git a/nova/api/direct.py b/nova/api/direct.py
index 8ceae299c..5e6c7c882 100644
--- a/nova/api/direct.py
+++ b/nova/api/direct.py
@@ -42,6 +42,7 @@ from nova import exception
from nova import flags
from nova import utils
from nova import wsgi
+import nova.api.openstack.wsgi
# Global storage for registering modules.
@@ -251,7 +252,7 @@ class Reflection(object):
return self._methods[method]
-class ServiceWrapper(wsgi.Controller):
+class ServiceWrapper(object):
"""Wrapper to dynamically povide a WSGI controller for arbitrary objects.
With lightweight introspection allows public methods on the object to
@@ -265,7 +266,7 @@ class ServiceWrapper(wsgi.Controller):
def __init__(self, service_handle):
self.service_handle = service_handle
- @webob.dec.wsgify(RequestClass=wsgi.Request)
+ @webob.dec.wsgify(RequestClass=nova.api.openstack.wsgi.Request)
def __call__(self, req):
arg_dict = req.environ['wsgiorg.routing_args'][1]
action = arg_dict['action']
@@ -289,8 +290,11 @@ class ServiceWrapper(wsgi.Controller):
try:
content_type = req.best_match_content_type()
- default_xmlns = self.get_default_xmlns(req)
- return self._serialize(result, content_type, default_xmlns)
+ serializer = {
+ 'application/xml': nova.api.openstack.wsgi.XMLSerializer(),
+ 'application/json': nova.api.openstack.wsgi.JSONSerializer(),
+ }[content_type]
+ return serializer.serialize(result)
except:
raise exception.Error("returned non-serializable type: %s"
% result)
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index 32cd689ca..bb1a96812 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -23,7 +23,6 @@ import webob
from nova import exception
from nova import flags
from nova import log as logging
-from nova import wsgi
LOG = logging.getLogger('nova.api.openstack.common')
@@ -146,9 +145,3 @@ def get_id_from_href(href):
except:
LOG.debug(_("Error extracting id from href: %s") % href)
raise webob.exc.HTTPBadRequest(_('could not parse id from href'))
-
-
-class OpenstackController(wsgi.Controller):
- def get_default_xmlns(self, req):
- # Use V10 by default
- return XML_NS_V10
diff --git a/nova/api/openstack/consoles.py b/nova/api/openstack/consoles.py
index 36d570803..97304affe 100644
--- a/nova/api/openstack/consoles.py
+++ b/nova/api/openstack/consoles.py
@@ -44,11 +44,10 @@ def _translate_detail_keys(cons):
class Controller(object):
- """The Consoles Controller for the Openstack API"""
+ """The Consoles controller for the Openstack API"""
def __init__(self):
self.console_api = console.API()
- super(Controller, self).__init__()
def index(self, req, server_id):
"""Returns a list of consoles for this instance"""
diff --git a/nova/api/openstack/contrib/volumes.py b/nova/api/openstack/contrib/volumes.py
index 18de2ec71..b00790b7f 100644
--- a/nova/api/openstack/contrib/volumes.py
+++ b/nova/api/openstack/contrib/volumes.py
@@ -22,7 +22,6 @@ from nova import exception
from nova import flags
from nova import log as logging
from nova import volume
-from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import extensions
from nova.api.openstack import faults
@@ -64,7 +63,7 @@ def _translate_volume_summary_view(context, vol):
return d
-class VolumeController(wsgi.Controller):
+class VolumeController(object):
"""The Volumes API controller for the OpenStack API."""
_serialization_metadata = {
@@ -124,15 +123,14 @@ class VolumeController(wsgi.Controller):
res = [entity_maker(context, vol) for vol in limited_list]
return {'volumes': res}
- def create(self, req):
+ def create(self, req, body):
"""Creates a new volume."""
context = req.environ['nova.context']
- env = self._deserialize(req.body, req.get_content_type())
- if not env:
+ if not body:
return faults.Fault(exc.HTTPUnprocessableEntity())
- vol = env['volume']
+ vol = body['volume']
size = vol['size']
LOG.audit(_("Create volume of %s GB"), size, context=context)
new_volume = self.volume_api.create(context, size,
@@ -175,7 +173,7 @@ def _translate_attachment_summary_view(_context, vol):
return d
-class VolumeAttachmentController(wsgi.Controller):
+class VolumeAttachmentController(object):
"""The volume attachment API controller for the Openstack API.
A child resource of the server. Note that we use the volume id
@@ -219,17 +217,16 @@ class VolumeAttachmentController(wsgi.Controller):
return {'volumeAttachment': _translate_attachment_detail_view(context,
vol)}
- def create(self, req, server_id):
+ def create(self, req, server_id, body):
"""Attach a volume to an instance."""
context = req.environ['nova.context']
- env = self._deserialize(req.body, req.get_content_type())
- if not env:
+ if not body:
return faults.Fault(exc.HTTPUnprocessableEntity())
instance_id = server_id
- volume_id = env['volumeAttachment']['volumeId']
- device = env['volumeAttachment']['device']
+ volume_id = body['volumeAttachment']['volumeId']
+ device = body['volumeAttachment']['device']
msg = _("Attach volume %(volume_id)s to instance %(server_id)s"
" at %(device)s") % locals()
@@ -259,7 +256,7 @@ class VolumeAttachmentController(wsgi.Controller):
# TODO(justinsb): How do I return "accepted" here?
return {'volumeAttachment': attachment}
- def update(self, _req, _server_id, _id):
+ def update(self, req, server_id, id, body):
"""Update a volume attachment. We don't currently support this."""
return faults.Fault(exc.HTTPBadRequest())
diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py
index 7ea7afef6..73f174e07 100644
--- a/nova/api/openstack/extensions.py
+++ b/nova/api/openstack/extensions.py
@@ -27,9 +27,10 @@ import webob.exc
from nova import exception
from nova import flags
from nova import log as logging
-from nova import wsgi
+from nova import wsgi as base_wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
+from nova.api.openstack import wsgi
LOG = logging.getLogger('extensions')
@@ -116,28 +117,34 @@ class ExtensionDescriptor(object):
return response_exts
-class ActionExtensionController(common.OpenstackController):
-
+class ActionExtensionController(object):
def __init__(self, application):
-
self.application = application
self.action_handlers = {}
def add_action(self, action_name, handler):
self.action_handlers[action_name] = handler
- def action(self, req, id):
-
- input_dict = self._deserialize(req.body, req.get_content_type())
+ def action(self, req, id, body):
for action_name, handler in self.action_handlers.iteritems():
- if action_name in input_dict:
- return handler(input_dict, req, id)
+ if action_name in body:
+ return handler(body, req, id)
# no action handler found (bump to downstream application)
res = self.application
return res
-class ResponseExtensionController(common.OpenstackController):
+class ActionExtensionResource(wsgi.Resource):
+
+ def __init__(self, application):
+ controller = ActionExtensionController(application)
+ super(ActionExtensionResource, self).__init__(controller)
+
+ def add_action(self, action_name, handler):
+ self.controller.add_action(action_name, handler)
+
+
+class ResponseExtensionController(object):
def __init__(self, application):
self.application = application
@@ -157,7 +164,11 @@ class ResponseExtensionController(common.OpenstackController):
headers = res.headers
except AttributeError:
default_xmlns = None
- body = self._serialize(res, content_type, default_xmlns)
+ serializer = {
+ 'application/xml': wsgi.XMLSerializer(),
+ 'application/json': wsgi.JSONSerializer(),
+ }[content_type]
+ body = serializer.serialize(res)
headers = {"Content-Type": content_type}
res = webob.Response()
res.body = body
@@ -165,7 +176,17 @@ class ResponseExtensionController(common.OpenstackController):
return res
-class ExtensionController(common.OpenstackController):
+class ResponseExtensionResource(wsgi.Resource):
+
+ def __init__(self, application):
+ controller = ResponseExtensionController(application)
+ super(ResponseExtensionResource, self).__init__(controller)
+
+ def add_handler(self, handler):
+ self.controller.add_handler(handler)
+
+
+class ExtensionController(object):
def __init__(self, extension_manager):
self.extension_manager = extension_manager
@@ -198,7 +219,7 @@ class ExtensionController(common.OpenstackController):
raise faults.Fault(webob.exc.HTTPNotFound())
-class ExtensionMiddleware(wsgi.Middleware):
+class ExtensionMiddleware(base_wsgi.Middleware):
"""Extensions middleware for WSGI."""
@classmethod
def factory(cls, global_config, **local_config):
@@ -207,43 +228,43 @@ class ExtensionMiddleware(wsgi.Middleware):
return cls(app, **local_config)
return _factory
- def _action_ext_controllers(self, application, ext_mgr, mapper):
- """Return a dict of ActionExtensionController-s by collection."""
- action_controllers = {}
+ def _action_ext_resources(self, application, ext_mgr, mapper):
+ """Return a dict of ActionExtensionResource objects by collection."""
+ action_resources = {}
for action in ext_mgr.get_actions():
- if not action.collection in action_controllers.keys():
- controller = ActionExtensionController(application)
+ if not action.collection in action_resources.keys():
+ resource = ActionExtensionResource(application)
mapper.connect("/%s/:(id)/action.:(format)" %
action.collection,
action='action',
- controller=controller,
+ controller=resource,
conditions=dict(method=['POST']))
mapper.connect("/%s/:(id)/action" % action.collection,
action='action',
- controller=controller,
+ controller=resource,
conditions=dict(method=['POST']))
- action_controllers[action.collection] = controller
+ action_resources[action.collection] = resource
- return action_controllers
+ return action_resources
- def _response_ext_controllers(self, application, ext_mgr, mapper):
- """Returns a dict of ResponseExtensionController-s by collection."""
- response_ext_controllers = {}
+ def _response_ext_resources(self, application, ext_mgr, mapper):
+ """Returns a dict of ResponseExtensionResource objects by collection."""
+ response_ext_resources = {}
for resp_ext in ext_mgr.get_response_extensions():
- if not resp_ext.key in response_ext_controllers.keys():
- controller = ResponseExtensionController(application)
+ if not resp_ext.key in response_ext_resources.keys():
+ resource = ResponseExtensionResource(application)
mapper.connect(resp_ext.url_route + '.:(format)',
action='process',
- controller=controller,
+ controller=resource,
conditions=resp_ext.conditions)
mapper.connect(resp_ext.url_route,
action='process',
- controller=controller,
+ controller=resource,
conditions=resp_ext.conditions)
- response_ext_controllers[resp_ext.key] = controller
+ response_ext_resources[resp_ext.key] = resource
- return response_ext_controllers
+ return response_ext_resources
def __init__(self, application, ext_mgr=None):
@@ -258,21 +279,21 @@ class ExtensionMiddleware(wsgi.Middleware):
LOG.debug(_('Extended resource: %s'),
resource.collection)
mapper.resource(resource.collection, resource.collection,
- controller=resource.controller,
+ controller=wsgi.Resource(resource.controller),
collection=resource.collection_actions,
member=resource.member_actions,
parent_resource=resource.parent)
# extended actions
- action_controllers = self._action_ext_controllers(application, ext_mgr,
+ 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)
- controller = action_controllers[action.collection]
- controller.add_action(action.action_name, action.handler)
+ resource = action_resources[action.collection]
+ resource.add_action(action.action_name, action.handler)
# extended responses
- resp_controllers = self._response_ext_controllers(application, ext_mgr,
+ resp_controllers = self._response_ext_resources(application, ext_mgr,
mapper)
for response_ext in ext_mgr.get_response_extensions():
LOG.debug(_('Extended response: %s'), response_ext.key)
@@ -422,7 +443,7 @@ class ExtensionManager(object):
class ResponseExtension(object):
- """Add data to responses from core nova OpenStack API controllers."""
+ """Add data to responses from core nova OpenStack API resources."""
def __init__(self, method, url_route, handler):
self.url_route = url_route
@@ -432,7 +453,7 @@ class ResponseExtension(object):
class ActionExtension(object):
- """Add custom actions to core nova OpenStack API controllers."""
+ """Add custom actions to core nova OpenStack API resources."""
def __init__(self, collection, action_name, handler):
self.collection = collection
diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py
index 87118ce19..fd36f8f17 100644
--- a/nova/api/openstack/faults.py
+++ b/nova/api/openstack/faults.py
@@ -19,8 +19,7 @@
import webob.dec
import webob.exc
-from nova import wsgi
-from nova.api.openstack import common
+from nova.api.openstack import wsgi
class Fault(webob.exc.HTTPException):
@@ -55,13 +54,21 @@ class Fault(webob.exc.HTTPException):
if code == 413:
retry = self.wrapped_exc.headers['Retry-After']
fault_data[fault_name]['retryAfter'] = retry
+
# 'code' is an attribute on the fault tag itself
- metadata = {'application/xml': {'attributes': {fault_name: 'code'}}}
- default_xmlns = common.XML_NS_V10
- serializer = wsgi.Serializer(metadata, default_xmlns)
+ metadata = {'attributes': {fault_name: 'code'}}
+
content_type = req.best_match_content_type()
- self.wrapped_exc.body = serializer.serialize(fault_data, content_type)
+
+ serializer = {
+ 'application/xml': wsgi.XMLSerializer(metadata=metadata,
+ xmlns=wsgi.XMLNS_V10),
+ 'application/json': wsgi.JSONSerializer(),
+ }[content_type]
+
+ self.wrapped_exc.body = serializer.serialize(fault_data)
self.wrapped_exc.content_type = content_type
+
return self.wrapped_exc
@@ -70,14 +77,6 @@ class OverLimitFault(webob.exc.HTTPException):
Rate-limited request response.
"""
- _serialization_metadata = {
- "application/xml": {
- "attributes": {
- "overLimitFault": "code",
- },
- },
- }
-
def __init__(self, message, details, retry_time):
"""
Initialize new `OverLimitFault` with relevant information.
@@ -97,8 +96,16 @@ class OverLimitFault(webob.exc.HTTPException):
Return the wrapped exception with a serialized body conforming to our
error format.
"""
- serializer = wsgi.Serializer(self._serialization_metadata)
content_type = request.best_match_content_type()
- content = serializer.serialize(self.content, content_type)
+ metadata = {"attributes": {"overLimitFault": "code"}}
+
+ serializer = {
+ 'application/xml': wsgi.XMLSerializer(metadata=metadata,
+ xmlns=wsgi.XMLNS_V10),
+ 'application/json': wsgi.JSONSerializer(),
+ }[content_type]
+
+ content = serializer.serialize(self.content)
self.wrapped_exc.body = content
+
return self.wrapped_exc
diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py
index ce0140265..506b63acf 100644
--- a/nova/api/openstack/image_metadata.py
+++ b/nova/api/openstack/image_metadata.py
@@ -20,7 +20,6 @@ from webob import exc
from nova import flags
from nova import quota
from nova import utils
-from nova import wsgi
from nova.api.openstack import faults
from nova.api.openstack import wsgi
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index e22854ebf..5a03573d8 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -31,7 +31,7 @@ FLAGS = flags.FLAGS
class Controller(object):
- """Base `wsgi.Controller` for retrieving/displaying images."""
+ """Base controller for retrieving/displaying images."""
def __init__(self, image_service=None, compute_service=None):
"""Initialize new `ImageController`.
@@ -99,21 +99,20 @@ class Controller(object):
self._image_service.delete(context, image_id)
return webob.exc.HTTPNoContent()
- def create(self, req):
+ def create(self, req, body):
"""Snapshot a server instance and save the image.
:param req: `wsgi.Request` object
"""
context = req.environ['nova.context']
content_type = req.get_content_type()
- image = self._deserialize(req.body, content_type)
- if not image:
+ if not body:
raise webob.exc.HTTPBadRequest()
try:
- server_id = image["image"]["serverId"]
- image_name = image["image"]["name"]
+ server_id = body["image"]["serverId"]
+ image_name = body["image"]["name"]
except KeyError:
raise webob.exc.HTTPBadRequest()
diff --git a/nova/api/openstack/versions.py b/nova/api/openstack/versions.py
index 3f9d91934..a8d785b52 100644
--- a/nova/api/openstack/versions.py
+++ b/nova/api/openstack/versions.py
@@ -18,13 +18,27 @@
import webob
import webob.dec
-from nova import wsgi
+from nova import wsgi as base_wsgi
import nova.api.openstack.views.versions
+from nova.api.openstack import wsgi
-class Versions(wsgi.Application):
- @webob.dec.wsgify(RequestClass=wsgi.Request)
- def __call__(self, req):
+class Versions(wsgi.Resource, base_wsgi.Application):
+ def __init__(self):
+ metadata = {
+ "attributes": {
+ "version": ["status", "id"],
+ "link": ["rel", "href"],
+ }
+ }
+
+ serializers = {
+ 'application/xml': wsgi.XMLSerializer(metadata=metadata),
+ }
+
+ super(Versions, self).__init__(None, serializers=serializers)
+
+ def dispatch(self, request, *args):
"""Respond to a request for all OpenStack API versions."""
version_objs = [
{
@@ -37,24 +51,6 @@ class Versions(wsgi.Application):
},
]
- builder = nova.api.openstack.views.versions.get_view_builder(req)
+ builder = nova.api.openstack.views.versions.get_view_builder(request)
versions = [builder.build(version) for version in version_objs]
- response = dict(versions=versions)
-
- metadata = {
- "application/xml": {
- "attributes": {
- "version": ["status", "id"],
- "link": ["rel", "href"],
- }
- }
- }
-
- content_type = req.best_match_content_type()
- body = wsgi.Serializer(metadata).serialize(response, content_type)
-
- response = webob.Response()
- response.content_type = content_type
- response.body = body
-
- return response
+ return dict(versions=versions)
diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py
index 9e0077932..97280c365 100644
--- a/nova/api/openstack/wsgi.py
+++ b/nova/api/openstack/wsgi.py
@@ -206,8 +206,7 @@ class Resource(object):
except exception.InvalidContentType:
return webob.exc.HTTPBadRequest(_("Unsupported Content-Type"))
- controller_method = getattr(self.controller, action)
- result = controller_method(req=request, **action_args)
+ result = self.dispatch(request, action, action_args)
response = self.serialize_response(accept, result)
@@ -222,6 +221,10 @@ class Resource(object):
return response
+ def dispatch(self, request, action, action_args):
+ controller_method = getattr(self.controller, action)
+ return controller_method(req=request, **action_args)
+
def serialize_response(self, content_type, response_body):
"""Serialize a dict into a string and wrap in a wsgi.Request object.
@@ -253,7 +256,7 @@ class Resource(object):
"""
action_args = self.get_action_args(request.environ)
- action = action_args.pop('action')
+ action = action_args.pop('action', None)
if request.method.lower() in ('post', 'put'):
if len(request.body) == 0:
@@ -275,14 +278,18 @@ class Resource(object):
return request.best_match_content_type()
def get_action_args(self, request_environment):
- args = request_environment['wsgiorg.routing_args'][1].copy()
+ try:
+ args = request_environment['wsgiorg.routing_args'][1].copy()
+
+ del args['controller']
- del args['controller']
+ if 'format' in args:
+ del args['format']
- if 'format' in args:
- del args['format']
+ return args
- return args
+ except KeyError:
+ return {}
def get_deserializer(self, content_type):
try: