From 9e2dfb61ed1c8f8c891c34ca4da2b46b69abd661 Mon Sep 17 00:00:00 2001 From: Qiaowei Ren Date: Sat, 2 Jan 2016 09:20:16 +0800 Subject: [PATCH] enable uefi boot This patch enables uefi boot with the libvirt driver. This feature is without some kind of functional testing and therefore considered experimental. Co-Authored-By: XinXiaohui Change-Id: I9d8b2896e4bd29f67ebab6ce1bc927aae271c3e7 Implements: blueprint boot-from-uefi --- nova/exception.py | 4 ++ nova/tests/unit/virt/libvirt/test_config.py | 25 ++++++++++ nova/tests/unit/virt/libvirt/test_driver.py | 50 +++++++++++++++++++ nova/virt/libvirt/config.py | 13 ++++- nova/virt/libvirt/driver.py | 32 ++++++++++++ .../bp-boot-from-uefi-b413b96017db76dd.yaml | 3 ++ 6 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/bp-boot-from-uefi-b413b96017db76dd.yaml diff --git a/nova/exception.py b/nova/exception.py index 3f2d72716138..7dc40e6a56e8 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -2034,6 +2034,10 @@ class RequestSpecNotFound(NotFound): msg_fmt = _("RequestSpec not found for instance %(instance_uuid)s") +class UEFINotSupported(Invalid): + msg_fmt = _("UEFI is not supported") + + class NMINotSupported(Invalid): msg_fmt = _("Injecting NMI is not supported") diff --git a/nova/tests/unit/virt/libvirt/test_config.py b/nova/tests/unit/virt/libvirt/test_config.py index 7ab290123aac..5b6c10b72338 100644 --- a/nova/tests/unit/virt/libvirt/test_config.py +++ b/nova/tests/unit/virt/libvirt/test_config.py @@ -1792,6 +1792,31 @@ class LibvirtConfigGuestTest(LibvirtConfigBaseTest): """) + def test_config_uefi(self): + obj = config.LibvirtConfigGuest() + obj.virt_type = "kvm" + obj.memory = 100 * units.Mi + obj.vcpus = 1 + obj.name = "uefi" + obj.uuid = "f01cf68d-515c-4daf-b85f-ef1424d93bfc" + obj.os_type = "x86_64" + obj.os_loader = '/tmp/OVMF.fd' + obj.os_loader_type = 'pflash' + xml = obj.to_xml() + + self.assertXmlEqual(xml, """ + + f01cf68d-515c-4daf-b85f-ef1424d93bfc + uefi + 104857600 + 1 + + x86_64 + /tmp/OVMF.fd + + + """) + def test_config_boot_menu(self): obj = config.LibvirtConfigGuest() obj.virt_type = "kvm" diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 3738ddd8a534..41b8e4c90f71 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -2632,6 +2632,56 @@ class LibvirtConnTestCase(test.NoDBTestCase): self.assertIsInstance(cfg.devices[2], vconfig.LibvirtConfigGuestConsole) + def test_has_uefi_support_with_invalid_version(self): + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + with mock.patch.object(drvr._host, + 'has_min_version', return_value=False): + self.assertFalse(drvr._has_uefi_support()) + + def test_has_uefi_support_not_supported_arch(self): + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + caps = vconfig.LibvirtConfigCaps() + caps.host = vconfig.LibvirtConfigCapsHost() + caps.host.cpu = vconfig.LibvirtConfigCPU() + caps.host.cpu.arch = "alpha" + self.assertFalse(drvr._has_uefi_support()) + + @mock.patch('os.path.exists', return_value=False) + def test_has_uefi_support_with_no_loader_existed(self, mock_exist): + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + self.assertFalse(drvr._has_uefi_support()) + + @mock.patch('os.path.exists', return_value=True) + def test_has_uefi_support(self, mock_has_version): + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + + caps = vconfig.LibvirtConfigCaps() + caps.host = vconfig.LibvirtConfigCapsHost() + caps.host.cpu = vconfig.LibvirtConfigCPU() + caps.host.cpu.arch = "x86_64" + + with mock.patch.object(drvr._host, + 'has_min_version', return_value=True): + self.assertTrue(drvr._has_uefi_support()) + + def test_get_guest_config_with_uefi(self): + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + + image_meta = objects.ImageMeta.from_dict({ + "disk_format": "raw", + "properties": {"hw_firmware_type": "uefi"}}) + instance_ref = objects.Instance(**self.test_instance) + + disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type, + instance_ref, + image_meta) + with test.nested( + mock.patch.object(drvr, "_has_uefi_support", + return_value=True)): + cfg = drvr._get_guest_config(instance_ref, [], + image_meta, disk_info) + self.assertEqual(cfg.os_loader_type, "pflash") + def test_get_guest_config_with_block_device(self): drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) diff --git a/nova/virt/libvirt/config.py b/nova/virt/libvirt/config.py index d325a69ab095..d7b70207f122 100644 --- a/nova/virt/libvirt/config.py +++ b/nova/virt/libvirt/config.py @@ -1858,6 +1858,7 @@ class LibvirtConfigGuest(LibvirtConfigObject): self.sysinfo = None self.os_type = None self.os_loader = None + self.os_loader_type = None self.os_kernel = None self.os_initrd = None self.os_cmdline = None @@ -1903,7 +1904,17 @@ class LibvirtConfigGuest(LibvirtConfigObject): if self.os_kernel is not None: os.append(self._text_node("kernel", self.os_kernel)) if self.os_loader is not None: - os.append(self._text_node("loader", self.os_loader)) + # Generate XML nodes for UEFI boot. + if self.os_loader_type == "pflash": + loader = self._text_node("loader", self.os_loader) + loader.set("type", "pflash") + loader.set("readonly", "yes") + os.append(loader) + nvram = self._text_node("nvram", "") + nvram.set("template", self.os_loader) + os.append(nvram) + else: + os.append(self._text_node("loader", self.os_loader)) if self.os_initrd is not None: os.append(self._text_node("initrd", self.os_initrd)) if self.os_cmdline is not None: diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index c520c3d9e782..2b1d838e3fe0 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -114,6 +114,8 @@ from nova.volume import encryptors libvirt = None +uefi_logged = False + LOG = logging.getLogger(__name__) # Downtime period in milliseconds @@ -307,6 +309,11 @@ DEFAULT_FIREWALL_DRIVER = "%s.%s" % ( libvirt_firewall.__name__, libvirt_firewall.IptablesFirewallDriver.__name__) +DEFAULT_UEFI_LOADER_PATH = { + "x86_64": "/usr/share/OVMF/OVMF_CODE.fd", + "aarch64": "/usr/share/AAVMF/AAVMF_CODE.fd" +} + MAX_CONSOLE_BYTES = 100 * units.Ki # The libvirt driver will prefix any disable reason codes with this string. @@ -422,6 +429,9 @@ MIN_QEMU_NUMA_HUGEPAGE_VERSION = (2, 1, 0) # fsFreeze/fsThaw requirement MIN_LIBVIRT_FSFREEZE_VERSION = (1, 2, 5) +# UEFI booting support +MIN_LIBVIRT_UEFI_VERSION = (1, 2, 9) + # Hyper-V paravirtualized time source MIN_LIBVIRT_HYPERV_TIMER_VERSION = (1, 2, 2) MIN_QEMU_HYPERV_TIMER_VERSION = (2, 0, 0) @@ -4118,6 +4128,14 @@ class LibvirtDriver(driver.ComputeDriver): return flavor return instance.flavor + def _has_uefi_support(self): + # This means that the host can support uefi booting for guests + supported_archs = [arch.X86_64, arch.AARCH64] + caps = self._host.get_capabilities() + return ((caps.host.cpu.arch in supported_archs) and + self._host.has_min_version(MIN_LIBVIRT_UEFI_VERSION) and + os.path.exists(DEFAULT_UEFI_LOADER_PATH[caps.host.cpu.arch])) + def _configure_guest_by_virt_type(self, guest, virt_type, caps, instance, image_meta, flavor, root_device_name): if virt_type == "xen": @@ -4127,6 +4145,20 @@ class LibvirtDriver(driver.ComputeDriver): if caps.host.cpu.arch in (arch.I686, arch.X86_64): guest.sysinfo = self._get_guest_config_sysinfo(instance) guest.os_smbios = vconfig.LibvirtConfigGuestSMBIOS() + hw_firmware_type = image_meta.properties.get('hw_firmware_type') + if hw_firmware_type == fields.FirmwareType.UEFI: + if self._has_uefi_support(): + global uefi_logged + if not uefi_logged: + LOG.warn(_LW("uefi support is without some kind of " + "functional testing and therefore " + "considered experimental.")) + uefi_logged = True + guest.os_loader = DEFAULT_UEFI_LOADER_PATH[ + caps.host.cpu.arch] + guest.os_loader_type = "pflash" + else: + raise exception.UEFINotSupported() guest.os_mach_type = self._get_machine_type(image_meta, caps) if image_meta.properties.get('hw_boot_menu') is None: guest.os_bootmenu = strutils.bool_from_string( diff --git a/releasenotes/notes/bp-boot-from-uefi-b413b96017db76dd.yaml b/releasenotes/notes/bp-boot-from-uefi-b413b96017db76dd.yaml new file mode 100644 index 000000000000..09e0dab68712 --- /dev/null +++ b/releasenotes/notes/bp-boot-from-uefi-b413b96017db76dd.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add support for enabling uefi boot with libvirt.