diff options
author | Kevin L. Mitchell <kevin.mitchell@rackspace.com> | 2012-06-20 11:43:03 -0500 |
---|---|---|
committer | Kevin L. Mitchell <kevin.mitchell@rackspace.com> | 2012-06-20 15:24:26 -0500 |
commit | 964adebb3c4ca297dd61487dffffca48283be3b5 (patch) | |
tree | b2780c078b1c38f2f71aa09b6ffd6685f6d2fa64 /nova | |
parent | cf1854946838bf14607d05acd6f347702372b744 (diff) | |
download | nova-964adebb3c4ca297dd61487dffffca48283be3b5.tar.gz nova-964adebb3c4ca297dd61487dffffca48283be3b5.tar.xz nova-964adebb3c4ca297dd61487dffffca48283be3b5.zip |
Admin action to reset states.
Adds an Admin API action to reset the state of an instance. This will
at least allow easy clean-up from bugs which corrupt the state of an
instance and inhibit the owner of the instance from deleting it.
Change-Id: Ia059cbd643e24e04dede06da330f444d03b07674
Diffstat (limited to 'nova')
-rw-r--r-- | nova/api/openstack/compute/contrib/admin_actions.py | 32 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_admin_actions.py | 60 | ||||
-rw-r--r-- | nova/tests/policy.json | 1 |
3 files changed, 93 insertions, 0 deletions
diff --git a/nova/api/openstack/compute/contrib/admin_actions.py b/nova/api/openstack/compute/contrib/admin_actions.py index 18adf8377..72815ed00 100644 --- a/nova/api/openstack/compute/contrib/admin_actions.py +++ b/nova/api/openstack/compute/contrib/admin_actions.py @@ -22,6 +22,7 @@ from nova.api.openstack import common from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import compute +from nova.compute import vm_states from nova import exception from nova import flags from nova import log as logging @@ -31,6 +32,10 @@ FLAGS = flags.FLAGS LOG = logging.getLogger(__name__) +# States usable in resetState action +state_map = dict(active=vm_states.ACTIVE, error=vm_states.ERROR) + + def authorize(context, action_name): action = 'admin_actions:%s' % action_name extensions.extension_authorizer('compute', action)(context) @@ -284,6 +289,33 @@ class AdminActionsController(wsgi.Controller): return webob.Response(status_int=202) + @wsgi.action('os-resetState') + def _reset_state(self, req, id, body): + """Permit admins to reset the state of a server.""" + context = req.environ["nova.context"] + authorize(context, 'resetState') + + # Identify the desired state from the body + try: + state = state_map[body["os-resetState"]["state"]] + except (TypeError, KeyError): + msg = _("Desired state must be specified. Valid states " + "are: %s") % ', '.join(sorted(state_map.keys())) + raise exc.HTTPBadRequest(explanation=msg) + + try: + instance = self.compute_api.get(context, id) + self.compute_api.update(context, instance, + vm_state=state, + task_state=None) + except exception.InstanceNotFound: + raise exc.HTTPNotFound(_("Server not found")) + except Exception: + readable = traceback.format_exc() + LOG.exception(_("Compute.api::resetState %s"), readable) + raise exc.HTTPUnprocessableEntity() + return webob.Response(status_int=202) + class Admin_actions(extensions.ExtensionDescriptor): """Enable admin-only server actions 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 922e09f0b..330e24be6 100644 --- a/nova/tests/api/openstack/compute/contrib/test_admin_actions.py +++ b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py @@ -17,6 +17,7 @@ import datetime import webob from nova.api.openstack import compute as compute_api +from nova.api.openstack.compute.contrib import admin_actions from nova import compute from nova.compute import vm_states from nova import context @@ -285,3 +286,62 @@ class CreateBackupTests(test.TestCase): request = self._get_request(body) response = request.get_response(self.app) self.assertEqual(response.status_int, 409) + + +class ResetStateTests(test.TestCase): + def setUp(self): + super(ResetStateTests, self).setUp() + + self.exists = True + self.kwargs = None + self.uuid = utils.gen_uuid() + + def fake_get(inst, context, instance_id): + if self.exists: + return dict(id=1, uuid=instance_id, vm_state=vm_states.ACTIVE) + raise exception.InstanceNotFound() + + def fake_update(inst, context, instance, **kwargs): + self.kwargs = kwargs + + self.stubs.Set(compute.API, 'get', fake_get) + self.stubs.Set(compute.API, 'update', fake_update) + self.admin_api = admin_actions.AdminActionsController() + + url = '/fake/servers/%s/action' % self.uuid + self.request = fakes.HTTPRequest.blank(url) + + def test_no_state(self): + self.assertRaises(webob.exc.HTTPBadRequest, + self.admin_api._reset_state, + self.request, 'inst_id', + {"os-resetState": None}) + + def test_bad_state(self): + self.assertRaises(webob.exc.HTTPBadRequest, + self.admin_api._reset_state, + self.request, 'inst_id', + {"os-resetState": {"state": "spam"}}) + + def test_no_instance(self): + self.exists = False + self.assertRaises(webob.exc.HTTPNotFound, + self.admin_api._reset_state, + self.request, 'inst_id', + {"os-resetState": {"state": "active"}}) + + def test_reset_active(self): + body = {"os-resetState": {"state": "active"}} + result = self.admin_api._reset_state(self.request, 'inst_id', body) + + self.assertEqual(result.status_int, 202) + self.assertEqual(self.kwargs, dict(vm_state=vm_states.ACTIVE, + task_state=None)) + + def test_reset_error(self): + body = {"os-resetState": {"state": "error"}} + result = self.admin_api._reset_state(self.request, 'inst_id', body) + + self.assertEqual(result.status_int, 202) + self.assertEqual(self.kwargs, dict(vm_state=vm_states.ERROR, + task_state=None)) diff --git a/nova/tests/policy.json b/nova/tests/policy.json index 8f8ea769c..e6b534f5a 100644 --- a/nova/tests/policy.json +++ b/nova/tests/policy.json @@ -78,6 +78,7 @@ "compute_extension:admin_actions:injectNetworkInfo": [], "compute_extension:admin_actions:createBackup": [], "compute_extension:admin_actions:migrateLive": [], + "compute_extension:admin_actions:resetState": [], "compute_extension:admin_actions:migrate": [], "compute_extension:aggregates": [], "compute_extension:certificates": [], |