From 340570af70f48b2e97a2e0b863fd819f85e92cb7 Mon Sep 17 00:00:00 2001 From: Cedric Bosdonnat Date: Mon, 8 Feb 2021 16:42:47 +0100 Subject: [PATCH] virt enhancements 3002.2 Xen spicevmc, DNS SRV records backports (#314) * Fix virtual network generated DNS XML for SRV records libvirt network's srv element doesn't take a `name` property but a `service` one. * Add missing property in virt.network_define dns srv doc * virt: convert spice generation tests to pytests * virt: don't add spicevmc channel to Xen VMs Xen fully virtualized VMs with spicevmc channel fail to start, so better not write it out in such cases. * virt: inverse the remaining asserts in pytests virt.network_update: handle missing ipv4 netmask attribute (#328) In the libvirt definition, the IPv4 netmask XML attribute may be replaced by the prefix one. Handle this situation gracefully rather than miserably failing. Handle volumes on stopped pools in virt.vm_info (#373) For VMs having at least a disk on a stopped volume, we don't want the user to get an exception when running virt.vm_info. Instead just provide less information. virt: use /dev/kvm to detect KVM (#383) checking for kvm_* modules to be loaded is not robust enough since the kernel could be compiled with builtin modules. /dev/kvm is much more reliable. virt: pass emulator when getting domain capabilities from libvirt (#394) On aarch64, for some emulated architectures like armv6l libvirt needs to have the emulator path to properly return the domain capabilities. Passing it will avoid virt.all_capabilities to fail on such architectures. --- changelog/59416.fixed | 1 + changelog/59692.fixed | 1 + changelog/60132.fixed | 1 + changelog/60419.fixed | 1 + changelog/60491.fixed | 1 + salt/modules/virt.py | 92 +++++---- salt/templates/virt/libvirt_domain.jinja | 2 +- .../pytests/unit/modules/virt/test_domain.py | 185 +++++++++++++----- .../pytests/unit/modules/virt/test_helpers.py | 6 +- tests/pytests/unit/modules/virt/test_host.py | 23 ++- .../pytests/unit/modules/virt/test_network.py | 70 ++++--- tests/pytests/unit/states/virt/test_domain.py | 144 +++++++------- .../pytests/unit/states/virt/test_network.py | 88 ++++----- tests/unit/modules/test_virt.py | 63 +----- 14 files changed, 371 insertions(+), 307 deletions(-) create mode 100644 changelog/59416.fixed create mode 100644 changelog/59692.fixed create mode 100644 changelog/60132.fixed create mode 100644 changelog/60419.fixed create mode 100644 changelog/60491.fixed diff --git a/changelog/59416.fixed b/changelog/59416.fixed new file mode 100644 index 0000000000..820124e99a --- /dev/null +++ b/changelog/59416.fixed @@ -0,0 +1 @@ +Don't create spicevmc channel for Xen virtual machines diff --git a/changelog/59692.fixed b/changelog/59692.fixed new file mode 100644 index 0000000000..b4f4533ccc --- /dev/null +++ b/changelog/59692.fixed @@ -0,0 +1 @@ +Don't fail updating network without netmask ip attribute diff --git a/changelog/60132.fixed b/changelog/60132.fixed new file mode 100644 index 0000000000..1e3bc96b98 --- /dev/null +++ b/changelog/60132.fixed @@ -0,0 +1 @@ +Gracefuly handle errors in virt.vm_info diff --git a/changelog/60419.fixed b/changelog/60419.fixed new file mode 100644 index 0000000000..44c782da48 --- /dev/null +++ b/changelog/60419.fixed @@ -0,0 +1 @@ +Check for /dev/kvm to detect KVM hypervisor. diff --git a/changelog/60491.fixed b/changelog/60491.fixed new file mode 100644 index 0000000000..256d29b5fb --- /dev/null +++ b/changelog/60491.fixed @@ -0,0 +1 @@ +Pass emulator path to get guest capabilities from libvirt diff --git a/salt/modules/virt.py b/salt/modules/virt.py index 17e3ba7b9b..8c0d743ba1 100644 --- a/salt/modules/virt.py +++ b/salt/modules/virt.py @@ -516,41 +516,50 @@ def _get_disks(conn, dom): def _get_disk_volume_data(pool_name, volume_name): qemu_target = "{}/{}".format(pool_name, volume_name) pool = conn.storagePoolLookupByName(pool_name) - vol = pool.storageVolLookupByName(volume_name) - vol_info = vol.info() - extra_properties = { - "virtual size": vol_info[1], - "disk size": vol_info[2], - } - - backing_files = [ - { - "file": node.find("source").get("file"), - "file format": node.find("format").get("type"), + extra_properties = {} + try: + vol = pool.storageVolLookupByName(volume_name) + vol_info = vol.info() + extra_properties = { + "virtual size": vol_info[1], + "disk size": vol_info[2], } - for node in elem.findall(".//backingStore[source]") - ] - if backing_files: - # We had the backing files in a flat list, nest them again. - extra_properties["backing file"] = backing_files[0] - parent = extra_properties["backing file"] - for sub_backing_file in backing_files[1:]: - parent["backing file"] = sub_backing_file - parent = sub_backing_file + backing_files = [ + { + "file": node.find("source").get("file"), + "file format": node.find("format").get("type"), + } + for node in elem.findall(".//backingStore[source]") + ] - else: - # In some cases the backing chain is not displayed by the domain definition - # Try to see if we have some of it in the volume definition. - vol_desc = ElementTree.fromstring(vol.XMLDesc()) - backing_path = vol_desc.find("./backingStore/path") - backing_format = vol_desc.find("./backingStore/format") - if backing_path is not None: - extra_properties["backing file"] = {"file": backing_path.text} - if backing_format is not None: - extra_properties["backing file"][ - "file format" - ] = backing_format.get("type") + if backing_files: + # We had the backing files in a flat list, nest them again. + extra_properties["backing file"] = backing_files[0] + parent = extra_properties["backing file"] + for sub_backing_file in backing_files[1:]: + parent["backing file"] = sub_backing_file + parent = sub_backing_file + + else: + # In some cases the backing chain is not displayed by the domain definition + # Try to see if we have some of it in the volume definition. + vol_desc = ElementTree.fromstring(vol.XMLDesc()) + backing_path = vol_desc.find("./backingStore/path") + backing_format = vol_desc.find("./backingStore/format") + if backing_path is not None: + extra_properties["backing file"] = { + "file": backing_path.text + } + if backing_format is not None: + extra_properties["backing file"][ + "file format" + ] = backing_format.get("type") + except libvirt.libvirtError: + # The volume won't be found if the pool is not started, just output less infos + log.info( + "Couldn't extract all volume informations: pool is likely not running or refreshed" + ) return (qemu_target, extra_properties) if disk_type == "file": @@ -5908,12 +5917,7 @@ def _is_kvm_hyper(): """ Returns a bool whether or not this node is a KVM hypervisor """ - try: - with salt.utils.files.fopen("/proc/modules") as fp_: - if "kvm_" not in salt.utils.stringutils.to_unicode(fp_.read()): - return False - except OSError: - # No /proc/modules? Are we on Windows? Or Solaris? + if not os.path.exists("/dev/kvm"): return False return "libvirtd" in __salt__["cmd.run"](__grains__["ps"]) @@ -6936,7 +6940,11 @@ def all_capabilities(**kwargs): host_caps = ElementTree.fromstring(conn.getCapabilities()) domains = [ [ - (guest.get("arch", {}).get("name", None), key) + ( + guest.get("arch", {}).get("name", None), + key, + guest.get("arch", {}).get("emulator", None), + ) for key in guest.get("arch", {}).get("domains", {}).keys() ] for guest in [ @@ -6954,10 +6962,10 @@ def all_capabilities(**kwargs): "domains": [ _parse_domain_caps( ElementTree.fromstring( - conn.getDomainCapabilities(None, arch, None, domain) + conn.getDomainCapabilities(emulator, arch, None, domain) ) ) - for (arch, domain) in flattened + for (arch, domain, emulator) in flattened ], } return result @@ -7584,7 +7592,7 @@ def network_update( if node.get("family", "ipv4") == "ipv4" ] for ip_node in ipv4_nodes: - netmask = ip_node.attrib.pop("netmask") + netmask = ip_node.attrib.pop("netmask", None) if netmask: address = ipaddress.ip_network( "{}/{}".format(ip_node.get("address"), netmask), strict=False diff --git a/salt/templates/virt/libvirt_domain.jinja b/salt/templates/virt/libvirt_domain.jinja index 4603dfd8de..6772b0db56 100644 --- a/salt/templates/virt/libvirt_domain.jinja +++ b/salt/templates/virt/libvirt_domain.jinja @@ -285,7 +285,7 @@ autoport='{{ yesno(not graphics.port and not graphics.tls_port) }}'> - {%- if graphics.type == "spice" %} + {%- if graphics.type == "spice" and hypervisor in ["qemu", "kvm"] %} diff --git a/tests/pytests/unit/modules/virt/test_domain.py b/tests/pytests/unit/modules/virt/test_domain.py index 3301b94d6d..9a1b2814f8 100644 --- a/tests/pytests/unit/modules/virt/test_domain.py +++ b/tests/pytests/unit/modules/virt/test_domain.py @@ -140,13 +140,14 @@ def test_update_xen_disk_volumes(make_mock_vm, make_mock_storage_pool): assert ret["definition"] virt.libvirt.openAuth().defineXML = virt.libvirt.openAuth().defineXML setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0]) - assert "block" == setxml.find(".//disk[3]").get("type") - assert "/path/to/vdb/vdb1" == setxml.find(".//disk[3]/source").get("dev") + assert setxml.find(".//disk[3]").get("type") == "block" + assert setxml.find(".//disk[3]/source").get("dev") == "/path/to/vdb/vdb1" # Note that my_vm-file-data was not an existing volume before the update - assert "file" == setxml.find(".//disk[4]").get("type") - assert "/path/to/default/my_vm_file-data" == setxml.find(".//disk[4]/source").get( - "file" + assert setxml.find(".//disk[4]").get("type") == "file" + assert ( + setxml.find(".//disk[4]/source").get("file") + == "/path/to/default/my_vm_file-data" ) @@ -191,6 +192,11 @@ def test_get_disks(make_mock_vm, make_mock_storage_pool):
+ + + + + @@ -204,11 +210,12 @@ def test_get_disks(make_mock_vm, make_mock_storage_pool): """ - domain_mock = make_mock_vm(vm_def) + make_mock_vm(vm_def) pool_mock = make_mock_storage_pool( "default", "dir", ["srv01_system", "srv01_data", "vm05_system"] ) + make_mock_storage_pool("stopped", "dir", []) # Append backing store to srv01_data volume XML description srv1data_mock = pool_mock.storageVolLookupByName("srv01_data") @@ -255,6 +262,7 @@ def test_get_disks(make_mock_vm, make_mock_storage_pool): }, }, }, + "vdd": {"type": "disk", "file": "stopped/vm05_data", "file format": "qcow2"}, "hda": { "type": "cdrom", "file format": "raw", @@ -322,7 +330,7 @@ def test_get_disk_convert_volumes(make_mock_vm, make_mock_storage_pool): subprocess_mock.Popen = popen_mock with patch.dict(virt.__dict__, {"subprocess": subprocess_mock}): - assert { + assert virt.get_disks("srv01") == { "vda": { "type": "disk", "file": "default/srv01_system", @@ -345,7 +353,7 @@ def test_get_disk_convert_volumes(make_mock_vm, make_mock_storage_pool): "disk size": 340525056, "virtual size": 214748364800, }, - } == virt.get_disks("srv01") + } def test_update_approx_mem(make_mock_vm): @@ -388,7 +396,7 @@ def test_gen_hypervisor_features(): hypervisor_features={"kvm-hint-dedicated": True}, ) root = ET.fromstring(xml_data) - assert "on" == root.find("features/kvm/hint-dedicated").attrib["state"] + assert root.find("features/kvm/hint-dedicated").attrib["state"] == "on" def test_update_hypervisor_features(make_mock_vm): @@ -423,7 +431,7 @@ def test_update_hypervisor_features(make_mock_vm): ret = virt.update("my_vm", hypervisor_features={"kvm-hint-dedicated": False}) assert ret["definition"] setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0]) - assert "off" == setxml.find("features/kvm/hint-dedicated").get("state") + assert setxml.find("features/kvm/hint-dedicated").get("state") == "off" # Add the features xml_def = """ @@ -442,7 +450,7 @@ def test_update_hypervisor_features(make_mock_vm): ret = virt.update("my_vm", hypervisor_features={"kvm-hint-dedicated": True}) assert ret["definition"] setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0]) - assert "on" == setxml.find("features/kvm/hint-dedicated").get("state") + assert setxml.find("features/kvm/hint-dedicated").get("state") == "on" def test_gen_clock(): @@ -463,8 +471,8 @@ def test_gen_clock(): clock={"adjustment": 3600, "utc": False}, ) root = ET.fromstring(xml_data) - assert "localtime" == root.find("clock").get("offset") - assert "3600" == root.find("clock").get("adjustment") + assert root.find("clock").get("offset") == "localtime" + assert root.find("clock").get("adjustment") == "3600" # Specific timezone xml_data = virt._gen_xml( @@ -480,8 +488,8 @@ def test_gen_clock(): clock={"timezone": "CEST"}, ) root = ET.fromstring(xml_data) - assert "timezone" == root.find("clock").get("offset") - assert "CEST" == root.find("clock").get("timezone") + assert root.find("clock").get("offset") == "timezone" + assert root.find("clock").get("timezone") == "CEST" # UTC xml_data = virt._gen_xml( @@ -497,7 +505,7 @@ def test_gen_clock(): clock={"utc": True}, ) root = ET.fromstring(xml_data) - assert "utc" == root.find("clock").get("offset") + assert root.find("clock").get("offset") == "utc" # Timers xml_data = virt._gen_xml( @@ -524,14 +532,16 @@ def test_gen_clock(): }, ) root = ET.fromstring(xml_data) - assert "utc" == root.find("clock").get("offset") - assert "3504000000" == root.find("clock/timer[@name='tsc']").get("frequency") - assert "native" == root.find("clock/timer[@name='tsc']").get("mode") - assert "catchup" == root.find("clock/timer[@name='rtc']").get("tickpolicy") - assert {"slew": "4636", "threshold": "123", "limit": "2342"} == root.find( - "clock/timer[@name='rtc']/catchup" - ).attrib - assert "no" == root.find("clock/timer[@name='hpet']").get("present") + assert root.find("clock").get("offset") == "utc" + assert root.find("clock/timer[@name='tsc']").get("frequency") == "3504000000" + assert root.find("clock/timer[@name='tsc']").get("mode") == "native" + assert root.find("clock/timer[@name='rtc']").get("tickpolicy") == "catchup" + assert root.find("clock/timer[@name='rtc']/catchup").attrib == { + "slew": "4636", + "threshold": "123", + "limit": "2342", + } + assert root.find("clock/timer[@name='hpet']").get("present") == "no" def test_update_clock(make_mock_vm): @@ -590,21 +600,23 @@ def test_update_clock(make_mock_vm): ) assert ret["definition"] setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0]) - assert "timezone" == setxml.find("clock").get("offset") - assert "CEST" == setxml.find("clock").get("timezone") - assert {"rtc", "hpet"} == {t.get("name") for t in setxml.findall("clock/timer")} - assert "catchup" == setxml.find("clock/timer[@name='rtc']").get("tickpolicy") - assert "wall" == setxml.find("clock/timer[@name='rtc']").get("track") - assert {"slew": "4636", "threshold": "123", "limit": "2342"} == setxml.find( - "clock/timer[@name='rtc']/catchup" - ).attrib - assert "yes" == setxml.find("clock/timer[@name='hpet']").get("present") + assert setxml.find("clock").get("offset") == "timezone" + assert setxml.find("clock").get("timezone") == "CEST" + assert {t.get("name") for t in setxml.findall("clock/timer")} == {"rtc", "hpet"} + assert setxml.find("clock/timer[@name='rtc']").get("tickpolicy") == "catchup" + assert setxml.find("clock/timer[@name='rtc']").get("track") == "wall" + assert setxml.find("clock/timer[@name='rtc']/catchup").attrib == { + "slew": "4636", + "threshold": "123", + "limit": "2342", + } + assert setxml.find("clock/timer[@name='hpet']").get("present") == "yes" # Revert to UTC ret = virt.update("my_vm", clock={"utc": True, "adjustment": None, "timers": None}) assert ret["definition"] setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0]) - assert {"offset": "utc"} == setxml.find("clock").attrib + assert setxml.find("clock").attrib == {"offset": "utc"} assert setxml.find("clock/timer") is None @@ -630,7 +642,7 @@ def test_update_stop_on_reboot_reset(make_mock_vm): assert ret["definition"] virt.libvirt.openAuth().defineXML = virt.libvirt.openAuth().defineXML setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0]) - assert "restart" == setxml.find("./on_reboot").text + assert setxml.find("./on_reboot").text == "restart" def test_update_stop_on_reboot(make_mock_vm): @@ -654,7 +666,7 @@ def test_update_stop_on_reboot(make_mock_vm): assert ret["definition"] virt.libvirt.openAuth().defineXML = virt.libvirt.openAuth().defineXML setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0]) - assert "destroy" == setxml.find("./on_reboot").text + assert setxml.find("./on_reboot").text == "destroy" def test_init_no_stop_on_reboot(make_capabilities): @@ -667,7 +679,7 @@ def test_init_no_stop_on_reboot(make_capabilities): virt.init("test_vm", 2, 2048, start=False) virt.libvirt.openAuth().defineXML = virt.libvirt.openAuth().defineXML setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0]) - assert "restart" == setxml.find("./on_reboot").text + assert setxml.find("./on_reboot").text == "restart" def test_init_stop_on_reboot(make_capabilities): @@ -680,7 +692,7 @@ def test_init_stop_on_reboot(make_capabilities): virt.init("test_vm", 2, 2048, stop_on_reboot=True, start=False) virt.libvirt.openAuth().defineXML = virt.libvirt.openAuth().defineXML setxml = ET.fromstring(virt.libvirt.openAuth().defineXML.call_args[0][0]) - assert "destroy" == setxml.find("./on_reboot").text + assert setxml.find("./on_reboot").text == "destroy" def test_init_hostdev_usb(make_capabilities, make_mock_device): @@ -722,8 +734,8 @@ def test_init_hostdev_usb(make_capabilities, make_mock_device): """ ) - assert expected_xml == strip_xml( - ET.tostring(setxml.find("./devices/hostdev")) + assert ( + strip_xml(ET.tostring(setxml.find("./devices/hostdev"))) == expected_xml ) @@ -764,8 +776,8 @@ def test_init_hostdev_pci(make_capabilities, make_mock_device): """ ) - assert expected_xml == strip_xml( - ET.tostring(setxml.find("./devices/hostdev")) + assert ( + strip_xml(ET.tostring(setxml.find("./devices/hostdev"))) == expected_xml ) @@ -938,11 +950,11 @@ def test_update_hostdev_changes(running, live, make_mock_device, make_mock_vm, t ET.tostring(xmlutil.strip_spaces(node)) for node in set_xml.findall("./devices/hostdev") ] - assert [usb_device_xml] == actual_hostdevs + assert actual_hostdevs == [usb_device_xml] if not test and live: attach_xml = strip_xml(domain_mock.attachDevice.call_args[0][0]) - assert usb_device_xml == attach_xml + assert attach_xml == usb_device_xml pci_device_xml = strip_xml( """ @@ -955,7 +967,7 @@ def test_update_hostdev_changes(running, live, make_mock_device, make_mock_vm, t """ ) detach_xml = strip_xml(domain_mock.detachDevice.call_args[0][0]) - assert pci_device_xml == detach_xml + assert detach_xml == pci_device_xml else: domain_mock.attachDevice.assert_not_called() domain_mock.detachDevice.assert_not_called() @@ -1012,14 +1024,16 @@ def test_diff_nics(): """ ).findall("interface") ret = virt._diff_interface_lists(old_nics, new_nics) - assert ["52:54:00:39:02:b1"] == [ - nic.find("mac").get("address") for nic in ret["unchanged"] + assert [nic.find("mac").get("address") for nic in ret["unchanged"]] == [ + "52:54:00:39:02:b1" ] - assert ["52:54:00:39:02:b2", "52:54:00:39:02:b4"] == [ - nic.find("mac").get("address") for nic in ret["new"] + assert [nic.find("mac").get("address") for nic in ret["new"]] == [ + "52:54:00:39:02:b2", + "52:54:00:39:02:b4", ] - assert ["52:54:00:39:02:b2", "52:54:00:39:02:b3"] == [ - nic.find("mac").get("address") for nic in ret["deleted"] + assert [nic.find("mac").get("address") for nic in ret["deleted"]] == [ + "52:54:00:39:02:b2", + "52:54:00:39:02:b3", ] @@ -1066,8 +1080,9 @@ def test_diff_nics_live_nochange(): """ ) ret = virt._diff_interface_lists(old_nics, new_nics) - assert ["52:54:00:03:02:15", "52:54:00:ea:2e:89"] == [ - nic.find("mac").get("address") for nic in ret["unchanged"] + assert [nic.find("mac").get("address") for nic in ret["unchanged"]] == [ + "52:54:00:03:02:15", + "52:54:00:ea:2e:89", ] @@ -1350,7 +1365,7 @@ def test_update_bootdev_unchanged(make_mock_vm, boot_dev): """ ) ret = virt.update("my_vm", boot_dev=boot_dev) - assert (boot_dev != "hd") == ret["definition"] + assert ret["definition"] == (boot_dev != "hd") if boot_dev == "hd": virt.libvirt.openAuth().defineXML.assert_not_called() else: @@ -2128,3 +2143,65 @@ def test_update_failure(make_mock_vm): "disk": {"attached": [], "detached": [], "updated": []}, "interface": {"attached": [], "detached": []}, } + +@pytest.mark.parametrize("hypervisor", ["kvm", "xen"]) +def test_gen_xml_spice_default(hypervisor): + """ + Test virt._gen_xml() with default spice graphics device + """ + xml_data = virt._gen_xml( + virt.libvirt.openAuth.return_value, + "hello", + 1, + 512, + {}, + {}, + hypervisor, + "hvm", + "x86_64", + graphics={"type": "spice"}, + ) + root = ET.fromstring(xml_data) + assert root.find("devices/graphics").attrib["type"] == "spice" + assert root.find("devices/graphics").attrib["autoport"] == "yes" + assert root.find("devices/graphics").attrib["listen"] == "0.0.0.0" + assert root.find("devices/graphics/listen").attrib["type"] == "address" + assert root.find("devices/graphics/listen").attrib["address"] == "0.0.0.0" + if hypervisor == "kvm": + assert ( + root.find(".//channel[@type='spicevmc']/target").get("name") + == "com.redhat.spice.0" + ) + else: + assert root.find(".//channel[@type='spicevmc']") is None + + +def test_gen_xml_spice(): + """ + Test virt._gen_xml() with spice graphics device + """ + xml_data = virt._gen_xml( + virt.libvirt.openAuth.return_value, + "hello", + 1, + 512, + {}, + {}, + "kvm", + "hvm", + "x86_64", + graphics={ + "type": "spice", + "port": 1234, + "tls_port": 5678, + "listen": {"type": "none"}, + }, + ) + root = ET.fromstring(xml_data) + assert root.find("devices/graphics").attrib["type"] == "spice" + assert root.find("devices/graphics").attrib["autoport"] == "no" + assert root.find("devices/graphics").attrib["port"] == "1234" + assert root.find("devices/graphics").attrib["tlsPort"] == "5678" + assert "listen" not in root.find("devices/graphics").attrib + assert root.find("devices/graphics/listen").attrib["type"] == "none" + assert "address" not in root.find("devices/graphics/listen").attrib diff --git a/tests/pytests/unit/modules/virt/test_helpers.py b/tests/pytests/unit/modules/virt/test_helpers.py index 4932d84ec4..e537b87aab 100644 --- a/tests/pytests/unit/modules/virt/test_helpers.py +++ b/tests/pytests/unit/modules/virt/test_helpers.py @@ -13,12 +13,12 @@ def append_to_XMLDesc(mocked, fragment): mocked.XMLDesc.return_value = ET.tostring(xml_doc) -def assert_xml_equals(expected, actual): +def assert_xml_equals(actual, expected): """ Assert that two ElementTree nodes are equal """ - assert xmlutil.to_dict(xmlutil.strip_spaces(expected), True) == xmlutil.to_dict( - xmlutil.strip_spaces(actual), True + assert xmlutil.to_dict(xmlutil.strip_spaces(actual), True) == xmlutil.to_dict( + xmlutil.strip_spaces(expected), True ) diff --git a/tests/pytests/unit/modules/virt/test_host.py b/tests/pytests/unit/modules/virt/test_host.py index 555deb23bb..c5cadb8aa0 100644 --- a/tests/pytests/unit/modules/virt/test_host.py +++ b/tests/pytests/unit/modules/virt/test_host.py @@ -1,5 +1,8 @@ +import os.path + import pytest import salt.modules.virt as virt +from tests.support.mock import MagicMock, patch from .conftest import loader_modules_config @@ -173,7 +176,7 @@ def test_node_devices(make_mock_device): ] virt.libvirt.openAuth().listAllDevices.return_value = mock_devs - assert [ + assert virt.node_devices() == [ { "name": "pci_1002_71c4", "caps": "pci", @@ -216,4 +219,20 @@ def test_node_devices(make_mock_device): "state": "down", "device name": "pci_0000_02_10_7", }, - ] == virt.node_devices() + ] + + +@pytest.mark.parametrize( + "dev_kvm, libvirtd", [(True, True), (False, False), (True, False)] +) +def test_is_kvm(dev_kvm, libvirtd): + """ + Test the virt._is_kvm_hyper() function + """ + with patch.dict(os.path.__dict__, {"exists": MagicMock(return_value=dev_kvm)}): + processes = ["libvirtd"] if libvirtd else [] + with patch.dict(virt.__grains__, {"ps": MagicMock(return_value="foo")}): + with patch.dict( + virt.__salt__, {"cmd.run": MagicMock(return_value=processes)} + ): + assert virt._is_kvm_hyper() == (dev_kvm and libvirtd) diff --git a/tests/pytests/unit/modules/virt/test_network.py b/tests/pytests/unit/modules/virt/test_network.py index ba592694c0..5ab4e0a449 100644 --- a/tests/pytests/unit/modules/virt/test_network.py +++ b/tests/pytests/unit/modules/virt/test_network.py @@ -19,10 +19,10 @@ def test_gen_xml(): """ xml_data = virt._gen_net_xml("network", "main", "bridge", "openvswitch") root = ET.fromstring(xml_data) - assert "network" == root.find("name").text - assert "main" == root.find("bridge").attrib["name"] - assert "bridge" == root.find("forward").attrib["mode"] - assert "openvswitch" == root.find("virtualport").attrib["type"] + assert root.find("name").text == "network" + assert root.find("bridge").attrib["name"] == "main" + assert root.find("forward").attrib["mode"] == "bridge" + assert root.find("virtualport").attrib["type"] == "openvswitch" def test_gen_xml_nat(): @@ -69,9 +69,9 @@ def test_gen_xml_nat(): mtu=9000, ) root = ET.fromstring(xml_data) - assert "network" == root.find("name").text - assert "main" == root.find("bridge").attrib["name"] - assert "nat" == root.find("forward").attrib["mode"] + assert root.find("name").text == "network" + assert root.find("bridge").attrib["name"] == "main" + assert root.find("forward").attrib["mode"] == "nat" expected_ipv4 = ET.fromstring( """ @@ -85,7 +85,7 @@ def test_gen_xml_nat(): """ ) - assert_xml_equals(expected_ipv4, root.find("./ip[@address='192.168.2.1']")) + assert_xml_equals(root.find("./ip[@address='192.168.2.1']"), expected_ipv4) expected_ipv6 = ET.fromstring( """ @@ -97,7 +97,7 @@ def test_gen_xml_nat(): """ ) - assert_xml_equals(expected_ipv6, root.find("./ip[@address='2001:db8:ca2:2::1']")) + assert_xml_equals(root.find("./ip[@address='2001:db8:ca2:2::1']"), expected_ipv6) actual_nat = ET.tostring(xmlutil.strip_spaces(root.find("./forward/nat"))) expected_nat = strip_xml( @@ -108,10 +108,10 @@ def test_gen_xml_nat(): """ ) - assert expected_nat == actual_nat + assert actual_nat == expected_nat - assert {"name": "acme.lab", "localOnly": "yes"} == root.find("./domain").attrib - assert "9000" == root.find("mtu").get("size") + assert root.find("./domain").attrib == {"name": "acme.lab", "localOnly": "yes"} + assert root.find("mtu").get("size") == "9000" def test_gen_xml_dns(): @@ -172,7 +172,7 @@ def test_gen_xml_dns(): """ ) - assert_xml_equals(expected_xml, root.find("./dns")) + assert_xml_equals(root.find("./dns"), expected_xml) def test_gen_xml_isolated(): @@ -191,9 +191,11 @@ def test_gen_xml_passthrough_interfaces(): "network", "virbr0", "passthrough", None, interfaces="eth10 eth11 eth12", ) root = ET.fromstring(xml_data) - assert "passthrough" == root.find("forward").get("mode") - assert ["eth10", "eth11", "eth12"] == [ - n.get("dev") for n in root.findall("forward/interface") + assert root.find("forward").get("mode") == "passthrough" + assert [n.get("dev") for n in root.findall("forward/interface")] == [ + "eth10", + "eth11", + "eth12", ] @@ -213,7 +215,7 @@ def test_gen_xml_hostdev_addresses(): """ ) - assert_xml_equals(expected_forward, root.find("./forward")) + assert_xml_equals(root.find("./forward"), expected_forward) def test_gen_xml_hostdev_pf(): @@ -232,7 +234,7 @@ def test_gen_xml_hostdev_pf(): """ ) actual_forward = ET.tostring(xmlutil.strip_spaces(root.find("./forward"))) - assert expected_forward == actual_forward + assert actual_forward == expected_forward def test_gen_xml_openvswitch(): @@ -268,7 +270,7 @@ def test_gen_xml_openvswitch(): """ ) - assert_xml_equals(expected_xml, ET.fromstring(xml_data)) + assert_xml_equals(ET.fromstring(xml_data), expected_xml) @pytest.mark.parametrize( @@ -308,7 +310,7 @@ def test_define(make_mock_network, autostart, start): """ ) define_mock = virt.libvirt.openAuth().networkDefineXML - assert expected_xml == strip_xml(define_mock.call_args[0][0]) + assert strip_xml(define_mock.call_args[0][0]) == expected_xml if autostart: mock_network.setAutostart.assert_called_with(1) @@ -364,8 +366,11 @@ def test_update_nat_nochange(make_mock_network): define_mock.assert_not_called() -@pytest.mark.parametrize("test", [True, False]) -def test_update_nat_change(make_mock_network, test): +@pytest.mark.parametrize( + "test, netmask", + [(True, "netmask='255.255.255.0'"), (True, "prefix='24'"), (False, "prefix='24'")], +) +def test_update_nat_change(make_mock_network, test, netmask): """ Test updating a NAT network with changes """ @@ -378,13 +383,15 @@ def test_update_nat_change(make_mock_network, test): - + - """ + """.format( + netmask + ) ) assert virt.network_update( "default", @@ -417,7 +424,7 @@ def test_update_nat_change(make_mock_network, test): """ ) - assert expected_xml == strip_xml(define_mock.call_args[0][0]) + assert strip_xml(define_mock.call_args[0][0]) == expected_xml @pytest.mark.parametrize("change", [True, False], ids=["changed", "unchanged"]) @@ -438,11 +445,14 @@ def test_update_hostdev_pf(make_mock_network, change): """ ) - assert change == virt.network_update( - "test-hostdev", - None, - "hostdev", - physical_function="eth0" if not change else "eth1", + assert ( + virt.network_update( + "test-hostdev", + None, + "hostdev", + physical_function="eth0" if not change else "eth1", + ) + == change ) define_mock = virt.libvirt.openAuth().networkDefineXML if change: diff --git a/tests/pytests/unit/states/virt/test_domain.py b/tests/pytests/unit/states/virt/test_domain.py index a4ae8c0694..c705785bf5 100644 --- a/tests/pytests/unit/states/virt/test_domain.py +++ b/tests/pytests/unit/states/virt/test_domain.py @@ -21,14 +21,14 @@ def test_defined_no_change(test): "virt.init": init_mock, }, ): - assert { + assert virt.defined("myvm") == { "name": "myvm", "changes": {"myvm": {"definition": False}}, "result": True, "comment": "Domain myvm unchanged", - } == virt.defined("myvm") + } init_mock.assert_not_called() - assert [domain_update_call("myvm", test=test)] == update_mock.call_args_list + assert update_mock.call_args_list == [domain_update_call("myvm", test=test)] def test_defined_new_with_connection(test): @@ -72,12 +72,7 @@ def test_defined_new_with_connection(test): {"type": "tcp", "port": 22223, "protocol": "telnet"}, {"type": "pty"}, ] - assert { - "name": "myvm", - "result": True if not test else None, - "changes": {"myvm": {"definition": True}}, - "comment": "Domain myvm defined", - } == virt.defined( + assert virt.defined( "myvm", cpu=2, mem=2048, @@ -103,7 +98,12 @@ def test_defined_new_with_connection(test): serials=serials, consoles=consoles, host_devices=["pci_0000_00_17_0"], - ) + ) == { + "name": "myvm", + "result": True if not test else None, + "changes": {"myvm": {"definition": True}}, + "comment": "Domain myvm defined", + } if not test: init_mock.assert_called_with( "myvm", @@ -160,16 +160,16 @@ def test_defined_update(test): "initrd": "/root/f8-i386-initrd", "cmdline": "console=ttyS0 ks=http://example.com/f8-i386/os/", } - assert { + assert virt.defined("myvm", cpu=2, boot=boot,) == { "name": "myvm", "changes": {"myvm": {"definition": True, "cpu": True}}, "result": True if not test else None, "comment": "Domain myvm updated", - } == virt.defined("myvm", cpu=2, boot=boot,) + } init_mock.assert_not_called() - assert [ + assert update_mock.call_args_list == [ domain_update_call("myvm", cpu=2, test=test, boot=boot) - ] == update_mock.call_args_list + ] def test_defined_update_error(test): @@ -189,7 +189,7 @@ def test_defined_update_error(test): "virt.init": init_mock, }, ): - assert { + assert virt.defined("myvm", cpu=2, boot_dev="cdrom hd") == { "name": "myvm", "changes": { "myvm": { @@ -200,7 +200,7 @@ def test_defined_update_error(test): }, "result": True if not test else None, "comment": "Domain myvm updated with live update(s) failures", - } == virt.defined("myvm", cpu=2, boot_dev="cdrom hd") + } init_mock.assert_not_called() update_mock.assert_called_with( "myvm", @@ -245,16 +245,16 @@ def test_defined_update_definition_error(test): "virt.init": init_mock, }, ): - assert { + assert virt.defined("myvm", cpu=2) == { "name": "myvm", "changes": {}, "result": False, "comment": "error message", - } == virt.defined("myvm", cpu=2) + } init_mock.assert_not_called() - assert [ + assert update_mock.call_args_list == [ domain_update_call("myvm", cpu=2, test=test) - ] == update_mock.call_args_list + ] @pytest.mark.parametrize("running", ["running", "shutdown"]) @@ -279,12 +279,12 @@ def test_running_no_change(test, running): if running == "shutdown": changes["started"] = True comment = "Domain myvm started" - assert { + assert virt.running("myvm") == { "name": "myvm", "result": True, "changes": {"myvm": changes}, "comment": comment, - } == virt.running("myvm") + } if running == "shutdown" and not test: start_mock.assert_called() else: @@ -326,12 +326,7 @@ def test_running_define(test): "listen": {"type": "address", "address": "192.168.0.1"}, } - assert { - "name": "myvm", - "result": True if not test else None, - "changes": {"myvm": {"definition": True, "started": True}}, - "comment": "Domain myvm defined and started", - } == virt.running( + assert virt.running( "myvm", cpu=2, mem=2048, @@ -353,7 +348,12 @@ def test_running_define(test): connection="someconnection", username="libvirtuser", password="supersecret", - ) + ) == { + "name": "myvm", + "result": True if not test else None, + "changes": {"myvm": {"definition": True, "started": True}}, + "comment": "Domain myvm defined and started", + } if not test: init_mock.assert_called_with( "myvm", @@ -412,12 +412,12 @@ def test_running_start_error(): "virt.list_domains": MagicMock(return_value=["myvm"]), }, ): - assert { + assert virt.running("myvm") == { "name": "myvm", "changes": {"myvm": {"definition": False}}, "result": False, "comment": "libvirt error msg", - } == virt.running("myvm") + } @pytest.mark.parametrize("running", ["running", "shutdown"]) @@ -441,14 +441,14 @@ def test_running_update(test, running): changes = {"myvm": {"definition": True, "cpu": True}} if running == "shutdown": changes["myvm"]["started"] = True - assert { + assert virt.running("myvm", cpu=2) == { "name": "myvm", "changes": changes, "result": True if not test else None, "comment": "Domain myvm updated" if running == "running" else "Domain myvm updated and started", - } == virt.running("myvm", cpu=2) + } if running == "shutdown" and not test: start_mock.assert_called() else: @@ -470,12 +470,12 @@ def test_running_definition_error(): "virt.list_domains": MagicMock(return_value=["myvm"]), }, ): - assert { + assert virt.running("myvm", cpu=3) == { "name": "myvm", "changes": {}, "result": False, "comment": "error message", - } == virt.running("myvm", cpu=3) + } def test_running_update_error(): @@ -494,7 +494,7 @@ def test_running_update_error(): "virt.list_domains": MagicMock(return_value=["myvm"]), }, ): - assert { + assert virt.running("myvm", cpu=2) == { "name": "myvm", "changes": { "myvm": { @@ -505,7 +505,7 @@ def test_running_update_error(): }, "result": True, "comment": "Domain myvm updated with live update(s) failures", - } == virt.running("myvm", cpu=2) + } update_mock.assert_called_with( "myvm", cpu=2, @@ -552,14 +552,14 @@ def test_stopped(test, running): if running == "running": changes = {"stopped": [{"domain": "myvm", "shutdown": True}]} comment = "Machine has been shut down" - assert { + assert virt.stopped( + "myvm", connection="myconnection", username="user", password="secret", + ) == { "name": "myvm", "changes": changes, "comment": comment, "result": True if not test or running == "shutdown" else None, - } == virt.stopped( - "myvm", connection="myconnection", username="user", password="secret", - ) + } if not test and running == "running": shutdown_mock.assert_called_with( "myvm", @@ -586,12 +586,12 @@ def test_stopped_error(): ), }, ): - assert { + assert virt.stopped("myvm") == { "name": "myvm", "changes": {"ignored": [{"domain": "myvm", "issue": "Some error"}]}, "result": False, "comment": "No changes had happened", - } == virt.stopped("myvm") + } def test_stopped_not_existing(test): @@ -603,12 +603,12 @@ def test_stopped_not_existing(test): with patch.dict( virt.__salt__, {"virt.list_domains": MagicMock(return_value=[])}, ): - assert { + assert virt.stopped("myvm") == { "name": "myvm", "changes": {}, "comment": "No changes had happened", "result": False, - } == virt.stopped("myvm") + } @pytest.mark.parametrize("running", ["running", "shutdown"]) @@ -631,14 +631,14 @@ def test_powered_off(test, running): if running == "running": changes = {"unpowered": [{"domain": "myvm", "stop": True}]} comment = "Machine has been powered off" - assert { + assert virt.powered_off( + "myvm", connection="myconnection", username="user", password="secret", + ) == { "name": "myvm", "result": True if not test or running == "shutdown" else None, "changes": changes, "comment": comment, - } == virt.powered_off( - "myvm", connection="myconnection", username="user", password="secret", - ) + } if not test and running == "running": stop_mock.assert_called_with( "myvm", @@ -666,12 +666,12 @@ def test_powered_off_error(): ), }, ): - assert { + assert virt.powered_off("myvm") == { "name": "myvm", "result": False, "changes": {"ignored": [{"domain": "myvm", "issue": "Some error"}]}, "comment": "No changes had happened", - } == virt.powered_off("myvm") + } def test_powered_off_not_existing(): @@ -686,12 +686,12 @@ def test_powered_off_not_existing(): ret.update( {"changes": {}, "result": False, "comment": "No changes had happened"} ) - assert { + assert virt.powered_off("myvm") == { "name": "myvm", "changes": {}, "result": False, "comment": "No changes had happened", - } == virt.powered_off("myvm") + } def test_snapshot(test): @@ -707,18 +707,18 @@ def test_snapshot(test): "virt.snapshot": snapshot_mock, }, ): - assert { - "name": "myvm", - "result": True if not test else None, - "changes": {"saved": [{"domain": "myvm", "snapshot": True}]}, - "comment": "Snapshot has been taken", - } == virt.snapshot( + assert virt.snapshot( "myvm", suffix="snap", connection="myconnection", username="user", password="secret", - ) + ) == { + "name": "myvm", + "result": True if not test else None, + "changes": {"saved": [{"domain": "myvm", "snapshot": True}]}, + "comment": "Snapshot has been taken", + } if not test: snapshot_mock.assert_called_with( "myvm", @@ -745,12 +745,12 @@ def test_snapshot_error(): ), }, ): - assert { + assert virt.snapshot("myvm") == { "name": "myvm", "result": False, "changes": {"ignored": [{"domain": "myvm", "issue": "Some error"}]}, "comment": "No changes had happened", - } == virt.snapshot("myvm") + } def test_snapshot_not_existing(test): @@ -761,12 +761,12 @@ def test_snapshot_not_existing(test): with patch.dict( virt.__salt__, {"virt.list_domains": MagicMock(return_value=[])} ): - assert { + assert virt.snapshot("myvm") == { "name": "myvm", "changes": {}, "result": False, "comment": "No changes had happened", - } == virt.snapshot("myvm") + } def test_rebooted(test): @@ -782,14 +782,14 @@ def test_rebooted(test): "virt.reboot": reboot_mock, }, ): - assert { + assert virt.rebooted( + "myvm", connection="myconnection", username="user", password="secret", + ) == { "name": "myvm", "result": True if not test else None, "changes": {"rebooted": [{"domain": "myvm", "reboot": True}]}, "comment": "Machine has been rebooted", - } == virt.rebooted( - "myvm", connection="myconnection", username="user", password="secret", - ) + } if not test: reboot_mock.assert_called_with( "myvm", @@ -816,12 +816,12 @@ def test_rebooted_error(): ), }, ): - assert { + assert virt.rebooted("myvm") == { "name": "myvm", "result": False, "changes": {"ignored": [{"domain": "myvm", "issue": "Some error"}]}, "comment": "No changes had happened", - } == virt.rebooted("myvm") + } def test_rebooted_not_existing(test): @@ -832,9 +832,9 @@ def test_rebooted_not_existing(test): with patch.dict( virt.__salt__, {"virt.list_domains": MagicMock(return_value=[])} ): - assert { + assert virt.rebooted("myvm") == { "name": "myvm", "changes": {}, "result": False, "comment": "No changes had happened", - } == virt.rebooted("myvm") + } diff --git a/tests/pytests/unit/states/virt/test_network.py b/tests/pytests/unit/states/virt/test_network.py index 668eee0c64..a68acfa236 100644 --- a/tests/pytests/unit/states/virt/test_network.py +++ b/tests/pytests/unit/states/virt/test_network.py @@ -19,12 +19,7 @@ def test_network_defined_not_existing(test): "virt.network_define": define_mock, }, ): - assert { - "name": "mynet", - "changes": {"mynet": "Network defined"}, - "result": None if test else True, - "comment": "Network mynet defined", - } == virt.network_defined( + assert virt.network_defined( "mynet", "br2", "bridge", @@ -58,7 +53,12 @@ def test_network_defined_not_existing(test): connection="myconnection", username="user", password="secret", - ) + ) == { + "name": "mynet", + "changes": {"mynet": "Network defined"}, + "result": None if test else True, + "comment": "Network mynet defined", + } if not test: define_mock.assert_called_with( "mynet", @@ -117,16 +117,16 @@ def test_network_defined_no_change(test): "virt.network_update": update_mock, }, ): - assert { + assert virt.network_defined("mynet", "br2", "bridge") == { "name": "mynet", "changes": {}, "result": True, "comment": "Network mynet unchanged", - } == virt.network_defined("mynet", "br2", "bridge") + } define_mock.assert_not_called() - assert [ + assert update_mock.call_args_list == [ network_update_call("mynet", "br2", "bridge", test=True) - ] == update_mock.call_args_list + ] def test_network_defined_change(test): @@ -148,12 +148,7 @@ def test_network_defined_change(test): "virt.network_set_autostart": autostart_mock, }, ): - assert { - "name": "mynet", - "changes": {"mynet": "Network updated, autostart flag changed"}, - "result": None if test else True, - "comment": "Network mynet updated, autostart flag changed", - } == virt.network_defined( + assert virt.network_defined( "mynet", "br2", "bridge", @@ -187,7 +182,12 @@ def test_network_defined_change(test): connection="myconnection", username="user", password="secret", - ) + ) == { + "name": "mynet", + "changes": {"mynet": "Network updated, autostart flag changed"}, + "result": None if test else True, + "comment": "Network mynet updated, autostart flag changed", + } define_mock.assert_not_called() expected_update_kwargs = { "vport": "openvswitch", @@ -226,7 +226,7 @@ def test_network_defined_change(test): ) ] if test: - assert calls == update_mock.call_args_list + assert update_mock.call_args_list == calls autostart_mock.assert_not_called() else: calls.append( @@ -234,7 +234,7 @@ def test_network_defined_change(test): "mynet", "br2", "bridge", **expected_update_kwargs, test=False ) ) - assert calls == update_mock.call_args_list + assert update_mock.call_args_list == calls autostart_mock.assert_called_with( "mynet", state="off", @@ -258,12 +258,12 @@ def test_network_defined_error(test): ) }, ): - assert { + assert virt.network_defined("mynet", "br2", "bridge") == { "name": "mynet", "changes": {}, "result": False, "comment": "Some error", - } == virt.network_defined("mynet", "br2", "bridge") + } define_mock.assert_not_called() @@ -285,12 +285,7 @@ def test_network_running_not_existing(test): "virt.network_start": start_mock, }, ): - assert { - "name": "mynet", - "changes": {"mynet": "Network defined and started"}, - "comment": "Network mynet defined and started", - "result": None if test else True, - } == virt.network_running( + assert virt.network_running( "mynet", "br2", "bridge", @@ -324,7 +319,12 @@ def test_network_running_not_existing(test): connection="myconnection", username="user", password="secret", - ) + ) == { + "name": "mynet", + "changes": {"mynet": "Network defined and started"}, + "comment": "Network mynet defined and started", + "result": None if test else True, + } if not test: define_mock.assert_called_with( "mynet", @@ -390,15 +390,15 @@ def test_network_running_nochange(test): "virt.network_update": update_mock, }, ): - assert { + assert virt.network_running("mynet", "br2", "bridge") == { "name": "mynet", "changes": {}, "comment": "Network mynet unchanged and is running", "result": None if test else True, - } == virt.network_running("mynet", "br2", "bridge") - assert [ + } + assert update_mock.call_args_list == [ network_update_call("mynet", "br2", "bridge", test=True) - ] == update_mock.call_args_list + ] def test_network_running_stopped(test): @@ -420,20 +420,20 @@ def test_network_running_stopped(test): "virt.network_update": update_mock, }, ): - assert { - "name": "mynet", - "changes": {"mynet": "Network started"}, - "comment": "Network mynet unchanged and started", - "result": None if test else True, - } == virt.network_running( + assert virt.network_running( "mynet", "br2", "bridge", connection="myconnection", username="user", password="secret", - ) - assert [ + ) == { + "name": "mynet", + "changes": {"mynet": "Network started"}, + "comment": "Network mynet unchanged and started", + "result": None if test else True, + } + assert update_mock.call_args_list == [ network_update_call( "mynet", "br2", @@ -443,7 +443,7 @@ def test_network_running_stopped(test): password="secret", test=True, ) - ] == update_mock.call_args_list + ] if not test: start_mock.assert_called_with( "mynet", @@ -468,9 +468,9 @@ def test_network_running_error(test): ), }, ): - assert { + assert virt.network_running("mynet", "br2", "bridge") == { "name": "mynet", "changes": {}, "comment": "Some error", "result": False, - } == virt.network_running("mynet", "br2", "bridge") + } diff --git a/tests/unit/modules/test_virt.py b/tests/unit/modules/test_virt.py index dd42ce4441..ec11b56d98 100644 --- a/tests/unit/modules/test_virt.py +++ b/tests/unit/modules/test_virt.py @@ -520,65 +520,6 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): root.find("devices/graphics/listen").attrib["address"], "myhost" ) - def test_gen_xml_spice_default(self): - """ - Test virt._gen_xml() with default spice graphics device - """ - diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello") - nicp = virt._nic_profile("default", "kvm") - xml_data = virt._gen_xml( - self.mock_conn, - "hello", - 1, - 512, - diskp, - nicp, - "kvm", - "hvm", - "x86_64", - graphics={"type": "spice"}, - ) - root = ET.fromstring(xml_data) - self.assertEqual(root.find("devices/graphics").attrib["type"], "spice") - self.assertEqual(root.find("devices/graphics").attrib["autoport"], "yes") - self.assertEqual(root.find("devices/graphics").attrib["listen"], "0.0.0.0") - self.assertEqual(root.find("devices/graphics/listen").attrib["type"], "address") - self.assertEqual( - root.find("devices/graphics/listen").attrib["address"], "0.0.0.0" - ) - - def test_gen_xml_spice(self): - """ - Test virt._gen_xml() with spice graphics device - """ - diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello") - nicp = virt._nic_profile("default", "kvm") - xml_data = virt._gen_xml( - self.mock_conn, - "hello", - 1, - 512, - diskp, - nicp, - "kvm", - "hvm", - "x86_64", - graphics={ - "type": "spice", - "port": 1234, - "tls_port": 5678, - "listen": {"type": "none"}, - }, - ) - root = ET.fromstring(xml_data) - self.assertEqual(root.find("devices/graphics").attrib["type"], "spice") - self.assertEqual(root.find("devices/graphics").attrib["autoport"], "no") - self.assertEqual(root.find("devices/graphics").attrib["port"], "1234") - self.assertEqual(root.find("devices/graphics").attrib["tlsPort"], "5678") - self.assertFalse("listen" in root.find("devices/graphics").attrib) - self.assertEqual(root.find("devices/graphics/listen").attrib["type"], "none") - self.assertFalse("address" in root.find("devices/graphics/listen").attrib) - def test_gen_xml_memory(self): """ Test virt._gen_xml() with advanced memory settings @@ -5140,6 +5081,10 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): {"qemu", "kvm"}, {domainCaps["domain"] for domainCaps in caps["domains"]}, ) + self.mock_conn.getDomainCapabilities.assert_called_with( + "/usr/bin/qemu-system-x86_64", "x86_64", None, "kvm" + ) + def test_network_tag(self): """ Test virt._get_net_xml() with VLAN tag -- 2.33.0