From c733462b229290210d6249e0f8fc7d2beaab9091 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Sun, 27 Nov 2011 23:26:49 -0800 Subject: [PATCH] Bug #897091: "nova actions" fails with HTTP 400 / TypeError if a server action has been performed Fix code in Controller.action that was overwriting the definition of the actions method with a dictionary. This meant that 'nova actions' would fail if 'nova reboot' had previously been called. Added two tests, one for the actions call in general, and one for this failure mode specifically. (cherry picked from commit 4a76167e354eed4aa98232fbf6c845f86ce4cf22) Change-Id: I695bb5c4dcfba96a5aba54125a8f3163e1a6a193 --- nova/api/openstack/servers.py | 8 +++--- nova/tests/api/openstack/test_servers.py | 35 +++++++++++++++++++++++- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 29ae375dab0..86816742e31 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -207,7 +207,7 @@ def _update(self, context, req, id, inst_dict): def action(self, req, id, body): """Multi-purpose method used to take actions on a server""" - self.actions = { + _actions = { 'changePassword': self._action_change_password, 'reboot': self._action_reboot, 'resize': self._action_resize, @@ -221,11 +221,11 @@ def action(self, req, id, body): admin_actions = { 'createBackup': self._action_create_backup, } - self.actions.update(admin_actions) + _actions.update(admin_actions) for key in body: - if key in self.actions: - return self.actions[key](body, req, id) + if key in _actions: + return _actions[key](body, req, id) else: msg = _("There is no such server action: %s") % (key,) raise exc.HTTPBadRequest(explanation=msg) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 817b8a3a282..43633bed676 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -42,6 +42,7 @@ import nova.db.api import nova.scheduler.api from nova.db.sqlalchemy.models import Instance +from nova.db.sqlalchemy.models import InstanceActions from nova.db.sqlalchemy.models import InstanceMetadata import nova.image.fake import nova.rpc @@ -229,6 +230,19 @@ def fake_compute_api(cls, req, id): return True +_fake_compute_actions = [ + dict( + created_at=str(datetime.datetime(2010, 11, 11, 11, 0, 0)), + action='Fake Action', + error='Fake Error', + ) + ] + + +def fake_compute_actions(_1, _2, _3): + return [InstanceActions(**a) for a in _fake_compute_actions] + + def find_host(self, context, instance_id): return "nova" @@ -272,7 +286,7 @@ def setUp(self): self.stubs.Set(nova.compute.API, 'suspend', fake_compute_api) self.stubs.Set(nova.compute.API, 'resume', fake_compute_api) self.stubs.Set(nova.compute.API, "get_diagnostics", fake_compute_api) - self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api) + self.stubs.Set(nova.compute.API, "get_actions", fake_compute_actions) self.webreq = common.webob_factory('/v1.0/servers') self.config_drive = None @@ -2417,6 +2431,25 @@ def test_get_all_server_details_v1_1(self): self.assertEqual(s['status'], 'BUILD') self.assertEqual(s['metadata']['seq'], str(i)) + def test_server_actions(self): + req = webob.Request.blank('/v1.1/fake/servers/%s/actions' % FAKE_UUID) + req.method = "GET" + res = req.get_response(fakes.wsgi_app()) + res_dict = json.loads(res.body) + self.assertEqual(res_dict, {'actions': _fake_compute_actions}) + + def test_server_actions_after_reboot(self): + """ + Bug #897091 was this failure mode -- the /actions call failed if + /action had been called first. + """ + req = webob.Request.blank('/v1.1/fake/servers/%s/action' % FAKE_UUID) + req.method = 'POST' + req.body = json.dumps(dict(reboot=dict(type="HARD"))) + req.headers["content-type"] = "application/json" + req.get_response(fakes.wsgi_app()) + self.test_server_actions() + def test_get_all_server_details_with_host(self): ''' We want to make sure that if two instances are on the same host, then