diff --git a/doc/notification_samples/instance-live_migration_rollback-end.json b/doc/notification_samples/instance-live_migration_rollback-end.json new file mode 100644 index 0000000000..d9aadd0b8f --- /dev/null +++ b/doc/notification_samples/instance-live_migration_rollback-end.json @@ -0,0 +1,75 @@ +{ + "event_type":"instance.live_migration_rollback.end", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state": null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.2" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/doc/notification_samples/instance-live_migration_rollback-start.json b/doc/notification_samples/instance-live_migration_rollback-start.json new file mode 100644 index 0000000000..9e866fb4fb --- /dev/null +++ b/doc/notification_samples/instance-live_migration_rollback-start.json @@ -0,0 +1,75 @@ +{ + "event_type":"instance.live_migration_rollback.start", + "payload":{ + "nova_object.data":{ + "architecture":"x86_64", + "availability_zone": "nova", + "created_at":"2012-10-29T13:42:11Z", + "deleted_at":null, + "display_name":"some-server", + "display_description":"some-server", + "fault":null, + "host":"compute", + "host_name":"some-server", + "ip_addresses": [{ + "nova_object.name": "IpPayload", + "nova_object.namespace": "nova", + "nova_object.version": "1.0", + "nova_object.data": { + "mac": "fa:16:3e:4c:2c:30", + "address": "192.168.1.3", + "port_uuid": "ce531f90-199f-48c0-816c-13e38010b442", + "meta": {}, + "version": 4, + "label": "private-network", + "device_name": "tapce531f90-19" + } + }], + "kernel_id":"", + "launched_at":"2012-10-29T13:42:11Z", + "image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6", + "metadata":{}, + "locked":false, + "node":"fake-mini", + "os_type":null, + "progress":0, + "ramdisk_id":"", + "reservation_id":"r-npxv0e40", + "state":"active", + "task_state": null, + "power_state":"running", + "tenant_id":"6f70656e737461636b20342065766572", + "terminated_at":null, + "auto_disk_config":"MANUAL", + "flavor": { + "nova_object.name": "FlavorPayload", + "nova_object.data": { + "flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3", + "name": "test_flavor", + "root_gb": 1, + "vcpus": 1, + "ephemeral_gb": 0, + "memory_mb": 512, + "disabled": false, + "rxtx_factor": 1.0, + "extra_specs": { + "hw:watchdog_action": "disabled" + }, + "projects": null, + "swap": 0, + "is_public": true, + "vcpu_weight": 0 + }, + "nova_object.version": "1.3", + "nova_object.namespace": "nova" + }, + "user_id":"fake", + "uuid":"178b0921-8f85-4257-88b6-2e743b5a975c" + }, + "nova_object.name":"InstanceActionPayload", + "nova_object.namespace":"nova", + "nova_object.version":"1.2" + }, + "priority":"INFO", + "publisher_id":"nova-compute:compute" +} diff --git a/nova/compute/manager.py b/nova/compute/manager.py index d40ca77e1c..3c1183dc8b 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -5805,6 +5805,10 @@ class ComputeManager(manager.Manager): self._notify_about_instance_usage(context, instance, "live_migration._rollback.start") + compute_utils.notify_about_instance_action(context, instance, + self.host, + action=fields.NotificationAction.LIVE_MIGRATION_ROLLBACK, + phase=fields.NotificationPhase.START) do_cleanup, destroy_disks = self._live_migration_cleanup_flags( migrate_data) @@ -5816,6 +5820,10 @@ class ComputeManager(manager.Manager): self._notify_about_instance_usage(context, instance, "live_migration._rollback.end") + compute_utils.notify_about_instance_action(context, instance, + self.host, + action=fields.NotificationAction.LIVE_MIGRATION_ROLLBACK, + phase=fields.NotificationPhase.END) self._set_migration_status(migration, migration_status) diff --git a/nova/notifications/objects/instance.py b/nova/notifications/objects/instance.py index fdf759cd5d..fc7e68eb4f 100644 --- a/nova/notifications/objects/instance.py +++ b/nova/notifications/objects/instance.py @@ -318,8 +318,8 @@ class InstanceStateUpdatePayload(base.NotificationPayloadBase): # @base.notification_sample('instance-live_migration_post-end.json') # @base.notification_sample('instance-live_migration_post_dest-start.json') # @base.notification_sample('instance-live_migration_post_dest-end.json') -# @base.notification_sample('instance-live_migration_rollback-start.json') -# @base.notification_sample('instance-live_migration_rollback-end.json') +@base.notification_sample('instance-live_migration_rollback-start.json') +@base.notification_sample('instance-live_migration_rollback-end.json') # @base.notification_sample('instance-live_migration_rollback_dest-start.json') # @base.notification_sample('instance-live_migration_rollback_dest-end.json') @base.notification_sample('instance-rebuild-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 f658e461db..b3ef08d94b 100644 --- a/nova/tests/functional/notification_sample_tests/test_instance.py +++ b/nova/tests/functional/notification_sample_tests/test_instance.py @@ -22,6 +22,64 @@ from nova.tests.functional.notification_sample_tests \ from nova.tests.unit import fake_notifier +class TestInstanceNotificationSampleWithMultipleCompute( + notification_sample_base.NotificationSampleTestBase): + + def setUp(self): + self.flags(use_neutron=True) + super(TestInstanceNotificationSampleWithMultipleCompute, self).setUp() + self.neutron = fixtures.NeutronFixture(self) + self.useFixture(self.neutron) + self.cinder = fixtures.CinderFixture(self) + self.useFixture(self.cinder) + + def test_live_migration_actions(self): + server = self._boot_a_server( + extra_params={'networks': [{'port': self.neutron.port_1['id']}]}) + self._wait_for_notification('instance.create.end') + # server will boot on host1 + self.useFixture(fixtures.ConfPatcher(host='host2')) + self.compute2 = self.start_service('compute', host='host2') + + actions = [ + self._test_live_migration_rollback, + ] + + for action in actions: + fake_notifier.reset() + action(server) + # Ensure that instance is in active state after an action + self._wait_for_state_change(self.admin_api, server, 'ACTIVE') + + @mock.patch('nova.compute.rpcapi.ComputeAPI.pre_live_migration', + side_effect=exception.LiveMigrationWithOldNovaNotSupported()) + def _test_live_migration_rollback(self, server, mock_migration): + 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_rollback.start') + self._wait_for_notification('instance.live_migration_rollback.end') + + self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS)) + self._verify_notification( + 'instance-live_migration_rollback-start', + replacements={ + 'reservation_id': server['reservation_id'], + 'uuid': server['id']}, + actual=fake_notifier.VERSIONED_NOTIFICATIONS[0]) + self._verify_notification( + 'instance-live_migration_rollback-end', + replacements={ + 'reservation_id': server['reservation_id'], + 'uuid': server['id']}, + actual=fake_notifier.VERSIONED_NOTIFICATIONS[1]) + + class TestInstanceNotificationSample( notification_sample_base.NotificationSampleTestBase): diff --git a/nova/tests/unit/compute/test_compute.py b/nova/tests/unit/compute/test_compute.py index 57aaec4579..c6706386d7 100644 --- a/nova/tests/unit/compute/test_compute.py +++ b/nova/tests/unit/compute/test_compute.py @@ -6345,12 +6345,18 @@ class ComputeTestCase(BaseTestCase, mock_bdms.return_value = [] + @mock.patch('nova.compute.utils.notify_about_instance_action') @mock.patch.object(self.compute, '_live_migration_cleanup_flags') @mock.patch.object(self.compute, 'network_api') - def _test(mock_nw_api, mock_lmcf): + def _test(mock_nw_api, mock_lmcf, mock_notify): mock_lmcf.return_value = False, False self.compute._rollback_live_migration(c, instance, 'foo', migrate_data=migrate_data) + mock_notify.assert_has_calls([ + mock.call(c, instance, self.compute.host, + action='live_migration_rollback', phase='start'), + mock.call(c, instance, self.compute.host, + action='live_migration_rollback', phase='end')]) mock_nw_api.setup_networks_on_host.assert_called_once_with( c, instance, self.compute.host) _test() @@ -6368,13 +6374,19 @@ class ComputeTestCase(BaseTestCase, mock_bdms.return_value = [] + @mock.patch('nova.compute.utils.notify_about_instance_action') @mock.patch.object(self.compute, '_live_migration_cleanup_flags') @mock.patch.object(self.compute, 'network_api') - def _test(mock_nw_api, mock_lmcf): + def _test(mock_nw_api, mock_lmcf, mock_notify): mock_lmcf.return_value = False, False self.compute._rollback_live_migration(c, instance, 'foo', migrate_data=migrate_data, migration_status='fake') + mock_notify.assert_has_calls([ + mock.call(c, instance, self.compute.host, + action='live_migration_rollback', phase='start'), + mock.call(c, instance, self.compute.host, + action='live_migration_rollback', phase='end')]) mock_nw_api.setup_networks_on_host.assert_called_once_with( c, instance, self.compute.host) _test() diff --git a/nova/tests/unit/compute/test_compute_mgr.py b/nova/tests/unit/compute/test_compute_mgr.py index 6547f013f3..25b0e565f0 100644 --- a/nova/tests/unit/compute/test_compute_mgr.py +++ b/nova/tests/unit/compute/test_compute_mgr.py @@ -5327,16 +5327,24 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase): def test_rollback_live_migration_handles_dict(self): compute = manager.ComputeManager() + @mock.patch('nova.compute.utils.notify_about_instance_action') @mock.patch.object(compute.network_api, 'setup_networks_on_host') @mock.patch.object(compute, '_notify_about_instance_usage') @mock.patch.object(compute, '_live_migration_cleanup_flags') @mock.patch('nova.objects.BlockDeviceMappingList.get_by_instance_uuid') - def _test(mock_bdm, mock_lmcf, mock_notify, mock_nwapi): + def _test(mock_bdm, mock_lmcf, mock_notify, mock_nwapi, + mock_notify_about_instance_action): mock_bdm.return_value = [] mock_lmcf.return_value = False, False + mock_instance = mock.MagicMock() compute._rollback_live_migration(self.context, - mock.MagicMock(), + mock_instance, 'foo', {}) + mock_notify_about_instance_action.assert_has_calls([ + mock.call(self.context, mock_instance, compute.host, + action='live_migration_rollback', phase='start'), + mock.call(self.context, mock_instance, compute.host, + action='live_migration_rollback', phase='end')]) self.assertIsInstance(mock_lmcf.call_args_list[0][0][0], migrate_data_obj.LiveMigrateData) diff --git a/nova/virt/fake.py b/nova/virt/fake.py index ec446df8f3..da77747b7c 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -38,6 +38,7 @@ from nova import exception from nova.i18n import _LW from nova.objects import diagnostics as diagnostics_obj from nova.objects import fields as obj_fields +from nova.objects import migrate_data from nova.virt import driver from nova.virt import hardware from nova.virt import virtapi @@ -497,11 +498,23 @@ class FakeDriver(driver.ComputeDriver): src_compute_info, dst_compute_info, block_migration=False, disk_over_commit=False): - return {} + data = migrate_data.LibvirtLiveMigrateData() + data.filename = 'fake' + data.image_type = CONF.libvirt.images_type + data.graphics_listen_addr_vnc = CONF.vnc.vncserver_listen + data.graphics_listen_addr_spice = CONF.spice.server_listen + data.serial_listen_addr = None + data.block_migration = block_migration + data.disk_over_commit = disk_over_commit or False # called with None + data.disk_available_mb = 100000 + data.is_shared_block_storage = True + data.is_shared_instance_path = True + + return data def check_can_live_migrate_source(self, context, instance, dest_check_data, block_device_info=None): - return + return dest_check_data def finish_migration(self, context, migration, instance, disk_info, network_info, image_meta, resize_instance,