From e2185168f812286dd140936df64261e67568a410 Mon Sep 17 00:00:00 2001 From: Takashi NATSUME Date: Thu, 28 Feb 2019 13:49:41 +0900 Subject: [PATCH] Fix an error when generating a host ID When instance action events are created by periodic tasks, the project IDs of them become null (None). It causes an error when 'hostId' is generated in the "Show Server Action Details" (GET /servers/{server_id}/os-instance-actions/{request_id}) API. Fix the issue by using the project ID of the server if the project ID of the event is None. Conflicts: nova/tests/unit/api/openstack/compute/test_instance_actions.py The conflicts are due to not having the following changes in Rocky. I7f5f08691ca3f73073c66c29dddb996fb2c2b266 If91c179e3823c8b0da744a9363906b0f7b05c326 I44546bc9798708a48a250cc3a21bdbcabe2649e1 Change-Id: Iac07fcddd4cc3321c6efe702066eb8af6a875418 Closes-Bug: #1817542 (cherry picked from commit 31fe7c76009e1c6d7859036e44b057d081b059b5) --- .../api/openstack/compute/instance_actions.py | 9 ++- .../compute/test_instance_actions.py | 63 ++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/compute/instance_actions.py b/nova/api/openstack/compute/instance_actions.py index aef7440340ce..e24b72cf0a1b 100644 --- a/nova/api/openstack/compute/instance_actions.py +++ b/nova/api/openstack/compute/instance_actions.py @@ -163,8 +163,15 @@ class InstanceActionsController(wsgi.Controller): if show_events: events_raw = self.action_api.action_events_get(context, instance, action_id) + # NOTE(takashin): The project IDs of instance action events + # become null (None) when instance action events are created + # by periodic tasks. If the project ID is null (None), + # it causes an error when 'hostId' is generated. + # If the project ID is null (None), pass the project ID of + # the server instead of that of instance action events. action['events'] = [self._format_event( - evt, action['project_id'], show_traceback=show_traceback, + evt, action['project_id'] or instance.project_id, + show_traceback=show_traceback, show_host=show_host, show_hostid=show_hostid ) for evt in events_raw] return {'instanceAction': action} diff --git a/nova/tests/unit/api/openstack/compute/test_instance_actions.py b/nova/tests/unit/api/openstack/compute/test_instance_actions.py index a844a98c21f3..fa3273a3d392 100644 --- a/nova/tests/unit/api/openstack/compute/test_instance_actions.py +++ b/nova/tests/unit/api/openstack/compute/test_instance_actions.py @@ -14,6 +14,7 @@ # under the License. import copy +import datetime import mock from oslo_policy import policy as oslo_policy @@ -31,6 +32,8 @@ from nova import test from nova.tests.unit.api.openstack import fakes from nova.tests.unit import fake_server_actions from nova.tests import uuidsentinel as uuids +from nova import utils + FAKE_UUID = fake_server_actions.FAKE_UUID FAKE_REQUEST_ID = fake_server_actions.FAKE_REQUEST_ID1 @@ -314,7 +317,65 @@ class InstanceActionsTestV258(InstanceActionsTestV251): six.text_type(ex)) -class InstanceActionsTestV262(InstanceActionsTestV251): +class InstanceActionsTestV262(InstanceActionsTestV258): wsgi_api_version = "2.62" expect_event_hostId = True expect_event_host = True + instance_project_id = '26cde4489f6749a08834741678df3c4a' + + def fake_get(self, context, instance_uuid, expected_attrs=None, + cell_down_support=False): + return objects.Instance(uuid=instance_uuid, + project_id=self.instance_project_id) + + @mock.patch.object(compute_api.InstanceActionAPI, 'action_events_get') + @mock.patch.object(compute_api.InstanceActionAPI, + 'action_get_by_request_id') + def test_get_action_with_events_project_id_none(self, mock_action_get, + mock_action_events): + fake_request_id = 'req-%s' % uuids.req1 + + mock_action_get.return_value = objects.InstanceAction( + id=789, + action='stop', + instance_uuid=uuids.instance, + request_id=fake_request_id, + user_id=None, + project_id=None, + start_time=datetime.datetime(2019, 2, 28, 14, 28, 0, 0), + finish_time=None, + message='', + created_at=None, + updated_at=None, + deleted_at=None, + deleted=False) + + mock_action_events.return_value = [ + objects.InstanceActionEvent( + id=5, + action_id=789, + event='compute_stop_instance', + start_time=datetime.datetime(2019, 2, 28, 14, 28, 0, 0), + finish_time=datetime.datetime(2019, 2, 28, 14, 30, 0, 0), + result='Success', + traceback='', + created_at=None, + updated_at=None, + deleted_at=None, + deleted=False, + host='host2')] + + req = self._get_http_req('os-instance-actions/1', + use_admin_context=True) + res_dict = self.controller.show(req, uuids.instance, fake_request_id) + + # Assert that 'project_id' is null (None) in the response + self.assertIsNone(res_dict['instanceAction']['project_id']) + self.assertEqual('host2', + res_dict['instanceAction']['events'][0]['host']) + # Assert that the 'hostId' is based on 'host' and the project ID + # of the server + self.assertEqual(utils.generate_hostid( + res_dict['instanceAction']['events'][0]['host'], + self.instance_project_id), + res_dict['instanceAction']['events'][0]['hostId'])