From b5527c07fbfc4b52306c9a3fd10d8f818506ddbb Mon Sep 17 00:00:00 2001 From: Sundar Nadathur Date: Sun, 8 Dec 2019 16:06:34 -0800 Subject: [PATCH] Enable hard/soft reboot with accelerators. Blueprint: nova-cyborg-interaction Change-Id: Ibf9cca80e34c573a6dcc77dd88514bfa673a0b42 --- nova/compute/manager.py | 12 +++++ nova/tests/unit/compute/test_compute.py | 56 ++++++++++++++++++++- nova/tests/unit/virt/libvirt/test_driver.py | 6 ++- nova/virt/driver.py | 5 +- nova/virt/fake.py | 3 +- nova/virt/hyperv/driver.py | 3 +- nova/virt/ironic/driver.py | 6 ++- nova/virt/libvirt/driver.py | 9 ++-- nova/virt/powervm/driver.py | 5 +- nova/virt/vmwareapi/driver.py | 3 +- nova/virt/xenapi/driver.py | 3 +- nova/virt/zvm/driver.py | 3 +- 12 files changed, 98 insertions(+), 16 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index d4f32a9e6039..f5239017bc77 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -3680,6 +3680,15 @@ class ComputeManager(manager.Manager): # Manager-detach self.detach_volume(context, volume_id, instance) + def _get_accel_info(self, context, instance): + dp_name = instance.flavor.extra_specs.get('accel:device_profile') + if dp_name: + cyclient = cyborg.get_client(context) + accel_info = cyclient.get_arqs_for_instance(instance.uuid) + else: + accel_info = [] + return accel_info + @wrap_exception() @reverts_task_state @wrap_instance_event(prefix='compute') @@ -3714,6 +3723,8 @@ class ComputeManager(manager.Manager): network_info = self.network_api.get_instance_nw_info(context, instance) + accel_info = self._get_accel_info(context, instance) + self._notify_about_instance_usage(context, instance, "reboot.start") compute_utils.notify_about_instance_action( context, instance, self.host, @@ -3755,6 +3766,7 @@ class ComputeManager(manager.Manager): network_info, reboot_type, block_device_info=block_device_info, + accel_info=accel_info, bad_volumes_callback=bad_volumes_callback) except Exception as error: diff --git a/nova/tests/unit/compute/test_compute.py b/nova/tests/unit/compute/test_compute.py index d16a752f027f..308a5914bdfe 100644 --- a/nova/tests/unit/compute/test_compute.py +++ b/nova/tests/unit/compute/test_compute.py @@ -2975,6 +2975,7 @@ class ComputeTestCase(BaseTestCase, launched_at=timeutils.utcnow())) instance = objects.Instance._from_db_object(econtext, objects.Instance(), db_instance) + instance.flavor = self.default_flavor updated_dbinstance1 = fake_instance.fake_db_instance( **dict(uuid=uuids.db_instance_1, @@ -3042,7 +3043,8 @@ class ComputeTestCase(BaseTestCase, expected_call_info = { 'args': (econtext, instance, expected_nw_info, reboot_type), - 'kwargs': {'block_device_info': fake_block_dev_info}} + 'kwargs': {'block_device_info': fake_block_dev_info, + 'accel_info': []}} fault = exception.InstanceNotFound(instance_id='instance-0000') def fake_reboot(self, *args, **kwargs): @@ -3158,6 +3160,58 @@ class ComputeTestCase(BaseTestCase, def test_reboot_hard_and_delete_and_rescued(self): self._test_reboot(False, test_delete=True, test_unrescue=True) + @mock.patch('nova.virt.fake.FakeDriver.reboot') + @mock.patch('nova.objects.instance.Instance.save') + @mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid') + @mock.patch.object(compute_manager.ComputeManager, + '_get_instance_block_device_info') + @mock.patch.object(compute_manager.ComputeManager, + '_notify_about_instance_usage') + @mock.patch.object(compute_manager.ComputeManager, '_instance_update') + @mock.patch.object(db, 'instance_update_and_get_original') + @mock.patch.object(compute_manager.ComputeManager, '_get_power_state') + @mock.patch('nova.compute.utils.notify_about_instance_action') + def _test_reboot_with_accels(self, mock_notify_action, mock_get_power, + mock_get_orig, mock_update, mock_notify_usage, + mock_get_blk, mock_get_bdms, mock_inst_save, mock_reboot, + extra_specs=None, accel_info=None): + + self.compute.network_api.get_instance_nw_info = mock.Mock() + + reboot_type = 'SOFT' + instance = self._create_fake_instance_obj() + if extra_specs: + instance.flavor.extra_specs = extra_specs + + self.compute.reboot_instance(self.context, instance=instance, + block_device_info=None, reboot_type=reboot_type) + + mock_reboot.assert_called_once_with( + mock.ANY, instance, mock.ANY, reboot_type, + block_device_info=mock.ANY, + bad_volumes_callback=mock.ANY, + accel_info=accel_info or [] + ) + + return instance['uuid'] + + @mock.patch('nova.accelerator.cyborg._CyborgClient.get_arqs_for_instance') + def test_reboot_with_accels_ok(self, mock_get_arqs): + dp_name = 'mydp' + extra_specs = {'accel:device_profile': dp_name} + _, accel_info = fixtures.get_arqs(dp_name) + mock_get_arqs.return_value = accel_info + + instance_uuid = self._test_reboot_with_accels( + extra_specs=extra_specs, accel_info=accel_info) + + mock_get_arqs.assert_called_once_with(instance_uuid) + + @mock.patch('nova.accelerator.cyborg._CyborgClient.get_arqs_for_instance') + def test_reboot_with_accels_no_dp(self, mock_get_arqs): + self._test_reboot_with_accels(extra_specs=None, accel_info=None) + mock_get_arqs.assert_not_called() + @mock.patch.object(jsonutils, 'to_primitive') def test_reboot_fail(self, mock_to_primitive): self._test_reboot(False, fail_reboot=True) diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 398a041bbf20..481f343f0878 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -15483,9 +15483,10 @@ class LibvirtConnTestCase(test.NoDBTestCase, backend = self.useFixture(fake_imagebackend.ImageBackendFixture()) + accel_info = [{'k1': 'v1', 'k2': 'v2'}] with mock.patch('os.path.exists', return_value=True): drvr._hard_reboot(self.context, instance, network_info, - block_device_info) + block_device_info, accel_info=accel_info) disks = backend.disks @@ -15510,7 +15511,8 @@ class LibvirtConnTestCase(test.NoDBTestCase, mock_get_guest_xml.assert_called_once_with(self.context, instance, network_info, mock.ANY, mock.ANY, - block_device_info=block_device_info, mdevs=[uuids.mdev1]) + block_device_info=block_device_info, mdevs=[uuids.mdev1], + accel_info=accel_info) mock_create_domain_and_network.assert_called_once_with(self.context, dummyxml, instance, network_info, block_device_info=block_device_info, vifs_already_plugged=True) diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 9f0bcb65bd4f..61e86f768574 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -430,7 +430,8 @@ class ComputeDriver(object): raise NotImplementedError() def reboot(self, context, instance, network_info, reboot_type, - block_device_info=None, bad_volumes_callback=None): + block_device_info=None, bad_volumes_callback=None, + accel_info=None): """Reboot the specified instance. After this is called successfully, the instance's state @@ -445,6 +446,8 @@ class ComputeDriver(object): :param block_device_info: Info pertaining to attached volumes :param bad_volumes_callback: Function to handle any bad volumes encountered + :param accel_info: List of accelerator request dicts. The exact + data struct is doc'd in nova/virt/driver.py::spawn(). """ raise NotImplementedError() diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 5f5c91c7d3c2..51f057aeeaf0 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -208,7 +208,8 @@ class FakeDriver(driver.ComputeDriver): update_task_state(task_state=task_states.IMAGE_UPLOADING) def reboot(self, context, instance, network_info, reboot_type, - block_device_info=None, bad_volumes_callback=None): + block_device_info=None, bad_volumes_callback=None, + accel_info=None): # If the guest is not on the hypervisor and we're doing a hard reboot # then mimic the libvirt driver by spawning the guest. if (instance.uuid not in self.instances and diff --git a/nova/virt/hyperv/driver.py b/nova/virt/hyperv/driver.py index 388ddf89b899..3653ed1b6264 100644 --- a/nova/virt/hyperv/driver.py +++ b/nova/virt/hyperv/driver.py @@ -165,7 +165,8 @@ class HyperVDriver(driver.ComputeDriver): admin_password, network_info, block_device_info) def reboot(self, context, instance, network_info, reboot_type, - block_device_info=None, bad_volumes_callback=None): + block_device_info=None, bad_volumes_callback=None, + accel_info=None): self._vmops.reboot(instance, network_info, reboot_type) def destroy(self, context, instance, network_info, block_device_info=None, diff --git a/nova/virt/ironic/driver.py b/nova/virt/ironic/driver.py index 9409e4c97b6e..5cbbb6e0af1c 100644 --- a/nova/virt/ironic/driver.py +++ b/nova/virt/ironic/driver.py @@ -1371,7 +1371,8 @@ class IronicDriver(virt_driver.ComputeDriver): node.uuid, instance=instance) def reboot(self, context, instance, network_info, reboot_type, - block_device_info=None, bad_volumes_callback=None): + block_device_info=None, bad_volumes_callback=None, + accel_info=None): """Reboot the specified instance. NOTE: Unlike the libvirt driver, this method does not delete @@ -1386,7 +1387,8 @@ class IronicDriver(virt_driver.ComputeDriver): Ignored by this driver. :param bad_volumes_callback: Function to handle any bad volumes encountered. Ignored by this driver. - + :param accel_info: List of accelerator request dicts. The exact + data struct is doc'd in nova/virt/driver.py::spawn(). """ LOG.debug('Reboot(type %s) called for instance', reboot_type, instance=instance) diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 13ccc90311a1..bd7a15d8d554 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -3054,7 +3054,8 @@ class LibvirtDriver(driver.ComputeDriver): self._volume_refresh_connection_info(context, instance, volume_id) def reboot(self, context, instance, network_info, reboot_type, - block_device_info=None, bad_volumes_callback=None): + block_device_info=None, bad_volumes_callback=None, + accel_info=None): """Reboot a virtual machine, given an instance reference.""" if reboot_type == 'SOFT': # NOTE(vish): This will attempt to do a graceful shutdown/restart. @@ -3075,7 +3076,7 @@ class LibvirtDriver(driver.ComputeDriver): "Trying hard reboot.", instance=instance) return self._hard_reboot(context, instance, network_info, - block_device_info) + block_device_info, accel_info) def _soft_reboot(self, instance): """Attempt to shutdown and restart the instance gracefully. @@ -3127,7 +3128,7 @@ class LibvirtDriver(driver.ComputeDriver): return False def _hard_reboot(self, context, instance, network_info, - block_device_info=None): + block_device_info=None, accel_info=None): """Reboot a virtual machine, given an instance reference. Performs a Libvirt reset (if supported) on the domain. @@ -3169,7 +3170,7 @@ class LibvirtDriver(driver.ComputeDriver): xml = self._get_guest_xml(context, instance, network_info, disk_info, instance.image_meta, block_device_info=block_device_info, - mdevs=mdevs) + mdevs=mdevs, accel_info=accel_info) # NOTE(mdbooth): context.auth_token will not be set when we call # _hard_reboot from resume_state_on_host_boot() diff --git a/nova/virt/powervm/driver.py b/nova/virt/powervm/driver.py index 5e0b881f687b..6c6f0d342537 100644 --- a/nova/virt/powervm/driver.py +++ b/nova/virt/powervm/driver.py @@ -473,7 +473,8 @@ class PowerVMDriver(driver.ComputeDriver): vm.power_on(self.adapter, instance) def reboot(self, context, instance, network_info, reboot_type, - block_device_info=None, bad_volumes_callback=None): + block_device_info=None, bad_volumes_callback=None, + accel_info=None): """Reboot the specified instance. After this is called successfully, the instance's state @@ -489,6 +490,8 @@ class PowerVMDriver(driver.ComputeDriver): :param block_device_info: Info pertaining to attached volumes :param bad_volumes_callback: Function to handle any bad volumes encountered + :param accel_info: List of accelerator request dicts. The exact + data struct is doc'd in nova/virt/driver.py::spawn(). """ self._log_operation(reboot_type + ' reboot', instance) vm.reboot(self.adapter, instance, reboot_type == 'HARD') diff --git a/nova/virt/vmwareapi/driver.py b/nova/virt/vmwareapi/driver.py index 66cac7c03119..a7f5b163a1cb 100644 --- a/nova/virt/vmwareapi/driver.py +++ b/nova/virt/vmwareapi/driver.py @@ -570,7 +570,8 @@ class VMwareVCDriver(driver.ComputeDriver): self._vmops.snapshot(context, instance, image_id, update_task_state) def reboot(self, context, instance, network_info, reboot_type, - block_device_info=None, bad_volumes_callback=None): + block_device_info=None, bad_volumes_callback=None, + accel_info=None): """Reboot VM instance.""" self._vmops.reboot(instance, network_info, reboot_type) diff --git a/nova/virt/xenapi/driver.py b/nova/virt/xenapi/driver.py index e3952e142eba..64f1391c751a 100644 --- a/nova/virt/xenapi/driver.py +++ b/nova/virt/xenapi/driver.py @@ -252,7 +252,8 @@ class XenAPIDriver(driver.ComputeDriver): self._vmops.post_interrupted_snapshot_cleanup(context, instance) def reboot(self, context, instance, network_info, reboot_type, - block_device_info=None, bad_volumes_callback=None): + block_device_info=None, bad_volumes_callback=None, + accel_info=None): """Reboot VM instance.""" self._vmops.reboot(instance, reboot_type, bad_volumes_callback=bad_volumes_callback) diff --git a/nova/virt/zvm/driver.py b/nova/virt/zvm/driver.py index a60e36ae6ba9..781ae9f35a20 100644 --- a/nova/virt/zvm/driver.py +++ b/nova/virt/zvm/driver.py @@ -405,7 +405,8 @@ class ZVMDriver(driver.ComputeDriver): self._hypervisor.guest_unpause(instance.name) def reboot(self, context, instance, network_info, reboot_type, - block_device_info=None, bad_volumes_callback=None): + block_device_info=None, bad_volumes_callback=None, + accel_info=None): if reboot_type == 'SOFT': self._hypervisor.guest_reboot(instance.name)