diff --git a/doc/v3/api_samples/os-admin-actions/admin-actions-inject-network-info.json b/doc/v3/api_samples/os-admin-actions/admin-actions-inject-network-info.json index 6e59393b65..62e16737b0 100644 --- a/doc/v3/api_samples/os-admin-actions/admin-actions-inject-network-info.json +++ b/doc/v3/api_samples/os-admin-actions/admin-actions-inject-network-info.json @@ -1,3 +1,3 @@ { - "inject_network_info": null -} \ No newline at end of file + "injectNetworkInfo": null +} diff --git a/doc/v3/api_samples/os-admin-actions/admin-actions-reset-network.json b/doc/v3/api_samples/os-admin-actions/admin-actions-reset-network.json index 5ffe0baead..7c79cb68a5 100644 --- a/doc/v3/api_samples/os-admin-actions/admin-actions-reset-network.json +++ b/doc/v3/api_samples/os-admin-actions/admin-actions-reset-network.json @@ -1,3 +1,3 @@ { - "reset_network": null -} \ No newline at end of file + "resetNetwork": null +} diff --git a/doc/v3/api_samples/os-admin-actions/admin-actions-reset-server-state.json b/doc/v3/api_samples/os-admin-actions/admin-actions-reset-server-state.json index 753e7d2a24..013aed4824 100644 --- a/doc/v3/api_samples/os-admin-actions/admin-actions-reset-server-state.json +++ b/doc/v3/api_samples/os-admin-actions/admin-actions-reset-server-state.json @@ -1,5 +1,5 @@ { - "reset_state": { + "os-resetState": { "state": "active" } -} \ No newline at end of file +} diff --git a/nova/api/openstack/compute/plugins/v3/admin_actions.py b/nova/api/openstack/compute/plugins/v3/admin_actions.py index a5939be545..877be4ea51 100644 --- a/nova/api/openstack/compute/plugins/v3/admin_actions.py +++ b/nova/api/openstack/compute/plugins/v3/admin_actions.py @@ -43,7 +43,7 @@ class AdminActionsController(wsgi.Controller): self.compute_api = compute.API() @extensions.expected_errors((404, 409)) - @wsgi.action('reset_network') + @wsgi.action('resetNetwork') def _reset_network(self, req, id, body): """Permit admins to reset networking on a server.""" context = req.environ['nova.context'] @@ -57,7 +57,7 @@ class AdminActionsController(wsgi.Controller): return webob.Response(status_int=202) @extensions.expected_errors((404, 409)) - @wsgi.action('inject_network_info') + @wsgi.action('injectNetworkInfo') def _inject_network_info(self, req, id, body): """Permit admins to inject network info into a server.""" context = req.environ['nova.context'] @@ -71,7 +71,7 @@ class AdminActionsController(wsgi.Controller): return webob.Response(status_int=202) @extensions.expected_errors((400, 404)) - @wsgi.action('reset_state') + @wsgi.action('os-resetState') @validation.schema(reset_server_state.reset_state) def _reset_state(self, req, id, body): """Permit admins to reset the state of a server.""" @@ -79,7 +79,7 @@ class AdminActionsController(wsgi.Controller): authorize(context, 'reset_state') # Identify the desired state from the body - state = state_map[body["reset_state"]["state"]] + state = state_map[body["os-resetState"]["state"]] instance = common.get_instance(self.compute_api, context, id, want_objects=True) diff --git a/nova/api/openstack/compute/schemas/v3/reset_server_state.py b/nova/api/openstack/compute/schemas/v3/reset_server_state.py index ca8bd09337..1f1990e76a 100644 --- a/nova/api/openstack/compute/schemas/v3/reset_server_state.py +++ b/nova/api/openstack/compute/schemas/v3/reset_server_state.py @@ -15,7 +15,7 @@ reset_state = { 'type': 'object', 'properties': { - 'reset_state': { + 'os-resetState': { 'type': 'object', 'properties': { 'state': { @@ -27,6 +27,6 @@ reset_state = { 'additionalProperties': False, }, }, - 'required': ['reset_state'], + 'required': ['os-resetState'], 'additionalProperties': False, } 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 1e07b8e8d8..844c1779d2 100644 --- a/nova/tests/api/openstack/compute/contrib/test_admin_actions.py +++ b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py @@ -15,7 +15,10 @@ import webob from nova.api.openstack import common -from nova.api.openstack.compute.contrib import admin_actions +from nova.api.openstack.compute.contrib import admin_actions as \ + admin_actions_v2 +from nova.api.openstack.compute.plugins.v3 import admin_actions as \ + admin_actions_v21 from nova.compute import vm_states import nova.context from nova import exception @@ -29,29 +32,11 @@ from nova.tests import fake_instance class CommonMixin(object): - def setUp(self): - super(CommonMixin, self).setUp() - self.controller = admin_actions.AdminActionsController() - self.compute_api = self.controller.compute_api - self.context = nova.context.RequestContext('fake', 'fake') - - def _fake_controller(*args, **kwargs): - return self.controller - - self.stubs.Set(admin_actions, 'AdminActionsController', - _fake_controller) - - self.flags( - osapi_compute_extension=[ - 'nova.api.openstack.compute.contrib.select_extensions'], - osapi_compute_ext_list=['Admin_actions']) - - self.app = fakes.wsgi_app(init_only=('servers',), - fake_auth_context=self.context) - self.mox.StubOutWithMock(self.compute_api, 'get') + admin_actions = None + fake_url = None def _make_request(self, url, body): - req = webob.Request.blank('/v2/fake' + url) + req = webob.Request.blank(self.fake_url + url) req.method = 'POST' req.body = jsonutils.dumps(body) req.content_type = 'application/json' @@ -99,7 +84,6 @@ class CommonMixin(object): getattr(self.compute_api, method)(self.context, instance) self.mox.ReplayAll() - res = self._make_request('/servers/%s/action' % instance['uuid'], {action: None}) self.assertEqual(202, res.status_int) @@ -145,23 +129,101 @@ class CommonMixin(object): instance = self._stub_instance_get() - args, kwargs = compute_api_args_map.get(action, ((), {})) + args, kwargs = (), {} + act = None + + if compute_api_args_map: + args, kwargs = compute_api_args_map.get(action, ((), {})) + act = body_map.get(action) + getattr(self.compute_api, method)(self.context, instance, *args, **kwargs).AndRaise( - exception.InstanceIsLocked(instance_uuid=instance['uuid'])) - + exception.InstanceIsLocked(instance_uuid=instance['uuid'])) self.mox.ReplayAll() - res = self._make_request('/servers/%s/action' % instance['uuid'], - {action: body_map.get(action)}) + {action: act}) self.assertEqual(409, res.status_int) + self.assertIn('Instance %s is locked' % instance['uuid'], res.body) # Do these here instead of tearDown because this method is called # more than once for the same test case self.mox.VerifyAll() self.mox.UnsetStubs() -class AdminActionsTest(CommonMixin, test.NoDBTestCase): +class AdminActionsTestV21(CommonMixin, test.NoDBTestCase): + admin_actions = admin_actions_v21 + fake_url = '/v3' + + def setUp(self): + super(AdminActionsTestV21, self).setUp() + self.controller = self.admin_actions.AdminActionsController() + self.compute_api = self.controller.compute_api + self.context = nova.context.RequestContext('fake', 'fake') + + def _fake_controller(*args, **kwargs): + return self.controller + + self.stubs.Set(self.admin_actions, 'AdminActionsController', + _fake_controller) + + self.app = self._get_app() + self.mox.StubOutWithMock(self.compute_api, 'get') + + def _get_app(self): + return fakes.wsgi_app_v3(init_only=('servers', + 'os-admin-actions'), + fake_auth_context=self.context) + + def test_actions(self): + actions = ['resetNetwork', 'injectNetworkInfo'] + method_translations = {'resetNetwork': 'reset_network', + 'injectNetworkInfo': 'inject_network_info'} + + for action in actions: + method = method_translations.get(action) + self.mox.StubOutWithMock(self.compute_api, method or action) + self._test_action(action, method=method) + # Re-mock this. + self.mox.StubOutWithMock(self.compute_api, 'get') + + def test_actions_with_non_existed_instance(self): + actions = ['resetNetwork', 'injectNetworkInfo', 'os-resetState'] + body_map = {'os-resetState': {'state': 'active'}} + + for action in actions: + self._test_non_existing_instance(action, + body_map=body_map) + # Re-mock this. + self.mox.StubOutWithMock(self.compute_api, 'get') + + def test_actions_with_locked_instance(self): + actions = ['resetNetwork', 'injectNetworkInfo'] + method_translations = {'resetNetwork': 'reset_network', + 'injectNetworkInfo': 'inject_network_info'} + + for action in actions: + method = method_translations.get(action) + self.mox.StubOutWithMock(self.compute_api, method or action) + self._test_locked_instance(action, method=method) + # Re-mock this. + self.mox.StubOutWithMock(self.compute_api, 'get') + + +class AdminActionsTestV2(AdminActionsTestV21): + admin_actions = admin_actions_v2 + fake_url = '/v2/fake' + + def setUp(self): + super(AdminActionsTestV2, self).setUp() + self.flags( + osapi_compute_extension=[ + 'nova.api.openstack.compute.contrib.select_extensions'], + osapi_compute_ext_list=['Admin_actions']) + + def _get_app(self): + return fakes.wsgi_app(init_only=('servers',), + fake_auth_context=self.context) + def test_actions(self): actions = ['pause', 'unpause', 'suspend', 'resume', 'migrate', 'resetNetwork', 'injectNetworkInfo', 'lock', @@ -369,9 +431,29 @@ class AdminActionsTest(CommonMixin, test.NoDBTestCase): self.assertEqual(403, res.status_int) -class CreateBackupTests(CommonMixin, test.NoDBTestCase): +class CreateBackupTestsV2(CommonMixin, test.NoDBTestCase): + fake_url = '/v2/fake' + def setUp(self): - super(CreateBackupTests, self).setUp() + super(CreateBackupTestsV2, self).setUp() + self.controller = admin_actions_v2.AdminActionsController() + self.compute_api = self.controller.compute_api + self.context = nova.context.RequestContext('fake', 'fake') + + def _fake_controller(*args, **kwargs): + return self.controller + + self.stubs.Set(admin_actions_v2, 'AdminActionsController', + _fake_controller) + + self.flags( + osapi_compute_extension=[ + 'nova.api.openstack.compute.contrib.select_extensions'], + osapi_compute_ext_list=['Admin_actions']) + + self.app = fakes.wsgi_app(init_only=('servers',), + fake_auth_context=self.context) + self.mox.StubOutWithMock(self.compute_api, 'get') self.mox.StubOutWithMock(common, 'check_img_metadata_properties_quota') self.mox.StubOutWithMock(self.compute_api, @@ -548,43 +630,47 @@ class CreateBackupTests(CommonMixin, test.NoDBTestCase): self.assertEqual(400, res.status_int) -class ResetStateTests(test.NoDBTestCase): +class ResetStateTestsV21(test.NoDBTestCase): + admin_act = admin_actions_v21 + bad_request = exception.ValidationError + fake_url = '/servers' + def setUp(self): - super(ResetStateTests, self).setUp() - + super(ResetStateTestsV21, self).setUp() self.uuid = uuidutils.generate_uuid() - - self.admin_api = admin_actions.AdminActionsController() + self.admin_api = self.admin_act.AdminActionsController() self.compute_api = self.admin_api.compute_api - url = '/fake/servers/%s/action' % self.uuid - self.request = fakes.HTTPRequest.blank(url) + url = '%s/%s/action' % (self.fake_url, self.uuid) + self.request = self._get_request(url) self.context = self.request.environ['nova.context'] + def _get_request(self, url): + return fakes.HTTPRequestV3.blank(url) + def test_no_state(self): - self.assertRaises(webob.exc.HTTPBadRequest, + self.assertRaises(self.bad_request, self.admin_api._reset_state, self.request, self.uuid, - {"os-resetState": None}) + body={"os-resetState": None}) def test_bad_state(self): - self.assertRaises(webob.exc.HTTPBadRequest, + self.assertRaises(self.bad_request, self.admin_api._reset_state, self.request, self.uuid, - {"os-resetState": {"state": "spam"}}) + body={"os-resetState": {"state": "spam"}}) def test_no_instance(self): self.mox.StubOutWithMock(self.compute_api, 'get') - exc = exception.InstanceNotFound(instance_id='inst_id') + exc = exception.InstanceNotFound(instance_id='inst_ud') self.compute_api.get(self.context, self.uuid, expected_attrs=None, want_objects=True).AndRaise(exc) - self.mox.ReplayAll() self.assertRaises(webob.exc.HTTPNotFound, self.admin_api._reset_state, self.request, self.uuid, - {"os-resetState": {"state": "active"}}) + body={"os-resetState": {"state": "active"}}) def _setup_mock(self, expected): instance = objects.Instance() @@ -628,3 +714,12 @@ class ResetStateTests(test.NoDBTestCase): body=body) self.assertEqual(result.status_int, 202) + + +class ResetStateTestsV2(ResetStateTestsV21): + admin_act = admin_actions_v2 + bad_request = webob.exc.HTTPBadRequest + fake_url = '/fake/servers' + + def _get_request(self, url): + return fakes.HTTPRequest.blank(url) diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_admin_actions.py b/nova/tests/api/openstack/compute/plugins/v3/test_admin_actions.py deleted file mode 100644 index 728e85a943..0000000000 --- a/nova/tests/api/openstack/compute/plugins/v3/test_admin_actions.py +++ /dev/null @@ -1,262 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import webob - -from nova.api.openstack.compute.plugins.v3 import admin_actions -from nova.compute import vm_states -import nova.context -from nova import exception -from nova import objects -from nova.openstack.common import jsonutils -from nova.openstack.common import timeutils -from nova.openstack.common import uuidutils -from nova import test -from nova.tests.api.openstack import fakes -from nova.tests import fake_instance - - -class CommonMixin(object): - def setUp(self): - super(CommonMixin, self).setUp() - self.controller = admin_actions.AdminActionsController() - self.compute_api = self.controller.compute_api - self.context = nova.context.RequestContext('fake', 'fake') - - def _fake_controller(*args, **kwargs): - return self.controller - - self.stubs.Set(admin_actions, 'AdminActionsController', - _fake_controller) - - self.app = fakes.wsgi_app_v3(init_only=('servers', - 'os-admin-actions'), - fake_auth_context=self.context) - self.mox.StubOutWithMock(self.compute_api, 'get') - - def _make_request(self, url, body): - req = webob.Request.blank('/v3' + url) - req.method = 'POST' - req.body = jsonutils.dumps(body) - req.content_type = 'application/json' - return req.get_response(self.app) - - def _stub_instance_get(self, uuid=None): - if uuid is None: - uuid = uuidutils.generate_uuid() - instance = fake_instance.fake_db_instance( - id=1, uuid=uuid, vm_state=vm_states.ACTIVE, - task_state=None, launched_at=timeutils.utcnow()) - instance = objects.Instance._from_db_object( - self.context, objects.Instance(), instance) - self.compute_api.get(self.context, uuid, expected_attrs=None, - want_objects=True).AndReturn(instance) - return instance - - def _stub_instance_get_failure(self, exc_info, uuid=None): - if uuid is None: - uuid = uuidutils.generate_uuid() - self.compute_api.get(self.context, uuid, expected_attrs=None, - want_objects=True).AndRaise(exc_info) - return uuid - - def _test_non_existing_instance(self, action, body_map=None): - uuid = uuidutils.generate_uuid() - self._stub_instance_get_failure( - exception.InstanceNotFound(instance_id=uuid), uuid=uuid) - - self.mox.ReplayAll() - - res = self._make_request('/servers/%s/action' % uuid, - {action: body_map.get(action)}) - self.assertEqual(404, res.status_int) - # Do these here instead of tearDown because this method is called - # more than once for the same test case - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def _test_action(self, action, body=None, method=None): - if method is None: - method = action - - instance = self._stub_instance_get() - getattr(self.compute_api, method)(self.context, instance) - - self.mox.ReplayAll() - - res = self._make_request('/servers/%s/action' % instance['uuid'], - {action: None}) - self.assertEqual(202, res.status_int) - # Do these here instead of tearDown because this method is called - # more than once for the same test case - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def _test_invalid_state(self, action, method=None, body_map=None, - compute_api_args_map=None): - if method is None: - method = action - if body_map is None: - body_map = {} - if compute_api_args_map is None: - compute_api_args_map = {} - - instance = self._stub_instance_get() - - args, kwargs = compute_api_args_map.get(action, ((), {})) - - getattr(self.compute_api, method)(self.context, instance, - *args, **kwargs).AndRaise( - exception.InstanceInvalidState( - attr='vm_state', instance_uuid=instance['uuid'], - state='foo', method=method)) - - self.mox.ReplayAll() - - res = self._make_request('/servers/%s/action' % instance['uuid'], - {action: body_map.get(action)}) - self.assertEqual(409, res.status_int) - self.assertIn("Cannot \'%s\' while instance" % action, res.body) - # Do these here instead of tearDown because this method is called - # more than once for the same test case - self.mox.VerifyAll() - self.mox.UnsetStubs() - - def _test_locked_instance(self, action, method=None): - if method is None: - method = action - - instance = self._stub_instance_get() - getattr(self.compute_api, method)(self.context, instance).AndRaise( - exception.InstanceIsLocked(instance_uuid=instance['uuid'])) - - self.mox.ReplayAll() - - res = self._make_request('/servers/%s/action' % instance['uuid'], - {action: None}) - self.assertEqual(409, res.status_int) - self.assertIn('Instance %s is locked' % instance['uuid'], res.body) - # Do these here instead of tearDown because this method is called - # more than once for the same test case - self.mox.VerifyAll() - self.mox.UnsetStubs() - - -class AdminActionsTest(CommonMixin, test.NoDBTestCase): - def test_actions(self): - actions = ['reset_network', 'inject_network_info'] - - for action in actions: - self.mox.StubOutWithMock(self.compute_api, action) - self._test_action(action) - # Re-mock this. - self.mox.StubOutWithMock(self.compute_api, 'get') - - def test_actions_with_non_existed_instance(self): - actions = ['reset_network', 'inject_network_info', 'reset_state'] - body_map = {'reset_state': {'state': 'active'}} - for action in actions: - self._test_non_existing_instance(action, - body_map=body_map) - # Re-mock this. - self.mox.StubOutWithMock(self.compute_api, 'get') - - def test_actions_with_locked_instance(self): - actions = ['reset_network', 'inject_network_info'] - - for action in actions: - self.mox.StubOutWithMock(self.compute_api, action) - self._test_locked_instance(action) - # Re-mock this. - self.mox.StubOutWithMock(self.compute_api, 'get') - - -class ResetStateTests(test.NoDBTestCase): - def setUp(self): - super(ResetStateTests, self).setUp() - - self.uuid = uuidutils.generate_uuid() - - self.admin_api = admin_actions.AdminActionsController() - self.compute_api = self.admin_api.compute_api - - url = '/servers/%s/action' % self.uuid - self.request = fakes.HTTPRequestV3.blank(url) - self.context = self.request.environ['nova.context'] - - def test_no_state(self): - self.assertRaises(exception.ValidationError, - self.admin_api._reset_state, - self.request, self.uuid, - body={"reset_state": None}) - - def test_bad_state(self): - self.assertRaises(exception.ValidationError, - self.admin_api._reset_state, - self.request, self.uuid, - body={"reset_state": {"state": "spam"}}) - - def test_no_instance(self): - self.mox.StubOutWithMock(self.compute_api, 'get') - exc = exception.InstanceNotFound(instance_id='inst_ud') - self.compute_api.get(self.context, self.uuid, expected_attrs=None, - want_objects=True).AndRaise(exc) - - self.mox.ReplayAll() - - self.assertRaises(webob.exc.HTTPNotFound, - self.admin_api._reset_state, - self.request, self.uuid, - body={"reset_state": {"state": "active"}}) - - def _setup_mock(self, expected): - instance = objects.Instance() - instance.uuid = self.uuid - instance.vm_state = 'fake' - instance.task_state = 'fake' - instance.obj_reset_changes() - - self.mox.StubOutWithMock(instance, 'save') - self.mox.StubOutWithMock(self.compute_api, 'get') - - def check_state(admin_state_reset=True): - self.assertEqual(set(expected.keys()), - instance.obj_what_changed()) - for k, v in expected.items(): - self.assertEqual(v, getattr(instance, k), - "Instance.%s doesn't match" % k) - instance.obj_reset_changes() - - self.compute_api.get(self.context, instance.uuid, expected_attrs=None, - want_objects=True).AndReturn(instance) - instance.save(admin_state_reset=True).WithSideEffects(check_state) - - def test_reset_active(self): - self._setup_mock(dict(vm_state=vm_states.ACTIVE, - task_state=None)) - self.mox.ReplayAll() - - body = {"reset_state": {"state": "active"}} - result = self.admin_api._reset_state(self.request, self.uuid, - body=body) - self.assertEqual(202, result.status_int) - - def test_reset_error(self): - self._setup_mock(dict(vm_state=vm_states.ERROR, - task_state=None)) - self.mox.ReplayAll() - body = {"reset_state": {"state": "error"}} - result = self.admin_api._reset_state(self.request, self.uuid, - body=body) - self.assertEqual(202, result.status_int) diff --git a/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-inject-network-info.json.tpl b/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-inject-network-info.json.tpl index c841a86b8f..62e16737b0 100644 --- a/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-inject-network-info.json.tpl +++ b/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-inject-network-info.json.tpl @@ -1,3 +1,3 @@ { - "inject_network_info": null + "injectNetworkInfo": null } diff --git a/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-reset-network.json.tpl b/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-reset-network.json.tpl index 64754f7a2b..7c79cb68a5 100644 --- a/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-reset-network.json.tpl +++ b/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-reset-network.json.tpl @@ -1,3 +1,3 @@ { - "reset_network": null + "resetNetwork": null } diff --git a/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-reset-server-state.json.tpl b/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-reset-server-state.json.tpl index 0d51dbc09e..013aed4824 100644 --- a/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-reset-server-state.json.tpl +++ b/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-reset-server-state.json.tpl @@ -1,5 +1,5 @@ { - "reset_state": { + "os-resetState": { "state": "active" } } diff --git a/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-reset-state.json.tpl b/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-reset-state.json.tpl index ed373e95bb..72d9478678 100644 --- a/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-reset-state.json.tpl +++ b/nova/tests/integrated/v3/api_samples/os-admin-actions/admin-actions-reset-state.json.tpl @@ -1,5 +1,5 @@ { - 'reset_state': { + 'os-resetState': { 'state': 'active' } }