From e73cb83f9ecf918617b648770e22b49ae69e8a1b Mon Sep 17 00:00:00 2001 From: Ken'ichi Ohmichi Date: Tue, 21 Oct 2014 02:44:13 +0000 Subject: [PATCH] Use wsgi.response for v2.1 API Now a decorator @wsgi.response() can be used for specifying success status code. Most v2.1 APIs use the decorator and it makes the code more readable. This patch replaces webob.Response with the decorator for the other APIs. In addition, this patch changes some orders of decorators for showing success code and expected error codes clearly. Even if applying this patch, some webob.Response() calls still exist under the path nova/api/openstack/compute/plugins/v3/ because these cases need to contain some information in a response header. Partially implements blueprint v2-on-v3-api Change-Id: I419819b8e55427979e8d82b972cd237952a5b06b --- .../compute/plugins/v3/admin_actions.py | 7 +++-- .../compute/plugins/v3/attach_interfaces.py | 4 +-- .../openstack/compute/plugins/v3/consoles.py | 3 +-- .../compute/plugins/v3/deferred_delete.py | 4 +-- .../compute/plugins/v3/extended_volumes.py | 4 +-- .../compute/plugins/v3/flavor_manage.py | 11 ++++---- .../compute/plugins/v3/lock_server.py | 6 ++--- .../compute/plugins/v3/migrate_server.py | 6 ++--- .../openstack/compute/plugins/v3/multinic.py | 7 ++--- .../openstack/compute/plugins/v3/networks.py | 7 +++-- .../compute/plugins/v3/pause_server.py | 5 ++-- .../compute/plugins/v3/server_groups.py | 3 +-- .../openstack/compute/plugins/v3/servers.py | 26 ++++++++----------- .../openstack/compute/plugins/v3/shelve.py | 9 +++---- .../compute/plugins/v3/suspend_server.py | 5 ++-- .../openstack/compute/plugins/v3/volumes.py | 5 ++-- .../compute/contrib/test_admin_actions.py | 20 +++++++++++--- .../compute/contrib/test_attach_interfaces.py | 9 ++++++- .../compute/contrib/test_deferred_delete.py | 16 ++++++++++-- .../compute/contrib/test_flavor_manage.py | 10 ++++++- .../contrib/test_server_group_quotas.py | 9 ++++++- .../compute/contrib/test_server_groups.py | 9 ++++++- .../openstack/compute/contrib/test_volumes.py | 9 ++++++- .../plugins/v3/test_extended_volumes.py | 5 ++-- 24 files changed, 118 insertions(+), 81 deletions(-) diff --git a/nova/api/openstack/compute/plugins/v3/admin_actions.py b/nova/api/openstack/compute/plugins/v3/admin_actions.py index 877be4ea51ce..9543a03acd87 100644 --- a/nova/api/openstack/compute/plugins/v3/admin_actions.py +++ b/nova/api/openstack/compute/plugins/v3/admin_actions.py @@ -12,7 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. -import webob from webob import exc from nova.api.openstack import common @@ -42,6 +41,7 @@ class AdminActionsController(wsgi.Controller): super(AdminActionsController, self).__init__(*args, **kwargs) self.compute_api = compute.API() + @wsgi.response(202) @extensions.expected_errors((404, 409)) @wsgi.action('resetNetwork') def _reset_network(self, req, id, body): @@ -54,8 +54,8 @@ class AdminActionsController(wsgi.Controller): self.compute_api.reset_network(context, instance) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) - return webob.Response(status_int=202) + @wsgi.response(202) @extensions.expected_errors((404, 409)) @wsgi.action('injectNetworkInfo') def _inject_network_info(self, req, id, body): @@ -68,8 +68,8 @@ class AdminActionsController(wsgi.Controller): self.compute_api.inject_network_info(context, instance) except exception.InstanceIsLocked as e: raise exc.HTTPConflict(explanation=e.format_message()) - return webob.Response(status_int=202) + @wsgi.response(202) @extensions.expected_errors((400, 404)) @wsgi.action('os-resetState') @validation.schema(reset_server_state.reset_state) @@ -86,7 +86,6 @@ class AdminActionsController(wsgi.Controller): instance.vm_state = state instance.task_state = None instance.save(admin_state_reset=True) - return webob.Response(status_int=202) class AdminActions(extensions.V3APIExtensionBase): diff --git a/nova/api/openstack/compute/plugins/v3/attach_interfaces.py b/nova/api/openstack/compute/plugins/v3/attach_interfaces.py index 523d1b1b4e22..7c54fd2eba68 100644 --- a/nova/api/openstack/compute/plugins/v3/attach_interfaces.py +++ b/nova/api/openstack/compute/plugins/v3/attach_interfaces.py @@ -21,6 +21,7 @@ from webob import exc from nova.api.openstack import common from nova.api.openstack.compute.schemas.v3 import attach_interfaces from nova.api.openstack import extensions +from nova.api.openstack import wsgi from nova.api import validation from nova import compute from nova import exception @@ -137,6 +138,7 @@ class InterfaceAttachmentController(object): return self.show(req, server_id, vif['id']) + @wsgi.response(202) @extensions.expected_errors((404, 409, 501)) def delete(self, req, server_id, id): """Detach an interface from an instance.""" @@ -160,8 +162,6 @@ class InterfaceAttachmentController(object): common.raise_http_conflict_for_instance_invalid_state(state_error, 'detach_interface', server_id) - return webob.Response(status_int=202) - def _items(self, req, server_id, entity_maker): """Returns a list of attachments, transformed through entity_maker.""" context = req.environ['nova.context'] diff --git a/nova/api/openstack/compute/plugins/v3/consoles.py b/nova/api/openstack/compute/plugins/v3/consoles.py index 80b2c423cbe8..6af7f35f8d34 100644 --- a/nova/api/openstack/compute/plugins/v3/consoles.py +++ b/nova/api/openstack/compute/plugins/v3/consoles.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import webob from webob import exc from nova.api.openstack import extensions @@ -81,6 +80,7 @@ class ConsolesController(object): raise exc.HTTPNotFound(explanation=e.format_message()) return _translate_detail_keys(console) + @wsgi.response(202) @extensions.expected_errors(404) def delete(self, req, server_id, id): """Deletes a console.""" @@ -90,7 +90,6 @@ class ConsolesController(object): int(id)) except exception.ConsoleNotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) - return webob.Response(status_int=202) class Consoles(extensions.V3APIExtensionBase): diff --git a/nova/api/openstack/compute/plugins/v3/deferred_delete.py b/nova/api/openstack/compute/plugins/v3/deferred_delete.py index 58008497929b..2d381178bea0 100644 --- a/nova/api/openstack/compute/plugins/v3/deferred_delete.py +++ b/nova/api/openstack/compute/plugins/v3/deferred_delete.py @@ -33,6 +33,7 @@ class DeferredDeleteController(wsgi.Controller): super(DeferredDeleteController, self).__init__(*args, **kwargs) self.compute_api = compute.API() + @wsgi.response(202) @extensions.expected_errors((404, 409, 403)) @wsgi.action('restore') def _restore(self, req, id, body): @@ -48,8 +49,8 @@ class DeferredDeleteController(wsgi.Controller): except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'restore', id) - return webob.Response(status_int=202) + @wsgi.response(202) @extensions.expected_errors((404, 409)) @wsgi.action('forceDelete') def _force_delete(self, req, id, body): @@ -62,7 +63,6 @@ class DeferredDeleteController(wsgi.Controller): self.compute_api.force_delete(context, instance) except exception.InstanceIsLocked as e: raise webob.exc.HTTPConflict(explanation=e.format_message()) - return webob.Response(status_int=202) class DeferredDelete(extensions.V3APIExtensionBase): diff --git a/nova/api/openstack/compute/plugins/v3/extended_volumes.py b/nova/api/openstack/compute/plugins/v3/extended_volumes.py index 3a8314a996a1..17136fec8940 100644 --- a/nova/api/openstack/compute/plugins/v3/extended_volumes.py +++ b/nova/api/openstack/compute/plugins/v3/extended_volumes.py @@ -13,7 +13,6 @@ # under the License. """The Extended Volumes API extension.""" -import webob from webob import exc from nova.api.openstack import common @@ -52,6 +51,7 @@ class ExtendedVolumesController(wsgi.Controller): key = "%s:volumes_attached" % ExtendedVolumes.alias server[key] = [{'id': volume_id} for volume_id in volume_ids] + @wsgi.response(202) @extensions.expected_errors((400, 404, 409)) @wsgi.action('swap_volume_attachment') @validation.schema(extended_volumes.swap_volume_attachment) @@ -99,8 +99,6 @@ class ExtendedVolumesController(wsgi.Controller): msg = _("The volume was either invalid or not attached to the " "instance.") raise exc.HTTPNotFound(explanation=msg) - else: - return webob.Response(status_int=202) @wsgi.extends def show(self, req, resp_obj, id): diff --git a/nova/api/openstack/compute/plugins/v3/flavor_manage.py b/nova/api/openstack/compute/plugins/v3/flavor_manage.py index 1bfbdc1cc95d..e89d5effdf2c 100644 --- a/nova/api/openstack/compute/plugins/v3/flavor_manage.py +++ b/nova/api/openstack/compute/plugins/v3/flavor_manage.py @@ -32,8 +32,12 @@ class FlavorManageController(wsgi.Controller): def __init__(self): super(FlavorManageController, self).__init__() - @wsgi.action("delete") + # NOTE(oomichi): Return 202 for backwards compatibility but should be + # 204 as this operation complete the deletion of aggregate resource and + # return no response body. + @wsgi.response(202) @extensions.expected_errors((404)) + @wsgi.action("delete") def _delete(self, req, id): context = req.environ['nova.context'] authorize(context) @@ -46,11 +50,6 @@ class FlavorManageController(wsgi.Controller): flavors.destroy(flavor['name']) - # NOTE(oomichi): Return 202 for backwards compatibility but should be - # 204 as this operation complete the deletion of aggregate resource and - # return no response body. - return webob.Response(status_int=202) - # NOTE(oomichi): Return 200 for backwards compatibility but should be 201 # as this operation complete the creation of flavor resource. @wsgi.action("create") diff --git a/nova/api/openstack/compute/plugins/v3/lock_server.py b/nova/api/openstack/compute/plugins/v3/lock_server.py index 662e1b2baf6f..7149871499a2 100644 --- a/nova/api/openstack/compute/plugins/v3/lock_server.py +++ b/nova/api/openstack/compute/plugins/v3/lock_server.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import webob - from nova.api.openstack import common from nova.api.openstack import extensions from nova.api.openstack import wsgi @@ -33,6 +31,7 @@ class LockServerController(wsgi.Controller): super(LockServerController, self).__init__(*args, **kwargs) self.compute_api = compute.API() + @wsgi.response(202) @extensions.expected_errors(404) @wsgi.action('lock') def _lock(self, req, id, body): @@ -42,8 +41,8 @@ class LockServerController(wsgi.Controller): instance = common.get_instance(self.compute_api, context, id, want_objects=True) self.compute_api.lock(context, instance) - return webob.Response(status_int=202) + @wsgi.response(202) @extensions.expected_errors(404) @wsgi.action('unlock') def _unlock(self, req, id, body): @@ -53,7 +52,6 @@ class LockServerController(wsgi.Controller): instance = common.get_instance(self.compute_api, context, id, want_objects=True) self.compute_api.unlock(context, instance) - return webob.Response(status_int=202) class LockServer(extensions.V3APIExtensionBase): diff --git a/nova/api/openstack/compute/plugins/v3/migrate_server.py b/nova/api/openstack/compute/plugins/v3/migrate_server.py index 19210e2c9689..272a4c33a5df 100644 --- a/nova/api/openstack/compute/plugins/v3/migrate_server.py +++ b/nova/api/openstack/compute/plugins/v3/migrate_server.py @@ -14,7 +14,6 @@ # under the License. from oslo.utils import strutils -import webob from webob import exc from nova.api.openstack import common @@ -38,6 +37,7 @@ class MigrateServerController(wsgi.Controller): super(MigrateServerController, self).__init__(*args, **kwargs) self.compute_api = compute.API() + @wsgi.response(202) @extensions.expected_errors((400, 403, 404, 409)) @wsgi.action('migrate') def _migrate(self, req, id, body): @@ -61,8 +61,7 @@ class MigrateServerController(wsgi.Controller): except exception.NoValidHost as e: raise exc.HTTPBadRequest(explanation=e.format_message()) - return webob.Response(status_int=202) - + @wsgi.response(202) @extensions.expected_errors((400, 404, 409)) @wsgi.action('os-migrateLive') @validation.schema(migrate_server.migrate_live) @@ -102,7 +101,6 @@ class MigrateServerController(wsgi.Controller): except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'os-migrateLive', id) - return webob.Response(status_int=202) class MigrateServer(extensions.V3APIExtensionBase): diff --git a/nova/api/openstack/compute/plugins/v3/multinic.py b/nova/api/openstack/compute/plugins/v3/multinic.py index ba50b239e847..c61127e4840e 100644 --- a/nova/api/openstack/compute/plugins/v3/multinic.py +++ b/nova/api/openstack/compute/plugins/v3/multinic.py @@ -15,7 +15,6 @@ """The multinic extension.""" -import webob from webob import exc from nova.api.openstack import common @@ -36,6 +35,7 @@ class MultinicController(wsgi.Controller): super(MultinicController, self).__init__(*args, **kwargs) self.compute_api = compute.API() + @wsgi.response(202) @wsgi.action('addFixedIp') @extensions.expected_errors((400, 404)) @validation.schema(multinic.add_fixed_ip) @@ -52,8 +52,7 @@ class MultinicController(wsgi.Controller): except exception.NoMoreFixedIps as e: raise exc.HTTPBadRequest(explanation=e.format_message()) - return webob.Response(status_int=202) - + @wsgi.response(202) @wsgi.action('removeFixedIp') @extensions.expected_errors((400, 404)) @validation.schema(multinic.remove_fixed_ip) @@ -71,8 +70,6 @@ class MultinicController(wsgi.Controller): except exception.FixedIpNotFoundForSpecificInstance as e: raise exc.HTTPBadRequest(explanation=e.format_message()) - return webob.Response(status_int=202) - # Note: The class name is as it has to be for this to be loaded as an # extension--only first character capitalized. diff --git a/nova/api/openstack/compute/plugins/v3/networks.py b/nova/api/openstack/compute/plugins/v3/networks.py index 1f79a148c02d..de4715c23d0a 100644 --- a/nova/api/openstack/compute/plugins/v3/networks.py +++ b/nova/api/openstack/compute/plugins/v3/networks.py @@ -15,7 +15,6 @@ # under the License. import netaddr -import webob from webob import exc from nova.api.openstack import extensions @@ -88,9 +87,9 @@ class NetworkController(wsgi.Controller): result = [network_dict(context, net_ref) for net_ref in networks] return {'networks': result} + @wsgi.response(202) @extensions.expected_errors((404, 501)) @wsgi.action("disassociate") - @wsgi.response(202) def _disassociate_host_and_project(self, req, id, body): context = req.environ['nova.context'] authorize(context) @@ -117,6 +116,7 @@ class NetworkController(wsgi.Controller): raise exc.HTTPNotFound(explanation=msg) return {'network': network_dict(context, network)} + @wsgi.response(202) @extensions.expected_errors((404, 409)) def delete(self, req, id): context = req.environ['nova.context'] @@ -129,7 +129,6 @@ class NetworkController(wsgi.Controller): except exception.NetworkNotFound: msg = _("Network not found") raise exc.HTTPNotFound(explanation=msg) - return webob.Response(status_int=202) @extensions.expected_errors((400, 409, 501)) def create(self, req, body): @@ -171,8 +170,8 @@ class NetworkController(wsgi.Controller): raise exc.HTTPConflict(explanation=ex.format_message()) return {"network": network_dict(context, network)} - @extensions.expected_errors((400, 409, 501)) @wsgi.response(202) + @extensions.expected_errors((400, 409, 501)) def add(self, req, body): context = req.environ['nova.context'] authorize(context) diff --git a/nova/api/openstack/compute/plugins/v3/pause_server.py b/nova/api/openstack/compute/plugins/v3/pause_server.py index 2191dc46866c..1577471b3c3d 100644 --- a/nova/api/openstack/compute/plugins/v3/pause_server.py +++ b/nova/api/openstack/compute/plugins/v3/pause_server.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import webob from webob import exc from nova.api.openstack import common @@ -36,6 +35,7 @@ class PauseServerController(wsgi.Controller): super(PauseServerController, self).__init__(*args, **kwargs) self.compute_api = compute.API() + @wsgi.response(202) @extensions.expected_errors((404, 409, 501)) @wsgi.action('pause') def _pause(self, req, id, body): @@ -56,8 +56,8 @@ class PauseServerController(wsgi.Controller): except NotImplementedError: msg = _("Virt driver does not implement pause function.") raise exc.HTTPNotImplemented(explanation=msg) - return webob.Response(status_int=202) + @wsgi.response(202) @extensions.expected_errors((404, 409, 501)) @wsgi.action('unpause') def _unpause(self, req, id, body): @@ -78,7 +78,6 @@ class PauseServerController(wsgi.Controller): except NotImplementedError: msg = _("Virt driver does not implement pause function.") raise exc.HTTPNotImplemented(explanation=msg) - return webob.Response(status_int=202) class PauseServer(extensions.V3APIExtensionBase): diff --git a/nova/api/openstack/compute/plugins/v3/server_groups.py b/nova/api/openstack/compute/plugins/v3/server_groups.py index f3dcd9c4c9c9..7dc9087f5e34 100644 --- a/nova/api/openstack/compute/plugins/v3/server_groups.py +++ b/nova/api/openstack/compute/plugins/v3/server_groups.py @@ -136,6 +136,7 @@ class ServerGroupController(wsgi.Controller): raise webob.exc.HTTPNotFound(explanation=e.format_message()) return {'server_group': self._format_server_group(context, sg)} + @wsgi.response(204) @extensions.expected_errors(404) def delete(self, req, id): """Delete an server group.""" @@ -167,8 +168,6 @@ class ServerGroupController(wsgi.Controller): if quotas: quotas.commit() - return webob.Response(status_int=204) - @extensions.expected_errors(()) def index(self, req): """Returns a list of server groups.""" diff --git a/nova/api/openstack/compute/plugins/v3/servers.py b/nova/api/openstack/compute/plugins/v3/servers.py index 767eef1adcf9..659572c86c71 100644 --- a/nova/api/openstack/compute/plugins/v3/servers.py +++ b/nova/api/openstack/compute/plugins/v3/servers.py @@ -453,8 +453,8 @@ class ServersController(wsgi.Controller): req.cache_db_instance(instance) return self._view_builder.show(req, instance) - @extensions.expected_errors((400, 403, 409, 413)) @wsgi.response(202) + @extensions.expected_errors((400, 403, 409, 413)) @validation.schema(schema_server_create) def create(self, req, body): """Creates a new server for a given user.""" @@ -700,8 +700,8 @@ class ServersController(wsgi.Controller): # NOTE(gmann): Returns 204 for backwards compatibility but should be 202 # for representing async API as this API just accepts the request and # request hypervisor driver to complete the same in async mode. - @extensions.expected_errors((400, 404, 409)) @wsgi.response(204) + @extensions.expected_errors((400, 404, 409)) @wsgi.action('confirmResize') def _action_confirm_resize(self, req, id, body): context = req.environ['nova.context'] @@ -717,8 +717,8 @@ class ServersController(wsgi.Controller): common.raise_http_conflict_for_instance_invalid_state(state_error, 'confirmResize', id) - @extensions.expected_errors((400, 404, 409)) @wsgi.response(202) + @extensions.expected_errors((400, 404, 409)) @wsgi.action('revertResize') def _action_revert_resize(self, req, id, body): context = req.environ['nova.context'] @@ -736,10 +736,9 @@ class ServersController(wsgi.Controller): except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'revertResize', id) - return webob.Response(status_int=202) - @extensions.expected_errors((400, 404, 409)) @wsgi.response(202) + @extensions.expected_errors((400, 404, 409)) @wsgi.action('reboot') def _action_reboot(self, req, id, body): if 'reboot' in body and 'type' in body['reboot']: @@ -768,7 +767,6 @@ class ServersController(wsgi.Controller): except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'reboot', id) - return webob.Response(status_int=202) def _resize(self, req, instance_id, flavor_id, **kwargs): """Begin the resize process with given instance/flavor.""" @@ -810,10 +808,8 @@ class ServersController(wsgi.Controller): msg = _("Invalid instance image.") raise exc.HTTPBadRequest(explanation=msg) - return webob.Response(status_int=202) - - @extensions.expected_errors((404, 409)) @wsgi.response(204) + @extensions.expected_errors((404, 409)) def delete(self, req, id): """Destroys a server.""" try: @@ -859,8 +855,8 @@ class ServersController(wsgi.Controller): flavor_ref = data['server']['flavorRef'] return common.get_id_from_href(flavor_ref) - @extensions.expected_errors((400, 401, 403, 404, 409)) @wsgi.response(202) + @extensions.expected_errors((400, 401, 403, 404, 409)) @wsgi.action('resize') def _action_resize(self, req, id, body): """Resizes a given instance to the flavor size requested.""" @@ -880,10 +876,10 @@ class ServersController(wsgi.Controller): self.resize_extension_manager.map(self._resize_extension_point, resize_dict, resize_kwargs) - return self._resize(req, id, flavor_ref, **resize_kwargs) + self._resize(req, id, flavor_ref, **resize_kwargs) - @extensions.expected_errors((400, 403, 404, 409, 413)) @wsgi.response(202) + @extensions.expected_errors((400, 403, 404, 409, 413)) @wsgi.action('rebuild') @validation.schema(schema_server_rebuild) def _action_rebuild(self, req, id, body): @@ -958,8 +954,8 @@ class ServersController(wsgi.Controller): robj = wsgi.ResponseObject(view) return self._add_location(robj) - @extensions.expected_errors((400, 403, 404, 409)) @wsgi.response(202) + @extensions.expected_errors((400, 403, 404, 409)) @wsgi.action('createImage') @common.check_snapshots_enabled def _action_create_image(self, req, id, body): @@ -1045,6 +1041,7 @@ class ServersController(wsgi.Controller): except exception.InstanceNotFound as e: raise webob.exc.HTTPNotFound(explanation=e.format_message()) + @wsgi.response(202) @extensions.expected_errors((404, 409)) @wsgi.action('os-start') def _start_server(self, req, id, body): @@ -1058,8 +1055,8 @@ class ServersController(wsgi.Controller): except (exception.InstanceNotReady, exception.InstanceIsLocked, exception.InstanceInvalidState) as e: raise webob.exc.HTTPConflict(explanation=e.format_message()) - return webob.Response(status_int=202) + @wsgi.response(202) @extensions.expected_errors((404, 409)) @wsgi.action('os-stop') def _stop_server(self, req, id, body): @@ -1073,7 +1070,6 @@ class ServersController(wsgi.Controller): except (exception.InstanceNotReady, exception.InstanceIsLocked, exception.InstanceInvalidState) as e: raise webob.exc.HTTPConflict(explanation=e.format_message()) - return webob.Response(status_int=202) def remove_invalid_options(context, search_options, allowed_search_options): diff --git a/nova/api/openstack/compute/plugins/v3/shelve.py b/nova/api/openstack/compute/plugins/v3/shelve.py index a62bf352dd69..e74aa2ed8670 100644 --- a/nova/api/openstack/compute/plugins/v3/shelve.py +++ b/nova/api/openstack/compute/plugins/v3/shelve.py @@ -14,7 +14,6 @@ """The shelved mode extension.""" -import webob from webob import exc from nova.api.openstack import common @@ -36,6 +35,7 @@ class ShelveController(wsgi.Controller): super(ShelveController, self).__init__(*args, **kwargs) self.compute_api = compute.API() + @wsgi.response(202) @exts.expected_errors((404, 409)) @wsgi.action('shelve') def _shelve(self, req, id, body): @@ -53,8 +53,7 @@ class ShelveController(wsgi.Controller): common.raise_http_conflict_for_instance_invalid_state(state_error, 'shelve', id) - return webob.Response(status_int=202) - + @wsgi.response(202) @exts.expected_errors((404, 409)) @wsgi.action('shelveOffload') def _shelve_offload(self, req, id, body): @@ -73,8 +72,7 @@ class ShelveController(wsgi.Controller): 'shelveOffload', id) - return webob.Response(status_int=202) - + @wsgi.response(202) @exts.expected_errors((404, 409)) @wsgi.action('unshelve') def _unshelve(self, req, id, body): @@ -91,7 +89,6 @@ class ShelveController(wsgi.Controller): common.raise_http_conflict_for_instance_invalid_state(state_error, 'unshelve', id) - return webob.Response(status_int=202) class Shelve(exts.V3APIExtensionBase): diff --git a/nova/api/openstack/compute/plugins/v3/suspend_server.py b/nova/api/openstack/compute/plugins/v3/suspend_server.py index 3ae47c794d82..65b78298ad12 100644 --- a/nova/api/openstack/compute/plugins/v3/suspend_server.py +++ b/nova/api/openstack/compute/plugins/v3/suspend_server.py @@ -12,7 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. -import webob from webob import exc from nova.api.openstack import common @@ -36,6 +35,7 @@ class SuspendServerController(wsgi.Controller): super(SuspendServerController, self).__init__(*args, **kwargs) self.compute_api = compute.API() + @wsgi.response(202) @extensions.expected_errors((404, 409)) @wsgi.action('suspend') def _suspend(self, req, id, body): @@ -51,8 +51,8 @@ class SuspendServerController(wsgi.Controller): except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'suspend', id) - return webob.Response(status_int=202) + @wsgi.response(202) @extensions.expected_errors((404, 409)) @wsgi.action('resume') def _resume(self, req, id, body): @@ -68,7 +68,6 @@ class SuspendServerController(wsgi.Controller): except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'resume', id) - return webob.Response(status_int=202) class SuspendServer(extensions.V3APIExtensionBase): diff --git a/nova/api/openstack/compute/plugins/v3/volumes.py b/nova/api/openstack/compute/plugins/v3/volumes.py index 7b6dd8a2f0f1..a60eb363a51a 100644 --- a/nova/api/openstack/compute/plugins/v3/volumes.py +++ b/nova/api/openstack/compute/plugins/v3/volumes.py @@ -16,7 +16,6 @@ """The volumes extension.""" from oslo.utils import strutils -import webob from webob import exc from nova.api.openstack import common @@ -98,6 +97,7 @@ class VolumeController(wsgi.Controller): return {'volume': _translate_volume_detail_view(context, vol)} + @wsgi.response(202) @extensions.expected_errors(404) def delete(self, req, id): """Delete a volume.""" @@ -110,7 +110,6 @@ class VolumeController(wsgi.Controller): self.volume_api.delete(context, id) except exception.NotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) - return webob.Response(status_int=202) @extensions.expected_errors(()) def index(self, req): @@ -257,6 +256,7 @@ class SnapshotController(wsgi.Controller): return {'snapshot': _translate_snapshot_detail_view(context, vol)} + @wsgi.response(202) @extensions.expected_errors(404) def delete(self, req, id): """Delete a snapshot.""" @@ -269,7 +269,6 @@ class SnapshotController(wsgi.Controller): self.volume_api.delete_snapshot(context, id) except exception.NotFound as e: raise exc.HTTPNotFound(explanation=e.format_message()) - return webob.Response(status_int=202) @extensions.expected_errors(()) def index(self, req): diff --git a/nova/tests/api/openstack/compute/contrib/test_admin_actions.py b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py index ac015d0c9d8f..d801abda64fb 100644 --- a/nova/tests/api/openstack/compute/contrib/test_admin_actions.py +++ b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py @@ -702,8 +702,14 @@ class ResetStateTestsV21(test.NoDBTestCase): body = {"os-resetState": {"state": "active"}} result = self.admin_api._reset_state(self.request, self.uuid, body=body) - - self.assertEqual(result.status_int, 202) + # NOTE: on v2.1, http status code is set as wsgi_code of API + # method instead of status_int in a response object. + if isinstance(self.admin_api, + admin_actions_v21.AdminActionsController): + status_int = self.admin_api._reset_state.wsgi_code + else: + status_int = result.status_int + self.assertEqual(202, status_int) def test_reset_error(self): self._setup_mock(dict(vm_state=vm_states.ERROR, @@ -712,8 +718,14 @@ class ResetStateTestsV21(test.NoDBTestCase): body = {"os-resetState": {"state": "error"}} result = self.admin_api._reset_state(self.request, self.uuid, body=body) - - self.assertEqual(result.status_int, 202) + # NOTE: on v2.1, http status code is set as wsgi_code of API + # method instead of status_int in a response object. + if isinstance(self.admin_api, + admin_actions_v21.AdminActionsController): + status_int = self.admin_api._reset_state.wsgi_code + else: + status_int = result.status_int + self.assertEqual(202, status_int) class ResetStateTestsV2(ResetStateTestsV21): diff --git a/nova/tests/api/openstack/compute/contrib/test_attach_interfaces.py b/nova/tests/api/openstack/compute/contrib/test_attach_interfaces.py index e71b3544f39a..bfba7b7b03f4 100644 --- a/nova/tests/api/openstack/compute/contrib/test_attach_interfaces.py +++ b/nova/tests/api/openstack/compute/contrib/test_attach_interfaces.py @@ -221,7 +221,14 @@ class InterfaceAttachTestsV21(test.NoDBTestCase): req.environ['nova.context'] = self.context result = self.attachments.delete(req, FAKE_UUID1, FAKE_PORT_ID1) - self.assertEqual('202 Accepted', result.status) + # NOTE: on v2.1, http status code is set as wsgi_code of API + # method instead of status_int in a response object. + if isinstance(self.attachments, + attach_interfaces_v3.InterfaceAttachmentController): + status_int = self.attachments.delete.wsgi_code + else: + status_int = result.status_int + self.assertEqual(202, status_int) def test_detach_interface_instance_locked(self): def fake_detach_interface_from_locked_server(self, context, diff --git a/nova/tests/api/openstack/compute/contrib/test_deferred_delete.py b/nova/tests/api/openstack/compute/contrib/test_deferred_delete.py index 5d1979145a3a..0c335d0a0219 100644 --- a/nova/tests/api/openstack/compute/contrib/test_deferred_delete.py +++ b/nova/tests/api/openstack/compute/contrib/test_deferred_delete.py @@ -55,7 +55,13 @@ class DeferredDeleteExtensionTestV21(test.NoDBTestCase): self.mox.ReplayAll() res = self.extension._force_delete(self.fake_req, self.fake_uuid, self.fake_input_dict) - self.assertEqual(res.status_int, 202) + # NOTE: on v2.1, http status code is set as wsgi_code of API + # method instead of status_int in a response object. + if isinstance(self.extension, dd_v21.DeferredDeleteController): + status_int = self.extension._force_delete.wsgi_code + else: + status_int = res.status_int + self.assertEqual(202, status_int) def test_force_delete_instance_not_found(self): self.mox.StubOutWithMock(compute_api.API, 'get') @@ -97,7 +103,13 @@ class DeferredDeleteExtensionTestV21(test.NoDBTestCase): self.mox.ReplayAll() res = self.extension._restore(self.fake_req, self.fake_uuid, self.fake_input_dict) - self.assertEqual(res.status_int, 202) + # NOTE: on v2.1, http status code is set as wsgi_code of API + # method instead of status_int in a response object. + if isinstance(self.extension, dd_v21.DeferredDeleteController): + status_int = self.extension._restore.wsgi_code + else: + status_int = res.status_int + self.assertEqual(202, status_int) def test_restore_instance_not_found(self): self.mox.StubOutWithMock(compute_api.API, 'get') diff --git a/nova/tests/api/openstack/compute/contrib/test_flavor_manage.py b/nova/tests/api/openstack/compute/contrib/test_flavor_manage.py index 92502da3bef3..937fa8581e92 100644 --- a/nova/tests/api/openstack/compute/contrib/test_flavor_manage.py +++ b/nova/tests/api/openstack/compute/contrib/test_flavor_manage.py @@ -130,7 +130,15 @@ class FlavorManageTestV21(test.NoDBTestCase): def test_delete(self): req = fakes.HTTPRequest.blank(self.base_url + '/1234') res = self.controller._delete(req, 1234) - self.assertEqual(res.status_int, 202) + + # NOTE: on v2.1, http status code is set as wsgi_code of API + # method instead of status_int in a response object. + if isinstance(self.controller, + flavormanage_v21.FlavorManageController): + status_int = self.controller._delete.wsgi_code + else: + status_int = res.status_int + self.assertEqual(202, status_int) # subsequent delete should fail self.assertRaises(webob.exc.HTTPNotFound, diff --git a/nova/tests/api/openstack/compute/contrib/test_server_group_quotas.py b/nova/tests/api/openstack/compute/contrib/test_server_group_quotas.py index 8a516ae747eb..1f97c5ec4fe9 100644 --- a/nova/tests/api/openstack/compute/contrib/test_server_group_quotas.py +++ b/nova/tests/api/openstack/compute/contrib/test_server_group_quotas.py @@ -163,7 +163,14 @@ class ServerGroupQuotasTestV21(test.TestCase): req = fakes.HTTPRequest.blank('/v2/fake/os-server-groups/123') resp = self.controller.delete(req, '123') self.assertTrue(self.called) - self.assertEqual(resp.status_int, 204) + + # NOTE: on v2.1, http status code is set as wsgi_code of API + # method instead of status_int in a response object. + if isinstance(self.controller, sg_v3.ServerGroupController): + status_int = self.controller.delete.wsgi_code + else: + status_int = resp.status_int + self.assertEqual(204, status_int) class ServerGroupQuotasTestV2(ServerGroupQuotasTestV21): diff --git a/nova/tests/api/openstack/compute/contrib/test_server_groups.py b/nova/tests/api/openstack/compute/contrib/test_server_groups.py index 962df405b388..7d4b4d88ca97 100644 --- a/nova/tests/api/openstack/compute/contrib/test_server_groups.py +++ b/nova/tests/api/openstack/compute/contrib/test_server_groups.py @@ -354,7 +354,14 @@ class ServerGroupTestV21(test.TestCase): '/os-server-groups/123') resp = self.controller.delete(req, '123') self.assertTrue(self.called) - self.assertEqual(resp.status_int, 204) + + # NOTE: on v2.1, http status code is set as wsgi_code of API + # method instead of status_int in a response object. + if isinstance(self.controller, sg_v3.ServerGroupController): + status_int = self.controller.delete.wsgi_code + else: + status_int = resp.status_int + self.assertEqual(204, status_int) def test_delete_non_existing_server_group(self): req = fakes.HTTPRequest.blank(self._get_url() + diff --git a/nova/tests/api/openstack/compute/contrib/test_volumes.py b/nova/tests/api/openstack/compute/contrib/test_volumes.py index 6ef3fc102423..8d3c015ab02c 100644 --- a/nova/tests/api/openstack/compute/contrib/test_volumes.py +++ b/nova/tests/api/openstack/compute/contrib/test_volumes.py @@ -1006,7 +1006,14 @@ class DeleteSnapshotTestCaseV21(test.TestCase): self.req.method = 'DELETE' result = self.controller.delete(self.req, result['snapshot']['id']) - self.assertEqual(result.status_int, 202) + + # NOTE: on v2.1, http status code is set as wsgi_code of API + # method instead of status_int in a response object. + if isinstance(self.controller, volumes_v3.SnapshotController): + status_int = self.controller.delete.wsgi_code + else: + status_int = result.status_int + self.assertEqual(202, status_int) def test_delete_snapshot_not_exists(self): def fake_delete_snapshot_not_exist(self, context, snapshot_id): diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_extended_volumes.py b/nova/tests/api/openstack/compute/plugins/v3/test_extended_volumes.py index 1922005ba9fd..4de7b55878af 100644 --- a/nova/tests/api/openstack/compute/plugins/v3/test_extended_volumes.py +++ b/nova/tests/api/openstack/compute/plugins/v3/test_extended_volumes.py @@ -327,8 +327,9 @@ class ExtendedVolumesTest(test.TestCase): def test_swap_volume(self): self.stubs.Set(compute.api.API, 'swap_volume', fake_swap_volume) - result = self._test_swap() - self.assertEqual('202 Accepted', result.status) + # Check any exceptions don't happen and status code + self._test_swap() + self.assertEqual(202, self.Controller.swap.wsgi_code) def test_swap_volume_for_locked_server(self): def fake_swap_volume_for_locked_server(self, context, instance,