1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/vrtManager/hostdetails.py
catborise 27f4845086 perf(computes): the host CPU graph no longer waits a second per poll
Each poll of a host's overview graph held a worker for one second between
two CPU samples. The answer now carries its sample, signed with the time
and the compute; the next poll sends it back and the usage is taken since
then, so a point is the average since the previous one (normally 5 s, at
most 15 s). Without a sample, or with one that is altered, older or from
another host, or when the host restarted, it is measured over one second
as before.

A sample is one reading; the busy share was mixed from separate readings,
counted guest time twice (it is part of user) and added half a percent.
A host that cannot be read leaves a gap in both graphs instead of a zero
CPU point and a broken memory axis.
2026-10-09 09:53:12 +03:00

88 lines
3.2 KiB
Python

import time
from libvirt import VIR_NODE_CPU_STATS_ALL_CPUS
from vrtManager.connection import wvmConnect
from vrtManager.util import get_xml_path
# the times of virNodeGetCPUStats that add up to the elapsed CPU time; guest is
# already part of user and utilization is a percentage
CPU_TIMES = ("kernel", "user", "idle", "iowait")
def cpu_percent(before, after):
"""Busy share in percent between two (idle, total) samples, or None when
the second does not follow the first."""
idle = after[0] - before[0]
total = after[1] - before[1]
if total <= 0 or not 0 <= idle <= total:
return None
return round(100 * (total - idle) / total, 1)
def cpu_version(doc):
for info in doc.xpath("/sysinfo/processor/entry"):
elem = info.xpath("@name")[0]
if elem == "version":
return info.text
return "Unknown"
class wvmHostDetails(wvmConnect):
def get_memory_usage(self):
"""
Function return memory usage on node.
"""
all_mem = self.wvm.getInfo()[1] * 1048576
freemem = self.wvm.getMemoryStats(-1, 0)
if isinstance(freemem, dict):
free = (freemem["buffers"] + freemem["free"] + freemem["cached"]) * 1024
percent = abs(100 - free * 100 // all_mem)
usage = all_mem - free
return {"total": all_mem, "usage": usage, "percent": percent}
else:
return {"total": None, "usage": None, "percent": None}
def _cpu_times(self):
"""(idle, total) nanoseconds of all host CPUs, from one reading."""
stats = self.wvm.getCPUStats(VIR_NODE_CPU_STATS_ALL_CPUS, 0)
return stats["idle"], sum(stats.get(name, 0) for name in CPU_TIMES)
def get_cpu_usage(self, diff=True, previous=None):
"""
Busy share of all host CPUs in percent. diff=False: since the host
started. Otherwise since previous, an (idle, total, time) sample this
method returned, or over one second when there is none or it does not
lead to this one (the host restarted). Also returns the closing sample
and the seconds the usage covers.
"""
times = self._cpu_times()
if not diff:
return {"usage": cpu_percent((0, 0), times)}
now = time.time()
usage = None
if previous is not None:
usage = cpu_percent(previous[:2], times)
start = previous[2]
if usage is None:
before, start = times, now
time.sleep(1)
times = self._cpu_times()
now = time.time()
usage = cpu_percent(before, times)
return {"usage": usage, "sample": (*times, now), "window": now - start}
def get_node_info(self):
"""
Function return host server information: hostname, cpu, memory, ...
"""
info = [self.wvm.getHostname()] # hostname
info.append(self.wvm.getInfo()[0]) # architecture
info.append(self.wvm.getInfo()[1] * 1048576) # memory
info.append(self.wvm.getInfo()[2]) # cpu core count
info.append(
get_xml_path(self.wvm.getSysinfo(0), func=cpu_version)
) # cpu version
info.append(self.wvm.getURI()) # uri
return info