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:
Konstantinos Samaras-Tsakiris 2018-07-03 12:28:55 +00:00 committed by Matt Riedemann
parent 809799e581
commit ca543438e1
5 changed files with 97 additions and 10 deletions

View File

@ -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>""")

View File

@ -3813,12 +3813,16 @@ 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'
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,
@ -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)

View File

@ -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

View File

@ -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):

View 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