1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-10 12:16:49 +00:00

feat(instances): USB devices of the host given to a VM

- A Host devices tab on the VM page lists the VM's USB host devices, each
  once: how it is chosen (its address, or any device of its model), the
  device the running VM holds and whether its next start has it; its other
  host devices (PCI, SCSI, mediated) are listed for the XML editor.
- Superusers add a USB device of the host and take it back, on a running
  VM too. The add form is loaded when the tab opens, as its choices read
  every VM of the host; a hub, a device the host has its own disks or
  network interfaces on, or one another VM names is not offered, with the
  reason, and refused on the server under the compute's claim. A device is
  chosen by model when the host has one of it, else by its address.
- A VM without a USB controller is refused; a device is taken from each
  definition as that definition describes it.
- A clone gets none of the VM's host devices (the clone form says so), and
  a VM with host devices is not migrated. A snapshot revert is refused when
  it would give the VM a device another VM names or the host uses.
- A NIC that is a host device is no longer edited or removed from the
  Network tab: the XML editor changes it.
This commit is contained in:
catborise 2026-10-29 13:16:45 +03:00
parent 0977aef587
commit ac8ff62e7e
20 changed files with 679 additions and 94 deletions

View file

@ -13,7 +13,8 @@ from django.test import SimpleTestCase, TestCase
from django.urls import reverse
from instances.models import Instance
from lxml import html
from vrtManager.nodedevs import pci_address, wvmNodeDevices
from vrtManager.connection import wvmConnect
from vrtManager.nodedevs import node_devices, pci_address
def pci(slot, product, code, driver=None, group=None):
@ -76,10 +77,10 @@ USB_MODEL = "<hostdev mode='subsystem' type='usb'><source><vendor id='0x0627'/><
def devices(nodes=NODES, domains=()):
conn = wvmNodeDevices.__new__(wvmNodeDevices)
conn = wvmConnect.__new__(wvmConnect)
conn.wvm = MagicMock(**{"listAllDevices.return_value": [node(*n) for n in nodes],
"listAllDomains.return_value": list(domains)})
return conn.get_node_devices()
return node_devices(conn)
class NodeDevicesTests(SimpleTestCase):
@ -140,13 +141,13 @@ class NodeDevicesTests(SimpleTestCase):
err.get_error_code = lambda code=code: code
gone = node("pci_0000_00_0b_0", "")
gone.XMLDesc.side_effect = err
conn = wvmNodeDevices.__new__(wvmNodeDevices)
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, conn.get_node_devices)
self.assertRaises(libvirt.libvirtError, node_devices, conn)
else:
self.assertEqual([d["address"] for d in conn.get_node_devices()[0]], ["0000:00:06.0"])
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))

View file

@ -4,7 +4,8 @@ from django.contrib import messages
from django.shortcuts import get_object_or_404, render
from instances.models import Instance
from libvirt import libvirtError
from vrtManager.nodedevs import wvmNodeDevices
from vrtManager.connection import wvmConnect
from vrtManager.nodedevs import node_devices
@superuser_only
@ -14,8 +15,7 @@ def nodedevs(request, compute_id):
compute = get_object_or_404(Compute, pk=compute_id)
context = {"compute": compute}
try:
conn = wvmNodeDevices(compute.hostname, compute.login, compute.password, compute.type)
pci, usb = conn.get_node_devices()
pci, usb = node_devices(wvmConnect(compute.hostname, compute.login, compute.password, compute.type))
vm_ids = dict(Instance.objects.filter(compute=compute).values_list("name", "id"))
for device in pci + usb:
device["vms"] = [{"name": name, "id": vm_ids.get(name)} for name in device["vms"]]