diff --git a/doc/notification_samples/instance-live_migration_pre-end.json b/doc/notification_samples/instance-live_migration_pre-end.json new file mode 100644 index 000000000000..adeae1780aac --- /dev/null +++ b/doc/notification_samples/instance-live_migration_pre-end.json @@ -0,0 +1,11 @@ +{ + "event_type": "instance.live_migration_pre.end", + "payload": { + "$ref": "common_payloads/InstanceActionPayload.json#", + "nova_object.data": { + "task_state": "migrating" + } + }, + "priority": "INFO", + "publisher_id": "nova-compute:host2" +} diff --git a/doc/notification_samples/instance-live_migration_pre-start.json b/doc/notification_samples/instance-live_migration_pre-start.json new file mode 100644 index 000000000000..b9cb80c63cd7 --- /dev/null +++ b/doc/notification_samples/instance-live_migration_pre-start.json @@ -0,0 +1,11 @@ +{ + "event_type": "instance.live_migration_pre.start", + "payload": { + "$ref": "common_payloads/InstanceActionPayload.json#", + "nova_object.data": { + "task_state": "migrating" + } + }, + "priority": "INFO", + "publisher_id": "nova-compute:host2" +} diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 3ea26bf9708a..28d604e08eed 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -5746,6 +5746,10 @@ class ComputeManager(manager.Manager): self._notify_about_instance_usage( context, instance, "live_migration.pre.start", network_info=network_info) + compute_utils.notify_about_instance_action( + context, instance, self.host, + action=fields.NotificationAction.LIVE_MIGRATION_PRE, + phase=fields.NotificationPhase.START) migrate_data = self.driver.pre_live_migration(context, instance, @@ -5778,6 +5782,11 @@ class ComputeManager(manager.Manager): self._notify_about_instance_usage( context, instance, "live_migration.pre.end", network_info=network_info) + compute_utils.notify_about_instance_action( + context, instance, self.host, + action=fields.NotificationAction.LIVE_MIGRATION_PRE, + phase=fields.NotificationPhase.END) + # TODO(tdurakov): remove dict to object conversion once RPC API version # is bumped to 5.x if not got_migrate_data_object and migrate_data: diff --git a/nova/notifications/objects/instance.py b/nova/notifications/objects/instance.py index cdff44ec11b1..8e918d30329c 100644 --- a/nova/notifications/objects/instance.py +++ b/nova/notifications/objects/instance.py @@ -424,8 +424,8 @@ class InstanceStateUpdatePayload(base.NotificationPayloadBase): # @base.notification_sample('instance-evacuate.json') @base.notification_sample('instance-resize_finish-start.json') @base.notification_sample('instance-resize_finish-end.json') -# @base.notification_sample('instance-live_migration_pre-start.json') -# @base.notification_sample('instance-live_migration_pre-end.json') +@base.notification_sample('instance-live_migration_pre-start.json') +@base.notification_sample('instance-live_migration_pre-end.json') @base.notification_sample('instance-live_migration_abort-start.json') @base.notification_sample('instance-live_migration_abort-end.json') # @base.notification_sample('instance-live_migration_post-start.json') diff --git a/nova/tests/functional/notification_sample_tests/test_instance.py b/nova/tests/functional/notification_sample_tests/test_instance.py index 7c1ba0604a25..a3560fccf6d7 100644 --- a/nova/tests/functional/notification_sample_tests/test_instance.py +++ b/nova/tests/functional/notification_sample_tests/test_instance.py @@ -52,6 +52,7 @@ class TestInstanceNotificationSampleWithMultipleCompute( actions = [ self._test_live_migration_rollback, self._test_live_migration_abort, + self._test_live_migration_pre, ] for action in actions: @@ -88,6 +89,38 @@ class TestInstanceNotificationSampleWithMultipleCompute( 'uuid': server['id']}, actual=fake_notifier.VERSIONED_NOTIFICATIONS[1]) + def _test_live_migration_pre(self, server): + post = { + 'os-migrateLive': { + 'host': 'host2', + 'block_migration': True, + 'force': True, + } + } + self.admin_api.post_server_action(server['id'], post) + self._wait_for_notification('instance.live_migration_pre.end') + self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + self._verify_notification( + 'instance-live_migration_pre-start', + replacements={ + 'reservation_id': server['reservation_id'], + 'uuid': server['id']}, + actual=fake_notifier.VERSIONED_NOTIFICATIONS[0]) + self._verify_notification( + 'instance-live_migration_pre-end', + replacements={ + 'reservation_id': server['reservation_id'], + 'uuid': server['id']}, + actual=fake_notifier.VERSIONED_NOTIFICATIONS[1]) + migrations = self.admin_api.get_active_migrations(server['id']) + self.assertEqual(1, len(migrations)) + # FakeLiveMigrateDriver needs force_complete to finish migration + # TODO(elod.illes): Enhance FakeLiveMigrateDriver to finish migration + # automatically. This enhancement probably will happen as part of the + # patch proposing instance.live_migration_post.end transformation. + self.admin_api.force_complete_migration(server['id'], + migrations[0]['id']) + def _test_live_migration_abort(self, server): post = { "os-migrateLive": { @@ -106,31 +139,43 @@ class TestInstanceNotificationSampleWithMultipleCompute( self._wait_for_notification('instance.live_migration_abort.start') self._wait_for_state_change(self.admin_api, server, 'ACTIVE') self._wait_for_notification('instance.live_migration_abort.end') - self.assertEqual(4, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + self.assertEqual(6, len(fake_notifier.VERSIONED_NOTIFICATIONS)) self._verify_notification( - 'instance-live_migration_abort-start', + 'instance-live_migration_pre-start', replacements={ 'reservation_id': server['reservation_id'], 'uuid': server['id']}, actual=fake_notifier.VERSIONED_NOTIFICATIONS[0]) self._verify_notification( - 'instance-live_migration_abort-end', + 'instance-live_migration_pre-end', replacements={ 'reservation_id': server['reservation_id'], 'uuid': server['id']}, actual=fake_notifier.VERSIONED_NOTIFICATIONS[1]) self._verify_notification( - 'instance-live_migration_rollback-start', + 'instance-live_migration_abort-start', replacements={ 'reservation_id': server['reservation_id'], 'uuid': server['id']}, actual=fake_notifier.VERSIONED_NOTIFICATIONS[2]) self._verify_notification( - 'instance-live_migration_rollback-end', + 'instance-live_migration_abort-end', replacements={ 'reservation_id': server['reservation_id'], 'uuid': server['id']}, actual=fake_notifier.VERSIONED_NOTIFICATIONS[3]) + self._verify_notification( + 'instance-live_migration_rollback-start', + replacements={ + 'reservation_id': server['reservation_id'], + 'uuid': server['id']}, + actual=fake_notifier.VERSIONED_NOTIFICATIONS[4]) + self._verify_notification( + 'instance-live_migration_rollback-end', + replacements={ + 'reservation_id': server['reservation_id'], + 'uuid': server['id']}, + actual=fake_notifier.VERSIONED_NOTIFICATIONS[5]) class TestInstanceNotificationSample( diff --git a/nova/tests/unit/compute/test_compute.py b/nova/tests/unit/compute/test_compute.py index b26c9b9baeb3..c6deace0932d 100644 --- a/nova/tests/unit/compute/test_compute.py +++ b/nova/tests/unit/compute/test_compute.py @@ -5844,7 +5844,9 @@ class ComputeTestCase(BaseTestCase, @mock.patch.object(fake.FakeDriver, 'ensure_filtering_rules_for_instance') @mock.patch.object(fake.FakeDriver, 'pre_live_migration') - def test_pre_live_migration_works_correctly(self, mock_pre, mock_ensure): + @mock.patch('nova.compute.utils.notify_about_instance_action') + def test_pre_live_migration_works_correctly(self, mock_notify, + mock_pre, mock_ensure): # Confirm setup_compute_volume is called when volume is mounted. def stupid(*args, **kwargs): return fake_network.fake_get_instance_nw_info(self) @@ -5877,6 +5879,12 @@ class ComputeTestCase(BaseTestCase, self.assertEqual(msg.event_type, 'compute.instance.live_migration.pre.end') + mock_notify.assert_has_calls([ + mock.call(c, instance, 'fake-mini', + action='live_migration_pre', phase='start'), + mock.call(c, instance, 'fake-mini', + action='live_migration_pre', phase='end')]) + mock_pre.assert_called_once_with( test.MatchType(nova.context.RequestContext), test.MatchType(objects.Instance), diff --git a/nova/tests/unit/compute/test_compute_mgr.py b/nova/tests/unit/compute/test_compute_mgr.py index 5953015a43f0..c37ed703c715 100644 --- a/nova/tests/unit/compute/test_compute_mgr.py +++ b/nova/tests/unit/compute/test_compute_mgr.py @@ -6300,6 +6300,7 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase): def test_pre_live_migration_handles_dict(self): compute = manager.ComputeManager() + @mock.patch.object(compute_utils, 'notify_about_instance_action') @mock.patch.object(compute, '_notify_about_instance_usage') @mock.patch.object(compute, 'network_api') @mock.patch.object(compute.driver, 'pre_live_migration') @@ -6308,7 +6309,7 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase): @mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid') def _test(mock_get_bdms, mock_ivbi, mock_gibdi, mock_plm, mock_nwapi, - mock_notify): + mock_notify, mock_notify_about_inst): mock_get_bdms.return_value = [] instance = fake_instance.fake_instance_obj(self.context, uuid=uuids.instance) @@ -6316,6 +6317,11 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase): mock_plm.return_value = migrate_data r = compute.pre_live_migration(self.context, instance, False, {}, {}) + mock_notify_about_inst.assert_has_calls([ + mock.call(self.context, instance, 'fake-mini', + action='live_migration_pre', phase='start'), + mock.call(self.context, instance, 'fake-mini', + action='live_migration_pre', phase='end')]) self.assertIsInstance(r, dict) self.assertIsInstance(mock_plm.call_args_list[0][0][5], migrate_data_obj.LiveMigrateData) @@ -6372,6 +6378,7 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase): migrate_data = migrate_data_obj.LiveMigrateData() migrate_data.old_vol_attachment_ids = {} + @mock.patch.object(compute_utils, 'notify_about_instance_action') @mock.patch.object(compute.volume_api, 'attachment_complete') @mock.patch.object(vol_bdm, 'save') @mock.patch.object(compute, '_notify_about_instance_usage') @@ -6384,7 +6391,7 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase): @mock.patch.object(compute.volume_api, 'attachment_create') def _test(mock_attach, mock_get_bdms, mock_ivbi, mock_gibdi, mock_plm, mock_nwapi, mock_notify, - mock_bdm_save, mock_attach_complete): + mock_bdm_save, mock_attach_complete, mock_notify_about_inst): mock_get_bdms.return_value = [vol_bdm, image_bdm] mock_attach.return_value = {'id': new_attachment_id} @@ -6394,6 +6401,11 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase): r = compute.pre_live_migration(self.context, instance, False, {}, migrate_data) + mock_notify_about_inst.assert_has_calls([ + mock.call(self.context, instance, 'fake-mini', + action='live_migration_pre', phase='start'), + mock.call(self.context, instance, 'fake-mini', + action='live_migration_pre', phase='end')]) self.assertIsInstance(r, migrate_data_obj.LiveMigrateData) self.assertIsInstance(mock_plm.call_args_list[0][0][5], migrate_data_obj.LiveMigrateData) diff --git a/nova/virt/fake.py b/nova/virt/fake.py index c95844ac4ea9..58f5f7d0a527 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -691,7 +691,8 @@ class FakeLiveMigrateDriver(FakeDriver): time.sleep(0.1) if self._abort_migration: - recover_method(context, instance, dest) + recover_method(context, instance, dest, migrate_data, + migration_status='cancelled') else: post_method(context, instance, dest, block_migration, migrate_data)