mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-09 03:36:48 +00:00
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.
86 lines
4.5 KiB
Python
86 lines
4.5 KiB
Python
"""What each compute has given its VMs (Compute.capacity) and the Computes
|
|
page that shows it."""
|
|
|
|
from unittest.mock import patch
|
|
|
|
from computes.models import Compute
|
|
from django.contrib.auth import get_user_model
|
|
from django.test import TestCase
|
|
from django.urls import reverse
|
|
from libvirt import libvirtError
|
|
from lxml import html
|
|
|
|
GIB = 1024 ** 3
|
|
|
|
|
|
def pool(name, size_gib, free_gib, status=1):
|
|
return {"name": name, "status": status, "size": size_gib * GIB, "free": free_gib * GIB}
|
|
|
|
|
|
class CapacityTests(TestCase):
|
|
def setUp(self):
|
|
self.compute = Compute(name="host-one", hostname="192.0.2.1", login="u", password="p", type=1)
|
|
self.compute.status = True
|
|
|
|
@patch("computes.models.wvmStorages")
|
|
@patch("computes.models.wvmHostDetails")
|
|
def test_a_host_tells_what_it_gave_and_its_active_pools(self, details, storages):
|
|
details.return_value.get_capacity.return_value = {"cpus": 16}
|
|
storages.return_value.get_storages_info.return_value = [pool("default", 500, 120), pool("off", 10, 0, status=0)]
|
|
self.assertEqual(self.compute.capacity, {"cpus": 16, "pools": [pool("default", 500, 120)]})
|
|
|
|
@patch("computes.models.wvmHostDetails")
|
|
def test_a_host_whose_read_fails_has_no_capacity(self, details):
|
|
# a VM that goes while the host is read, for one
|
|
details.return_value.get_capacity.side_effect = libvirtError("Domain not found")
|
|
self.assertIsNone(self.compute.capacity)
|
|
|
|
@patch("computes.models.wvmHostDetails")
|
|
def test_a_down_host_is_not_read(self, details):
|
|
self.compute.status = False
|
|
self.assertIsNone(self.compute.capacity)
|
|
details.assert_not_called()
|
|
|
|
|
|
class ComputesPageTests(TestCase):
|
|
def test_the_list_shows_what_each_compute_has_given(self):
|
|
admin = get_user_model().objects.create_superuser("cap-admin", "cap@example.com", "pw")
|
|
self.client.force_login(admin)
|
|
c1 = Compute.objects.create(name="host-one", hostname="192.0.2.1", login="u", password="p", type=1)
|
|
Compute.objects.create(name="host-two", hostname="192.0.2.2", login="u", password="p", type=1)
|
|
found = {c1.id: {"cpus": 16, "memory": 64 * GIB, "vcpus": {"all": 40, "running": 24},
|
|
"assigned": {"all": 48 * GIB, "running": 32 * GIB},
|
|
"pools": [pool("default", 500, 120)]}}
|
|
with patch.object(Compute, "capacity", new=property(lambda c: found.get(c.id))), \
|
|
patch.object(Compute, "status", new=True):
|
|
page = html.fromstring(self.client.get(reverse("computes")).content)
|
|
headers = [" ".join(th.text_content().split()) for th in page.xpath("//thead//th")]
|
|
self.assertEqual(headers, ["Host", "vCPUs", "Memory", "Storage", "Actions"])
|
|
one, two = page.xpath("//tbody/tr")
|
|
cells = [" ".join(td.text_content().split()) for td in one.xpath("td")]
|
|
self.assertIn("host-one", cells[0])
|
|
self.assertIn("2.5:1", cells[1])
|
|
self.assertIn("40 vCPUs on 16 CPUs 24 running", cells[1])
|
|
self.assertIn("48.0 GB given of 64.0 GB", cells[2])
|
|
self.assertIn("32.0 GB of it running", cells[2])
|
|
self.assertIn("120.0 GB free of 500.0 GB", cells[3])
|
|
# host-two could not be read: one cell says so
|
|
self.assertIn("capacity could not be read", " ".join(two.text_content().split()))
|
|
|
|
def test_the_default_pool_shows_and_the_others_open_on_a_click(self):
|
|
admin = get_user_model().objects.create_superuser("cap-pools", "pools@example.com", "pw")
|
|
self.client.force_login(admin)
|
|
Compute.objects.create(name="host-one", hostname="192.0.2.1", login="u", password="p", type=1)
|
|
# libvirt lists the pools in no order
|
|
pools = [pool("nfs", 2000, 1000), pool("default", 500, 120), pool("iso", 100, 90), pool("backup", 900, 800)]
|
|
given = {"cpus": 16, "memory": 64 * GIB, "vcpus": {"all": 8, "running": 8},
|
|
"assigned": {"all": 8 * GIB, "running": 8 * GIB}, "pools": pools}
|
|
with patch.object(Compute, "capacity", new=given), patch.object(Compute, "status", new=True):
|
|
page = html.fromstring(self.client.get(reverse("computes")).content)
|
|
(storage,) = page.xpath("//tbody/tr/td[4]")
|
|
shown = " ".join(" ".join(storage.xpath("./div//text()")).split())
|
|
self.assertIn("default", shown)
|
|
self.assertNotIn("backup", shown)
|
|
(more,) = storage.xpath(".//details")
|
|
self.assertEqual(" ".join(more.xpath("summary")[0].text_content().split()), "3 more pools")
|
|
self.assertEqual(more.xpath(".//div[@class='small']/text()"), ["backup", "iso", "nfs"])
|