1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/vrtManager/tests/livetest.py
catborise bdc8a6755c test: each test is in the tests package of the code it tests
Tests that build vrtManager objects and touch no view, form or model of
an app lived in that app; vrtManager/tests already holds such tests, so
the convention was split. They move to vrtManager/tests: the firmware,
CPU mode and PCIe port definitions of a new VM (test_create), USB and PCI
host devices, the console listener, host CPU reads, interface reads,
deletes and XML, network filters, PCI passthrough rules, node devices,
network changes, addresses and leases, pool XML, secrets, and the live
tests that use vrtManager only (disk resize, ISO, snapshots, XML, live
clone). livetest.py, the live-test helper six apps import, and the
network test driver move with them.

Others go to their app: the quota with a host down to instances, the
401/403 Logs rows (webvirtcloud's middleware) to webvirtcloud, the VM
page's tab script test to instances, novncd's own tests out of the
console views tests, the compute lock and input validation tests out of
the computes views tests, the interface views out of the forms tests.
instances/tests/test_views.py, entirely live, is test_live_views.py.

Test modules imported fixtures from each other; shared ones are now in
vrtManager/tests/fakes.py (migration fakes, node devices, IOMMU groups,
leases, a NAT network, a Ceph secret), interfaces/tests/fakes.py,
console/tests/daemons.py (novncd loaded once), livetest.wait_for and
instances/tests/create_page.BODY. Every live test checks
livetest.enabled(). The suite runs the same 2097 tests.
2026-11-06 13:48:04 +03:00

137 lines
4.1 KiB
Python

"""Safety helpers for the live libvirt test suite.
Everything the suite creates is named with PREFIX, and its volumes live
in a dedicated pool. Cleanup only removes prefixed domains and the
volumes of that pool, so a broken test cannot delete other VMs or images
on a shared host. inventory() lets a suite check that nothing else
changed.
"""
import os
import libvirt
import time
PREFIX = "wvc-test-"
POOL = "wvc-test"
POOL_PATH = "/var/lib/libvirt/wvc-test"
UNDEFINE_FLAGS = (
libvirt.VIR_DOMAIN_UNDEFINE_MANAGED_SAVE
| libvirt.VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA
| libvirt.VIR_DOMAIN_UNDEFINE_NVRAM
| libvirt.VIR_DOMAIN_UNDEFINE_CHECKPOINTS_METADATA
)
def enabled():
"""The suite creates and deletes VMs, so it only runs on an explicitly named host."""
return bool(os.environ.get("TEST_LIBVIRT_HOST"))
def ensure_pool(conn):
try:
pool = conn.storagePoolLookupByName(POOL)
except libvirt.libvirtError:
pool = conn.storagePoolDefineXML(
f"<pool type='dir'><name>{POOL}</name><target><path>{POOL_PATH}</path></target></pool>"
)
pool.build(0)
if not pool.isActive():
pool.create(0)
pool.refresh(0)
return pool
def cleanup(conn):
"""Remove prefixed domains and every volume of the test pool."""
for dom in conn.listAllDomains():
if not dom.name().startswith(PREFIX):
continue
persistent = dom.isPersistent()
if dom.isActive():
dom.destroy()
if persistent:
dom.undefineFlags(UNDEFINE_FLAGS)
try:
pool = conn.storagePoolLookupByName(POOL)
except libvirt.libvirtError:
return
if pool.isActive():
pool.refresh(0)
for vol in pool.listAllVolumes():
vol.delete(0)
def remove_pool(conn):
cleanup(conn)
try:
pool = conn.storagePoolLookupByName(POOL)
except libvirt.libvirtError:
return
if pool.isActive():
pool.destroy()
pool.delete(0)
pool.undefine()
def inventory(conn):
"""Domains and volumes outside the test namespace."""
domains = {(d.name(), d.UUIDString()) for d in conn.listAllDomains() if not d.name().startswith(PREFIX)}
volumes = set()
for pool in conn.listAllStoragePools():
if pool.name() == POOL or not pool.isActive():
continue
pool.refresh(0)
volumes.update((pool.name(), v.name()) for v in pool.listAllVolumes())
return domains, volumes
def create_volume(conn, name, size_mib=64, fmt="qcow2"):
"""A volume named name.fmt in the test pool; returns its path."""
assert name.startswith(PREFIX)
pool = conn.storagePoolLookupByName(POOL)
vol = pool.createXML(
f"<volume><name>{name}.{fmt}</name><capacity unit='MiB'>{size_mib}</capacity>"
f"<target><format type='{fmt}'/></target></volume>",
0,
)
return vol.path()
OVMF_CODE = "/usr/share/edk2/ovmf/OVMF_CODE.fd"
def define_vm(conn, name, disk_paths, uefi=False, extra_devices="", disk_format="qcow2"):
"""A small q35 VM without an OS; disks are attached as vda, vdb, ..."""
assert name.startswith(PREFIX)
disks = "".join(
f"<disk type='file' device='disk'><driver name='qemu' type='{disk_format}'/>"
f"<source file='{path}'/><target dev='vd{chr(ord('a') + i)}' bus='virtio'/></disk>"
for i, path in enumerate(disk_paths)
)
firmware = (
"<os><type arch='x86_64' machine='q35'>hvm</type>"
f"<loader readonly='yes' type='pflash'>{OVMF_CODE}</loader></os>"
if uefi
else "<os><type arch='x86_64' machine='q35'>hvm</type></os>"
)
return conn.defineXML(
f"<domain type='kvm'><name>{name}</name><memory unit='MiB'>128</memory><vcpu>1</vcpu>"
f"{firmware}<features><acpi/></features><devices>{disks}{extra_devices}</devices></domain>"
)
def volume_exists(conn, path):
pool = conn.storagePoolLookupByName(POOL)
pool.refresh(0)
return os.path.basename(path) in pool.listVolumes()
def wait_for(predicate, seconds):
end = time.monotonic() + seconds
while time.monotonic() < end:
if predicate():
return True
time.sleep(0.5)
return False