mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
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.
46 lines
2.1 KiB
Python
46 lines
2.1 KiB
Python
"""The network page's fixed-address dialog takes real MAC addresses and host
|
|
names: the browser refuses what its pattern attributes do not match. Runs
|
|
on a private libvirt test driver."""
|
|
|
|
import re
|
|
import tempfile
|
|
from unittest.mock import patch
|
|
|
|
import libvirt
|
|
from computes.models import Compute
|
|
from django.contrib.auth import get_user_model
|
|
from django.test import TestCase
|
|
from django.urls import reverse
|
|
from lxml import html
|
|
from networks.tests.driver import without_inactive_flag
|
|
|
|
NODE = """<node><network><name>lab</name><bridge name='virbr9'/>
|
|
<ip address='192.0.2.1' netmask='255.255.255.0'><dhcp><range start='192.0.2.10' end='192.0.2.20'/></dhcp></ip>
|
|
</network></node>"""
|
|
|
|
|
|
class FixedAddressFormTests(TestCase):
|
|
def test_macs_and_host_names_are_accepted(self):
|
|
node = tempfile.NamedTemporaryFile("w", suffix=".xml")
|
|
self.addCleanup(node.close)
|
|
node.write(NODE)
|
|
node.flush()
|
|
conn = libvirt.open(f"test://{node.name}")
|
|
self.addCleanup(conn.close)
|
|
without_inactive_flag(self)
|
|
compute = Compute.objects.create(name="fixed", hostname="localhost", login="", password="", type=4)
|
|
self.client.force_login(get_user_model().objects.create_superuser("fixed", "fixed@example.com", "pw"))
|
|
with patch("vrtManager.connection.connection_manager.get_connection", return_value=conn):
|
|
page = html.fromstring(self.client.get(reverse("network", args=[compute.id, "lab"])).content)
|
|
dialog = page.get_element_by_id("FixedAddress")
|
|
mac = re.compile(dialog.xpath(".//input[@name='mac']/@pattern")[0])
|
|
name = re.compile(dialog.xpath(".//input[@name='name']/@pattern")[0])
|
|
for value in ("52:54:00:ab:cd:ef", "52:54:00:AB:CD:EF", "0:1:0:1:2a:3b"):
|
|
with self.subTest(mac=value):
|
|
self.assertTrue(mac.fullmatch(value))
|
|
for value in ("web-01", "db.lan", "vm_1"):
|
|
with self.subTest(name=value):
|
|
self.assertTrue(name.fullmatch(value))
|
|
for value in ("a b", 'x"<'):
|
|
with self.subTest(refused=value):
|
|
self.assertFalse(name.fullmatch(value))
|