The host pages lose their last inline scripts: the overview's charts
(host_graphs.js), the New pool, New network and New interface dialogs
(pool_dialog.js, network_dialog.js, interface_dialog.js), a network's
and a network filter's XML editors and dialogs (network_page.js,
nwfilter_page.js, nwfilters_page.js), the secrets page's labels
(secrets_page.js, its texts from the view as usage_texts) and a pool's
page with the ISO upload (storage_page.js). Their URLs and messages are
data attributes of the elements they belong to; the upload reads its
token and address from its form. No template has an inline script or
an inline event handler any more.
The host pages' dialogs (new network, fixed address, DHCP range,
bandwidth limit, new interface, new filter, filter rule, filter clone,
new storage pool, new secret, a secret's value) and the VM page's XML
review closed with a grey filled Cancel beside their action; the
redone dialogs use an outlined Cancel, so the action is the one filled
button.
As the storage and network pages; plan reviewed with Codex.
- The list is grouped by type (bridges, bonds, VLANs, ethernet; a type
the host has none of is left out), three cards a row, with a search
of their names, MACs and networks (table_filter.js, which now takes
any element with data-search as a row); a card per interface: its link
as the kernel reports it (up green, down grey, another state or
unknown told as such, no red card for a link that is merely down),
MAC, MTU, speed when known, the members it reports and the libvirt
networks that use it, linked.
- An interface's page: an Interface card (type, libvirt's own state
where it manages interfaces, link, start mode, MAC, MTU, speed, STP,
networks using it); on a host whose libvirt changes interfaces, a
card per family with how it is addressed and its addresses; the
members of a bridge, bond or VLAN with their link and speed, or that
none is reported; for a bridge, the running VMs' cards on it,
directly or through a network using it, with their tap and MAC,
linked (wvmInterface.get_vms: one domain listing). Not the bridge's
delay: netcf tells it in seconds, the udev backend in hundredths.
Start or Stop in its heading, Delete in its menu, disabled while it
runs, on hosts that can change interfaces only.
- A host whose libvirt only reads interfaces says so once, with that
it does not tell their addresses, instead of "None" on every page.
- The New interface dialog is one form, as the New pool and New
network ones (choice_dialog.js, radio_card.html): Bridge or
Ethernet, a bridge's member from the devices it can take, an
ethernet interface's device from the ones the host does not define
yet, a static address's fields with its choice; a refused interface,
or one libvirt refuses, opens it again with what was entered; a made
one opens its page.
- networks_by_device maps the libvirt networks to their bridges or
interfaces on the page's own connection.
An accessibility scan with every modal opened found 49 fields without a
name: the labels of the instance create forms (custom, template and each
flavor's modal), the interface, network, secret and storage pool modals
did not point at their fields. Each label now has a for= and its field an
id, prefixed per form (and per flavor), so a click on the label focuses
the field too. The "Advanced" collapse buttons and the memory and
processing bars are named.
The VCPU config select had id="vcpu_mode" in each of these forms, nine
times on the create page; the unique-ID test did not see it as the test
driver reports no CPU modes. The id goes (nothing used it), and the test
now renders that branch.
An accessibility scan (axe-core, WCAG 2 A/AA) of every page found 87
elements without a name. Icon-only buttons and links (the navbar's
administration menu on every page, the add/create buttons, the flavor
delete and back buttons) get a title, which is also their tooltip; the
labels of the language, architecture, chipset and filter selects point
at them; the memory bar is named. The shared delete confirmation page
had an empty <title> and heading, as no view passes "title": it says
"Delete <object>". The power-off form's aria-label was misspelled.
Rendered through every URL on libvirt's test driver, the pages had
duplicate IDs and markup browsers repaired silently:
- Each flavor modal's "Advanced" button toggled #flav-add-collapse, the
first modal's section; every modal now has its own. The flavor and
template copies of the custom tab's control IDs, and the template
tab's unused images input, are gone.
- The VM page's action icons and the XML editors were styled by
repeated IDs; they use classes.
- Modal forms opened inside modal-body and closed after modal-footer;
they wrap both. Stray end tags and duplicate attributes are removed,
paragraphs no longer hold modals or rows, table heads have a row, and
the create page closes its row and column.
- html lang is the active language instead of "en".
A test walks every URL as a superuser and fails on a repeated ID. The
console page still has no doctype: its full-height layout relies on
quirks mode and needs its own check.
The IPv4 address and gateway were required even for DHCP or no IPv4, so
such an interface could not be created; malformed addresses passed and a
missing prefix crashed the XML builder. They are validated like the IPv6
fields now (netmask notation accepted, IPv6 scope ids rejected), and a
static address without a gateway gets no empty route. Interface names
follow the Linux rule (up to 15 letters, digits, . _ -). A bridge member,
or the device an ethernet interface configures, must be a device of the
host (the page no longer falls back to made-up names), a bridge cannot
contain itself, and needs STP and a non-negative delay.
The form checked both with re.match("^[0-9a-f./:]+|^$"), which tests
only how a value starts: "2001:db8::10/64'/><evil/>" passed and went
into the interface XML, which is built with f-strings, while upper-case
hex was refused. An address without a prefix passed too and then failed
with a ValueError when the XML was built.
The address is parsed with ipaddress.IPv6Interface and stored as
address/prefix (/128 when none is given), the gateway with
IPv6Address. A static configuration needs an address, and the XML gets
a route only when a gateway is given instead of gateway=''. The HTML
patterns accept upper-case hex.
Eight of the ten form and XML checks fail on the previous code.