1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 11:16:47 +00:00
webvirtcloud/instances/tests/test_list_rpc.py
catborise 337063bf7b 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.
2026-11-05 07:33:13 +03:00

95 lines
4.2 KiB
Python

"""Instance lists read their VMs once a host (get_domain_rows): the states in
one call and each VM's live XML once, no lookup or info call a VM; on
libvirt's test driver."""
from collections import Counter
from unittest.mock import PropertyMock, patch
import libvirt
from appsettings.models import AppSettings
from computes.models import Compute
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
from instances.models import Instance
DOMAIN = """<domain type='test'><name>{name}</name><uuid>{uuid}</uuid><title>{name} title</title>
<memory unit='MiB'>4096</memory><currentMemory unit='MiB'>2048</currentMemory><vcpu current='3'>4</vcpu>
<os><type arch='i686'>hvm</type></os></domain>"""
VMS = {"vm-a": "11111111-1111-1111-1111-111111111111", "vm-b": "22222222-2222-2222-2222-222222222222"}
class ListReadTests(TestCase):
def setUp(self):
self.client.force_login(
get_user_model().objects.create_superuser(username="rpc-admin", password="pwd", email="rpc@example.com")
)
self.conn = libvirt.open("test:///default")
self.addCleanup(self.conn.close)
self.compute = Compute.objects.create(name="rpc-compute", hostname="localhost", type=4, login="", password="")
for name, uuid in VMS.items():
domain = self.conn.defineXML(DOMAIN.format(name=name, uuid=uuid))
domain.create()
self.addCleanup(domain.undefine)
self.addCleanup(domain.destroy)
Instance.objects.create(compute=self.compute, name=name, uuid=uuid)
def get(self, url):
calls = Counter()
def counted(cls, name):
real = getattr(cls, name)
def call(*args, **kwargs):
calls[name] += 1
return real(*args, **kwargs)
return patch.object(cls, name, call)
patches = [patch("instances.views.utils.refr"), patch("computes.views.utils.refresh_instance_database"),
patch("vrtManager.connection.connection_manager.get_connection", return_value=self.conn),
counted(libvirt.virDomain, "info"), counted(libvirt.virDomain, "XMLDesc"),
counted(libvirt.virConnect, "lookupByUUIDString")]
# the grouped list's host line; the test driver has no memory stats
patches += [patch.object(Compute, k, new_callable=PropertyMock, return_value=v)
for k, v in {"cpu_count": 1, "ram_size": 1024, "ram_usage": 0}.items()]
for p in patches:
p.start()
try:
response = self.client.get(url)
finally:
for p in reversed(patches):
p.stop()
self.assertEqual(response.status_code, 200)
return response, calls
def assert_read_once_a_host(self, url):
response, calls = self.get(url)
# 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")
def test_nongrouped_index(self):
AppSettings.objects.filter(key="VIEW_INSTANCES_LIST_STYLE").update(value="nongrouped")
self.assert_read_once_a_host(reverse("instances:index"))
def test_grouped_index(self):
AppSettings.objects.filter(key="VIEW_INSTANCES_LIST_STYLE").update(value="grouped")
self.assert_read_once_a_host(reverse("instances:index"))
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")