"""The host's node devices as vrtManager lists them: PCI devices by IOMMU group and USB devices, each with what the host has on it and the VMs that name it. Node device XML as libvirt 11.10 writes it; the connection is a mock, libvirt's test driver has no node devices.""" from unittest.mock import MagicMock import libvirt from django.test import SimpleTestCase from vrtManager import util from vrtManager.connection import wvmConnect from vrtManager.nodedevs import hostdev_problems, node_devices, pci_address from vrtManager.tests.fakes import NODES, PCI_0A, USB_1_3, node, pci, vm PCI_01 = "
" USB_MODEL = "" def devices(nodes=NODES, domains=()): conn = wvmConnect.__new__(wvmConnect) conn.wvm = MagicMock(**{"listAllDevices.return_value": [node(*n) for n in nodes], "listAllDomains.return_value": list(domains)}) return node_devices(conn) class NodeDevicesTests(SimpleTestCase): def test_pci_devices_by_group_with_what_the_host_has_on_them(self): pci_devices, _ = devices() self.assertEqual([(d["address"], d["group"]) for d in pci_devices], [("0000:00:02.0", 2), ("0000:00:03.0", 2), ("0000:00:06.0", 3), ("0000:00:0a.0", 4), ("0000:00:01.0", None)]) gpu, disk, nic, spare, _ = pci_devices # a graphics card's primary node is the host's; its render node is not a resource of its own self.assertEqual((gpu["resources"], disk["resources"], nic["resources"], spare["resources"]), (["/dev/dri/card0"], ["/dev/vda"], ["eth0"], [])) self.assertEqual((nic["kind"], nic["driver"], nic["ids"]), ("Network controller", "virtio-pci", "1af4:1000")) def test_usb_devices_with_their_classes_hubs_and_same_models(self): _, (hub, tablet_2, tablet_3) = devices() self.assertEqual((hub["kinds"], hub["same_model"]), (["Hub"], False)) self.assertEqual((tablet_2["kinds"], tablet_2["same_model"], tablet_3["same_model"]), (["Human interface"], True, True)) def test_the_vms_that_name_a_device(self): pci_devices, (_, tablet_2, tablet_3) = devices(domains=[vm("db-1", PCI_0A + USB_1_3), vm("kiosk-1", USB_MODEL)]) self.assertEqual([d["vms"] for d in pci_devices], [[], [], [], ["db-1"], []]) # by address: that device; by model only: any device of the model self.assertEqual((tablet_3["vms"], tablet_3["by_model"]), (["db-1"], ["kiosk-1"])) self.assertEqual((tablet_2["vms"], tablet_2["by_model"]), ([], ["kiosk-1"])) def test_a_usb_controller_holds_the_usb_devices_below_it(self): pci_devices, (hub, tablet_2, _) = devices(domains=[vm("kiosk-1", USB_1_3)]) controller = [d for d in pci_devices if d["address"] == "0000:00:01.0"][0] # its root hub is no device of the host's; the tablets are, one given to a VM self.assertEqual(controller["resources"], ["USB device 1/2 (QEMU Tablet)", "USB device 1/3 (QEMU Tablet)"]) self.assertEqual((hub["controller"], tablet_2["controller"]), ("0000:00:01.0", "0000:00:01.0")) def test_a_usb_device_on_a_controller_given_to_a_vm_is_not_given_to_another(self): pci_devices, usb_devices = devices(domains=[vm("db-1", PCI_01)]) reasons = [util.render(m["text"], m["values"], str) for m in hostdev_problems( "kiosk-1", pci_devices, usb_devices, set(), [("0x0627", "0x0001", (1, 2))])] self.assertIn("USB device 1/2 (QEMU Tablet) is on the PCI controller 0000:00:01.0, given to db-1", reasons) def test_a_usb_device_is_named_as_libvirt_picks_one(self): def named(hostdev): _, (_, tablet_2, tablet_3) = devices(domains=[vm("v", hostdev)]) return [(d["vms"], d["by_model"]) for d in (tablet_2, tablet_3)] # an address with other IDs: another device now at that address other = USB_1_3.replace("0x0627", "0x046d") address_only = "
" self.assertEqual(named(other), [([], []), ([], [])]) self.assertEqual(named(address_only), [(["v"], []), ([], [])]) self.assertEqual(named(USB_MODEL), [([], ["v"]), ([], ["v"])]) def test_a_pci_device_named_as_a_nic_or_only_for_the_next_start(self): nic = ("
" "") pci_devices, _ = devices(domains=[vm("nic-1", nic), vm("next-1", "", saved=PCI_0A)]) self.assertEqual({d["address"]: d["vms"] for d in pci_devices if d["vms"]}, {"0000:00:06.0": ["nic-1"], "0000:00:0a.0": ["next-1"]}) def test_sr_iov_functions_and_unnamed_hardware(self): vf = pci("0x0b", "", "0x020000", group=5)[1].replace( "", "").replace( "", "
") pf = NODES[8][1].replace("", "
") pci_devices, _ = devices(nodes=[("pci_0000_00_0a_0", pf), ("pci_0000_00_0b_0", vf)]) self.assertEqual([(d["physfn"], d["vfs"], d["product"]) for d in pci_devices], [("", 1, "Ethernet controller"), ("0000:00:0a.0", 0, "0x10ed")]) def test_a_device_gone_meanwhile_is_left_out_other_errors_are_raised(self): for code, raised in ((libvirt.VIR_ERR_NO_NODE_DEVICE, False), (libvirt.VIR_ERR_OPERATION_FAILED, True)): err = libvirt.libvirtError("gone") err.get_error_code = lambda code=code: code gone = node("pci_0000_00_0b_0", "") gone.XMLDesc.side_effect = err conn = wvmConnect.__new__(wvmConnect) conn.wvm = MagicMock(**{"listAllDevices.return_value": [gone, node(*NODES[0])], "listAllDomains.return_value": []}) with self.subTest(code=code): if raised: self.assertRaises(libvirt.libvirtError, node_devices, conn) else: self.assertEqual([d["address"] for d in node_devices(conn)[0]], ["0000:00:06.0"]) def test_a_pci_address_from_libvirts_hex_or_numbers(self): self.assertEqual(pci_address("0x0000", "0x00", "0x1f", "0x3"), pci_address(0, 0, 31, 3)) self.assertEqual(pci_address(0, 0, 31, 3), "0000:00:1f.3")