From 12cf893fd9bc787d2c5c6ef0a8ca0f81582ead8a Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Mon, 8 Dec 2014 14:30:06 +0000 Subject: [PATCH] libvirt: enable hyperv enlightenments for windows guests All windows guests (ie os_type=windows) now get the following enabled if running a new enough libvirt and QEMU Closes-bug: #1400315 Change-Id: Icd0fe9bda6402d9bf7a4bab8077f0ce755703999 --- nova/tests/unit/virt/libvirt/test_driver.py | 84 +++++++++++++++++++++ nova/virt/libvirt/driver.py | 25 +++++- 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index e9ec14fb82..c685e2c654 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -1598,6 +1598,90 @@ class LibvirtConnTestCase(test.NoDBTestCase): self.assertEqual("hypervclock", cfg.clock.timers[3].name) self.assertTrue(cfg.clock.timers[3].present) + self.assertEqual(3, len(cfg.features)) + self.assertIsInstance(cfg.features[0], + vconfig.LibvirtConfigGuestFeatureACPI) + self.assertIsInstance(cfg.features[1], + vconfig.LibvirtConfigGuestFeatureAPIC) + self.assertIsInstance(cfg.features[2], + vconfig.LibvirtConfigGuestFeatureHyperV) + + @mock.patch.object(host.Host, 'has_min_version') + @mock.patch.object(objects.Flavor, 'get_by_id') + def test_get_guest_config_windows_hyperv_feature1(self, + mock_flavor, + mock_version): + def fake_version(lv_ver=None, hv_ver=None, hv_type=None): + if lv_ver == (1, 0, 0) and hv_ver == (1, 1, 0): + return True + return False + + mock_version.side_effect = fake_version + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + instance_ref = objects.Instance(**self.test_instance) + instance_ref['os_type'] = 'windows' + flavor = instance_ref.get_flavor() + flavor.extra_specs = {} + mock_flavor.return_value = flavor + + disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type, + instance_ref) + cfg = conn._get_guest_config(instance_ref, + _fake_network_info(self.stubs, 1), + {}, disk_info) + + self.assertIsInstance(cfg.clock, + vconfig.LibvirtConfigGuestClock) + self.assertEqual(cfg.clock.offset, "localtime") + + self.assertEqual(3, len(cfg.features)) + self.assertIsInstance(cfg.features[0], + vconfig.LibvirtConfigGuestFeatureACPI) + self.assertIsInstance(cfg.features[1], + vconfig.LibvirtConfigGuestFeatureAPIC) + self.assertIsInstance(cfg.features[2], + vconfig.LibvirtConfigGuestFeatureHyperV) + + self.assertTrue(cfg.features[2].relaxed) + self.assertFalse(cfg.features[2].spinlocks) + self.assertFalse(cfg.features[2].vapic) + + @mock.patch.object(host.Host, 'has_min_version') + @mock.patch.object(objects.Flavor, 'get_by_id') + def test_get_guest_config_windows_hyperv_feature2(self, + mock_flavor, + mock_version): + mock_version.return_value = True + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + instance_ref = objects.Instance(**self.test_instance) + instance_ref['os_type'] = 'windows' + flavor = instance_ref.get_flavor() + flavor.extra_specs = {} + mock_flavor.return_value = flavor + + disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type, + instance_ref) + cfg = conn._get_guest_config(instance_ref, + _fake_network_info(self.stubs, 1), + {}, disk_info) + + self.assertIsInstance(cfg.clock, + vconfig.LibvirtConfigGuestClock) + self.assertEqual(cfg.clock.offset, "localtime") + + self.assertEqual(3, len(cfg.features)) + self.assertIsInstance(cfg.features[0], + vconfig.LibvirtConfigGuestFeatureACPI) + self.assertIsInstance(cfg.features[1], + vconfig.LibvirtConfigGuestFeatureAPIC) + self.assertIsInstance(cfg.features[2], + vconfig.LibvirtConfigGuestFeatureHyperV) + + 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) + @mock.patch.object(objects.Flavor, 'get_by_id') def test_get_guest_config_with_two_nics(self, mock_flavor): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index aa68d6859b..2023f5a282 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -365,6 +365,10 @@ MIN_LIBVIRT_FSFREEZE_VERSION = (1, 2, 5) MIN_LIBVIRT_HYPERV_TIMER_VERSION = (1, 2, 2) MIN_QEMU_HYPERV_TIMER_VERSION = (2, 0, 0) +MIN_LIBVIRT_HYPERV_FEATURE_VERSION = (1, 0, 0) +MIN_LIBVIRT_HYPERV_FEATURE_EXTRA_VERSION = (1, 1, 0) +MIN_QEMU_HYPERV_FEATURE_VERSION = (1, 1, 0) + class LibvirtDriver(driver.ComputeDriver): @@ -3665,7 +3669,7 @@ class LibvirtDriver(driver.ComputeDriver): tmhyperv.present = True clk.add_timer(tmhyperv) - def _set_features(self, guest, caps, virt_type): + def _set_features(self, guest, os_type, caps, virt_type): if virt_type == "xen": # PAE only makes sense in X86 if caps.host.cpu.arch in (arch.I686, arch.X86_64): @@ -3675,6 +3679,23 @@ class LibvirtDriver(driver.ComputeDriver): guest.features.append(vconfig.LibvirtConfigGuestFeatureACPI()) guest.features.append(vconfig.LibvirtConfigGuestFeatureAPIC()) + if (virt_type in ("qemu", "kvm") and + os_type == 'windows' and + self._host.has_min_version(MIN_LIBVIRT_HYPERV_FEATURE_VERSION, + MIN_QEMU_HYPERV_FEATURE_VERSION)): + hv = vconfig.LibvirtConfigGuestFeatureHyperV() + hv.relaxed = True + + if self._host.has_min_version( + MIN_LIBVIRT_HYPERV_FEATURE_EXTRA_VERSION): + hv.spinlocks = True + # Increase spinlock retries - value recommended by + # KVM maintainers who certify Windows guests + # with Microsoft + hv.spinlock_retries = 8191 + hv.vapic = True + guest.features.append(hv) + def _create_serial_console_devices(self, guest, instance, flavor, image_meta): if CONF.serial_console.enabled: @@ -3870,7 +3891,7 @@ class LibvirtDriver(driver.ComputeDriver): else: guest.os_boot_dev = blockinfo.get_boot_order(disk_info) - self._set_features(guest, caps, virt_type) + self._set_features(guest, instance.os_type, caps, virt_type) self._set_clock(guest, instance.os_type, image_meta, virt_type) storage_configs = self._get_guest_storage_config(