From 5e26f5a473151f84ed73423a13decff3d6917573 Mon Sep 17 00:00:00 2001 From: catborise Date: Mon, 5 Oct 2026 11:10:01 +0300 Subject: [PATCH] fix(computes): overview state comes from the connection The overview page decided whether the host is up with a TCP probe of the compute's hostname. For an ssh compute named by an ssh_config alias or reached through ProxyJump, that name and port cannot be reached from WebVirtCloud directly, so the probe failed while the connection worked: the page showed the host details and never started the CPU and memory graphs. The overview now uses Compute.status, the connection WebVirtCloud uses for everything else. The compute list shows why a host is not connected (Compute.connection_error) under "Not Connected", for example a host key verification failure. Checked against the ssh compute of the test setup through an ssh_config alias: the previous code renders the overview with the graphs off, the new one with them on. Both new tests fail on the previous code. --- computes/templates/computes/list.html | 4 ++- computes/test_overview_status.py | 37 +++++++++++++++++++++++++++ computes/views.py | 13 +++------- 3 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 computes/test_overview_status.py diff --git a/computes/templates/computes/list.html b/computes/templates/computes/list.html index 3a5f1718..d5850d9c 100644 --- a/computes/templates/computes/list.html +++ b/computes/templates/computes/list.html @@ -37,7 +37,9 @@ {{ compute.name }} - {% if compute.status is True %}{% trans "Connected" %}{% else %}{% trans "Not Connected" %}{% endif %} + {% if compute.status is True %}{% trans "Connected" %}{% else %}{% trans "Not Connected" %} +
{{ compute.connection_error }} + {% endif %} {{ compute.details|default:"" }} diff --git a/computes/test_overview_status.py b/computes/test_overview_status.py new file mode 100644 index 00000000..54074c20 --- /dev/null +++ b/computes/test_overview_status.py @@ -0,0 +1,37 @@ +from unittest.mock import PropertyMock, patch + +from django.contrib.auth import get_user_model +from django.test import TestCase +from django.urls import reverse + +from computes.models import Compute + +ERROR = "Connection Failed: Host key verification failed." + + +class OverviewStatusTests(TestCase): + def setUp(self): + self.client.force_login( + get_user_model().objects.create_superuser(username="ov-admin", password="pwd", email="ov@example.com") + ) + # an ssh_config alias: not a name a TCP probe can reach + self.compute = Compute.objects.create(name="ov-alias", hostname="wvc-alias", login="root", password="", type=2) + + @patch("vrtManager.connection.connection_manager.host_is_up", return_value=False) + @patch("computes.views.wvmHostDetails") + def test_overview_state_comes_from_the_connection(self, host_details, host_is_up): + conn = host_details.return_value + conn.get_node_info.return_value = ("host", "x86_64", 1024, 2, "cpu", "qemu+ssh://root@wvc-alias/system") + conn.get_memory_usage.return_value = {"total": 1024, "usage": 512, "percent": 50} + with patch.object(Compute, "status", new_callable=PropertyMock, return_value=True): + response = self.client.get(reverse("overview", args=[self.compute.id])) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context["status"], "true") + + def test_compute_list_shows_why_a_host_is_not_connected(self): + with patch.object(Compute, "status", new_callable=PropertyMock, return_value=False), patch.object( + Compute, "connection_error", new_callable=PropertyMock, return_value=ERROR + ): + response = self.client.get(reverse("computes")) + self.assertContains(response, "Not Connected") + self.assertContains(response, ERROR) diff --git a/computes/views.py b/computes/views.py index 4195bf20..4bd6801f 100644 --- a/computes/views.py +++ b/computes/views.py @@ -7,10 +7,7 @@ from django.urls import reverse from django.utils import timezone from instances.models import Instance from libvirt import libvirtError -from vrtManager.connection import ( - connection_manager, - wvmConnect, -) +from vrtManager.connection import wvmConnect from vrtManager.hostdetails import wvmHostDetails from computes.forms import ( @@ -39,11 +36,9 @@ def computes(request): @superuser_only def overview(request, compute_id): compute = get_object_or_404(Compute, pk=compute_id) - status = ( - "true" - if connection_manager.host_is_up(compute.type, compute.hostname) is True - else "false" - ) + # the connection itself, not a TCP probe: ssh aliases and ProxyJump hosts + # are not reachable by name and port from here + status = "true" if compute.status else "false" conn = wvmHostDetails( compute.hostname,