From f3f46b532c528c31fbcdc0d18b18105c42a0d74c Mon Sep 17 00:00:00 2001 From: "Leandro I. Costantino" Date: Mon, 3 Mar 2014 21:09:52 -0300 Subject: [PATCH] Rescue API handle NotImplementedError There are several nova virt drivers that don't implement the rescue API, but the os compute API doesn't handle NotImplementedError, it returns a 400 instead of a 501. The patch add the proper logic to return a 501 instead. Change-Id: Ia649c4dadd50985efed631ce8f3e4b212646766e Closes-Bug: #1287367 --- nova/api/openstack/compute/contrib/rescue.py | 8 +++++ .../openstack/compute/plugins/v3/rescue.py | 12 +++++-- .../openstack/compute/contrib/test_rescue.py | 33 +++++++++++++++++++ .../compute/plugins/v3/test_rescue.py | 33 +++++++++++++++++++ 4 files changed, 84 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/compute/contrib/rescue.py b/nova/api/openstack/compute/contrib/rescue.py index 0233be24e5..4625dd3840 100644 --- a/nova/api/openstack/compute/contrib/rescue.py +++ b/nova/api/openstack/compute/contrib/rescue.py @@ -67,6 +67,10 @@ class RescueController(wsgi.Controller): except exception.InstanceNotRescuable as non_rescuable: raise exc.HTTPBadRequest( explanation=non_rescuable.format_message()) + except NotImplementedError: + msg = _("The rescue operation is not implemented by this " + "cloud.") + raise exc.HTTPNotImplemented(explanation=msg) return {'adminPass': password} @@ -81,6 +85,10 @@ class RescueController(wsgi.Controller): except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'unrescue') + except NotImplementedError: + msg = _("The unrescue operation is not implemented by this cloud.") + raise exc.HTTPNotImplemented(explanation=msg) + return webob.Response(status_int=202) diff --git a/nova/api/openstack/compute/plugins/v3/rescue.py b/nova/api/openstack/compute/plugins/v3/rescue.py index 9b15372851..ffe565b408 100644 --- a/nova/api/openstack/compute/plugins/v3/rescue.py +++ b/nova/api/openstack/compute/plugins/v3/rescue.py @@ -25,6 +25,7 @@ from nova.api.openstack import wsgi from nova.api import validation from nova import compute from nova import exception +from nova.openstack.common.gettextutils import _ from nova import utils @@ -42,7 +43,7 @@ class RescueController(wsgi.Controller): self.compute_api = compute.API() @wsgi.response(202) - @extensions.expected_errors((400, 404, 409)) + @extensions.expected_errors((400, 404, 409, 501)) @wsgi.action('rescue') @validation.schema(rescue.rescue) def _rescue(self, req, id, body): @@ -68,13 +69,16 @@ class RescueController(wsgi.Controller): except exception.InstanceNotRescuable as non_rescuable: raise exc.HTTPBadRequest( explanation=non_rescuable.format_message()) + except NotImplementedError: + msg = _("The rescue operation is not implemented by this cloud.") + raise exc.HTTPNotImplemented(explanation=msg) if CONF.enable_instance_password: return {'admin_password': password} else: return {} - @extensions.expected_errors((404, 409)) + @extensions.expected_errors((404, 409, 501)) @wsgi.action('unrescue') def _unrescue(self, req, id, body): """Unrescue an instance.""" @@ -87,6 +91,10 @@ class RescueController(wsgi.Controller): except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, 'unrescue') + except NotImplementedError: + msg = _("The unrescue operation is not implemented by this cloud.") + raise exc.HTTPNotImplemented(explanation=msg) + return webob.Response(status_int=202) diff --git a/nova/tests/api/openstack/compute/contrib/test_rescue.py b/nova/tests/api/openstack/compute/contrib/test_rescue.py index 1dfa7b7ac7..7d78d35eb6 100644 --- a/nova/tests/api/openstack/compute/contrib/test_rescue.py +++ b/nova/tests/api/openstack/compute/contrib/test_rescue.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import mock from oslo.config import cfg import webob @@ -128,3 +129,35 @@ class RescueTest(test.NoDBTestCase): resp = req.get_response(self.app) self.assertEqual(resp.status_int, 400) + + @mock.patch('nova.compute.api.API.rescue') + def test_rescue_raises_not_implemented(self, rescue_mock): + body = dict(rescue=None) + + def fake_rescue(*args, **kwargs): + raise NotImplementedError('not implemented') + + rescue_mock.side_effect = fake_rescue + req = webob.Request.blank('/v2/fake/servers/test_inst/action') + req.method = "POST" + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(self.app) + self.assertEqual(resp.status_int, 501) + + @mock.patch('nova.compute.api.API.unrescue') + def test_unrescue_raises_not_implemented(self, unrescue_mock): + body = dict(unrescue=None) + + def fake_unrescue(*args, **kwargs): + raise NotImplementedError('not implemented') + + unrescue_mock.side_effect = fake_unrescue + req = webob.Request.blank('/v2/fake/servers/test_inst/action') + req.method = "POST" + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(self.app) + self.assertEqual(resp.status_int, 501) diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_rescue.py b/nova/tests/api/openstack/compute/plugins/v3/test_rescue.py index 6b84e31f66..bf461ca6e0 100644 --- a/nova/tests/api/openstack/compute/plugins/v3/test_rescue.py +++ b/nova/tests/api/openstack/compute/plugins/v3/test_rescue.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import mock from oslo.config import cfg import webob @@ -168,3 +169,35 @@ class RescueTest(test.NoDBTestCase): resp = req.get_response(self.app) self.assertEqual(400, resp.status_int) + + @mock.patch('nova.compute.api.API.rescue') + def test_rescue_raises_not_implemented(self, rescue_mock): + body = dict(rescue=None) + + def fake_rescue(*args, **kwargs): + raise NotImplementedError('fake message') + + rescue_mock.side_effect = fake_rescue + req = webob.Request.blank('/v3/servers/test_inst/action') + req.method = "POST" + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(self.app) + self.assertEqual(resp.status_int, 501) + + @mock.patch('nova.compute.api.API.unrescue') + def test_unrescue_raises_not_implemented(self, unrescue_mock): + body = dict(unrescue=None) + + def fake_unrescue(*args, **kwargs): + raise NotImplementedError('fake message') + + unrescue_mock.side_effect = fake_unrescue + req = webob.Request.blank('/v3/servers/test_inst/action') + req.method = "POST" + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" + + resp = req.get_response(self.app) + self.assertEqual(resp.status_int, 501)