From c7c08e590eb0ea2452a1a1d925297ba43fa609b0 Mon Sep 17 00:00:00 2001 From: Daniel Pawlik Date: Tue, 25 Apr 2017 15:12:29 +0000 Subject: [PATCH] Add `img_hide_hypervisor_id` image property Image hide_hypervisor_id property helps libvirt set an xml instance file property, which will hide on guest host KVM hypervisor signature ("KVMKVMKVM\0\0\0"). According to the commit message in QEMU repository [1]: "The latest Nvidia driver (337.88) specifically checks for KVM as the hypervisor and reports Code 43 for the driver in a Windows guest when found. Removing or changing the KVM signature is sufficient for the driver to load and work." DocImpact: New feature ``img_hide_hypervisor_id`` image property should be added in the glance-property-keys page of the cli-reference docs [2]. [1]: http://git.qemu.org/?p=qemu.git;a=commitdiff;h=f522d2a [2]: https://docs.openstack.org/cli-reference/glance-property-keys.html Implements: blueprint add-kvm-hidden-feature Co-Authored-By: Adam Kijak Change-Id: Ie8227fececa40e502aaa39d77de2a1cd0cd72682 --- nova/objects/image_meta.py | 8 +++- nova/tests/unit/objects/test_image_meta.py | 7 +++ nova/tests/unit/objects/test_objects.py | 2 +- nova/tests/unit/virt/libvirt/test_config.py | 4 ++ nova/tests/unit/virt/libvirt/test_driver.py | 48 +++++++++++++++++++ nova/virt/libvirt/config.py | 14 ++++++ nova/virt/libvirt/driver.py | 9 +++- .../hide_hypervisor_id-6f93e7552336930d.yaml | 32 +++++++++++++ 8 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/hide_hypervisor_id-6f93e7552336930d.yaml diff --git a/nova/objects/image_meta.py b/nova/objects/image_meta.py index c55ef46886ab..3beb5007b658 100644 --- a/nova/objects/image_meta.py +++ b/nova/objects/image_meta.py @@ -167,12 +167,15 @@ class ImageMetaProps(base.NovaObject): # Version 1.16: WatchdogActionField supports 'disabled' enum. # Version 1.17: Add lan9118 as valid nic for hw_vif_model property for qemu # Version 1.18: Pull signature properties from cursive library - VERSION = '1.18' + # Version 1.19: Added 'img_hide_hypervisor_id' type field + VERSION = '1.19' def obj_make_compatible(self, primitive, target_version): super(ImageMetaProps, self).obj_make_compatible(primitive, target_version) target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 19): + primitive.pop('img_hide_hypervisor_id', None) if target_version < (1, 16) and 'hw_watchdog_action' in primitive: # Check to see if hw_watchdog_action was set to 'disabled' and if # so, remove it since not specifying it is the same behavior. @@ -415,6 +418,9 @@ class ImageMetaProps(base.NovaObject): # string indicating type of key used to compute image signature 'img_signature_key_type': fields.ImageSignatureKeyTypeField(), + # boolean - hide hypervisor signature on instance + 'img_hide_hypervisor_id': fields.FlexibleBooleanField(), + # string of username with admin privileges 'os_admin_user': fields.StringField(), diff --git a/nova/tests/unit/objects/test_image_meta.py b/nova/tests/unit/objects/test_image_meta.py index ccbc62c23970..1e3ca5eb0c26 100644 --- a/nova/tests/unit/objects/test_image_meta.py +++ b/nova/tests/unit/objects/test_image_meta.py @@ -323,3 +323,10 @@ class TestImageMetaProps(test.NoDBTestCase): props = {'os_secure_boot': "required"} secure_props = objects.ImageMetaProps.from_dict(props) self.assertEqual("required", secure_props.os_secure_boot) + + def test_obj_make_compatible_img_hide_hypervisor_id(self): + """Tests that checks if we pop img_hide_hypervisor_id.""" + obj = objects.ImageMetaProps(img_hide_hypervisor_id=True) + primitive = obj.obj_to_primitive('1.0') + self.assertNotIn('img_hide_hypervisor_id', + primitive['nova_object.data']) diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index 870e14289611..e58fbfc51645 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1096,7 +1096,7 @@ object_data = { 'HVSpec': '1.2-de06bcec472a2f04966b855a49c46b41', 'IDEDeviceBus': '1.0-29d4c9f27ac44197f01b6ac1b7e16502', 'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d', - 'ImageMetaProps': '1.18-3e5975251f5843e817de68ac83274c27', + 'ImageMetaProps': '1.19-dc9581ff2b80d8c33462889916b82df0', 'Instance': '2.3-4f98ab23f4b0a25fabb1040c8f5edecc', 'InstanceAction': '1.1-f9f293e526b66fca0d05c3b3a2d13914', 'InstanceActionEvent': '1.1-e56a64fa4710e43ef7af2ad9d6028b33', diff --git a/nova/tests/unit/virt/libvirt/test_config.py b/nova/tests/unit/virt/libvirt/test_config.py index 99382f50309a..c3b3d07d0d35 100644 --- a/nova/tests/unit/virt/libvirt/test_config.py +++ b/nova/tests/unit/virt/libvirt/test_config.py @@ -2100,6 +2100,7 @@ class LibvirtConfigGuestTest(LibvirtConfigBaseTest): config.LibvirtConfigGuestFeatureACPI(), config.LibvirtConfigGuestFeatureAPIC(), config.LibvirtConfigGuestFeaturePAE(), + config.LibvirtConfigGuestFeatureKvmHidden() ] obj.sysinfo = config.LibvirtConfigGuestSysinfo() @@ -2158,6 +2159,9 @@ class LibvirtConfigGuestTest(LibvirtConfigBaseTest): + + + 100 diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 7ed44e367d7f..c3a8479f8c07 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -4916,6 +4916,54 @@ class LibvirtConnTestCase(test.NoDBTestCase): self.assertEqual(10000, cfg.cputune.shares) self.assertEqual(20000, cfg.cputune.period) + def test_get_guest_config_with_hiding_hypervisor_id(self): + self.flags(virt_type='kvm', group='libvirt') + + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + + instance_ref = objects.Instance(**self.test_instance) + + image_meta = objects.ImageMeta.from_dict({ + "disk_format": "raw", + "properties": {"img_hide_hypervisor_id": "true"}}) + + disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type, + instance_ref, + image_meta) + + cfg = drvr._get_guest_config(instance_ref, + [], + image_meta, + disk_info) + + self.assertTrue( + any(isinstance(feature, vconfig.LibvirtConfigGuestFeatureKvmHidden) + for feature in cfg.features)) + + def test_get_guest_config_without_hiding_hypervisor_id(self): + self.flags(virt_type='kvm', group='libvirt') + + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + + instance_ref = objects.Instance(**self.test_instance) + + image_meta = objects.ImageMeta.from_dict({ + "disk_format": "raw", + "properties": {"img_hide_hypervisor_id": "false"}}) + + disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type, + instance_ref, + image_meta) + + cfg = drvr._get_guest_config(instance_ref, + [], + image_meta, + disk_info) + + self.assertFalse( + any(isinstance(feature, vconfig.LibvirtConfigGuestFeatureKvmHidden) + for feature in cfg.features)) + @mock.patch.object( host.Host, "is_cpu_control_policy_capable", return_value=True) def test_get_guest_config_with_bogus_cpu_quota(self, is_able): diff --git a/nova/virt/libvirt/config.py b/nova/virt/libvirt/config.py index 1cf4d4fe4f7f..8a7ebf4d22bb 100644 --- a/nova/virt/libvirt/config.py +++ b/nova/virt/libvirt/config.py @@ -2000,6 +2000,20 @@ class LibvirtConfigGuestFeaturePAE(LibvirtConfigGuestFeature): **kwargs) +class LibvirtConfigGuestFeatureKvmHidden(LibvirtConfigGuestFeature): + + def __init__(self, **kwargs): + super(LibvirtConfigGuestFeatureKvmHidden, self).__init__("kvm", + **kwargs) + + def format_dom(self): + root = super(LibvirtConfigGuestFeatureKvmHidden, self).format_dom() + + root.append(etree.Element("hidden", state="on")) + + return root + + class LibvirtConfigGuestFeatureHyperV(LibvirtConfigGuestFeature): # QEMU requires at least this value to be set diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 8ee14cbc1834..29bc9e05ffa7 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -4156,7 +4156,7 @@ class LibvirtDriver(driver.ComputeDriver): tmhyperv.present = True clk.add_timer(tmhyperv) - def _set_features(self, guest, os_type, caps, virt_type): + def _set_features(self, guest, os_type, caps, virt_type, image_meta): if virt_type == "xen": # PAE only makes sense in X86 if caps.host.cpu.arch in (fields.Architecture.I686, @@ -4181,6 +4181,10 @@ class LibvirtDriver(driver.ComputeDriver): hv.vapic = True guest.features.append(hv) + if (virt_type in ("qemu", "kvm") and + image_meta.properties.get('img_hide_hypervisor_id')): + guest.features.append(vconfig.LibvirtConfigGuestFeatureKvmHidden()) + def _check_number_of_serial_console(self, num_ports): virt_type = CONF.libvirt.virt_type if (virt_type in ("kvm", "qemu") and @@ -4695,7 +4699,8 @@ class LibvirtDriver(driver.ComputeDriver): self._conf_non_lxc_uml(virt_type, guest, root_device_name, rescue, instance, inst_path, image_meta, disk_info) - self._set_features(guest, instance.os_type, caps, virt_type) + self._set_features(guest, instance.os_type, caps, virt_type, + image_meta) self._set_clock(guest, instance.os_type, image_meta, virt_type) storage_configs = self._get_guest_storage_config( diff --git a/releasenotes/notes/hide_hypervisor_id-6f93e7552336930d.yaml b/releasenotes/notes/hide_hypervisor_id-6f93e7552336930d.yaml new file mode 100644 index 000000000000..2754b84eb097 --- /dev/null +++ b/releasenotes/notes/hide_hypervisor_id-6f93e7552336930d.yaml @@ -0,0 +1,32 @@ +--- +features: + - | + Some hypervisors add a signature to their guests, e.g. KVM is adding + ``KVMKVMKVM\0\0\0``, Xen: ``XenVMMXenVMM``. + The existence of a hypervisor signature enables some paravirtualization + features on the guest as well as disallowing certain drivers which test + for the hypervisor to load e.g. Nvidia driver [1]: + "The latest Nvidia driver (337.88) specifically checks + for KVM as the hypervisor and reports Code 43 for the + driver in a Windows guest when found. Removing or + changing the KVM signature is sufficient for the driver + to load and work." + + The new ``img_hide_hypervisor_id`` image metadata property hides the + hypervisor signature for the guest. + + Currently only the libvirt compute driver can hide hypervisor signature + for the guest host. + + To verify if hiding hypervisor id is working on Linux based system:: + + $ cpuid | grep -i hypervisor_id + + The result should not be (for KVM hypervisor):: + + $ hypervisor_id = KVMKVMKVM\0\0\0 + + You can enable this feature by setting the ``img_hide_hypervisor_id=true`` + property in a Glance image. + + [1]: http://git.qemu.org/?p=qemu.git;a=commitdiff;h=f522d2a