diff --git a/computes/templates/computes/capacity_vcpus.html b/computes/templates/computes/capacity_vcpus.html index 11c29843..f49174bf 100644 --- a/computes/templates/computes/capacity_vcpus.html +++ b/computes/templates/computes/capacity_vcpus.html @@ -1,5 +1,18 @@ {% load i18n %} -{# the vCPUs a host has given its VMs: given, what its list cell and its overview show #} +{# the vCPUs a host has given its VMs: given, from views.shown_capacity #}
{{ given.ratio|floatformat:1 }}:1
-
{% blocktrans count counter=given.vcpus.all %}{{ counter }} vCPU{% plural %}{{ counter }} vCPUs{% endblocktrans %} {% blocktrans count counter=given.cpus %}on {{ counter }} CPU{% plural %}on {{ counter }} CPUs{% endblocktrans %}
-
{% blocktrans with running=given.vcpus.running %}{{ running }} running{% endblocktrans %}
+
{% blocktrans count counter=given.vcpus.all %}{{ counter }} vCPU{% plural %}{{ counter }} vCPUs{% endblocktrans %} {% blocktrans count counter=given.cpus %}on {{ counter }} CPU{% plural %}on {{ counter }} CPUs{% endblocktrans %}
+{# running VMs' vCPUs, then shut-off VMs', on a scale that holds the CPUs or all given, whichever is more #} +
+
+
+
+
+
+
+
+
{% blocktrans with running=given.vcpus.running %}{{ running }} running{% endblocktrans %}
diff --git a/computes/templates/computes/list.html b/computes/templates/computes/list.html index 1a57583e..599fe60d 100644 --- a/computes/templates/computes/list.html +++ b/computes/templates/computes/list.html @@ -1,7 +1,6 @@ {% extends "base.html" %} {% load i18n %} {% load static %} -{% load common_tags %} {% load bootstrap_icons %} {% block title %}{% trans "Computes" %}{% endblock %} {% block content %} @@ -12,73 +11,110 @@

{% trans "Computes" %}

-
- {% if not computes %} -
-
- {% bs_icon 'exclamation-triangle'%} {% trans "Warning" %}: {% trans "You don't have any computes" %} -
+{% if not computes %} +
+ {% bs_icon 'exclamation-triangle'%} {% trans "Warning" %}: {% trans "You don't have any computes" %} +
+{% else %} + {# the totals of the hosts the panel reaches #} +
+
+
+
{% bs_icon 'hdd-rack' %} {% trans "Hosts" %}
+
{{ totals.connected }} / {{ totals.hosts }}
+
{% trans "connected" %}
+
- {% else %} -
- {# position-relative: the hidden status texts are positioned within it, not the page #} -
- - - - - - - - - - - {% for compute in computes %} - {% with given=compute.shown_capacity %} - - - {% if given %} - - +
+
+
{% bs_icon 'server' %} {% trans "VMs" %}
+
{{ totals.running }} / {{ totals.vms }}
+
{% trans "running" %}
+
+
+
+
+
{% bs_icon 'cpu' %} {% trans "vCPUs given" %}
+
{{ totals.vcpus }}
+
{% blocktrans count counter=totals.cpus %}on {{ counter }} CPU{% plural %}on {{ counter }} CPUs{% endblocktrans %}
+
+
+
+
+
{% bs_icon 'memory' %} {% trans "Memory given" %}
+
{{ totals.assigned|filesizeformat }}
+
{% blocktrans with memory=totals.memory|filesizeformat %}of {{ memory }}{% endblocktrans %}
+
+
+ + + {# position-relative: the hidden status texts are positioned within it, not the page #} +
+
{% trans "Host" %}{% trans "vCPUs" %}{% trans "Memory" %}{% trans "Actions" %}
-
- {% if compute.status is True %} - - {% trans "Connected" %} - {% else %} - - {% trans "Not Connected" %} - {% endif %} - {{ compute.name }} -
-
{{ compute.hostname }}{% if compute.details %}
{{ compute.details }}{% endif %}
- {% if compute.status is not True %} -
{{ compute.connection_error }}
- {% endif %} -
- {% include "computes/capacity_vcpus.html" %} - - {% include "computes/capacity_memory.html" %} -
+ + + + + + + + + + + {% for compute in computes %} + {% with given=compute.shown_capacity %} + + + {% trans "Not connected" %} {% endif %} - - - {% endwith %} - {% endfor %} - -
{% trans "Host" %}{% trans "VMs" %}{% trans "vCPUs" %}{% trans "Memory" %}{% trans "Actions" %}
+
+ {{ compute.name }} + {% if compute.status is True %} + {% trans "Connected" %} {% else %} -
{% if compute.status is True %}{% trans "The capacity could not be read." %}{% else %}{% trans "No capacity while the host cannot be reached." %}{% endif %} -
- {# a host the panel cannot reach opens too: its page says why #} - {% bs_icon 'eye-fill' %} - {% bs_icon 'pencil-fill' %} - {% bs_icon 'x-circle-fill' %} -
-
-
-
- {% endif %} -
+ {{ compute.connection_name }} +
+
{{ compute.hostname }}
+ {% if compute.details %}
{{ compute.details }}
{% endif %} + {% if compute.status is not True %} +
{{ compute.connection_error }}
+ {% endif %} + + {% if given %} + + {{ given.vm_count }} +
{% blocktrans with running=given.vms.running %}{{ running }} running{% endblocktrans %}
+ + + {% include "computes/capacity_vcpus.html" %} + + + {% include "computes/capacity_memory.html" %} + + {% else %} + {% if compute.status is True %}{% trans "The capacity could not be read." %}{% else %}{% trans "No capacity while the host cannot be reached." %}{% endif %} + {% endif %} + + {# fixed: the menu would be cut off by the table's scrolling box #} + + + + {% endwith %} + {% endfor %} + + +
+{% 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" %} + 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 @@ + + + + + + + +