"""A new VM's firmware and TPM: the choices come from the host's domain capabilities, UEFI is libvirt's firmware autoselection (Secure Boot with Microsoft's keys enrolled, as Windows 11 needs) and a TPM is an emulated TPM 2.0 (swtpm).""" from unittest.mock import MagicMock, patch from django.test import SimpleTestCase from lxml import etree from vrtManager.connection import wvmConnect from vrtManager.create import wvmCreate FULL = """ efi yesno yesno tpm-tistpm-crb emulatorexternal 2.0 """ # Secure Boot firmware without Microsoft's keys, and no swtpm BARE = """ efi yesno no """ # only Secure Boot firmware, and a TPM of another machine SECURE_ONLY = """ efi yes yes tpm-spapr emulator 2.0 """ # libvirt before 12.1: no firmwareFeatures, the loaders say whether they do Secure Boot EARLIER = """ efi yesno """ EARLIER_PLAIN = """ efi """ DOM_CAPS = {"domain": "kvm", "os_support": "yes", "cpu_modes": ["host-model"]} def host(dom_cap_xml): conn = wvmConnect.__new__(wvmConnect) conn.get_dom_cap_xml = lambda arch, machine: dom_cap_xml return conn def defined(firmware, tpm_model=None): conn = wvmCreate.__new__(wvmCreate) conn.wvm = MagicMock() with patch.object(wvmCreate, "get_capabilities", return_value={"features": ["acpi"], "os_type": "hvm"}), \ patch.object(wvmCreate, "get_dom_capabilities", return_value=DOM_CAPS): conn.create_instance(name="vm-1", memory=512, vcpu=1, vcpu_mode="host-model", uuid="00000000-0000-4000-8000-0000000000c2", arch="x86_64", machine="q35", firmware=firmware, volumes=[], networks="", nwfilter="", virtio=True, listener_addr="0.0.0.0", add_cdrom="None", add_input="None", tpm_model=tpm_model) return etree.fromstring(conn.wvm.defineXML.call_args.args[0]) class HostOffersTests(SimpleTestCase): def test_a_host_with_enrolled_keys_and_swtpm_offers_everything(self): self.assertEqual(host(FULL).get_firmwares("x86_64", "q35"), ["BIOS", "UEFI", "UEFI Secure Boot"]) self.assertEqual(host(FULL).get_tpm_model("x86_64", "q35"), "tpm-crb") def test_secure_boot_without_microsofts_keys_and_no_swtpm_are_not_offered(self): self.assertEqual(host(BARE).get_firmwares("x86_64", "q35"), ["BIOS", "UEFI"]) self.assertIsNone(host(BARE).get_tpm_model("x86_64", "q35")) def test_bios_is_x86_only_and_secure_boot_x86_64_only(self): self.assertEqual(host(FULL).get_firmwares("aarch64", "virt"), ["UEFI"]) def test_uefi_is_offered_only_where_secure_boot_can_be_off(self): self.assertEqual(host(SECURE_ONLY).get_firmwares("x86_64", "q35"), ["BIOS", "UEFI Secure Boot"]) def test_an_earlier_libvirt_offers_what_its_loaders_do(self): self.assertEqual(host(EARLIER).get_firmwares("x86_64", "q35"), ["BIOS", "UEFI", "UEFI Secure Boot"]) self.assertEqual(host(EARLIER_PLAIN).get_firmwares("x86_64", "q35"), ["BIOS", "UEFI"]) def test_a_machine_without_crb_or_tis_takes_its_own_tpm_model(self): self.assertEqual(host(SECURE_ONLY).get_tpm_model("ppc64le", "pseries"), "tpm-spapr") class DefinitionTests(SimpleTestCase): def features(self, tree): return {f.get("name"): f.get("enabled") for f in tree.findall("os/firmware/feature")} def test_bios_leaves_the_firmware_to_the_machine(self): tree = defined("BIOS") self.assertIsNone(tree.find("os").get("firmware")) self.assertEqual((self.features(tree), tree.find("features/smm")), ({}, None)) def test_uefi_is_picked_by_libvirt_without_secure_boot(self): tree = defined("UEFI") self.assertEqual(tree.find("os").get("firmware"), "efi") self.assertEqual(self.features(tree), {"secure-boot": "no"}) self.assertIsNone(tree.find("features/smm")) def test_secure_boot_has_microsofts_keys_and_smm(self): tree = defined("UEFI Secure Boot") self.assertEqual(tree.find("os").get("firmware"), "efi") self.assertEqual(self.features(tree), {"secure-boot": "yes", "enrolled-keys": "yes"}) self.assertEqual(tree.find("features/smm").get("state"), "on") def test_a_tpm_is_an_emulated_tpm_2(self): self.assertEqual(defined("UEFI").findall("devices/tpm"), []) tpm = defined("UEFI Secure Boot", tpm_model="tpm-crb").find("devices/tpm") self.assertEqual((tpm.get("model"), tpm.find("backend").get("type"), tpm.find("backend").get("version")), ("tpm-crb", "emulator", "2.0"))