mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-11 04:36:48 +00:00
Each host is a row of what it has committed, read live from the host (in parallel, by a 10 s deadline): - Host: a status dot, the name, its address and details; when it cannot be reached, why. - vCPUs: the overcommit ratio, large (warning above 2:1, red above 4:1), over "N vCPUs on M CPUs" and the running VMs' share. - Memory: memory given to all VMs against the host's memory, as one bar: running VMs dark, shut-off VMs light (they take their memory again when they start), and a line where the host's memory ends; red when the VMs were given more than the host has. - Storage: the default pool (or the first) with its used share and free space; the others, a host may have dozens, open on a click. A host that cannot be reached, or does not answer in time, says so in one line instead. vCPUs and Memory sort by ratio and share. A host whose read hangs is not read again until that read ends, so a stuck host keeps at most one thread. The table's hidden status texts stay inside it, so the page does not widen on a phone.
82 lines
3 KiB
Python
82 lines
3 KiB
Python
"""
|
|
What each compute has given its VMs: vCPUs and memory of all VMs (a
|
|
shut-off VM takes its share again when it starts) and of the running ones,
|
|
against the host's CPUs and memory, and the free space of its active
|
|
storage pools. Read live from libvirt for the Computes page.
|
|
"""
|
|
|
|
import threading
|
|
import time
|
|
from concurrent import futures
|
|
|
|
from libvirt import VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE, VIR_DOMAIN_SHUTOFF, libvirtError
|
|
|
|
MAX_WORKERS = 8
|
|
|
|
# A libvirt call may hang: its thread outlives the page. Until it ends, its
|
|
# compute is not read again, so the threads stay at one per compute.
|
|
_reading = set()
|
|
_reading_lock = threading.Lock()
|
|
|
|
|
|
def read(compute):
|
|
"""The compute's capacity, or None when it cannot be reached. A VM that
|
|
goes while it is read is skipped."""
|
|
if compute.status is not True:
|
|
return None
|
|
conn = compute.connection
|
|
_arch, memory_mib, cpus, *_ = conn.getInfo()
|
|
vcpus = {"all": 0, "running": 0}
|
|
assigned = {"all": 0, "running": 0}
|
|
for dom in conn.listAllDomains(0):
|
|
try:
|
|
state, _max_memory, memory_kib, vcpu_count, _cpu_time = dom.info()
|
|
except libvirtError:
|
|
continue
|
|
for share in ("all", "running") if state != VIR_DOMAIN_SHUTOFF else ("all",):
|
|
vcpus[share] += vcpu_count
|
|
assigned[share] += memory_kib * 1024
|
|
pools = []
|
|
for pool in conn.listAllStoragePools(VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE):
|
|
try:
|
|
_state, size, _allocation, available = pool.info()
|
|
except libvirtError:
|
|
continue
|
|
pools.append((pool.name(), size, available))
|
|
return {"cpus": cpus, "memory": memory_mib * 1024 * 1024, "vcpus": vcpus, "assigned": assigned,
|
|
"pools": sorted(pools)}
|
|
|
|
|
|
def _read_once(compute):
|
|
try:
|
|
return read(compute)
|
|
finally:
|
|
with _reading_lock:
|
|
_reading.discard(compute.id)
|
|
|
|
|
|
def read_all(computes, deadline=10.0):
|
|
"""{compute id: capacity} of the computes read by the deadline (seconds),
|
|
in parallel; a compute down, failing, late, or still being read for an
|
|
earlier page is left out."""
|
|
with _reading_lock:
|
|
computes = [compute for compute in computes if compute.id not in _reading]
|
|
_reading.update(compute.id for compute in computes)
|
|
pool = futures.ThreadPoolExecutor(max_workers=MAX_WORKERS)
|
|
end = time.monotonic() + deadline
|
|
try:
|
|
reads = {pool.submit(_read_once, compute): compute.id for compute in computes}
|
|
done, _late = futures.wait(reads, timeout=max(0, end - time.monotonic()))
|
|
found = {}
|
|
for future in done:
|
|
try:
|
|
result = future.result()
|
|
except libvirtError:
|
|
continue
|
|
if result is not None:
|
|
found[reads[future]] = result
|
|
return found
|
|
finally:
|
|
pool.shutdown(wait=False, cancel_futures=True)
|
|
with _reading_lock: # a read cancelled before it started ends nothing itself
|
|
_reading.difference_update(cid for future, cid in reads.items() if future.cancelled())
|