- {# position-relative: the hidden status texts are positioned within it, not the page #}
-
+{% endif %}
{% endblock content %}
{% block script %}
diff --git a/computes/templates/create_comp_block.html b/computes/templates/create_comp_block.html
index 3d1b6b0f..9d178f74 100644
--- a/computes/templates/create_comp_block.html
+++ b/computes/templates/create_comp_block.html
@@ -1,11 +1,25 @@
{% load i18n %}
-{% load django_bootstrap5 %}
{% load bootstrap_icons %}
-
-
-
{% trans "TCP" %}
-
{% trans "SSH" %}
-
{% trans "TLS" %}
-
{% trans "Local" %}
-
{% bs_icon "plus-circle-fill" %}
+
+
+ {% bs_icon 'plus-lg' %} {% trans "Add host" %}
+
+
diff --git a/computes/tests/test_capacity.py b/computes/tests/test_capacity.py
index 34a99ade..dda82f71 100644
--- a/computes/tests/test_capacity.py
+++ b/computes/tests/test_capacity.py
@@ -50,14 +50,20 @@ class ComputesPageTests(TestCase):
page = html.fromstring(self.client.get(reverse("computes")).content)
headers = [" ".join(th.text_content().split()) for th in page.xpath("//thead//th")]
# no pools: measuring their space scans each pool of each host (views.host_pools)
- self.assertEqual(headers, ["Host", "vCPUs", "Memory", "Actions"])
+ self.assertEqual(headers, ["Host", "VMs", "vCPUs", "Memory", "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("75 % 48.0 GB / 64.0 GB", cells[2])
- self.assertIn("32.0 GB running", cells[2])
+ self.assertIn("host-one Connected TCP 192.0.2.1", cells[0])
+ self.assertEqual(cells[1], "6 3 running")
+ self.assertIn("2.5:1", cells[2])
+ self.assertIn("40 vCPUs on 16 CPUs", cells[2])
+ self.assertIn("24 running", cells[2])
+ self.assertIn("75 % 48.0 GB / 64.0 GB", cells[3])
+ self.assertIn("32.0 GB running", cells[3])
+ # the totals of the hosts read: host-two's capacity could not be
+ totals = [" ".join(card.text_content().split()) for card in page.xpath("//div[@class='card-body']")]
+ self.assertEqual(totals, ["Hosts 2 / 2 connected", "VMs 3 / 6 running", "vCPUs given 40 on 16 CPUs",
+ "Memory given 48.0 GB of 64.0 GB"])
# host-two could not be read: one cell says so
self.assertIn("capacity could not be read", " ".join(two.text_content().split()))
@@ -72,7 +78,7 @@ class SingularTests(TestCase):
with patch.object(Compute, "capacity", new=given), patch.object(Compute, "status", new=True):
page = html.fromstring(self.client.get(reverse("computes")).content)
line = self.client.get(reverse("compute_capacity", args=[c.id])).content.decode()
- (cell,) = page.xpath("//tbody/tr/td[2]")
+ (cell,) = page.xpath("//tbody/tr/td[3]")
self.assertIn("1 vCPU on 1 CPU", " ".join(cell.text_content().split()))
self.assertEqual(" ".join(line.split()), "Given to VMs: 1 vCPU (1.0:1), 1.0 GB memory.")
diff --git a/computes/tests/test_overview.py b/computes/tests/test_overview.py
index 7d06ff40..3aaa67fe 100644
--- a/computes/tests/test_overview.py
+++ b/computes/tests/test_overview.py
@@ -117,4 +117,5 @@ class OverviewTestCase(TestCase):
def test_the_list_links_the_overview_of_a_host_it_cannot_reach(self):
with patch.object(Compute, "status", new_callable=PropertyMock, return_value=False):
page = html.fromstring(self.client.get(reverse("computes")).content)
- self.assertTrue(page.xpath(f"//a[@title='Overview'][@href='{self.url}']"))
+ # its name and its menu's Overview
+ self.assertEqual(len(page.xpath(f"//tbody//a[@href='{self.url}']")), 2)
diff --git a/computes/tests/test_overview_status.py b/computes/tests/test_overview_status.py
index d181cf4e..8e8ac2db 100644
--- a/computes/tests/test_overview_status.py
+++ b/computes/tests/test_overview_status.py
@@ -22,5 +22,5 @@ class OverviewStatusTests(TestCase):
Compute, "connection_error", new_callable=PropertyMock, return_value=ERROR
):
response = self.client.get(reverse("computes"))
- self.assertContains(response, "Not Connected")
+ self.assertContains(response, "Not connected")
self.assertContains(response, ERROR)
diff --git a/computes/views.py b/computes/views.py
index 45210e48..12d3c24b 100644
--- a/computes/views.py
+++ b/computes/views.py
@@ -12,6 +12,7 @@ from instances.models import Instance
from libvirt import libvirtError
from logs.audit import audit, changed_fields, snapshot
from operations import claims
+from vrtManager.connection import CONN_SOCKET, CONN_SSH, CONN_TCP, CONN_TLS
from vrtManager.hostdetails import wvmHostDetails
from vrtManager.storage import wvmStorages
@@ -44,6 +45,11 @@ def shown_capacity(compute):
given["bar"] = {"running": assigned["running"] * 100 / scale, "stopped": assigned["stopped"] * 100 / scale,
"over": assigned["all"] > memory}
given["memory_percent"] = round(assigned["all"] * 100 / memory) if memory else 0
+ # the vCPU bar likewise: running and shut-off VMs' vCPUs against the CPUs
+ vcpus, cpus = given["vcpus"], given["cpus"]
+ vcpus["stopped"] = vcpus["all"] - vcpus["running"]
+ cpu_scale = max(vcpus["all"], cpus) or 1
+ given["cpu_bar"] = {"running": vcpus["running"] * 100 / cpu_scale, "stopped": vcpus["stopped"] * 100 / cpu_scale}
given["vm_count"] = sum(given["vms"].values())
return given
@@ -63,13 +69,29 @@ def host_pools(compute):
return {"pool": pools[0] if pools else None, "more_pools": pools[1:]}
+# how the panel reaches a compute, by Compute.type
+CONNECTIONS = {CONN_TCP: "TCP", CONN_SSH: "SSH", CONN_TLS: "TLS", CONN_SOCKET: "Socket"}
+
+
@superuser_only
def computes(request):
- """The computes, with what each has given its VMs."""
+ """The computes, with what each has given its VMs, and the totals of the
+ ones the panel reaches."""
computes = list(Compute.objects.order_by("name"))
+ totals = {"hosts": len(computes), "connected": 0, "vms": 0, "running": 0,
+ "vcpus": 0, "cpus": 0, "assigned": 0, "memory": 0}
for compute in computes:
- compute.shown_capacity = shown_capacity(compute)
- return render(request, "computes/list.html", {"computes": computes})
+ compute.connection_name = CONNECTIONS.get(compute.type, "")
+ compute.shown_capacity = given = shown_capacity(compute)
+ totals["connected"] += compute.status is True
+ if given:
+ totals["vms"] += given["vm_count"]
+ totals["running"] += given["vms"]["running"]
+ totals["vcpus"] += given["vcpus"]["all"]
+ totals["cpus"] += given["cpus"]
+ totals["assigned"] += given["assigned"]["all"]
+ totals["memory"] += given["memory"]
+ return render(request, "computes/list.html", {"computes": computes, "totals": totals})
@superuser_only
diff --git a/static/icon_cache/hdd-rack_None_None_None.svg b/static/icon_cache/hdd-rack_None_None_None.svg
new file mode 100644
index 00000000..1f397f5d
--- /dev/null
+++ b/static/icon_cache/hdd-rack_None_None_None.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/static/icon_cache/plus-lg_None_None_None.svg b/static/icon_cache/plus-lg_None_None_None.svg
new file mode 100644
index 00000000..b79c1778
--- /dev/null
+++ b/static/icon_cache/plus-lg_None_None_None.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/static/icon_cache/three-dots_None_None_None.svg b/static/icon_cache/three-dots_None_None_None.svg
new file mode 100644
index 00000000..662ea7ed
--- /dev/null
+++ b/static/icon_cache/three-dots_None_None_None.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+