mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
feat(networks): the network pages on Bootstrap's cards, tables and dialogs
As the storage pages; reviewed with Codex. - The list is a card per network: its name with Active or Stopped and Autostart badges, its type (network_mode: Isolated, NAT, Routed, Open, Bridge, Macvtap, SR-IOV), its device, and each address with its DHCP range; a bridge or macvtap network's addresses come from the outside network. - A network's page: its heading starts or stops it, the menu sets autostart and deletes a stopped one. A Network card tells its state, type, bridge or interface, MAC and what the type does; a card per address (every <ip>, each with its DHCP range dialog, which libvirt takes on a stopped network); its fixed addresses of every subnet in one table, searched, each with Edit and Delete, one dialog adding and editing them; the VMs whose definition names it, connected or configured, linked (wvmNetwork.get_vms, one domain listing, a VM undefined meanwhile left out); its bandwidth limits where libvirt sets them (not on macvtap or SR-IOV networks), in KiB/s, from the saved definition with a note that a running network takes them when restarted; its XML in an accordion. The page reads the running and the saved definition once each, and only to render itself: its actions, which all redirect, read nothing of it first. - The New network dialog is one form, as the New pool one: the type as radio cards (the radio_card.html include, now shared by both), the chosen type's fields only, IPv6 with its box, a bridge or interface suggested from the host's (get_host_interfaces, not the bridges libvirt made for its own networks); a refused network or one libvirt refuses opens the dialog again with what was entered, the reasons beside the fields and focus on the first. - What the New pool and New network dialogs share is one script, static/js/choice_dialog.js: the radio cards show their fieldsets (data-kinds) and a refused form opens again focused on its first refused field; each dialog keeps only its own changes. - The fixed-address dialogs per family go to the page's own dialog.
This commit is contained in:
parent
0c9a002ef4
commit
0ee03c0971
15 changed files with 947 additions and 757 deletions
|
|
@ -1,7 +1,7 @@
|
|||
"""Status colours keep their contrast on every theme: a pool, network or
|
||||
interface card's header, and a status word, is the theme's pale status
|
||||
background with its dark status text, a pair that does not depend on the
|
||||
page's background (dark themes keep light-mode emphasis colours)."""
|
||||
"""Status colours keep their contrast on every theme: an interface card's
|
||||
header, and a status word, is the theme's pale status background with its
|
||||
dark status text, a pair that does not depend on the page's background
|
||||
(dark themes keep light-mode emphasis colours)."""
|
||||
|
||||
from unittest.mock import PropertyMock, patch
|
||||
|
||||
|
|
@ -9,7 +9,6 @@ import libvirt
|
|||
from computes.models import Compute
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase
|
||||
from networks.tests.driver import without_inactive_flag
|
||||
from django.urls import reverse
|
||||
from lxml import html
|
||||
|
||||
|
|
@ -18,7 +17,6 @@ class StatusContrastTests(TestCase):
|
|||
def setUp(self):
|
||||
self.conn = libvirt.open("test:///default")
|
||||
self.addCleanup(self.conn.close)
|
||||
without_inactive_flag(self)
|
||||
self.compute = Compute.objects.create(name="contrast", hostname="localhost", login="", password="", type=4)
|
||||
self.client.force_login(get_user_model().objects.create_superuser("contrast", "c@example.com", "pw"))
|
||||
|
||||
|
|
@ -29,7 +27,7 @@ class StatusContrastTests(TestCase):
|
|||
return html.fromstring(response.content)
|
||||
|
||||
def test_card_headers_are_pale_with_dark_text(self):
|
||||
for name in ("networks", "interfaces"):
|
||||
for name in ("interfaces",):
|
||||
with self.subTest(page=name):
|
||||
headers = self.page(name).xpath("//div[contains(@class, 'card-header')]")
|
||||
self.assertTrue(headers)
|
||||
|
|
@ -40,11 +38,10 @@ class StatusContrastTests(TestCase):
|
|||
self.assertTrue({"text-success-emphasis", "text-danger-emphasis"} & set(link), link)
|
||||
|
||||
def test_status_words_are_pale_badges_with_dark_text(self):
|
||||
status = self.page("network", "default").xpath("//span[normalize-space()='ON' or normalize-space()='OFF']")
|
||||
# the test driver has no node memory statistics for the host row
|
||||
with patch.object(Compute, "ram_usage", new_callable=PropertyMock, return_value=1):
|
||||
status += self.page("instances:index", compute=False).xpath("//span[normalize-space()='Connected']")
|
||||
self.assertEqual(len(status), 3)
|
||||
status = self.page("instances:index", compute=False).xpath("//span[normalize-space()='Connected']")
|
||||
self.assertEqual(len(status), 1)
|
||||
for span in status:
|
||||
self.assertIn(span.get("class"), ("badge bg-success-subtle text-success-emphasis",
|
||||
"badge bg-danger-subtle text-danger-emphasis"))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue