summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--etc/nova/policy.json1
-rw-r--r--nova/api/openstack/compute/contrib/admin_actions.py32
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_admin_actions.py60
-rw-r--r--nova/tests/policy.json1
4 files changed, 94 insertions, 0 deletions
diff --git a/etc/nova/policy.json b/etc/nova/policy.json
index 6fa6da5fb..f61c4dcac 100644
--- a/etc/nova/policy.json
+++ b/etc/nova/policy.json
@@ -22,6 +22,7 @@
"compute_extension:admin_actions:injectNetworkInfo": [["rule:admin_api"]],
"compute_extension:admin_actions:createBackup": [["rule:admin_or_owner"]],
"compute_extension:admin_actions:migrateLive": [["rule:admin_api"]],
+ "compute_extension:admin_actions:resetState": [["rule:admin_api"]],
"compute_extension:admin_actions:migrate": [["rule:admin_api"]],
"compute_extension:aggregates": [["rule:admin_api"]],
"compute_extension:certificates": [],
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": [],