1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 19:56:48 +00:00

refactor(computes): the Computes page reads capacity through vrtManager

The capacity read lived in its own module with raw libvirt calls and a
thread pool with a deadline, beside helpers that already read the same
things. It now uses them:

- wvmHostDetails.get_capacity() sums the vCPUs and memory of all and of
  the running VMs from get_host_instances(), against the host's info.
- get_storages_info() also gives each pool's free space.
- Compute.capacity joins the two, like cpu_count and ram_size, and is
  None when the host is down or the read fails.

The page reads the hosts one after another, as it already connected to
each of them; a host that cannot be reached is given up after the
connect timeout. computes/capacity.py and its thread handling are gone.
The page looks and sorts as before.
This commit is contained in:
catborise 2026-10-25 12:15:23 +03:00
parent 1436ed13af
commit d9f9ebab58
8 changed files with 114 additions and 185 deletions

View file

@ -1,6 +1,6 @@
import time
from libvirt import VIR_NODE_CPU_STATS_ALL_CPUS
from libvirt import VIR_DOMAIN_SHUTOFF, VIR_NODE_CPU_STATS_ALL_CPUS
from vrtManager.connection import wvmConnect
from vrtManager.util import get_xml_path
@ -83,3 +83,18 @@ class wvmHostDetails(wvmConnect):
) # cpu version
info.append(self.wvm.getURI()) # uri
return info
def get_capacity(self):
"""
What the host has given its VMs: vCPUs and memory (bytes) of all VMs
and of the running ones, against its CPUs and memory. A shut-off VM
takes its share again when it starts.
"""
vcpus = {"all": 0, "running": 0}
assigned = {"all": 0, "running": 0}
for vm in self.get_host_instances(raw_mem_size=True).values():
for share in ("all",) if vm["status"] == VIR_DOMAIN_SHUTOFF else ("all", "running"):
vcpus[share] += int(vm["vcpu"])
assigned[share] += vm["memory"]
return {"cpus": self.host_info[2], "memory": self.host_info[1] * 1048576,
"vcpus": vcpus, "assigned": assigned}

View file

@ -14,7 +14,7 @@ class wvmStorages(wvmConnect):
stg_status = stg.isActive()
stg_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
stg_vol = len(stg.listVolumes()) if stg_status else None
stg_size = stg.info()[1]
_state, stg_size, _allocation, stg_free = stg.info()
storages.append(
{
"name": pool,
@ -22,6 +22,7 @@ class wvmStorages(wvmConnect):
"type": stg_type,
"volumes": stg_vol,
"size": stg_size,
"free": stg_free,
}
)
return storages

View file

@ -13,9 +13,19 @@ from django.conf import settings
if not settings.configured:
settings.configure(MAC_OUI="52:54:10")
from libvirt import VIR_CONNECT_LIST_NODE_DEVICES_CAP_NET, VIR_ERR_NO_SUPPORT, VIR_ERR_OPERATION_DENIED, VIR_ERR_XML_ERROR, libvirtError
from libvirt import (
VIR_CONNECT_LIST_NODE_DEVICES_CAP_NET,
VIR_DOMAIN_PAUSED,
VIR_DOMAIN_RUNNING,
VIR_DOMAIN_SHUTOFF,
VIR_ERR_NO_SUPPORT,
VIR_ERR_OPERATION_DENIED,
VIR_ERR_XML_ERROR,
libvirtError,
)
from vrtManager.connection import wvmConnect
from vrtManager.hostdetails import wvmHostDetails
from vrtManager.storage import wvmStorages
CAPABILITIES = """<capabilities><host><cpu><arch>x86_64</arch></cpu></host>
<guest><os_type>hvm</os_type><arch name='x86_64'><emulator>/usr/bin/qemu-kvm</emulator>
@ -132,3 +142,32 @@ class ChangeInterfacesTestCase(unittest.TestCase):
self.assertIs(conn.can_change_interfaces(), changeable)
# an invalid definition: nothing can be defined by asking
conn.wvm.interfaceDefineXML.assert_called_once_with("<interface/>", 0)
class CapacityTestCase(unittest.TestCase):
def test_a_host_gives_its_vms_cpus_and_memory(self):
conn = connection(wvmHostDetails)
gib = 1024 ** 3
# get_host_instances gives the vCPUs as text, the memory in bytes
conn.get_host_instances = MagicMock(return_value={
"web": {"status": VIR_DOMAIN_RUNNING, "vcpu": "4", "memory": 4 * gib},
"db": {"status": VIR_DOMAIN_PAUSED, "vcpu": "2", "memory": 2 * gib},
"old": {"status": VIR_DOMAIN_SHUTOFF, "vcpu": "8", "memory": 8 * gib},
})
self.assertEqual(conn.get_capacity(), {
"cpus": 4, "memory": 2 * gib,
"vcpus": {"all": 14, "running": 6},
"assigned": {"all": 14 * gib, "running": 6 * gib},
})
conn.get_host_instances.assert_called_once_with(raw_mem_size=True)
class StoragesInfoTestCase(unittest.TestCase):
def test_each_pool_tells_its_free_space(self):
conn = connection(wvmStorages)
pool = MagicMock(**{"isActive.return_value": 1, "XMLDesc.return_value": "<pool type='dir'/>",
"listVolumes.return_value": ["a.qcow2"], "info.return_value": [2, 500, 380, 120]})
conn.get_storages = lambda: ["default"]
conn.get_storage = lambda name: pool
(info,) = conn.get_storages_info()
self.assertEqual((info["name"], info["size"], info["free"]), ("default", 500, 120))