Hide hypervisor id on windows guests
Blueprints hide-hypervisor-id-flavor-extra-spec [1] and add-kvm-hidden-feature [2] allow hiding KVM's signature for guests, which is necessary for Nvidia drivers to work in VMs with passthrough GPUs. While this works well for linux guests on KVM, it doesn't work for Windows guests. For them, KVM emulates some HyperV features. With the current implementation, KVM's signature is hidden, but HyperV's is not, and Nvidia drivers don't work in Windows VMs. This change generates an extra element in the libvirt xml for Windows guests on KVM which obfuscates HyperV's signature too, controlled by the existing image and flavor parameters (img_hide_hypervisor_id and hide_hypervisor_id correspondingly). The extra xml element is <vendor_id state='on' value='1234567890ab'/> in features/hyperv. [1] https://blueprints.launchpad.net/nova/+spec/hide-hypervisor-id-flavor-extra-spec [2] https://blueprints.launchpad.net/nova/+spec/add-kvm-hidden-feature Change-Id: Iaaeae9281301f14f4ae9b43f4a06de58b699fd68 Closes-Bug: 1779845
This commit is contained in:
parent
809799e581
commit
ca543438e1
@ -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):
|
||||
<relaxed state="on"/>
|
||||
<vapic state="on"/>
|
||||
<spinlocks state="on" retries="4095"/>
|
||||
<vendor_id state="on" value="1234567890ab"/>
|
||||
</hyperv>""")
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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):
|
||||
|
11
releasenotes/notes/bug-1779845-8819eea6e91fb09c.yaml
Normal file
11
releasenotes/notes/bug-1779845-8819eea6e91fb09c.yaml
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user