Skip to content

Commit 506a28b

Browse files
committed
Do not require Xen to get VM stats
It's not completely Xen agnostic because then it would not be possible to query stubdomains, as libvirt is not aware of them. On the other hand, non-Xen has one more API methods working. When not using Xen, construct the dictionary as "xc.domain_getinfo()" would, so the info loop can consume from both inputs.
1 parent 32358e6 commit 506a28b

1 file changed

Lines changed: 80 additions & 15 deletions

File tree

qubes/app.py

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -404,12 +404,12 @@ def get_vm_stats(self, previous_time=None, previous=None, only_vm=None):
404404
- ``cpu_usage_raw`` - CPU usage in %.
405405
- ``cpu_usage`` - CPU usage in % (normalized to number of vcpus).
406406
407-
This function requires Xen hypervisor.
407+
This function requires Xen hypervisor for detailed overview.
408408
409409
..warning:
410410
411411
This function may return info about implementation-specific VMs,
412-
like stubdomains for HVM
412+
like stubdomains for HVM, aggregated to the connected domain.
413413
414414
:param previous: previous measurement
415415
:param previous_time: time of previous measurement
@@ -428,46 +428,106 @@ def get_vm_stats(self, previous_time=None, previous=None, only_vm=None):
428428

429429
current_time = time.time()
430430
current = {}
431-
if not self.app.vmm.is_xen:
432-
raise NotImplementedError("This function requires Xen hypervisor")
433431
if only_vm:
434432
xid = only_vm.xid
435433
if xid < 0:
436434
raise qubes.exc.QubesVMNotRunningError(only_vm)
437-
info = self.app.vmm.xc.domain_getinfo(xid, 1)
438-
if info[0]["domid"] != xid:
439-
raise qubes.exc.QubesVMNotRunningError(only_vm)
435+
if self.app.vmm.is_xen:
436+
info = self.app.vmm.xc.domain_getinfo(xid, 1)
437+
if info[0]["domid"] != xid:
438+
raise qubes.exc.QubesVMNotRunningError(only_vm)
439+
if (
440+
only_vm_stubdom_xid := getattr(only_vm, "stubdom_xid", -1)
441+
) and only_vm_stubdom_xid > 0:
442+
stubdom_info = self.app.vmm.xc.domain_getinfo(
443+
only_vm_stubdom_xid, 1
444+
)[0]
445+
info.append(stubdom_info)
446+
else:
447+
if not only_vm.libvirt_domain:
448+
raise qubes.exc.QubesVMNotRunningError(only_vm)
449+
dom_info = only_vm.libvirt_domain.info()
450+
info = [
451+
{
452+
"domid": only_vm.xid,
453+
"maxmem_kb": dom_info[1],
454+
"mem_kb": dom_info[2],
455+
"online_vcpus": dom_info[3],
456+
"cpu_time": dom_info[4],
457+
}
458+
]
440459
else:
441-
info = self.app.vmm.xc.domain_getinfo(0, 1024)
442-
443-
# TODO: add stubdomain stats to actual VMs
444-
for vm in info:
445-
domid = vm["domid"]
460+
if self.app.vmm.is_xen:
461+
info = self.app.vmm.xc.domain_getinfo(0, 1024)
462+
else:
463+
running = self.app.vmm.libvirt_conn.listAllDomains(
464+
libvirt.VIR_CONNECT_GET_ALL_DOMAINS_STATS_ACTIVE
465+
)
466+
info = []
467+
for dom in running:
468+
dom_info = dom.info()
469+
dom_info_dict = {
470+
"domid": dom.ID(),
471+
"maxmem_kb": dom_info[1],
472+
"mem_kb": dom_info[2],
473+
"online_vcpus": dom_info[3],
474+
"cpu_time": dom_info[4],
475+
}
476+
info.append(dom_info_dict)
477+
478+
def sum_stubdom(key, info, domid, stubdom_xid):
479+
data = info[domid][key]
480+
if stubdom_xid and stubdom_xid > 0:
481+
data += info[stubdom_xid][key]
482+
return data
483+
484+
stubdoms = []
485+
info = sorted(info, key=lambda domain: domain["domid"])
486+
info = {v["domid"]: v for v in info}
487+
for domid in info.keys():
488+
stubdom_xid = None
489+
if domid in stubdoms:
490+
continue
446491
current[domid] = {}
447492
current[domid].setdefault("is_stubdom", None)
493+
448494
if domid in previous:
449495
name = previous[domid]["name"]
450496
current[domid]["is_stubdom"] = previous[domid]["is_stubdom"]
497+
if current[domid]["is_stubdom"]:
498+
continue
451499
else:
452500
name = self.app.get_name_from_domid(domid)
453501
if name == "Domain-0":
454502
name = "dom0"
455503
current[domid]["name"] = name
504+
456505
qube = None
457506
if current[domid]["is_stubdom"] in [None, False]:
458507
try:
459508
qube = self.app.domains[current[domid]["name"]]
460509
except KeyError:
461510
current[domid]["is_stubdom"] = True
511+
if domid not in stubdoms:
512+
stubdoms.append(domid)
513+
continue
462514
else:
463515
current[domid]["is_stubdom"] = False
516+
if (
517+
qube
518+
and (stubdom_xid := getattr(qube, "stubdom_xid", None))
519+
and stubdom_xid > 0
520+
):
521+
stubdoms.append(stubdom_xid)
464522

465523
if getattr(qube, "virt_mode", None) == "hvm":
466524
# Consider videoram.
467525
memory_assigned_key = "maxmem_kb"
468526
else:
469527
memory_assigned_key = "mem_kb"
470-
current[domid]["memory_assigned_KiB"] = vm[memory_assigned_key]
528+
current[domid]["memory_assigned_KiB"] = sum_stubdom(
529+
memory_assigned_key, info, domid, stubdom_xid
530+
)
471531
current[domid]["memory_kb"] = current[domid]["memory_assigned_KiB"]
472532
current[domid]["memory_used_KiB"] = current[domid][
473533
"memory_assigned_KiB"
@@ -485,8 +545,13 @@ def get_vm_stats(self, previous_time=None, previous=None, only_vm=None):
485545
current[domid]["memory_used_KiB"] = int(meminfo)
486546
else:
487547
del untrusted_meminfo
488-
current[domid]["online_vcpus"] = max(vm["online_vcpus"], 1)
489-
current[domid]["cpu_time"] = round(vm["cpu_time"])
548+
549+
current[domid]["online_vcpus"] = max(
550+
sum_stubdom("online_vcpus", info, domid, stubdom_xid), 1
551+
)
552+
current[domid]["cpu_time"] = round(
553+
sum_stubdom("cpu_time", info, domid, stubdom_xid)
554+
)
490555
if domid in previous:
491556
current[domid]["cpu_usage_raw"] = round(
492557
(current[domid]["cpu_time"] - previous[domid]["cpu_time"])

0 commit comments

Comments
 (0)