mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
perf(computes): a host's capacity is one libvirt call, not one per VM
The Computes page, the host overview and the new-VM dialog's capacity line read each VM's XML for its memory and vCPUs: a round trip a VM, about 200 ms for 150 VMs on a LAN, more with each host. libvirt's domain stats give them for every VM in one call; a running VM busy with a job is not waited for (NOWAIT) and its sizes come from its XML as before.
This commit is contained in:
parent
56a3ca4c99
commit
bf736942eb
2 changed files with 37 additions and 12 deletions
|
|
@ -2,12 +2,17 @@ import time
|
|||
|
||||
from libvirt import (
|
||||
libvirtError,
|
||||
VIR_CONNECT_GET_ALL_DOMAINS_STATS_NOWAIT,
|
||||
VIR_DOMAIN_BLOCKED,
|
||||
VIR_DOMAIN_PAUSED,
|
||||
VIR_DOMAIN_RUNNING,
|
||||
VIR_DOMAIN_SHUTOFF,
|
||||
VIR_DOMAIN_STATS_BALLOON,
|
||||
VIR_DOMAIN_STATS_STATE,
|
||||
VIR_DOMAIN_STATS_VCPU,
|
||||
VIR_NODE_CPU_STATS_ALL_CPUS,
|
||||
)
|
||||
from lxml import etree
|
||||
|
||||
from vrtManager.connection import wvmConnect
|
||||
from vrtManager.util import get_xml_path
|
||||
|
|
@ -111,8 +116,7 @@ class wvmHostDetails(wvmConnect):
|
|||
vcpus = {"all": 0, "running": 0}
|
||||
assigned = {"all": 0, "running": 0}
|
||||
vms = {"running": 0, "paused": 0, "shutoff": 0, "other": 0}
|
||||
# the VM lists' read: the states in one call, each VM's XML once
|
||||
for (state, _max_memory, memory, vcpu), _title in self.get_domain_rows().values():
|
||||
for state, memory, vcpu in self._domain_sizes():
|
||||
for share in ("all",) if state == VIR_DOMAIN_SHUTOFF else ("all", "running"):
|
||||
vcpus[share] += vcpu
|
||||
assigned[share] += memory * 1024
|
||||
|
|
@ -120,6 +124,22 @@ class wvmHostDetails(wvmConnect):
|
|||
return {"cpus": self.host_info[2], "memory": self.host_info[1] * 1048576,
|
||||
"vcpus": vcpus, "assigned": assigned, "vms": vms}
|
||||
|
||||
def _domain_sizes(self):
|
||||
"""Each VM's state, memory (KiB) and vCPUs, in one call: not its XML,
|
||||
one round trip a VM. A running VM busy with a job (a migration, a
|
||||
hung QEMU) is not waited for: its sizes come from its XML."""
|
||||
flags = VIR_DOMAIN_STATS_STATE | VIR_DOMAIN_STATS_BALLOON | VIR_DOMAIN_STATS_VCPU
|
||||
for dom, stats in self.wvm.getAllDomainStats(flags, VIR_CONNECT_GET_ALL_DOMAINS_STATS_NOWAIT):
|
||||
memory, vcpu = stats.get("balloon.current"), stats.get("vcpu.current")
|
||||
if memory is None or vcpu is None:
|
||||
try:
|
||||
doc = etree.fromstring(dom.XMLDesc(0))
|
||||
except libvirtError:
|
||||
continue # gone meanwhile
|
||||
memory, vcpu = int(doc.findtext("currentMemory")), doc.find("vcpu")
|
||||
vcpu = int(vcpu.get("current", vcpu.text))
|
||||
yield stats["state.state"], memory, vcpu
|
||||
|
||||
def get_cpu_topology(self):
|
||||
"""
|
||||
The host's logical CPUs and MHz, and from the CPUs of its NUMA cells
|
||||
|
|
|
|||
|
|
@ -158,19 +158,24 @@ class CapacityTestCase(unittest.TestCase):
|
|||
conn = connection(wvmHostDetails)
|
||||
gib = 1024 ** 3
|
||||
kib = gib // 1024
|
||||
# get_domain_rows: info (state, max memory KiB, memory KiB, vCPUs) and title
|
||||
conn.get_domain_rows = MagicMock(return_value={
|
||||
"u-web": ([VIR_DOMAIN_RUNNING, 8 * kib, 4 * kib, 4], ""),
|
||||
"u-db": ([VIR_DOMAIN_PAUSED, 2 * kib, 2 * kib, 2], ""),
|
||||
"u-old": ([VIR_DOMAIN_SHUTOFF, 8 * kib, 8 * kib, 8], ""),
|
||||
})
|
||||
busy = MagicMock(**{"XMLDesc.return_value": f"<domain><currentMemory>{kib}</currentMemory>"
|
||||
"<vcpu current='1'>4</vcpu></domain>"})
|
||||
idle = MagicMock()
|
||||
# one call for every VM; a VM busy with a job has only its state: its XML gives the rest
|
||||
conn.wvm.getAllDomainStats = MagicMock(return_value=[
|
||||
(idle, {"state.state": VIR_DOMAIN_RUNNING, "balloon.current": 4 * kib, "vcpu.current": 4}),
|
||||
(idle, {"state.state": VIR_DOMAIN_PAUSED, "balloon.current": 2 * kib, "vcpu.current": 2}),
|
||||
(idle, {"state.state": VIR_DOMAIN_SHUTOFF, "balloon.current": 8 * kib, "vcpu.current": 8}),
|
||||
(busy, {"state.state": VIR_DOMAIN_RUNNING}),
|
||||
])
|
||||
self.assertEqual(conn.get_capacity(), {
|
||||
"cpus": 4, "memory": 2 * gib,
|
||||
"vcpus": {"all": 14, "running": 6},
|
||||
"assigned": {"all": 14 * gib, "running": 6 * gib},
|
||||
"vms": {"running": 1, "paused": 1, "shutoff": 1, "other": 0},
|
||||
"vcpus": {"all": 15, "running": 7},
|
||||
"assigned": {"all": 15 * gib, "running": 7 * gib},
|
||||
"vms": {"running": 2, "paused": 1, "shutoff": 1, "other": 0},
|
||||
})
|
||||
conn.get_domain_rows.assert_called_once_with()
|
||||
self.assertEqual(conn.wvm.getAllDomainStats.call_count, 1)
|
||||
idle.XMLDesc.assert_not_called()
|
||||
|
||||
|
||||
class CpuTopologyTestCase(unittest.TestCase):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue