From 95562544eb319e9242caf5958bd0fd41364e9705 Mon Sep 17 00:00:00 2001 From: catborise Date: Sat, 31 Oct 2026 19:56:02 +0300 Subject: [PATCH] feat(instances): Add PCIe slots for a VM that has none free An existing q35 VM whose PCIe root ports are all used takes no new virtio disk or network device while it runs, and the only way to give it more was editing its XML. The Disks tab of a PCIe machine now counts its slots, "PCIe slots: 0 free of 7", in red when none is free, with what its next start will have when that differs, and Add PCIe slots adds four root ports to its persistent definition: libvirt numbers and places them, and a running VM gets them at its next start. The no-slot advice of Add disk and Add network device points at it. --- instances/models.py | 10 +++ .../templates/instances/settings_tab.html | 15 ++++ instances/tests/test_pci_slots.py | 79 +++++++++++++++++++ instances/tests/test_pending_edits.py | 13 +++ instances/urls.py | 1 + instances/views.py | 34 +++++++- vrtManager/instance.py | 20 +++++ 7 files changed, 168 insertions(+), 4 deletions(-) diff --git a/instances/models.py b/instances/models.py index 91370ce9..a840a3df 100644 --- a/instances/models.py +++ b/instances/models.py @@ -161,6 +161,16 @@ class Instance(models.Model): net["target"] = live_targets.get(net["mac"], "") return networks + @cached_property + def pcie_slots(self) -> tuple[int, int] | None: + """(PCIe slots, free ones) the VM has now; None: not a PCIe machine.""" + return self.proxy.get_pcie_slots() + + @cached_property + def config_pcie_slots(self) -> tuple[int, int] | None: + """The same at its next start, which added slots wait for while it runs.""" + return self.proxy.get_pcie_slots(config=True) + @cached_property def config_disks(self) -> list[dict]: """Disks of the persistent definition, which the disk list, its edit diff --git a/instances/templates/instances/settings_tab.html b/instances/templates/instances/settings_tab.html index db38abaa..26c576fb 100644 --- a/instances/templates/instances/settings_tab.html +++ b/instances/templates/instances/settings_tab.html @@ -274,6 +274,21 @@ {% endfor %} + {% if instance.pcie_slots %} + {# a PCIe machine gives each virtio disk and network device a slot; a running VM gets no new one #} + {% with total=instance.pcie_slots.0 free=instance.pcie_slots.1 next=instance.config_pcie_slots %} +
+ + {% blocktrans %}PCIe slots: {{ free }} free of {{ total }}{% endblocktrans %}{% if next and next.1 != free %} · {% blocktrans with next_free=next.1 %}{{ next_free }} free from the next start{% endblocktrans %}{% endif %} + + {% if request.user.is_superuser %} +
{% csrf_token %} + +
+ {% endif %} +
+ {% endwith %} + {% endif %}
diff --git a/instances/tests/test_pci_slots.py b/instances/tests/test_pci_slots.py index 0efeac87..32a30739 100644 --- a/instances/tests/test_pci_slots.py +++ b/instances/tests/test_pci_slots.py @@ -102,3 +102,82 @@ class NoPciSlotTests(TestCase): NO_SLOT) self.assertEqual(len(texts), 1) self.assertIn("shut it down", texts[0]) + + +DOMAIN = """ + +
+
+
+
+
+