diff --git a/doc/notification_samples/common_payloads/InstanceActionRebuildPayload.json b/doc/notification_samples/common_payloads/InstanceActionRebuildPayload.json index 5b641db59b2f..35cf2646478b 100644 --- a/doc/notification_samples/common_payloads/InstanceActionRebuildPayload.json +++ b/doc/notification_samples/common_payloads/InstanceActionRebuildPayload.json @@ -1,6 +1,8 @@ { "$ref": "InstanceActionPayload.json", "nova_object.data": { + "architecture": null, + "image_uuid": "a2459075-d96c-40d5-893e-577ff92e721c", "trusted_image_certificates": [ "rebuild-cert-id-1", "rebuild-cert-id-2" diff --git a/doc/notification_samples/instance-rebuild-end.json b/doc/notification_samples/instance-rebuild-end.json index fa350e59d142..4b48d5c59ceb 100644 --- a/doc/notification_samples/instance-rebuild-end.json +++ b/doc/notification_samples/instance-rebuild-end.json @@ -1,12 +1,6 @@ { "event_type": "instance.rebuild.end", "publisher_id": "nova-compute:compute", - "payload": { - "$ref":"common_payloads/InstanceActionRebuildPayload.json#", - "nova_object.data": { - "architecture": null, - "image_uuid": "a2459075-d96c-40d5-893e-577ff92e721c" - } - }, + "payload": {"$ref": "common_payloads/InstanceActionRebuildPayload.json#"}, "priority": "INFO" } diff --git a/doc/notification_samples/instance-rebuild-error.json b/doc/notification_samples/instance-rebuild-error.json index 9cb138f83f9c..464cce1f36a4 100644 --- a/doc/notification_samples/instance-rebuild-error.json +++ b/doc/notification_samples/instance-rebuild-error.json @@ -15,8 +15,6 @@ "nova_object.version": "1.1", "nova_object.namespace": "nova" }, - "architecture": null, - "image_uuid": "a2459075-d96c-40d5-893e-577ff92e721c", "task_state": "rebuilding" } }, diff --git a/doc/notification_samples/instance-rebuild-start.json b/doc/notification_samples/instance-rebuild-start.json index 3dc65ff59316..9aee591b1ec8 100644 --- a/doc/notification_samples/instance-rebuild-start.json +++ b/doc/notification_samples/instance-rebuild-start.json @@ -3,10 +3,8 @@ "event_type": "instance.rebuild.start", "publisher_id": "nova-compute:compute", "payload": { - "$ref":"common_payloads/InstanceActionRebuildPayload.json#", + "$ref": "common_payloads/InstanceActionRebuildPayload.json#", "nova_object.data": { - "architecture": null, - "image_uuid": "a2459075-d96c-40d5-893e-577ff92e721c", "task_state": "rebuilding" } } diff --git a/doc/notification_samples/instance-rebuild_scheduled.json b/doc/notification_samples/instance-rebuild_scheduled.json new file mode 100644 index 000000000000..2553489b90c6 --- /dev/null +++ b/doc/notification_samples/instance-rebuild_scheduled.json @@ -0,0 +1,11 @@ +{ + "priority": "INFO", + "event_type": "instance.rebuild_scheduled", + "publisher_id": "nova-conductor:compute", + "payload": { + "$ref": "common_payloads/InstanceActionRebuildPayload.json#", + "nova_object.data": { + "task_state": "rebuilding" + } + } +} diff --git a/nova/compute/utils.py b/nova/compute/utils.py index 2a9a18a95da3..8c8dd6c807c6 100644 --- a/nova/compute/utils.py +++ b/nova/compute/utils.py @@ -723,13 +723,18 @@ def notify_about_server_group_add_member(context, group_id): @rpc.if_notifications_enabled -def notify_about_instance_rebuild(context, instance, host, phase=None, +def notify_about_instance_rebuild(context, instance, host, + action=fields.NotificationAction.REBUILD, + phase=None, + source=fields.NotificationSource.COMPUTE, exception=None, bdms=None, tb=None): """Send versioned notification about instance rebuild :param instance: the instance which the action performed on :param host: the host emitting the notification + :param action: the name of the action :param phase: the phase of the action + :param source: the source of the notification :param exception: the thrown exception (used in error notifications) :param bdms: BlockDeviceMappingList object for the instance. If it is not provided then we will load it from the db if so configured @@ -745,10 +750,10 @@ def notify_about_instance_rebuild(context, instance, host, phase=None, context=context, priority=priority, publisher=notification_base.NotificationPublisher( - host=host, source=fields.NotificationSource.COMPUTE), + host=host, source=source), event_type=notification_base.EventType( object='instance', - action=fields.NotificationAction.REBUILD, + action=action, phase=phase), payload=payload) notification.emit(context) diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py index e525bad66d7d..771ee4f57a4f 100644 --- a/nova/conductor/manager.py +++ b/nova/conductor/manager.py @@ -45,6 +45,7 @@ from nova import network from nova import notifications from nova import objects from nova.objects import base as nova_object +from nova.objects import fields from nova import profiler from nova import rpc from nova.scheduler import client as scheduler_client @@ -969,6 +970,10 @@ class ComputeTaskManager(base.Base): compute_utils.notify_about_instance_usage( self.notifier, context, instance, "rebuild.scheduled") + compute_utils.notify_about_instance_rebuild( + context, instance, host, + action=fields.NotificationAction.REBUILD_SCHEDULED, + source=fields.NotificationSource.CONDUCTOR) instance.availability_zone = ( availability_zones.get_host_availability_zone( diff --git a/nova/notifications/objects/base.py b/nova/notifications/objects/base.py index e4d6f6971987..06e8578888c8 100644 --- a/nova/notifications/objects/base.py +++ b/nova/notifications/objects/base.py @@ -59,7 +59,9 @@ class EventType(NotificationObject): # NotificationActionField enum # Version 1.11: LOCK is added to NotificationActionField enum # Version 1.12: UNLOCK is added to NotificationActionField enum - VERSION = '1.12' + # Version 1.13: REBUILD_SCHEDULED value is added to the + # NotificationActionField enum + VERSION = '1.13' fields = { 'object': fields.StringField(nullable=False), diff --git a/nova/notifications/objects/instance.py b/nova/notifications/objects/instance.py index 74ce1fc51856..43b1a8d0a564 100644 --- a/nova/notifications/objects/instance.py +++ b/nova/notifications/objects/instance.py @@ -660,6 +660,7 @@ class InstanceActionRescueNotification(base.NotificationBase): } +@base.notification_sample('instance-rebuild_scheduled.json') @base.notification_sample('instance-rebuild-start.json') @base.notification_sample('instance-rebuild-end.json') @base.notification_sample('instance-rebuild-error.json') diff --git a/nova/objects/fields.py b/nova/objects/fields.py index 23b4f6520a71..cf7c4caf51dc 100644 --- a/nova/objects/fields.py +++ b/nova/objects/fields.py @@ -808,6 +808,7 @@ class NotificationAction(BaseNovaEnum): LIVE_MIGRATION_ROLLBACK_DEST = 'live_migration_rollback_dest' LIVE_MIGRATION_ROLLBACK = 'live_migration_rollback' REBUILD = 'rebuild' + REBUILD_SCHEDULED = 'rebuild_scheduled' INTERFACE_DETACH = 'interface_detach' RESIZE_CONFIRM = 'resize_confirm' RESIZE_PREP = 'resize_prep' @@ -833,7 +834,8 @@ class NotificationAction(BaseNovaEnum): LIVE_MIGRATION_ROLLBACK_DEST, REBUILD, INTERFACE_DETACH, RESIZE_CONFIRM, RESIZE_PREP, RESIZE_REVERT, SHELVE_OFFLOAD, SOFT_DELETE, TRIGGER_CRASH_DUMP, UNRESCUE, UNSHELVE, ADD_HOST, - REMOVE_HOST, ADD_MEMBER, UPDATE_METADATA, LOCK, UNLOCK) + REMOVE_HOST, ADD_MEMBER, UPDATE_METADATA, LOCK, UNLOCK, + REBUILD_SCHEDULED) # TODO(rlrossit): These should be changed over to be a StateMachine enum from diff --git a/nova/tests/functional/notification_sample_tests/test_instance.py b/nova/tests/functional/notification_sample_tests/test_instance.py index 910cb92436bd..f63df830a819 100644 --- a/nova/tests/functional/notification_sample_tests/test_instance.py +++ b/nova/tests/functional/notification_sample_tests/test_instance.py @@ -1042,15 +1042,28 @@ class TestInstanceNotificationSample( self._wait_for_state_change(self.api, server, expected_status='ACTIVE') + # 0. instance.rebuild_scheduled + # 1. instance.exists + # 2. instance.rebuild.start + # 3. instance.detach.start + # 4. instance.detach.end + # 5. instance.rebuild.end # The compute/manager will detach every volume during rebuild - self.assertEqual(5, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + self.assertEqual(6, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + self._verify_notification( + 'instance-rebuild_scheduled', + replacements={ + 'reservation_id': server['reservation_id'], + 'uuid': server['id'], + 'trusted_image_certificates': None}, + actual=fake_notifier.VERSIONED_NOTIFICATIONS[0]) self._verify_notification( 'instance-rebuild-start', replacements={ 'reservation_id': server['reservation_id'], 'uuid': server['id'], 'trusted_image_certificates': None}, - actual=fake_notifier.VERSIONED_NOTIFICATIONS[1]) + actual=fake_notifier.VERSIONED_NOTIFICATIONS[2]) self._verify_notification( 'instance-volume_detach-start', replacements={ @@ -1059,7 +1072,7 @@ class TestInstanceNotificationSample( 'architecture': None, 'image_uuid': 'a2459075-d96c-40d5-893e-577ff92e721c', 'uuid': server['id']}, - actual=fake_notifier.VERSIONED_NOTIFICATIONS[2]) + actual=fake_notifier.VERSIONED_NOTIFICATIONS[3]) self._verify_notification( 'instance-volume_detach-end', replacements={ @@ -1068,14 +1081,14 @@ class TestInstanceNotificationSample( 'architecture': None, 'image_uuid': 'a2459075-d96c-40d5-893e-577ff92e721c', 'uuid': server['id']}, - actual=fake_notifier.VERSIONED_NOTIFICATIONS[3]) + actual=fake_notifier.VERSIONED_NOTIFICATIONS[4]) self._verify_notification( 'instance-rebuild-end', replacements={ 'reservation_id': server['reservation_id'], 'uuid': server['id'], 'trusted_image_certificates': None}, - actual=fake_notifier.VERSIONED_NOTIFICATIONS[4]) + actual=fake_notifier.VERSIONED_NOTIFICATIONS[5]) def test_rebuild_server_with_trusted_cert(self): # NOTE(gabor_antal): Rebuild changes the image used by the instance, @@ -1107,14 +1120,26 @@ class TestInstanceNotificationSample( self._wait_for_state_change(self.api, server, expected_status='ACTIVE') + # 0. instance.rebuild_scheduled + # 1. instance.exists + # 2. instance.rebuild.start + # 3. instance.detach.start + # 4. instance.detach.end + # 5. instance.rebuild.end # The compute/manager will detach every volume during rebuild - self.assertEqual(5, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + self.assertEqual(6, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + self._verify_notification( + 'instance-rebuild_scheduled', + replacements={ + 'reservation_id': server['reservation_id'], + 'uuid': server['id']}, + actual=fake_notifier.VERSIONED_NOTIFICATIONS[0]) self._verify_notification( 'instance-rebuild-start', replacements={ 'reservation_id': server['reservation_id'], 'uuid': server['id']}, - actual=fake_notifier.VERSIONED_NOTIFICATIONS[1]) + actual=fake_notifier.VERSIONED_NOTIFICATIONS[2]) self._verify_notification( 'instance-volume_detach-start', replacements={ @@ -1123,7 +1148,7 @@ class TestInstanceNotificationSample( 'architecture': None, 'image_uuid': 'a2459075-d96c-40d5-893e-577ff92e721c', 'uuid': server['id']}, - actual=fake_notifier.VERSIONED_NOTIFICATIONS[2]) + actual=fake_notifier.VERSIONED_NOTIFICATIONS[3]) self._verify_notification( 'instance-volume_detach-end', replacements={ @@ -1132,13 +1157,13 @@ class TestInstanceNotificationSample( 'architecture': None, 'image_uuid': 'a2459075-d96c-40d5-893e-577ff92e721c', 'uuid': server['id']}, - actual=fake_notifier.VERSIONED_NOTIFICATIONS[3]) + actual=fake_notifier.VERSIONED_NOTIFICATIONS[4]) self._verify_notification( 'instance-rebuild-end', replacements={ 'reservation_id': server['reservation_id'], 'uuid': server['id']}, - actual=fake_notifier.VERSIONED_NOTIFICATIONS[4]) + actual=fake_notifier.VERSIONED_NOTIFICATIONS[5]) @mock.patch('nova.compute.manager.ComputeManager.' '_do_rebuild_instance_with_claim') @@ -1165,8 +1190,8 @@ class TestInstanceNotificationSample( notification = self._get_notifications('instance.rebuild.error') self.assertEqual(1, len(notification)) - tb = fake_notifier.VERSIONED_NOTIFICATIONS[0]['payload'][ - 'nova_object.data']['fault']['nova_object.data']['traceback'] + tb = notification[0]['payload']['nova_object.data']['fault'][ + 'nova_object.data']['traceback'] self.assertIn('raise exception.VirtualInterfaceCreateException()', tb) self._verify_notification( diff --git a/nova/tests/unit/conductor/test_conductor.py b/nova/tests/unit/conductor/test_conductor.py index b8a6a3516692..34c274d07dda 100644 --- a/nova/tests/unit/conductor/test_conductor.py +++ b/nova/tests/unit/conductor/test_conductor.py @@ -1255,7 +1255,8 @@ class _BaseTaskTestCase(object): instance=inst_obj, **compute_args) - def test_rebuild_instance_with_scheduler(self): + @mock.patch('nova.compute.utils.notify_about_instance_rebuild') + def test_rebuild_instance_with_scheduler(self, mock_notify): inst_obj = self._create_fake_instance_obj() inst_obj.host = 'noselect' expected_host = 'thebesthost' @@ -1299,6 +1300,9 @@ class _BaseTaskTestCase(object): self.assertEqual(inst_obj.project_id, fake_spec.project_id) self.assertEqual('compute.instance.rebuild.scheduled', fake_notifier.NOTIFICATIONS[0].event_type) + mock_notify.assert_called_once_with( + self.context, inst_obj, 'thebesthost', action='rebuild_scheduled', + source='nova-conductor') def test_rebuild_instance_with_scheduler_no_host(self): inst_obj = self._create_fake_instance_obj() @@ -1414,7 +1418,8 @@ class _BaseTaskTestCase(object): instance=inst_obj, **compute_args) - def test_rebuild_instance_with_request_spec(self): + @mock.patch('nova.compute.utils.notify_about_instance_rebuild') + def test_rebuild_instance_with_request_spec(self, mock_notify): inst_obj = self._create_fake_instance_obj() inst_obj.host = 'noselect' expected_host = 'thebesthost' @@ -1453,6 +1458,9 @@ class _BaseTaskTestCase(object): **compute_args) self.assertEqual('compute.instance.rebuild.scheduled', fake_notifier.NOTIFICATIONS[0].event_type) + mock_notify.assert_called_once_with( + self.context, inst_obj, 'thebesthost', action='rebuild_scheduled', + source='nova-conductor') class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase): diff --git a/nova/tests/unit/notifications/objects/test_notification.py b/nova/tests/unit/notifications/objects/test_notification.py index 26a890f75984..217edfc0f260 100644 --- a/nova/tests/unit/notifications/objects/test_notification.py +++ b/nova/tests/unit/notifications/objects/test_notification.py @@ -369,7 +369,7 @@ notification_object_data = { 'AuditPeriodPayload': '1.0-2b429dd307b8374636703b843fa3f9cb', 'BandwidthPayload': '1.0-ee2616a7690ab78406842a2b68e34130', 'BlockDevicePayload': '1.0-29751e1b6d41b1454e36768a1e764df8', - 'EventType': '1.12-db573dfb0e85f269194dcd3b1628b0d2', + 'EventType': '1.13-38edd755949d09025d66261f9ff46549', 'ExceptionNotification': '1.0-a73147b93b520ff0061865849d3dfa56', 'ExceptionPayload': '1.1-6c43008bd81885a63bc7f7c629f0793b', 'FlavorNotification': '1.0-a73147b93b520ff0061865849d3dfa56',