diff --git a/nova/tests/unit/virt/libvirt/test_config.py b/nova/tests/unit/virt/libvirt/test_config.py index eada5a2b14a1..a11d2df79f48 100644 --- a/nova/tests/unit/virt/libvirt/test_config.py +++ b/nova/tests/unit/virt/libvirt/test_config.py @@ -2182,6 +2182,7 @@ class LibvirtConfigGuestFeatureTest(LibvirtConfigBaseTest): obj.relaxed = True obj.vapic = True obj.spinlocks = True + obj.vendorid_spoof = True xml = obj.to_xml() self.assertXmlEqual(xml, """ @@ -2189,6 +2190,7 @@ class LibvirtConfigGuestFeatureTest(LibvirtConfigBaseTest): + """) diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index adbff9c1ee82..8a8f2cec1fda 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -3813,13 +3813,17 @@ class LibvirtConnTestCase(test.NoDBTestCase, self.assertIsInstance(cfg.features[2], vconfig.LibvirtConfigGuestFeatureHyperV) - @mock.patch.object(host.Host, 'has_min_version') - def test_get_guest_config_windows_hyperv_feature2(self, mock_version): - mock_version.return_value = True + @mock.patch.object(host.Host, 'has_min_version', + new=mock.Mock(return_value=True)) + def _test_get_guest_config_windows_hyperv( + self, flavor=None, image_meta=None, hvid_hidden=False): drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = objects.Instance(**self.test_instance) instance_ref['os_type'] = 'windows' - image_meta = objects.ImageMeta.from_dict(self.test_image_meta) + if flavor is not None: + instance_ref.flavor = flavor + if image_meta is None: + image_meta = objects.ImageMeta.from_dict(self.test_image_meta) disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type, instance_ref, @@ -3832,18 +3836,67 @@ class LibvirtConnTestCase(test.NoDBTestCase, vconfig.LibvirtConfigGuestClock) self.assertEqual(cfg.clock.offset, "localtime") - self.assertEqual(3, len(cfg.features)) + num_features = 4 if hvid_hidden else 3 + self.assertEqual(num_features, len(cfg.features)) self.assertIsInstance(cfg.features[0], vconfig.LibvirtConfigGuestFeatureACPI) self.assertIsInstance(cfg.features[1], vconfig.LibvirtConfigGuestFeatureAPIC) self.assertIsInstance(cfg.features[2], vconfig.LibvirtConfigGuestFeatureHyperV) + if hvid_hidden: + self.assertIsInstance(cfg.features[3], + vconfig.LibvirtConfigGuestFeatureKvmHidden) self.assertTrue(cfg.features[2].relaxed) self.assertTrue(cfg.features[2].spinlocks) self.assertEqual(8191, cfg.features[2].spinlock_retries) self.assertTrue(cfg.features[2].vapic) + self.assertEqual(hvid_hidden, cfg.features[2].vendorid_spoof) + + def test_get_guest_config_windows_hyperv_feature2(self): + self._test_get_guest_config_windows_hyperv() + + def test_get_guest_config_windows_hyperv_all_hide_flv(self): + # Similar to test_get_guest_config_windows_hyperv_feature2 + # but also test hiding the HyperV signature with the flavor + # extra_spec "hide_hypervisor_id" + flavor_hide_id = fake_flavor.fake_flavor_obj(self.context, + extra_specs={"hide_hypervisor_id": "true"}, + expected_attrs={"extra_specs"}) + # this works for kvm (the default, tested below) and qemu + self.flags(virt_type='qemu', group='libvirt') + + self._test_get_guest_config_windows_hyperv( + flavor=flavor_hide_id, hvid_hidden=True) + + def test_get_guest_config_windows_hyperv_all_hide_img(self): + # Similar to test_get_guest_config_windows_hyperv_feature2 + # but also test hiding the HyperV signature with the image + # property "img_hide_hypervisor_id" + image_meta = objects.ImageMeta.from_dict({ + "disk_format": "raw", + "properties": {"img_hide_hypervisor_id": "true"}}) + + self._test_get_guest_config_windows_hyperv( + image_meta=image_meta, hvid_hidden=True) + + def test_get_guest_config_windows_hyperv_all_hide_flv_img(self): + # Similar to test_get_guest_config_windows_hyperv_feature2 + # but also test hiding the HyperV signature with both the flavor + # extra_spec "hide_hypervisor_id" and the image property + # "img_hide_hypervisor_id" + flavor_hide_id = fake_flavor.fake_flavor_obj(self.context, + extra_specs={"hide_hypervisor_id": "true"}, + expected_attrs={"extra_specs"}) + self.flags(virt_type='qemu', group='libvirt') + + image_meta = objects.ImageMeta.from_dict({ + "disk_format": "raw", + "properties": {"img_hide_hypervisor_id": "true"}}) + + self._test_get_guest_config_windows_hyperv( + flavor=flavor_hide_id, image_meta=image_meta, hvid_hidden=True) def test_get_guest_config_with_two_nics(self): drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) diff --git a/nova/virt/libvirt/config.py b/nova/virt/libvirt/config.py index b1e43bdccd2f..ec0a19af9a81 100644 --- a/nova/virt/libvirt/config.py +++ b/nova/virt/libvirt/config.py @@ -2334,6 +2334,8 @@ class LibvirtConfigGuestFeatureHyperV(LibvirtConfigGuestFeature): # QEMU requires at least this value to be set MIN_SPINLOCK_RETRIES = 4095 + # The spoofed vendor_id can be any alphanumeric string + SPOOFED_VENDOR_ID = "1234567890ab" def __init__(self, **kwargs): super(LibvirtConfigGuestFeatureHyperV, self).__init__("hyperv", @@ -2343,6 +2345,8 @@ class LibvirtConfigGuestFeatureHyperV(LibvirtConfigGuestFeature): self.vapic = False self.spinlocks = False self.spinlock_retries = self.MIN_SPINLOCK_RETRIES + self.vendorid_spoof = False + self.vendorid = self.SPOOFED_VENDOR_ID def format_dom(self): root = super(LibvirtConfigGuestFeatureHyperV, self).format_dom() @@ -2354,6 +2358,9 @@ class LibvirtConfigGuestFeatureHyperV(LibvirtConfigGuestFeature): if self.spinlocks: root.append(etree.Element("spinlocks", state="on", retries=str(self.spinlock_retries))) + if self.vendorid_spoof: + root.append(etree.Element("vendor_id", state="on", + value=self.vendorid)) return root diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 89622734ad0e..f89da6745808 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -4737,6 +4737,10 @@ class LibvirtDriver(driver.ComputeDriver): def _set_features(self, guest, os_type, caps, virt_type, image_meta, flavor): + hide_hypervisor_id = (strutils.bool_from_string( + flavor.extra_specs.get('hide_hypervisor_id')) or + image_meta.properties.get('img_hide_hypervisor_id')) + if virt_type == "xen": # PAE only makes sense in X86 if caps.host.cpu.arch in (fields.Architecture.I686, @@ -4759,13 +4763,23 @@ class LibvirtDriver(driver.ComputeDriver): # with Microsoft hv.spinlock_retries = 8191 hv.vapic = True + + # NOTE(kosamara): Spoofing the vendor_id aims to allow the nvidia + # driver to work on windows VMs. At the moment, the nvidia driver + # checks for the hyperv vendorid, and if it doesn't find that, it + # works. In the future, its behaviour could become more strict, + # checking for the presence of other hyperv feature flags to + # determine that it's loaded in a VM. If that happens, this + # workaround will not be enough, and we'll need to drop the whole + # hyperv element. + # That would disable some optimizations, reducing the guest's + # performance. + if hide_hypervisor_id: + hv.vendorid_spoof = True + guest.features.append(hv) - flavor_hide_kvm = strutils.bool_from_string( - flavor.get('extra_specs', {}).get('hide_hypervisor_id')) - if (virt_type in ("qemu", "kvm") and - (image_meta.properties.get('img_hide_hypervisor_id') or - flavor_hide_kvm)): + if (virt_type in ("qemu", "kvm") and hide_hypervisor_id): guest.features.append(vconfig.LibvirtConfigGuestFeatureKvmHidden()) def _check_number_of_serial_console(self, num_ports): diff --git a/releasenotes/notes/bug-1779845-8819eea6e91fb09c.yaml b/releasenotes/notes/bug-1779845-8819eea6e91fb09c.yaml new file mode 100644 index 000000000000..f8d19a454902 --- /dev/null +++ b/releasenotes/notes/bug-1779845-8819eea6e91fb09c.yaml @@ -0,0 +1,11 @@ +--- +fixes: + - | + Blueprints `hide-hypervisor-id-flavor-extra-spec`_ and + `add-kvm-hidden-feature`_ enabled NVIDIA drivers in Linux guests using KVM + and QEMU, but support was not included for Windows guests. This is now + fixed. See `bug 1779845`_ for details. + + .. _hide-hypervisor-id-flavor-extra-spec: https://blueprints.launchpad.net/nova/+spec/hide-hypervisor-id-flavor-extra-spec + .. _add-kvm-hidden-feature: https://blueprints.launchpad.net/nova/+spec/add-kvm-hidden-feature + .. _bug 1779845: https://bugs.launchpad.net/nova/+bug/1779845