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

perf(instances): a VM list reads only the VMs it shows

#394 read each host's states in one call and every domain's XML once,
for the title, memory and vCPUs. A user's list shows only their own VMs,
but it read the XML of every VM on those hosts: a user with two VMs on a
host of 300 made about 301 libvirt calls a list load, where the old
per-VM reads made 6.

get_domain_rows takes the UUIDs the list shows and reads only their XML;
the states still come in one call. The capacity reads every domain, as
before.
This commit is contained in:
catborise 2026-11-04 23:58:36 +03:00
parent 02d4d02757
commit 337063bf7b
5 changed files with 28 additions and 11 deletions

View file

@ -65,8 +65,8 @@ class ListReadTests(TestCase):
def assert_read_once_a_host(self, url):
response, calls = self.get(url)
# every domain of the host once (the test driver's own, and other tests', too)
self.assertEqual(calls, {"XMLDesc": len(self.conn.listAllDomains())})
# each VM the list shows once; the host's other domains (the test driver's own) not at all
self.assertEqual(calls, {"XMLDesc": len(VMS)})
self.assertContains(response, "2048 MiB", count=len(VMS))
self.assertContains(response, "Active")
self.assertContains(response, "vm-a title")
@ -81,3 +81,15 @@ class ListReadTests(TestCase):
def test_compute_instances(self):
self.assert_read_once_a_host(reverse("instances", args=[self.compute.id]))
def test_a_user_s_list_reads_only_their_vms(self):
from accounts.models import UserInstance
user = get_user_model().objects.create_user("rpc-user", password="pwd")
UserInstance.objects.create(user=user, instance=Instance.objects.get(name="vm-a"))
self.client.force_login(user)
response, calls = self.get(reverse("instances:index"))
# the other VMs of the host, the test driver's own and other tests' included, are not read
self.assertEqual(calls, {"XMLDesc": 1})
self.assertContains(response, "vm-a title")
self.assertNotContains(response, "vm-b")

View file

@ -31,7 +31,7 @@ class UnreachableListTests(TestCase):
or up with the error its listing raises."""
up = rows is not None
def domain_rows():
def domain_rows(uuids=None):
if isinstance(rows, Exception):
raise rows
return rows

View file

@ -58,7 +58,7 @@ class StateTests(TestCase):
self.client.force_login(user)
props = {"status": True, "connection_error": None, "cpu_count": 1, "ram_size": 1, "ram_usage": 0,
"proxy": SimpleNamespace(get_domain_rows=lambda: rows)}
"proxy": SimpleNamespace(get_domain_rows=lambda uuids=None: rows)}
patches = [patch.object(Compute, k, new_callable=PropertyMock, return_value=v) for k, v in props.items()]
patches += [patch("instances.views.utils.refr"), patch("computes.views.utils.refresh_instance_database")]
for p in patches:

View file

@ -356,10 +356,12 @@ def read_list_rows(instances):
"""Each VM's info and title for a VM list, read once a host
(get_domain_rows), not three calls a VM; a VM whose host or whose
domain cannot be read has no info: the list shows it unknown."""
rows = {}
rows, shown = {}, {}
for inst in instances:
shown.setdefault(inst.compute_id, set()).add(inst.uuid)
for compute in {inst.compute_id: inst.compute for inst in instances}.values():
try:
rows[compute.pk] = compute.proxy.get_domain_rows() if compute.status is True else {}
rows[compute.pk] = compute.proxy.get_domain_rows(shown[compute.pk]) if compute.status is True else {}
except libvirtError:
rows[compute.pk] = {}
for inst in instances:

View file

@ -1098,13 +1098,16 @@ class wvmConnect(object):
return {dom.UUIDString(): [dom.name(), stats["state.state"]]
for dom, stats in self.wvm.getAllDomainStats(libvirt.VIR_DOMAIN_STATS_STATE)}
def get_domain_rows(self):
"""{uuid: (info, title)} of every domain, as the VM lists show them:
info in virDomainGetInfo's shape (state, max memory KiB, memory KiB,
vCPUs), the states in one call and the rest from each domain's live
XML, which the title needs anyway. A domain gone meanwhile is left out."""
def get_domain_rows(self, uuids=None):
"""{uuid: (info, title)} of every domain, or of those in uuids, as the
VM lists show them: info in virDomainGetInfo's shape (state, max
memory KiB, memory KiB, vCPUs), the states in one call and the rest
from each domain's live XML, which the title needs anyway. A domain
gone meanwhile is left out."""
rows = {}
for dom, stats in self.wvm.getAllDomainStats(libvirt.VIR_DOMAIN_STATS_STATE):
if uuids is not None and dom.UUIDString() not in uuids:
continue # a VM the list does not show: its XML is not read
try:
doc = etree.fromstring(dom.XMLDesc(0))
except libvirtError: