"""The host overview page: a host the panel reaches, one it cannot reach, and one whose details cannot be read.""" 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 libvirt import libvirtError from lxml import html from computes.models import Compute GIB = 1024 ** 3 GIVEN = {"cpus": 8, "memory": 32 * GIB, "vcpus": {"all": 12, "running": 6}, "assigned": {"all": 24 * GIB, "running": 12 * GIB}, "ratio": 1.5, "vms": {"running": 3, "paused": 1, "shutoff": 2, "other": 0}, "pools": [{"name": "default", "status": 1, "size": 500 * GIB, "free": 120 * GIB}]} def text(page, xpath): return " ".join(" ".join(page.xpath(xpath)[0].itertext()).split()) class OverviewTestCase(TestCase): def setUp(self): self.client.force_login(get_user_model().objects.create_superuser("ov-admin", "ov@example.com", "pw")) self.compute = Compute.objects.create(name="host-one", hostname="192.0.2.1", login="u", password="p", type=1, details="rack 4") self.url = reverse("overview", args=[self.compute.id]) def page(self, connected=True, given=GIVEN): with patch.object(Compute, "status", new_callable=PropertyMock, return_value=connected), \ patch.object(Compute, "connection_error", new_callable=PropertyMock, return_value="Connection Failed: 192.0.2.1 did not answer within 5 s"), \ patch.object(Compute, "capacity", new=given and {**given}): response = self.client.get(self.url) self.assertEqual(response.status_code, 200) return html.fromstring(response.content) @patch("computes.views.wvmHostDetails") def test_a_host_shows_its_vms_capacity_hardware_and_software(self, details): conn = details.return_value conn.get_node_info.return_value = ("wvc-host", "x86_64", 32 * GIB, 8, "Xeon", "qemu+tcp://192.0.2.1/system") conn.get_cpu_topology.return_value = {"nodes": 2, "sockets": 2, "cores": 4, "mhz": 2400, "cpus": 8} conn.wvm.getType.return_value = "QEMU" conn.get_version.return_value = "10.1.0" conn.get_lib_version.return_value = "11.10.0" conn.get_emulator.return_value = "/usr/libexec/qemu-kvm" conn.get_hypervisors_domain_types.return_value = {"i686": ["kvm", "qemu"], "x86_64": ["kvm", "qemu"]} page = self.page() self.assertIn("Connected", text(page, "//p[span]")) self.assertIn("rack 4", text(page, "//p[span]")) cards = [" ".join(" ".join(card.itertext()).split()) for card in page.xpath("//div[@class='card-body' or contains(@class,'card-body ')]")] self.assertIn("VMs 6 3 running, 2 shut off, 1 paused Instances →", cards) self.assertTrue(any(card.startswith("vCPUs 1.5:1 12 vCPUs on 8 CPUs 6 running") for card in cards), cards) self.assertTrue(any("24.0 GB given of 32.0 GB" in card for card in cards), cards) self.assertTrue(any("120.0 GB free of 500.0 GB" in card for card in cards), cards) hardware = text(page, "//h4[.='Hardware']/following-sibling::dl") self.assertIn("Logical CPUs 8 Physical cores 4 Sockets 2", hardware) self.assertIn("NUMA nodes 2", hardware) software = text(page, "//h4[.='Software and connection']/following-sibling::dl") self.assertIn("Hypervisor QEMU 10.1.0", software) self.assertIn("x86_64 (kvm, qemu)", software) # the charts poll only for a host that was read self.assertTrue(page.xpath("//canvas[@id='cpuChart']")) @patch("computes.views.wvmHostDetails") def test_a_host_the_panel_cannot_reach_keeps_its_page_with_the_reason(self, details): page = self.page(connected=False, given=None) details.assert_not_called() self.assertIn("Not connected", text(page, "//p[span]")) self.assertIn("did not answer within 5 s", text(page, "//div[@role='alert']")) # the way back: its sections, its connection and its removal self.assertTrue(page.xpath("//nav[@aria-label='Host sections']")) self.assertTrue(page.xpath(f"//a[@href='{reverse('compute_update', args=[self.compute.id])}']")) self.assertTrue(page.xpath(f"//a[@href='{reverse('compute_delete', args=[self.compute.id])}']")) self.assertFalse(page.xpath("//canvas")) @patch("computes.views.wvmHostDetails") def test_details_that_cannot_be_read_leave_the_capacity_and_the_load(self, details): details.return_value.get_node_info.return_value = ("h", "x86_64", GIB, 2, None, "qemu+tcp://192.0.2.1/system") details.return_value.get_lib_version.side_effect = libvirtError("cannot read the version") page = self.page() self.assertIn("cannot read the version", text(page, "//div[@role='alert']")) self.assertTrue(page.xpath("//h5[.='VMs']")) # the load is read on its own, by the charts self.assertTrue(page.xpath("//canvas[@id='cpuChart']")) self.assertFalse(page.xpath("//h4[.='Hardware']")) 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}']"))