mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-09 03:36:48 +00:00
perf(instances): the VM page reads each disk, the state and host info once
Measured on a VM with two disks, a CD-ROM and a NIC: 70 libvirt calls per page, now 43. Within the page's cached_reads block the VM's state, the host's info and domain capabilities, and each disk path's volume are read once; the running and the persistent disk lists share a path's volume. The ISO list takes the active pools in one call instead of looking each up by name; it still refreshes them, so an image copied in by hand shows up. The opt-in DRBD status waits at most 10 s for the compute's ssh, then leaves the status unknown instead of holding the page.
This commit is contained in:
parent
95b4418c96
commit
439f24041e
6 changed files with 278 additions and 52 deletions
|
|
@ -1,14 +1,18 @@
|
|||
"""wvmInstance.cached_reads(): inside the block the domain XML (per flags) and
|
||||
the host capabilities are read from libvirt once; outside it every read goes
|
||||
to libvirt, as code that changes the VM needs."""
|
||||
"""wvmInstance.cached_reads(): inside the block the domain XML (per flags),
|
||||
the VM's state, the host capabilities and info, and each disk's volume are
|
||||
read from libvirt once; outside it every read goes to libvirt, as code that
|
||||
changes the VM needs."""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
if not settings.configured:
|
||||
settings.configure(MAC_OUI="52:54:10")
|
||||
|
||||
from libvirt import libvirtError
|
||||
from vrtManager.connection import wvmConnect
|
||||
from vrtManager.instance import wvmInstance
|
||||
|
||||
|
||||
|
|
@ -17,6 +21,10 @@ class FakeDomain:
|
|||
self.reads = []
|
||||
self.fail = False
|
||||
|
||||
def info(self):
|
||||
self.reads.append("info")
|
||||
return [1, 0, 0, 0, 0]
|
||||
|
||||
def XMLDesc(self, flags):
|
||||
if self.fail:
|
||||
raise RuntimeError("libvirt error")
|
||||
|
|
@ -29,6 +37,10 @@ class FakeConn:
|
|||
self.caps = 0
|
||||
self.defined = []
|
||||
|
||||
def getInfo(self):
|
||||
self.caps += 1
|
||||
return ["x86_64", 2048, 4, 0, 1, 2, 2, 1]
|
||||
|
||||
def getCapabilities(self):
|
||||
self.caps += 1
|
||||
return "<capabilities/>"
|
||||
|
|
@ -111,3 +123,100 @@ class CachedReadsTestCase(unittest.TestCase):
|
|||
b._XMLDesc(0)
|
||||
b._XMLDesc(0)
|
||||
self.assertEqual((a.instance.reads, b.instance.reads), ([0], [0, 0]))
|
||||
|
||||
def test_the_state_and_host_info_are_read_once(self):
|
||||
p = proxy()
|
||||
with p.cached_reads():
|
||||
for _ in range(2):
|
||||
p.get_status()
|
||||
p.get_max_memory()
|
||||
p.get_max_cpus()
|
||||
self.assertEqual((p.instance.reads, p.wvm.caps), (["info"], 1))
|
||||
p.get_status()
|
||||
self.assertEqual(p.instance.reads, ["info", "info"])
|
||||
|
||||
def test_domain_capabilities_are_read_once_per_arch_and_machine(self):
|
||||
p = proxy()
|
||||
with patch.object(wvmConnect, "get_dom_cap_xml", return_value="<domainCapabilities/>") as read:
|
||||
with p.cached_reads():
|
||||
for _ in range(2):
|
||||
p.get_dom_cap_xml("x86_64", "q35")
|
||||
p.get_dom_cap_xml("x86_64", "pc")
|
||||
p.get_dom_cap_xml("x86_64", "q35")
|
||||
self.assertEqual([c.args for c in read.call_args_list], [("x86_64", "q35"), ("x86_64", "pc"), ("x86_64", "q35")])
|
||||
|
||||
|
||||
def volume_proxy(paths):
|
||||
"""A proxy whose host has volumes at paths (pool "pool", size 10, used 4)."""
|
||||
p = proxy()
|
||||
p.lookups = []
|
||||
|
||||
def lookup(path):
|
||||
p.lookups.append(path)
|
||||
if path not in paths:
|
||||
raise libvirtError("no storage vol with matching path")
|
||||
vol = MagicMock()
|
||||
vol.name.return_value = path.rsplit("/", 1)[-1]
|
||||
vol.info.return_value = [0, 10, 4]
|
||||
vol.storagePoolLookupByVolume.return_value.name.return_value = "pool"
|
||||
return vol
|
||||
|
||||
p.get_volume_by_path = lookup
|
||||
return p
|
||||
|
||||
|
||||
class VolumeDetailsTestCase(unittest.TestCase):
|
||||
def test_inside_the_block_each_path_is_looked_up_once(self):
|
||||
p = volume_proxy({"/p/a.qcow2", "/p/b.raw"})
|
||||
with p.cached_reads():
|
||||
for _ in range(2):
|
||||
self.assertEqual(p._volume_details("/p/a.qcow2"), ("a.qcow2", 10, 4, "pool"))
|
||||
p._volume_details("/p/b.raw")
|
||||
self.assertIsNone(p._volume_details("/dev/sdx"))
|
||||
self.assertEqual(p.lookups, ["/p/a.qcow2", "/p/b.raw", "/dev/sdx"])
|
||||
|
||||
def test_outside_the_block_and_after_a_change_the_volume_is_read_again(self):
|
||||
p = volume_proxy({"/p/a.qcow2"})
|
||||
p._volume_details("/p/a.qcow2")
|
||||
with p.cached_reads():
|
||||
p._volume_details("/p/a.qcow2")
|
||||
p._defineXML("<domain/>")
|
||||
p._volume_details("/p/a.qcow2")
|
||||
p._volume_details("/p/a.qcow2")
|
||||
self.assertEqual(len(p.lookups), 4)
|
||||
|
||||
|
||||
def disk(path, cache):
|
||||
return (
|
||||
f"<disk type='file' device='disk'><driver name='qemu' type='qcow2' cache='{cache}'/>"
|
||||
f"<source file='{path}'/><target dev='vda' bus='virtio'/></disk>"
|
||||
)
|
||||
|
||||
|
||||
class DiskListsTestCase(unittest.TestCase):
|
||||
"""The page lists the running and the persistent disks; both share a
|
||||
path's volume, each keeps its own settings."""
|
||||
|
||||
def lists(self, live, config, volumes):
|
||||
p = volume_proxy(volumes)
|
||||
xml = {0: f"<domain><devices>{live}</devices></domain>", 3: f"<domain><devices>{config}</devices></domain>"}
|
||||
p.instance.XMLDesc = lambda flags: xml[flags]
|
||||
with p.cached_reads():
|
||||
return p, p.get_disk_devices(), p.get_disk_devices(config=True)
|
||||
|
||||
def test_one_path_in_both_lists_is_looked_up_once(self):
|
||||
p, live, config = self.lists(disk("/p/a.qcow2", "none"), disk("/p/a.qcow2", "writeback"), {"/p/a.qcow2"})
|
||||
self.assertEqual(p.lookups, ["/p/a.qcow2"])
|
||||
self.assertEqual([(d["image"], d["storage"], d["size"], d["cache"]) for d in live], [("a.qcow2", "pool", 10, "none")])
|
||||
self.assertEqual([d["cache"] for d in config], ["writeback"])
|
||||
self.assertIsNot(live[0], config[0])
|
||||
|
||||
def test_a_pending_disk_change_is_looked_up_on_its_own(self):
|
||||
p, live, config = self.lists(disk("/p/a.qcow2", "none"), disk("/p/b.qcow2", "none"), {"/p/a.qcow2", "/p/b.qcow2"})
|
||||
self.assertEqual(p.lookups, ["/p/a.qcow2", "/p/b.qcow2"])
|
||||
self.assertEqual((live[0]["image"], config[0]["image"]), ("a.qcow2", "b.qcow2"))
|
||||
|
||||
def test_a_disk_outside_any_pool_shows_its_path(self):
|
||||
p, live, _ = self.lists(disk("/srv/x.img", "none"), disk("/srv/x.img", "none"), set())
|
||||
self.assertEqual(p.lookups, ["/srv/x.img"])
|
||||
self.assertEqual((live[0]["image"], live[0]["storage"], live[0]["size"]), ("/srv/x.img", None, None))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue