diff --git a/.coveragerc b/.coveragerc index ff6415d7..13210d9c 100644 --- a/.coveragerc +++ b/.coveragerc @@ -5,3 +5,4 @@ exclude_lines = # Don't complain if tests don't hit defensive assertion code: raise NotImplementedError + .*raise_programming_error.* diff --git a/tests/data/cli/compare/virt-xml-edit-cpu-host-copy.xml b/tests/data/cli/compare/virt-xml-edit-cpu-host-copy.xml new file mode 100644 index 00000000..7b0873e1 --- /dev/null +++ b/tests/data/cli/compare/virt-xml-edit-cpu-host-copy.xml @@ -0,0 +1,2 @@ +Domain 'test-many-devices' defined successfully. +Changes will take effect after the domain is fully powered off. \ No newline at end of file diff --git a/tests/data/xmlparse/change-cpumode-out.xml b/tests/data/xmlparse/change-cpumode-out.xml index 23cc3e3c..f8a36a0a 100644 --- a/tests/data/xmlparse/change-cpumode-out.xml +++ b/tests/data/xmlparse/change-cpumode-out.xml @@ -16,6 +16,6 @@ - qemu64 + Skylake-Client-IBRS diff --git a/tests/test_cli.py b/tests/test_cli.py index 17b26d4b..2ef766d4 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1161,6 +1161,7 @@ c.add_compare("4a64cc71-19c4-2fd0-2323-3050941ea3c3 --edit --boot network,cdrom" c.add_compare("--confirm 1 --edit --cpu host-passthrough", "prompt-response", input_text="yes") # prompt response, also using domid lookup c.add_compare("--edit --print-diff --qemu-commandline clearxml=yes", "edit-clearxml-qemu-commandline", input_file=(XMLDIR + "/virtxml-qemu-commandline-clear.xml")) c.add_compare("--connect %(URI-KVM)s test-hyperv-uefi --edit --boot uefi", "hyperv-uefi-collision") +c.add_compare("--connect %(URI-KVM)s test-many-devices --edit --cpu host-copy", "edit-cpu-host-copy") c = vixml.add_category("simple edit diff", "test-for-virtxml --edit --print-diff --define") diff --git a/tests/test_xmlconfig.py b/tests/test_xmlconfig.py index dccc6a17..5d7060ea 100644 --- a/tests/test_xmlconfig.py +++ b/tests/test_xmlconfig.py @@ -146,6 +146,9 @@ class TestXMLMisc(unittest.TestCase): def testCPUTopology(self): # Test CPU topology determining cpu = virtinst.DomainCpu(self.conn) + cpu.set_topology_defaults(6) + assert cpu.sockets is None + cpu.sockets = "2" cpu.set_topology_defaults(6) self.assertEqual([cpu.sockets, cpu.cores, cpu.threads], [2, 3, 1]) diff --git a/tests/test_xmlparse.py b/tests/test_xmlparse.py index f4fc562c..526e2943 100644 --- a/tests/test_xmlparse.py +++ b/tests/test_xmlparse.py @@ -345,9 +345,13 @@ class XMLParseTest(unittest.TestCase): self._alter_compare(guest.get_xml(), outfile) def testAlterCpuMode(self): - guest, outfile = self._get_test_content("change-cpumode") - + xml = open(DATADIR + "change-cpumode-in.xml").read() + outfile = DATADIR + "change-cpumode-out.xml" + conn = utils.URIs.openconn(utils.URIs.kvm_q35) + guest = virtinst.Guest(conn, xml) check = self._make_checker(guest.cpu) + + guest.cpu.model = "foo" check("mode", "host-passthrough") guest.cpu.check_security_features(guest) check("secure", False) @@ -360,8 +364,25 @@ class XMLParseTest(unittest.TestCase): guest.cpu.check_security_features(guest) check("secure", False) + # Test actually filling in security values, and removing them + guest.cpu.secure = True + guest.cpu.set_model(guest, "Skylake-Client-IBRS") + guest.cpu.check_security_features(guest) + check("secure", True) + guest.cpu.set_model(guest, "EPYC-IBPB") + guest.cpu.check_security_features(guest) + check("secure", True) + guest.cpu.secure = False + guest.cpu.set_model(guest, "Skylake-Client-IBRS") + guest.cpu.check_security_features(guest) + check("secure", False) self._alter_compare(guest.get_xml(), outfile) + # Hits a codepath when domcaps don't provide the needed info + guest = virtinst.Guest(self.conn, xml) + guest.cpu.check_security_features(guest) + assert guest.cpu.secure is False + def testAlterDisk(self): """ Test changing DeviceDisk() parameters after parsing @@ -1561,3 +1582,17 @@ class XMLParseTest(unittest.TestCase): # Little test for DeviceAddress.pretty_desc assert devs[-1].address.pretty_desc() == "0:0:0:3" + + def testCPUHostModelOnly(self): + """ + Hit the validation paths for default HOST_MODEL_ONLY + """ + guest = virtinst.Guest(self.kvmconn) + guest.x86_cpu_default = guest.cpu.SPECIAL_MODE_HOST_MODEL_ONLY + guest.set_defaults(guest) + assert guest.cpu.model == "Opteron_G4" + + # pylint: disable=protected-access + guest.cpu.model = "idontexist" + guest.cpu._validate_default_host_model_only(guest) + assert guest.cpu.model is None diff --git a/virtinst/domain/cpu.py b/virtinst/domain/cpu.py index 5fb3d226..8620b784 100644 --- a/virtinst/domain/cpu.py +++ b/virtinst/domain/cpu.py @@ -7,6 +7,7 @@ from ..logger import log from ..xmlbuilder import XMLBuilder, XMLProperty, XMLChildProperty +from .. import xmlutil class _CPUCellSibling(XMLBuilder): @@ -106,8 +107,8 @@ class DomainCpu(XMLBuilder): self.clear() self.set_model(guest, self.conn.caps.host.cpu.model) else: - raise RuntimeError("programming error: unknown " - "special cpu mode '%s'" % val) + xmlutil.raise_programming_error( + True, "unknown special cpu mode '%s'" % val) self.special_mode_was_set = True @@ -129,7 +130,6 @@ class DomainCpu(XMLBuilder): """ domcaps = guest.lookup_domcaps() features = domcaps.get_cpu_security_features() - if len(features) == 0: self.secure = False return @@ -187,7 +187,7 @@ class DomainCpu(XMLBuilder): cpu = self.conn.caps.host.cpu model = cpu.model fallback = None - if not model: + if not model: # pragma: no cover raise ValueError(_("No host CPU reported in capabilities")) self.mode = "custom" @@ -218,18 +218,13 @@ class DomainCpu(XMLBuilder): def set_topology_defaults(self, vcpus): """ - Fill in unset topology values, using the passed vcpus count if - required + Fill in unset topology values, using the passed vcpus count. + Will not set topology from scratch, this just fills in missing + topology values. """ - if vcpus is None: - if self.sockets is None: - self.sockets = 1 - if self.threads is None: - self.threads = 1 - if self.cores is None: - self.cores = 1 + if not self.has_topology(): + return - vcpus = int(vcpus or 0) if not self.sockets: if not self.cores: self.sockets = vcpus // self.threads @@ -273,12 +268,12 @@ class DomainCpu(XMLBuilder): # is not actually supported by qemu/kvm # combo which will be reported in if not self.model: - return + return # pragma: no cover domcaps = guest.lookup_domcaps() domcaps_mode = domcaps.cpu.get_mode("custom") if not domcaps_mode: - return + return # pragma: no cover cpu_model = domcaps_mode.get_model(self.model) if cpu_model and cpu_model.usable != "no":