diff --git a/doc/api_samples/servers/v2.17/server-action-trigger-crash-dump.json b/doc/api_samples/servers/v2.17/server-action-trigger-crash-dump.json new file mode 100644 index 000000000000..968c7c05e8b7 --- /dev/null +++ b/doc/api_samples/servers/v2.17/server-action-trigger-crash-dump.json @@ -0,0 +1,3 @@ +{ + "trigger_crash_dump": null +} diff --git a/doc/api_samples/versions/v21-version-get-resp.json b/doc/api_samples/versions/v21-version-get-resp.json index 4cb8d06a338e..d5c0c6658713 100644 --- a/doc/api_samples/versions/v21-version-get-resp.json +++ b/doc/api_samples/versions/v21-version-get-resp.json @@ -19,7 +19,7 @@ } ], "status": "CURRENT", - "version": "2.16", + "version": "2.17", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/doc/api_samples/versions/versions-get-resp.json b/doc/api_samples/versions/versions-get-resp.json index 67f189f8c1aa..068d70fb2ebf 100644 --- a/doc/api_samples/versions/versions-get-resp.json +++ b/doc/api_samples/versions/versions-get-resp.json @@ -22,7 +22,7 @@ } ], "status": "CURRENT", - "version": "2.16", + "version": "2.17", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/etc/nova/policy.json b/etc/nova/policy.json index 84cd7465f3dd..6c467360ce94 100644 --- a/etc/nova/policy.json +++ b/etc/nova/policy.json @@ -269,6 +269,7 @@ "os_compute_api:servers:create_image:allow_volume_backed": "", "os_compute_api:servers:start": "rule:admin_or_owner", "os_compute_api:servers:stop": "rule:admin_or_owner", + "os_compute_api:servers:trigger_crash_dump": "rule:admin_or_owner", "os_compute_api:os-access-ips:discoverable": "", "os_compute_api:os-access-ips": "", "os_compute_api:os-admin-actions": "rule:admin_api", diff --git a/nova/api/openstack/api_version_request.py b/nova/api/openstack/api_version_request.py index 81d19d43685b..da9af09ba510 100644 --- a/nova/api/openstack/api_version_request.py +++ b/nova/api/openstack/api_version_request.py @@ -58,6 +58,7 @@ REST_API_VERSION_HISTORY = """REST API Version History: adminPass from the response body * 2.15 - Add soft-affinity and soft-anti-affinity policies * 2.16 - Exposes host_status for servers/detail and servers/{server_id} + * 2.17 - Add trigger_crash_dump to server actions """ # The minimum and maximum versions of the API supported @@ -66,7 +67,7 @@ REST_API_VERSION_HISTORY = """REST API Version History: # Note(cyeoh): This only applies for the v2.1 API once microversions # support is fully merged. It does not affect the V2 API. _MIN_API_VERSION = "2.1" -_MAX_API_VERSION = "2.16" +_MAX_API_VERSION = "2.17" DEFAULT_API_VERSION = _MIN_API_VERSION diff --git a/nova/api/openstack/compute/schemas/servers.py b/nova/api/openstack/compute/schemas/servers.py index 43932436004c..0779f825f4ee 100644 --- a/nova/api/openstack/compute/schemas/servers.py +++ b/nova/api/openstack/compute/schemas/servers.py @@ -161,3 +161,14 @@ reboot = { 'required': ['reboot'], 'additionalProperties': False } + +trigger_crash_dump = { + 'type': 'object', + 'properties': { + 'trigger_crash_dump': { + 'type': 'null' + } + }, + 'required': ['trigger_crash_dump'], + 'additionalProperties': False +} diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index fda67f30cbfb..f08970b2f579 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -1162,6 +1162,26 @@ class ServersController(wsgi.Controller): common.raise_http_conflict_for_instance_invalid_state(state_error, 'stop', id) + @wsgi.Controller.api_version("2.17") + @wsgi.response(202) + @extensions.expected_errors((400, 404, 409)) + @wsgi.action('trigger_crash_dump') + @validation.schema(schema_servers.trigger_crash_dump) + def _action_trigger_crash_dump(self, req, id, body): + """Trigger crash dump in an instance""" + context = req.environ['nova.context'] + instance = self._get_instance(context, id) + authorize(context, instance, 'trigger_crash_dump') + try: + self.compute_api.trigger_crash_dump(context, instance) + except exception.InstanceInvalidState as state_error: + common.raise_http_conflict_for_instance_invalid_state(state_error, + 'trigger_crash_dump', id) + except (exception.InstanceNotReady, exception.InstanceIsLocked) as e: + raise webob.exc.HTTPConflict(explanation=e.format_message()) + except exception.NMINotSupported as e: + raise webob.exc.HTTPBadRequest(explanation=e.format_message()) + def remove_invalid_options(context, search_options, allowed_search_options): """Remove search options that are not valid for non-admin API/context.""" diff --git a/nova/api/openstack/rest_api_version_history.rst b/nova/api/openstack/rest_api_version_history.rst index 4564d8fb33e5..92732e197649 100644 --- a/nova/api/openstack/rest_api_version_history.rst +++ b/nova/api/openstack/rest_api_version_history.rst @@ -157,3 +157,9 @@ user documentation. Exposes new host_status attribute for servers/detail and servers/{server_id}. Ability to get nova-compute status when querying servers. By default, this is only exposed to cloud administrators. + +2.17 +---- + + Add a new API for triggering crash dump in an instance. Different operation + systems in instance may need different configurations to trigger crash dump. diff --git a/nova/tests/functional/api_sample_tests/api_samples/servers/v2.17/server-action-trigger-crash-dump.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/servers/v2.17/server-action-trigger-crash-dump.json.tpl new file mode 100644 index 000000000000..968c7c05e8b7 --- /dev/null +++ b/nova/tests/functional/api_sample_tests/api_samples/servers/v2.17/server-action-trigger-crash-dump.json.tpl @@ -0,0 +1,3 @@ +{ + "trigger_crash_dump": null +} diff --git a/nova/tests/functional/api_sample_tests/api_samples/versions/v21-version-get-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/versions/v21-version-get-resp.json.tpl index 61da9c796e4b..25978ae2fc72 100644 --- a/nova/tests/functional/api_sample_tests/api_samples/versions/v21-version-get-resp.json.tpl +++ b/nova/tests/functional/api_sample_tests/api_samples/versions/v21-version-get-resp.json.tpl @@ -19,7 +19,7 @@ } ], "status": "CURRENT", - "version": "2.16", + "version": "2.17", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/nova/tests/functional/api_sample_tests/api_samples/versions/versions-get-resp.json.tpl b/nova/tests/functional/api_sample_tests/api_samples/versions/versions-get-resp.json.tpl index fd2160d81621..d472ed1389bd 100644 --- a/nova/tests/functional/api_sample_tests/api_samples/versions/versions-get-resp.json.tpl +++ b/nova/tests/functional/api_sample_tests/api_samples/versions/versions-get-resp.json.tpl @@ -22,7 +22,7 @@ } ], "status": "CURRENT", - "version": "2.16", + "version": "2.17", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/nova/tests/functional/api_sample_tests/test_servers.py b/nova/tests/functional/api_sample_tests/test_servers.py index 7d4288944e90..6d8221cf948f 100644 --- a/nova/tests/functional/api_sample_tests/test_servers.py +++ b/nova/tests/functional/api_sample_tests/test_servers.py @@ -267,3 +267,19 @@ class ServersSampleMultiStatusJsonTest(ServersSampleBase): response = self._do_get('servers?status=active&status=error') subs = {'id': uuid} self._verify_response('servers-list-resp', subs, response, 200) + + +class ServerTriggerCrashDumpJsonTest(ServersSampleBase): + sample_dir = 'servers' + microversion = '2.17' + scenarios = [('v2_17', {'api_major_version': 'v2.1'})] + + def test_trigger_crash_dump(self): + uuid = self._post_server() + + response = self._do_post('servers/%s/action' % uuid, + 'server-action-trigger-crash-dump', + {}, + api_version=self.microversion) + self.assertEqual(response.status_code, 202) + self.assertEqual(response.content, "") diff --git a/nova/tests/unit/api/openstack/compute/test_serversV21.py b/nova/tests/unit/api/openstack/compute/test_serversV21.py index e3a0e96e7f3e..dc8a571e920a 100644 --- a/nova/tests/unit/api/openstack/compute/test_serversV21.py +++ b/nova/tests/unit/api/openstack/compute/test_serversV21.py @@ -1937,6 +1937,91 @@ class ServersControllerUpdateTest(ControllerTest): self.controller.update, req, FAKE_UUID, body=body) +class ServersControllerTriggerCrashDumpTest(ControllerTest): + + def setUp(self): + super(ServersControllerTriggerCrashDumpTest, self).setUp() + + self.instance = fakes.stub_instance_obj(None, + vm_state=vm_states.ACTIVE) + + def fake_get(ctrl, ctxt, uuid): + if uuid != FAKE_UUID: + raise webob.exc.HTTPNotFound(explanation='fakeout') + return self.instance + + self.useFixture( + fixtures.MonkeyPatch('nova.api.openstack.compute.servers.' + 'ServersController._get_instance', + fake_get)) + + self.req = fakes.HTTPRequest.blank('/servers/%s/action' % FAKE_UUID) + self.req.api_version_request =\ + api_version_request.APIVersionRequest('2.17') + self.body = dict(trigger_crash_dump=None) + + @mock.patch.object(compute_api.API, 'trigger_crash_dump') + def test_trigger_crash_dump(self, mock_trigger_crash_dump): + ctxt = self.req.environ['nova.context'] + self.controller._action_trigger_crash_dump(self.req, FAKE_UUID, + body=self.body) + mock_trigger_crash_dump.assert_called_with(ctxt, self.instance) + + def test_trigger_crash_dump_policy_failed(self): + rule_name = "os_compute_api:servers:trigger_crash_dump" + self.policy.set_rules({rule_name: "project_id:non_fake"}) + exc = self.assertRaises(exception.PolicyNotAuthorized, + self.controller._action_trigger_crash_dump, + self.req, FAKE_UUID, body=self.body) + self.assertIn("os_compute_api:servers:trigger_crash_dump", + exc.format_message()) + + @mock.patch.object(compute_api.API, 'trigger_crash_dump', + fake_start_stop_not_ready) + def test_trigger_crash_dump_not_ready(self): + self.assertRaises(webob.exc.HTTPConflict, + self.controller._action_trigger_crash_dump, + self.req, FAKE_UUID, body=self.body) + + @mock.patch.object(compute_api.API, 'trigger_crash_dump', + fakes.fake_actions_to_locked_server) + def test_trigger_crash_dump_locked_server(self): + self.assertRaises(webob.exc.HTTPConflict, + self.controller._action_trigger_crash_dump, + self.req, FAKE_UUID, body=self.body) + + @mock.patch.object(compute_api.API, 'trigger_crash_dump', + fake_start_stop_invalid_state) + def test_trigger_crash_dump_invalid_state(self): + self.assertRaises(webob.exc.HTTPConflict, + self.controller._action_trigger_crash_dump, + self.req, FAKE_UUID, body=self.body) + + def test_trigger_crash_dump_with_bogus_id(self): + self.assertRaises(webob.exc.HTTPNotFound, + self.controller._action_trigger_crash_dump, + self.req, 'test_inst', body=self.body) + + def test_trigger_crash_dump_schema_invalid_type(self): + self.body['trigger_crash_dump'] = 'not null' + self.assertRaises(exception.ValidationError, + self.controller._action_trigger_crash_dump, + self.req, FAKE_UUID, body=self.body) + + def test_trigger_crash_dump_schema_extra_property(self): + self.body['extra_property'] = 'extra' + self.assertRaises(exception.ValidationError, + self.controller._action_trigger_crash_dump, + self.req, FAKE_UUID, body=self.body) + + @mock.patch.object(compute_api.API, 'trigger_crash_dump', + side_effect=exception.NMINotSupported) + def test_trigger_crash_dump_not_supported(self, mock_trigger_crash_dump): + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller._action_trigger_crash_dump, + self.req, FAKE_UUID, body=self.body) + + class ServerStatusTest(test.TestCase): def setUp(self): @@ -3876,6 +3961,17 @@ class ServersPolicyEnforcementV21(test.NoDBTestCase): rule, rule_name, self.controller._stop_server, self.req, FAKE_UUID, body={}) + @mock.patch.object(servers.ServersController, '_get_instance') + def test_trigger_crash_dump_policy_failed(self, _get_instance_mock): + _get_instance_mock.return_value = None + rule_name = "os_compute_api:servers:trigger_crash_dump" + rule = {rule_name: "project:non_fake"} + self.req.api_version_request =\ + api_version_request.APIVersionRequest('2.17') + self._common_policy_check( + rule, rule_name, self.controller._action_trigger_crash_dump, + self.req, FAKE_UUID, body={'trigger_crash_dump': None}) + def test_index_policy_failed(self): rule_name = "os_compute_api:servers:index" rule = {rule_name: "project:non_fake"} diff --git a/nova/tests/unit/api/openstack/compute/test_versions.py b/nova/tests/unit/api/openstack/compute/test_versions.py index 6a61c21731da..653c3d625bc0 100644 --- a/nova/tests/unit/api/openstack/compute/test_versions.py +++ b/nova/tests/unit/api/openstack/compute/test_versions.py @@ -66,7 +66,7 @@ EXP_VERSIONS = { "v2.1": { "id": "v2.1", "status": "CURRENT", - "version": "2.16", + "version": "2.17", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z", "links": [ @@ -128,7 +128,7 @@ class VersionsTestV20(test.NoDBTestCase): { "id": "v2.1", "status": "CURRENT", - "version": "2.16", + "version": "2.17", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z", "links": [ diff --git a/nova/tests/unit/fake_policy.py b/nova/tests/unit/fake_policy.py index 49877c516158..3fee5ac2d05f 100644 --- a/nova/tests/unit/fake_policy.py +++ b/nova/tests/unit/fake_policy.py @@ -124,6 +124,7 @@ policy_data = """ "os_compute_api:servers:update": "", "os_compute_api:servers:start": "", "os_compute_api:servers:stop": "", + "os_compute_api:servers:trigger_crash_dump": "", "os_compute_api:os-access-ips": "", "compute_extension:accounts": "", "compute_extension:admin_actions:pause": "", diff --git a/nova/tests/unit/test_policy.py b/nova/tests/unit/test_policy.py index ef33717b22f3..10542acabcbc 100644 --- a/nova/tests/unit/test_policy.py +++ b/nova/tests/unit/test_policy.py @@ -382,6 +382,7 @@ class RealRolePolicyTestCase(test.NoDBTestCase): "compute_extension:simple_tenant_usage:show", "os_compute_api:servers:start", "os_compute_api:servers:stop", +"os_compute_api:servers:trigger_crash_dump", "os_compute_api:os-create-backup", "os_compute_api:ips:index", "os_compute_api:ips:show", diff --git a/releasenotes/notes/bp-instance-crash-dump-7ccbba7799dc66f9.yaml b/releasenotes/notes/bp-instance-crash-dump-7ccbba7799dc66f9.yaml new file mode 100644 index 000000000000..2f513fbebf67 --- /dev/null +++ b/releasenotes/notes/bp-instance-crash-dump-7ccbba7799dc66f9.yaml @@ -0,0 +1,4 @@ +--- +features: + - A new server action trigger_crash_dump has been added to the REST API in + microversion 2.17.