The Boot pane shows Autostart and the boot menu as switches that save on
change, and the boot order as one list of the VM's disks, CD-ROM drives
and NICs: the devices in the order first, checked and numbered, the
others below. Up and Down move the checked devices; Apply order saves.
- The boot menu can be changed while the VM runs: it is in the
persistent definition, so it applies at the next start, as the boot
order does.
- The boot order editor no longer needs the boot menu enabled.
- A VM that boots by device type (<os><boot dev='hd'/>) names those
types; checking devices replaces that order.
- An order is refused while a device the list does not show (a host
device, a floppy) has a boot place, so Apply does not drop it; the
message points to the XML editor.
- An empty order is refused with a message instead of being ignored.
- get_bootorder gives a NIC's full MAC as its target.
- The unused jQuery multiselect plugin is removed.
Settings > Network was a table whose fields were read-only inputs cut
off at phone width, an unlabelled "+", three icon buttons per NIC and
a separate QoS table keyed by MAC. The owner chose card layout from
three rendered options, with the QoS limits folding out like the
device details:
- A card per NIC: a VM's only NIC spans the row, two or more sit side
by side from md. Each has its name and target, the link state as a
switch, its source and IPv4 addresses, and "Device details"
(type, MAC, model, NWFilter, IPv6) in a <details>.
- "QoS limits" in a second <details>, its summary naming the limited
directions: a form per inbound or outbound limit, with Save and
Delete limit, and the units.
- Labelled Edit, Add QoS and Delete in the card footer; "Add network
device" by the heading. The edit and add-QoS dialogs are titled and
labelled, and the edit dialog's fields have labels.
The limits come from each NIC of the persistent definition, so the QoS
table, Instance.config_qos and wvmInstance.get_all_qos are removed.
Settings > Disk was a bold title with an unlabelled "+", a CD-ROM row
in an input group, and a table whose actions were four alike icons.
The owner chose this layout from three rendered options:
- Removable media: each CD-ROM drive a card with its device, bus and
image, the ISO select and Mount (or Unmount), and Detach; "Add
CD-ROM" by the heading.
- Disks: a card each; one disk spans the row, two or more sit side by
side from md, and the device details are capped at 32rem so values
stay near their labels. The capacity large,
the used size, the pool and the source path, the device's settings
under "Device details" (they were in a popover), and labelled Edit,
Move, Detach and Delete (danger) in its footer; "Add disk" by the
heading.
- While the VM runs, "Add CD-ROM" adds the drive on the SCSI bus, and
a SCSI drive can be detached: IDE and SATA drives cannot be plugged
into or out of a running VM, SCSI ones can. Adding a drive was
disabled while the VM ran. One line says so, and why detaching other
drives and detaching or deleting disks waits for a shutdown, instead
of each button alone being greyed.
The forms, their fields and actions are unchanged. The edit dialog of each
disk is titled with its device, and its fields' ids carry the disk's
number: the dialogs of two disks had the same ids, and the title was not
the dialog's label.
The top of a VM's page was one line of values between pipes, an icon for
the guest agent and a hidden snapshot count, then a row of big action
icons; the host links sat in a bar fixed to the bottom of the window,
over the content.
One card now holds it all (design chosen by the owner from four):
- the name with the state badge, the title under it, and where the VM
is: its host and the host's VM list for administrators, the VM list
for owners;
- OS details (with a connected guest agent) and Refresh;
- the guest agent's state in words and the snapshot count;
- CPU, memory, each disk with its device name and size, and the IPv4
addresses, labelled;
- the page's sections as small labelled pills in the card's footer: one
row from 992 px, wrapping below.
The page has no other heading: base.html's heading area is a block it
leaves out.
Each section below it (Power, Access, Resize, Snapshot, Settings, Stats,
Destroy) is a card of the same kind, so the page reads as one design
(owner's choice of two): its sub-tabs as a small underlined nav in the
card's header, its panes in the card's body without their own borders.
Buttons are of normal size in the theme's primary colour, and those that
cannot be undone (Power Cycle, Force Off, Destroy) in the danger colour.
The Stats charts lose their coloured borders. instance_tabs.js finds the
Settings tabs by their tablist role.
The bottom bar and its setting (VIEW_INSTANCE_DETAIL_BOTTOM_BAR) are
removed, and with it the CSS that kept the old Settings tabs on one line.
Queuing a long operation put "... is being ...; follow it in the job
panel." above the page, while the job panel on the same page showed the
job and its progress. Every such form returns to a page with the job
panel for the job's resource (the VM, the compute's VM list, the pool),
and the panel shows a job even when it ends before its first poll. The
eight messages are removed: migrate, clone, create, volume clone and the
snapshot create, revert and delete jobs. Refusals stay messages.
The Disks tab has a Move button for each disk: it opens a dialog to
choose another storage pool of the same host, and the move runs as a job.
- Running or paused VM: libvirt copies the disk with a block job
(TRANSIENT_JOB, as the VM is persistent) into a file it creates in the
pool, then pivots. The pivot names the copy in both the running and
the next start's definition.
- Shut-off VM: the volume is copied into the pool and the copy named in
the definition.
The source file goes then, unless a VM or volume of any compute uses it
(shared storage holds it for every host that mounts it), a compute
cannot be read, or the delete fails: the job lists such a file under
"Left behind", and the move stands.
A disk moves when:
- the VM is defined and running, paused or shut off, without snapshots,
a saved state or a block job;
- the disk is a writable, unshared, unencrypted raw or qcow2 file of a
storage pool, without a backing file, and the same file in both
definitions;
- the pool keeps disk files, is another directory, and has no file of
that name (libvirt would write into it).
Otherwise it is refused with the reason before anything changes.
The job claims the VM and the file's name in both pools. A cancel aborts
the block copy, and ends the job cancelled once the VM is back on its
file. A failed move deletes the copy it made, unless a definition names
it (a lost reply after the pivot); a file there before the copy began is
never deleted. Recovery after a stopped worker
ends a copy left mirroring, matched by its destination, and reads the
outcome from the definitions.
Superusers only, like the other disk edits.
A migration needed every disk on storage both hosts share: it was refused
when the destination lacked a disk file, and a file of the same name
there was taken for the VM's own disk.
The Migrate tab now lists the VM's disk files for the chosen destination.
For each disk it offers the destination's pools that keep disk files
(directory, filesystem and NFS pools). A disk on storage both hosts share
can also stay where it is.
A directory counts as shared when both hosts have it as a storage pool
and a marker file created on the source exists on the destination. A
directory the probe cannot tell is never taken as shared.
libvirt copies the chosen disks during the migration: migrate3 with
NON_SHARED_DISK for those targets, and destination and persistent
definitions naming the new paths, where libvirt creates the files.
- A shut-off VM is started paused for the copy and shut off on the
destination; its CPUs never run. Its UEFI variables are copied first,
as the destination would start from the firmware's template. If the
migration fails, the VM is shut off on the source again.
- Refused before anything changes, each disk with its reason:
- a disk neither shared nor given a pool, also one only the next
start's definition has;
- CD-ROM images, read-only or encrypted disks, formats other than raw
and qcow2, and disks with a backing file;
- a pool that is not of disk files, or that has a file of that name,
which libvirt would reuse and overwrite;
- two disks copied to one file.
- The job claims each copy's name in its pool, and the source volume.
- A failed or cancelled migration deletes the copies libvirt made, and
the UEFI variables copy, unless a VM on the destination has them.
- The source files stay and are named in the log. With "Delete the
source files of copied disks" they go, except one another VM or volume
uses; a failed delete is logged and the migration stands.
The NVRAM sharing check uses the same probe: a shut-off move stops when
it cannot be told, and the source file stays.
The API's migrate call takes disk_pools ({target: pool}) and
delete_source.
The 403, 404 and 500 pages said "Oops!" and one generic sentence; a bad
request and a failed CSRF check fell back to Django's plain pages.
Every error page now shares templates/error.html: a card with the status
in a coloured badge with an icon, a heading in plain words, what happened
and what to do, and Back and Home buttons. It uses only theme-defined
Bootstrap classes, so it reads in the light and dark themes alike.
- 400: the request was not accepted; a new address belongs in ALLOWED_HOSTS.
- 403: the account lacks the permission; ask an administrator.
- 403 CSRF: the form's security check failed; reload and submit again,
and where to look when it happens on every form.
- 404: the path, and that the object may be gone or hidden from the account.
- 500: a libvirt failure on a page says the host's data could not be
loaded, under libvirt's message; any other failure says it was logged
and shows the time to give an administrator.
- With the Host devices tab, a superuser's ten settings tabs took two lines
in 22 of the 27 themes (up to 166 px short in Darkly): the themes' tab
font sizes and spacing differ. The tabs take one font size and the same
side spacing in every theme; the Users tab no longer has narrower spacing
than the others.
- Measured in the 27 themes at 992, 1200 and 1440 px: one line in each,
with at least 150 px to spare, room for the guest agent's info tab too.
- The host navigation takes one font size and spaces its dividers alike on
both sides in every theme: Zephyr doubled the space after each divider,
and with the Devices section its eight sections took two lines.
- Its labels are shorter: Storage, Filters.
- Measured in the 27 themes at 992, 1200 and 1440 px: one line in each,
with at least 73 px to spare (Lux had 20 px before, Zephyr was 13 px
short).
The USB records named a throwaway loop variable `_`, the module's gettext.
Python 3.12.3, the Docker image's, inlines comprehensions so that this made
`_` an unbound local of the function, and the class labels' `_("Other")` in
the next comprehension raised UnboundLocalError: every Devices page failed.
Later 3.12 releases do not; the loop variables are now named.
- A running VM gets a PCI device of its host at once and for its next
start, in one libvirt call; a hot plug libvirt could not keep for the
next start says that the device is in the running VM only. A q35 VM
without a free pcie-root-port gets libvirt's error and where a slot
comes from.
- A PCI device is taken from the running VM and from its next start, each
as it has it; a refusal by the guest stops there. When the guest has
not let the device go within libvirt's wait, the page says the removal
is requested and the Logs row records the request: the device stays in
the running VM until the guest lets it go or the VM stops.
- A paused VM, or one with a saved state, does not change its PCI devices.
- A device for a running VM is checked alone too, as libvirt takes only
the hot-plugged device from its host driver.
- The Host devices tab gives a shut-off VM a PCI device of its host and
takes it back; libvirt binds the device to vfio-pci when the VM starts
and gives it back when it stops (managed). The VM's PCI devices are
listed with whether the running VM and its next start have them.
- A device is offered, and accepted on the server under the compute's
claim, only when it is no bridge of any kind (bridge header, PCIe port,
or of the bridge class: host bridge, LPC), the host has none of its own
network interfaces, disks or graphics card on it (else: bind it to
vfio-pci on the host first), no other VM names it, and every other
endpoint of its IOMMU group is the VM's too or has no driver or
vfio-pci and no other VM; bridges in the group stay with pcieport, and
a member that cannot be read keeps the group. Nothing in the group is
taken implicitly.
- A snapshot revert checks the PCI devices of the snapshot with the same
rules; migration and clones already leave host devices out.
- The add forms of the tab load together, when the tab opens.
- A Host devices tab on the VM page lists the VM's USB host devices, each
once: how it is chosen (its address, or any device of its model), the
device the running VM holds and whether its next start has it; its other
host devices (PCI, SCSI, mediated) are listed for the XML editor.
- Superusers add a USB device of the host and take it back, on a running
VM too. The add form is loaded when the tab opens, as its choices read
every VM of the host; a hub, a device the host has its own disks or
network interfaces on, or one another VM names is not offered, with the
reason, and refused on the server under the compute's claim. A device is
chosen by model when the host has one of it, else by its address.
- A VM without a USB controller is refused; a device is taken from each
definition as that definition describes it.
- A clone gets none of the VM's host devices (the clone form says so), and
a VM with host devices is not migrated. A snapshot revert is refused when
it would give the VM a device another VM names or the host uses.
- A NIC that is a host device is no longer edited or removed from the
Network tab: the XML editor changes it.
- A Devices section of each host lists its PCI devices by IOMMU group and
its USB devices: what each is (vendor, product, IDs, class, driver), what
the host itself has on it (its network interfaces, disks and graphics
card, anywhere below it) and the VMs whose definitions name it.
- A VM names a PCI device by its address, a USB device by its address or
by its model only; a VM that names a model only is shown on every device
of that model, which one it takes is known only when it starts.
- A host without IOMMU groups says that no PCI device can be given to a VM
and how to turn the IOMMU on.
- One listing of the host's node devices, one XML read each (a device gone
meanwhile is left out), and the VMs' definitions read once; nothing is
shown when they cannot be read, as a device would look free.
- Read only: giving devices to VMs comes in the next layers.
- A DHCP leases card on the page of a network that serves DHCP: each
lease's address, MAC or an IPv6 client's DUID, the host name the client
sent, the VM whose card has that MAC, linked, and when it ends.
- A lease that is a fixed address is marked; any other in one of the
network's DHCP subnets has a Make fixed button that fills in the fixed
address dialog with its address, MAC or DUID and name.
- Leases that cannot be read are said on the card; the rest of the page
stays. A network that does not run has none.
- The VMs on a network are read with the MACs of their cards on it, from
the definitions already read.
- A named CPU model is written without check='none' and with
fallback='forbid': libvirt checks before the start that the host runs
it, and it is not swapped for another model.
- Mode maximum is written as that mode; it was written as a model named
maximum.
- Model names up to 64 characters: the -noTSX and -IBRS models, the ones a
mixed set of hosts needs, were refused at 20.
- The mode or model is checked against the host's capabilities when the VM
is defined, from the page and the API alike; no CPU element (QEMU's
oldest default CPU) is no longer offered.
- The default CPU mode setting offers the modes the new VM dialog has;
no-model and custom, which matched none of them, become host-model.
- One searchable card: each secret's type, usage and UUID, whether it is
kept on disk or in memory and private, and the VMs and pools that use it,
linked. A row menu sets its value or deletes it; Delete is disabled while
the secret is in use, and its confirmation says what is not checked.
- One Set value dialog for every secret; its label and help follow the
type (Ceph key, CHAP password, passphrase), and it says a new passphrase
does not encrypt anew what the old one encrypted.
- New secret dialog: the usage's and value's labels and help follow the
type; the value is optional; Private and Ephemeral are switches with help.
- A secret is in use when a VM names it in a disk, a host device or its
vTPM, or a pool in its source, by UUID or by its usage within the usage
type, as they run and as saved. The vTPM, host devices and pools' saved
definitions were not checked before.
- A Ceph key is taken in base64, as `ceph auth get-key` prints it; any other
value is a password or passphrase stored as typed, spaces included.
- A new secret takes its value at once; a value refused leaves no secret.
- TLS and vTPM secrets can be created; the usage has no length limit.
- Every POST redirects; the list is read once and only with its users; a
secret, VM or pool gone meanwhile is left out.
- domain_trees() is shared by the in-use checks of secrets, network filters,
networks and pools; the storage page's Ceph secret picker reads the same
listing.
As the network and interface pages; plan reviewed with Codex.
- The list is one table with a search: each filter's chain, priority,
number of rules, the filters it references, and how many VMs and
filters use it directly; a row's menu clones it (one shared dialog,
the copy's name filled in) or deletes it, Delete disabled and saying
so while it is in use. Without the VMs (a host that fails to list
them) the list is not shown: it would offer to delete filters in use.
- New filter is one dialog with the filter as XML in the Ace editor,
from a starter filter, with what in, out and priority mean and a link
to libvirt's format.
- A filter's page: a Filter card (UUID, chain, priority, the VMs that
use it directly, linked, and the filters that reference it, linked);
its references with their parameters (one to a filter gone from the
host marked as missing, not linked), removed per row, added from a
select of the filters it can reference; its rules with the action as
a badge, the direction as to or from the VM, the priority (500 where
none is given), every protocol element with its attributes, a rule
of no protocol as any traffic of the chain, and when it does not
track connections; Add rule as an XML dialog; the XML, collapsed,
saved in place.
- The editors size themselves when shown and keep their label for
screen readers; tables keep their hidden
header text inside them, so a phone does not scroll the page
sideways. add_nwf_rule.html goes (the dialog is on the page).
Plan reviewed with Codex; libvirt's behaviour checked on a host.
- A filter is not deleted while a VM's interface references it, as the
VM runs or as saved for its next boot (a stopped VM too), or while
another filter references it: libvirt refuses only one a running VM
uses, and a VM using a filter that references a deleted one does not
start. The VMs are read from one listing, one or two XML reads each,
instead of a connection per VM; the refusal names them.
- Create refuses a name or UUID the host has, clone a name in use or
one outside the panel's names, and editing the XML must keep the
filter's name and UUID: each would replace another filter or make a
copy. Entered XML is read for these checks without expanding entities
or taking a DTD (util.parse_xml, shared with the network editor),
and a rule is one <rule> element.
- A rule or reference is deleted only while it reads as the page showed
it, not by index and three attributes: a rule alike in those or a
filter changed meanwhile loses nothing.
- A reference is added only to another filter of the host not
referenced yet; the picker offers those.
- References keep their parameters, in order (a name may repeat); a
rule shows every protocol element it has, a rule without one too.
- One change a POST (two would both start from the page's reading,
the second undoing the first); every change redirects and reports its
refusal as a message, a POST for a filter gone meanwhile going to the
list; the catch-all that showed any error goes, and so does
wvmInstance.get_filterrefs, which only the old delete check used. The list reads each filter's
XML once from one listing; a filter's page reads its XML once and
only the other filters' names; the per-filter XML dialog of the list
goes (a filter's page shows its XML). The XML editors send their text
untouched too.
The label and value rows of the host pages' cards (Overview, storage
pools, networks, interfaces) take Bootstrap's py-1 instead of the list
group's own padding, as the interface list's cards already do: about
half the height a row, the same on every page. A pool's space bar keeps
its padding.
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.
Plan reviewed with Codex; tried on a udev host (AlmaLinux 10) and a
netcf one (AlmaLinux 8).
- Changes are one change of the host's configuration (libvirt's
interface change transaction): a bridge that does not start used to
be undefined, leaving its member without the addresses and start
mode the bridge took from it; now the whole change rolls back, the
member's own configuration too, and so does a commit libvirt refuses.
A rollback that fails too is reported with the failure, saying the
host's configuration may be changed.
Deleting a bridge, which defines its
members again on their own, is one change as well.
- A running interface is not deleted: the page hid Delete, but a post
deleted it. A bridge with addresses and several members is refused
(which member takes the addresses is the user's choice); the
confirmation says that its members are defined again.
- A bridge takes only a device in no bridge, bond or VLAN as the host
has them configured, at any depth (a bridge's bond's devices), not
the loopback, a libvirt network's bridge or a VM's tap; an ethernet
interface configures only such a device too.
- Whether the host can change interfaces: unsupported or refused by
its access rules is a no, bad XML a yes, anything else (the
connection, the login) is raised instead of taken for a yes. A page
whose host could not be read offers no actions.
- Reads: the list reads each interface's XML once from one listing
(listAllInterfaces: every state, not the active and the defined ones
apart); a page reads the running and the saved definition once each
(cached_property). netcf writes a link speed of -1 for a card that
has none (a virtio one) and then refuses its own XML: the saved one
is read instead, where virsh and the page failed. Name and MAC come
from libvirt's object, without a call. Each family says how it is
addressed (DHCP, autoconf, static or none) and every address, its
prefix where it has one; no
address is not "DHCP" any more. A bridge's members carry their link
state and speed; an unknown speed (0, -1, 4294967295) is none.
- The interface API lists from the same reader. get_details,
get_bridge, get_bridge_slave_ifaces and the per-family getters go.
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.
Reviewed with Codex against libvirt's network format and driver.
- Addresses: wvmNetwork found a network's <ip> by its text, so one
written as <ip .../> or <ip><dhcp>... without whitespace had none,
and a second address of a family overwrote the first. ip_networks
reads every <ip> in order, its index being libvirt's parentIndex:
family (ipv4 when written), address, subnet, DHCP (a <dhcp>, also
with fixed addresses only), first range and fixed addresses. The
parentIndex was guessed from the number of families, wrong when IPv6
comes first; changes now name the address by its index.
- One read: the page read the network's XML once per getter, ten or
more round trips; it reads it once (a cached_property of the
request's wvmNetwork). A change starts from the saved definition
read anew (VIR_NETWORK_XML_INACTIVE), not the running one: two QoS
changes on a running network no longer undo the first. The XML
editor shows and changes the saved definition, and defines this
network only (its name, and its UUID if given), read for that with
no entity expanded and handed to libvirt as written; an XML declaring
its encoding is taken. The actions go back to the network's page by
its own URL.
- Fixed addresses are found by the address they had (a hidden field),
not by the editable box: changing it and pressing Delete deleted
another entry or none. A change deletes the entry and adds it anew
from a copy, keeping what the page does not show (a client id, a
lease), and puts it back if the add fails: libvirt's own change
finds an entry by any of its MAC, address or name, so it could
replace another entry, and never by an IPv6 DUID. The address must
be of the subnet, not the host's, and neither it, the MAC or DUID
nor the name another entry's; an entry gone meanwhile is refused,
not added anew; an unchanged one writes no audit row. They can be
added while the network is stopped; a running network saved with
other addresses than it runs with is not changed live, as
parentIndex would name another <ip> of it.
- An address without a netmask or prefix takes libvirt's (IPv4 its
class's, IPv6 /64), for its checks and the overlap check.
- The DHCP range must be in the subnet, in order and not take in the
host's address.
- The create form takes a known mode only ("bogus" made an isolated
network, a missing one a server error); NAT and routed networks need
an IPv4 subnet, an isolated one may have none, a bridge or macvtap
network needs its bridge or interface and gets no addresses; IPv6
only with its box (whose value was "false", so never); no name or
subnet of another network of the host. "Fixed Addresses" at create
goes: it made a random-MAC entry for every address of the range,
for no real VM.
- Refusals of a fixed address, the DHCP range, the XML and a QoS
removal are messages, not server errors.
- The IPv6 forwarding of a bridge or macvtap network is the outside
network's, not ROUTE.
- get_networks_info reads each network's XML once and gives its
autostart and addresses too. Unused can_pxe, DHCP lease helpers,
get_uuid and the per-family range and host getters go.
wvmStorage.update_volumes looked each volume up by name three times,
read its info twice and its XML once: six round trips per volume to
the host. It now lists the pool's volumes as objects (listAllVolumes)
and reads each one's XML only (volume_details): name, capacity,
allocation and format are all in it, and libvirt refreshes a volume
for its XML as it does for info(). A pool of N volumes takes 2 + N
calls instead of 2 + 6N; 100 volumes over SSH on a test host read in
about 110 ms instead of 720 ms, with the same values.
- A volume deleted between the listing and its read is left out; any
other error is raised. Volumes come sorted by name.
- get_volume_details (the API's one volume) reads it the same way.
- Only a refused refresh (a clone running in the pool) is tolerated,
not any exception.
- The pool page reads its volumes and space only to render itself;
its actions, which all redirect, no longer read every volume first.
- get_volume_size and get_volume_allocation go: nothing else used them.
- The pool list is a card per pool, two to a row, as the host
Overview's Hardware and Software cards: its name with Active or
Stopped and Autostart badges in the header, then its type, its path
(an RBD pool's Ceph pool), its volume count and its space as the
Overview shows it (computes/pool_bar.html, without the name again);
a stopped pool's space is unavailable, not zero.
wvmStorages.get_storages_info gives the path, autostart and percent
used, which computes.views.host_pools computed for itself.
- A pool's page has a Space card (percent, used of size, of what: the
filesystem of its path, its volume group or the Ceph cluster, which
other pools may share; free) and a Pool card (state, autostart,
type, path) instead of a two-column list mixing facts and forms.
Start or Stop is its heading's button; autostart and deleting a
stopped pool are in its menu; the confirmations stay.
- The volume table drops the row number; a volume's capacity and what
it has allocated share a column hidden on a phone; its format is a
badge; its actions are one group, Clone and a menu with Delete,
named for the volume.
- "New pool", "New volume" and "Upload ISO" are labelled buttons
instead of bare green pluses; their dialogs' titles name them.
- The New pool dialog is one form (design reviewed with Codex): the
pool's type as radio cards, as the VM create page chooses its disk
source (their style moves to webvirtcloud.css), one name, and only
the chosen type's fields, the others hidden and disabled.
- A directory's or a share's mount path follows the name
(/var/lib/libvirt/<name>) until it is edited: no default path of
another pool any more.
- ISO images is a directory preset named iso; when the host has an
iso pool, a card links to it instead.
- Ceph offers the host's Ceph secrets by their names, not every
secret by UUID; without one it says so and links the Secrets page.
- LVM asks to agree that the device is erased, the server too.
- AddStgPool checks each type's fields, a name or a path another
pool has, an RBD secret that is not one of the host's Ceph secrets,
and the names "." and "..". A refused pool, or one libvirt refuses,
opens the dialog again with what was entered, the reasons beside
the fields and focus on the first; the audit row for a taken name
stays.
- The page lists the volumes first: that refreshes the pool, and libvirt
measures a pool's space only when it is refreshed, so the size and
usage shown were as of the previous refresh.
- A space that cannot be read says so instead of 0 bytes, and the
pool's status, read from the same pool info, no longer fails the
page after it; a stopped pool says its space and volumes are
unavailable until it is started, instead of 0 bytes and nothing.
- The API's pool is measured after the same refresh, and a stopped
pool lists no volumes there instead of failing.
- A clone the job refuses (a name in use, a volume gone) is a message
on the page, not a server error.
- An ISO image is recognised by its name (storages.jobs.is_iso):
libvirt calls its format raw, so the template's "iso" check never
disabled its Clone button. The clone job refuses one too; the upload
uses the same rule.
- One clone dialog serves every volume, filled and reset by the button
that opens it, instead of one dialog per volume in the page; its
fields have labels and its title names the dialog.
- Volume rows are searched by name only (data-search), with a message
when none matches, in a live region for screen readers; names show
in full. The search script loads only
with the volume table: on a stopped pool's page it failed for want
of a search box. An empty pool says "This pool has no volumes".
- The unused wvmStorage.get_volumes_details goes: update_volumes is
the same.
- The state filter is a row of pills, All and each state, counting the
VMs the search finds (the chosen state aside); a state without any is
hidden unless chosen (All never), and a status line tells screen
readers how many VMs match. Every state keeps its pill.
- A labelled "New VM" button replaces the bare green plus; "Find IP"
says "Find IP / MAC", which it finds.
- States are badges; a shut-off VM is grey, not red: it is not a fault.
- A VM's cell holds its name, its title, and for those who see owners
its first owner as a badge, "+N" for the others (named for screen
readers and on hover); the separate user column goes.
- Each row has the same button group for a superuser or the VM's
owners, from the actions the user may use (states.allowed_actions),
all of it disabled on a VM an administrator paused; view-only users
get none. It holds Console, the power action
the state calls for (Start, Resume, Shut down, or Clone for a
template) and a menu of the others (Suspend, Force off, Power cycle),
what does not apply now disabled. The power button is an icon,
named on hover and for screen readers, so every group is as wide and
they line up from row to row; the power cut confirmations stay. On a
phone Console shows its icon only too.
- A grouped host's row has its name, a Connected or Not connected
badge, its VM count and a labelled host memory bar; an unreachable
host's row is table-warning. Its collapse control and the one for
all hosts are buttons, with aria-expanded kept.
- vCPUs and memory hide on a phone in both layouts, and table cells
keep Bootstrap's own padding there: Zephyr doubles it and a list's
last column no longer fit the screen.
- The search read a row's whole text, so a word on a button or in a
state label matched every row. VM rows now carry data-search (the
name, title and owners, and the host in the flat list), and
table_filter.js searches that text when a row has it; the state has
its own filter.
- Nothing matching left an empty table. table_filter.js shows the
page's #no-match while no row does; the VM list's says so and offers
"Clear the filters". A user with no VMs at all is told so instead of
an empty table.
- The grouped list's "#" toggled each host on its own: a host
collapsed by hand opened while the others closed. It now collapses
every host, or opens them all once all are collapsed.
- One alert names every host the panel cannot reach, with why, in
place of one dismissible alert per host; users who see no hosts keep
their own message.
- A host's VM list showed a VM's maximum memory, the Instances page
its current memory: both show the current memory now.
- The owners' users are prefetched with the VMs; the flat list's
Bootstrap 4 font-italic is fst-italic.
- Totals above the list: hosts connected of all, VMs running of all,
vCPUs given on the CPUs, memory given of the memory of the hosts the
panel reads, as cards; the connected count is red while a host is
not.
- A host's name links its overview; its state is a Connected or Not
connected badge (the dot goes), beside a badge of how the panel
reaches it (TCP, SSH, TLS, Socket); its address in monospace.
- A VMs column: the host's VMs, linked to its Instances, and how many
run.
- The vCPUs have a bar like the memory's: running VMs' and shut-off
VMs' vCPUs against the CPUs, amber over 2:1 and red over 4:1 as the
ratio is; the overview's vCPU card shares it.
- The three coloured buttons of each row are one menu: Overview, Edit
connection and Remove from panel (its popper fixed, as the table's
scrolling box would cut it off).
- The four green add buttons are one "Add host" menu that says what
each kind of connection is.
- The table is table-hover; a host the panel cannot reach has a
table-warning row.
libvirt measures a pool's size and free space only when the pool is
refreshed and keeps those figures until the next refresh. The pool
lists read them without a refresh, so they showed a pool's space as of
whenever libvirt last refreshed it: two hosts sharing one pool's
storage told different free space for it, each its own refresh's. On a
test host, a 1 GiB file left libvirt's free space unchanged while df
showed 1 GiB less, until the pool was refreshed.
get_storages_info refreshes each active pool before reading it; a
refresh libvirt refuses (while a volume is cloned in the pool) leaves
the last figures, as the pool pages already do. A refresh scans the
pool's volumes, so pools are read only where one host's are shown: a
host's overview (views.host_pools, its Storage card) and its Storages
page and API. The Computes list and the new-VM dialog's host lines,
which read every host at once, no longer show pools; Compute.capacity
is the vCPUs and memory given.
- The VM list's power forms (style="display:inline") are d-inline.
- The fields scripts show and hide are d-none, and the scripts add and
remove that class instead of calling jQuery's show() and hide(),
which could not undo it: the custom memory boxes of the Resize tab,
the ISO upload's error message and the clone dialog's metadata row.
- The please-wait dialog's full bar is w-100.
- The create-VM pages' own 56rem width (.create-vm) goes: pages stop
at 960px now, about the same.
- static/css/font-awesome.min.css goes: no page loaded it, no element
used its classes and its fonts were never in the repository.
The console pages carried style blocks for what Bootstrap 5.3 has:
- the page's column (flex, no padding, a #494949 background) is
d-flex flex-column p-0 bg-dark; its height stays 100dvh inline, as
vh-100 would run under a phone browser's bar;
- the VNC screen and the serial terminal fill the rest with
flex-grow-1, the screen overflow-hidden, the terminal bg-black; their
min-height: 0 stays inline (Bootstrap has no class for it);
- the rule centring the VNC canvas goes: noVNC centres its own canvas
and draws its own background.
The unused main_container id goes with its style block.
static/css/signin.css (308 lines) gave the sign-in and two-factor
sign-in pages their own components: a card, input groups, a submit
button, a footer and a status dot, with gradients, a blurred card,
entry and pulse animations, and rules that undid base.html's layout
(its container, its padding) from a "signin-page" class the pages'
scripts put on the body.
The pages are Bootstrap now: a card in the grid's centre column, the
logo a rounded-3 bg-primary block, the fields Bootstrap input-groups
with the password toggle an outline button, the submit a w-100
btn-primary, the footer a border-top line with a rounded-circle dot.
They follow the selected Bootswatch theme like every other page.
signin.css, its body class and its decoration go; so does the footer's
"v2.0.0", a version the panel never had.
The sign-out page (logout.html) was never shown: LOGOUT_REDIRECT_URL
sends a signed-out user to the sign-in page. It goes, with the view's
template name.
Three upstream rules in webvirtcloud.css set every page's layout
against Bootstrap 5.3:
- .container { max-width: 900px } held each page to 900px at any
screen width, below Bootstrap's own widths. Pages now take
Bootstrap's container widths up to its lg one, 960px, and stop
there on wider screens: its xl and xxl widths (1140px, 1320px) leave
the panel's forms and lists too wide;
- .page-header (margins of 5px 5px 20px 20px) put headings 20px right
of the content under them; its 14 uses are mb-3, flush with the
content (one carried Bootstrap 4's mr-auto, now me-auto);
- p { margin: 10px } indented every paragraph 10px from the content
beside it; paragraphs take Bootstrap's own bottom margin.
webvirtcloud.css keeps only what Bootstrap has no utility for: the
page width cap, the XML editor's place and height, the Tom Select
colour fix, the sortable table headers' arrows, the grouped VM list's
collapsed hosts and the charts' height.
webvirtcloud.css still carried the upstream panel's rules for what
Bootstrap 5.3 has as utilities and components:
- the page's bottom space (body padding) is pb-5 on base.html's body,
and the console page no longer has to undo it;
- the VM page's action tabs (.action-button, .action-block with a
fixed 65px width) are text-center with a d-block icon; its tab panes
(#power ... #undefine, one rule each) mt-3;
- the bordered sub-tab panes (.tab-pane-bordered, a fixed #eee border
that dark themes could not change) border border-top-0 p-3;
- the search box and header buttons (.search, .btn-header) m-1 and
mt-1; the SSH key select (.keyselect) a form-select;
- the boot order list (.multipleselect, fixed size, #c0c0c0 border) a
list-group of form-check-input items; its unused .multipleselect-on
goes;
- table cells centred by a global rule are align-middle on each table.
Two rules against Bootstrap's behaviour go: menus opened on hover on
wide screens (Bootstrap opens them on a click, and keyboard and touch
users had no hover), and "a { outline: 0 }", which hid the keyboard
focus ring.
The paragraph and page-header margins and the 900px container stay
for a separate change: they set every page's layout.
The fork's own rules in webvirtcloud.css redid what Bootstrap 5.3 has:
- the capacity bars (.cap-*) are Bootstrap progress bars: the memory a
.progress-stacked of running VMs' memory and shut-off VMs' (at half
opacity), each segment a progressbar with its amount as its value
text; the pool bars a thin .progress. The line where the host's
memory ends goes: the share above the bar says it, red when over;
- the status dot (.compute-dot) is a rounded-circle, the big figures
(.compute-ratio) fs-4, the Computes table's cell padding py-3;
- the steps of creating a VM (.create-steps) are a breadcrumb with "›"
as its divider;
- the XML and Advanced disclosures (.disclosure with a turning chevron)
are Bootstrap accordions;
- the chosen volumes and networks (.row-list) are list-unstyled, their
names text-break;
- a chart's container is position-relative by utility; its height stays.
Dead rules go too: .netIN, .netOUT, .diskRD, .diskWR, .input-xs and
.bottom-bar-margin had no users.
The Instances page (grouped and not) and a host's Instances page read
the VMs without an order, so they showed in whatever order the database
returned them: insertion order on SQLite, any order on PostgreSQL,
where it changed between page loads and made a state test fail at
random in CI. They are now ordered by name.
The memory given read "24.0 GB given of 32.0 GB": long, and the share
of the host's memory, what one looks for, had to be worked out. It is
now told as the vCPUs are: the share large ("75 %", amber over 90 %,
red over 100 %), then "24.0 GB / 32.0 GB", the bar and "12.0 GB
running". The Computes list's memory column shares it.
Every card of the overview has a Bootstrap card-header with an icon:
VMs, vCPUs, Memory, Storage, CPU in use and Memory in use (with the
current reading at its right), Hardware, Software and connection. The
last two were label and value lists under indented headings; they are
list-group-flush rows now, the label at the left, the value at the
right: counts in semibold, the hypervisor's and libvirt's versions as
badges, the guest architectures as light badges, the emulator and the
connection as code. The section headings, indented by the panel's
page-header margin unlike the cards, are gone: each card names itself.
The status line and notes are not paragraphs, which the panel indents.
#241 made the host sections pills: on a desktop they took two lines,
unlike the breadcrumb the pages had. computes/host_nav.html is the
breadcrumb again, one line at 1200 px and wider, still shared by the
eleven host pages: the section's own page is its bold item with
aria-current="page", an item page (a pool, a network) links its
section in bold with aria-current="true".
The capacity lines were kept on one line, made for the Computes
table: in the overview's narrower cards a long memory line ran out of
its card. They now wrap.
A host the panel could not reach made its overview the 500 page,
without the host's sections, and the Computes list gave such a host an
Overview button that led nowhere. The page now keeps the host's name,
sections, "Edit connection" and "Remove from panel" (the existing
confirmation), and says "Not connected" with the reason and a "Try
again". Details that cannot be read are said in an alert; the VMs,
capacity and load still show, as they are read on their own.
A reachable host's page leads with what it runs and what it gave:
- its VMs by state (running, shut off, paused, other), linked to
Instances;
- the vCPUs, memory and storage pools given, as on the Computes list,
which now shares those parts (computes/capacity_*.html) and their
preparation (views.shown_capacity) with it;
- CPU and memory in use, side by side, with the current reading in
words, a note on what the charts hold and when they were last
updated, or since when not; memory in GB like the rest of the page;
- Hardware and Software and connection: logical CPUs and clock from
virNodeGetInfo, but physical cores, sockets and NUMA nodes counted
from the CPUs of the capabilities' NUMA cells, as virNodeGetInfo
makes a topology up for hosts it cannot describe (asymmetric NUMA);
the hypervisor's own name and version, libvirt, emulator, the guest
architectures as text instead of menus of dead links. A host without
sysinfo shows no processor instead of failing.
Empty notes no longer show "None"; labels stack over their values on a
phone instead of running into them.
Pools that cannot be read no longer drop a host's vCPUs and memory from
its capacity: the storage part says so. get_capacity also counts the
VMs by state.
Eleven host pages (overview, instances, storages, networks, interfaces,
nwfilters, secrets and their item pages) each carried their own copy of
the links to the host's sections, as a breadcrumb: the sections are
siblings, not a path, and on a phone the separators were left at the
start of wrapped lines. The item pages marked no section at all.
computes/host_nav.html is the one nav, labelled "Host sections": pills
that wrap, the page's section marked active, with aria-current="page"
on the section's own page and "true" on an item of it.
42a1783 refuses a vCPU change on a running VM, also on a direct
request: the VM would get them only at its next start, past the quota
counted now. The live data-loss test still posted vCPUs to a running VM
and expected the change to be pending, so it failed on every connection
type. It now expects the refusal, with its message, and the definition's
vCPUs unchanged; the pending options part is as before.
A key held from the menu (Hold Ctrl, Alt or Windows) is the same key as
the keyboard's left Ctrl, Alt or Windows key. Pressing and releasing
that key on the screen let the VM's key go, while the menu still showed
it held. And when the key went down outside the screen (in the
Clipboard dialog) and came up on it, noVNC sent no release, as it had
not seen the key go down.
The release of that key where noVNC takes keys (the screen, the touch
keyboard) now lets a held key go itself, and clears the menu's mark. A
release elsewhere reaches no VM and changes nothing.
"Type it" sent each character's keysym and let QEMU pick the key from
its own US map, with Shift added for the US shifted characters. A VM
on another layout got other characters: on Turkish Q, '"' typed 'İ',
'@' typed "'", and ğ, ü, ş, ı, ö, ç typed nothing.
The Clipboard dialog now asks for the VM's keyboard layout and
remembers it per browser. Each character is typed as the key that makes
it on that layout (its scancode), with Shift, AltGr or both (AltGr
first: pressed after Shift, the right Alt is Alt on the Linux console),
so the VM's own layout makes the character. The layout is fixed when
typing starts. Text with a character no key types on the layout is not
typed at all, and the dialog lists those characters: a password or a
command with a character left out would be a wrong one.
A layout is one file in console/layouts/: its name and, for each key,
the characters it types plainly, with Shift, with AltGr and with
Shift+AltGr. The view offers every file, US first. dev/console_layout.py
writes a file from the system's XKB data as libxkbcommon computes it
(through ctypes, no new dependency), mapping keys to DOM codes with
noVNC's table from QEMU's keycodemapdb; it lists the dead keys it leaves
out, and drops the keys the keyboard model lacks (the key left of Z on
US pc104 keyboards). English (US) and Turkish Q come with it; the
README says how to add one.
The serial page had a white strip of inline styles with an untranslated
status and a browser-default button above the terminal, sent input while
disconnected, sized the terminal only on window load and resize, and left
the keyboard elsewhere after the key menu. When a superuser opened a
console open in another window, its user saw "[An administrator opened
this console]" and a plain "disconnected".
- console-xterm.html shares the VNC page's bar: status badge (Connecting,
Connected, Disconnected, the server's refusal), Disconnect, then
Reconnect (a new page for a new token). The script is
static/js/console_serial.js; the token reaches it as json_script data.
- The terminal takes the keyboard on connect and after the key menu; input
is sent only while connected; it fits its box on connect and whenever
the box changes size (ResizeObserver).
- A console that writes nothing yet gets a dim local hint: press Enter.
- socketiod sends the displaced client "taken_over" with the superuser's
username; the page shows "Taken over by <name>" in the bar and the
terminal. The superuser is not asked first (owner's decision).
- Fullscreen uses the shared console_keys.js handler (keyboard lock).
The VM page offered a lite and a full VNC console. Lite lacked a clipboard,
display settings and a way to reconnect; full put noVNC's own side bar under
the panel's bar, with a Fullscreen item that called a function noVNC does
not have, status messages hidden behind the bar, saved noVNC settings that
overrode the Access tab's, connection settings that cannot apply, and a
Connect button that reused the page's two-minute token and failed.
console-vnc.html and static/js/console_vnc.js replace both, on noVNC's RFB
with Bootstrap controls, and keep what the full view did:
- Status in the bar (Connecting, Connected, Disconnected, Connection lost,
Could not connect, Authentication failed with the server's reason on
hover); Disconnect, then Reconnect, which loads the page again for a new
token.
- The VNC password in a dialog (with a username when the server asks for
one), not a browser prompt; without passwordless console it is not in
the page.
- Send key(s) gains Hold Ctrl / Alt / Windows (noVNC's extra keys).
- Clipboard: what the VM copies, sending text to the VM's clipboard, and
typing it key by key (new: works in firmware, installers and login
prompts; Shift for the characters a US keyboard shifts, 25 ms a key as
firmware drops faster keys, verified in a UEFI shell).
- View: scaling (none, fit, resize the VM's screen), clip, drag a clipped
screen, view only, dot cursor, quality, compression; it starts as
Settings > Console says (a touch screen starts fitted).
- A Keyboard item on touch devices (noVNC's on-screen keyboard input,
key events and Android's text-only input), Fullscreen with keyboard
lock, the bell.
Dropped with the full view: noVNC's websocket host/port/path, repeater ID
and log level (the panel fixes them), automatic reconnect (the token is
single use in time), XVP power buttons (QEMU has no XVP) and shared-mode off.
The bar is compact (about 45 px in the 850x600 window). The Access tab has
one Console button instead of the Lite/Full split and four switches, and
it and the VM list open one window per VM (opening it again reloads it).
A stopped VM, an unreachable or unsupported console and a disabled serial
console rendered the lite VNC page under the error: it connected to novncd
anyway, showed "Something went wrong" and offered keys, Scale and Send
CtrlAltDel for a console that was not there. A refused console was a bare
text 403 inside the popup.
- console-error.html: the console shell with the VM's name and the reason,
no menu and no script; a refused or unknown VM gets it with 403 and
without the VM's name, as before for everyone (no superuser exception).
- The view signs the console token only for a page that connects.
- console-menu.html: the Send key(s) menu and Fullscreen, included by the
console pages; the serial page lists only the keys it sends (the VNC
groups did nothing there).
- Every console window is titled "<VM> - Console".
- The lite page no longer takes a VNC password from its link.
- The shell lost its 850px x 600px corner radius (a grey arc on the
serial and error pages) and spans the window (container-fluid).