The Access tab hid the console, the root password, the SSH key and the
remote viewer behind four sub-tabs. The owner chose a compact layout
from three rendered options: the state, a strip with the console and
the remote viewer, then two rows for root's password and key, each
saying why it waits when it does.
Codex reviewed access end to end; these are fixed:
- console.vv carries the console password only for users with the
passwordless console permission, as the console page passes it; it
gave it to every user who may open the console.
- The root password field is a labelled password input, not plain text
with a 24-character limit.
- Without a saved public key the key row is disabled and points to the
profile, instead of offering "None".
- A paused VM's console opens, as the console view allows.
- The remote viewer address and console.vv show only for a VNC or SPICE
console that listens beyond the host's loopback; a viewer on the
user's computer cannot reach the others, and a serial console gave a
pty://…:None address.
The Power tab hid each action behind a sub-tab of one sentence and one
button. The owner found the first rendered layouts crowded and chose a
compact one: the VM's state, then the actions its state allows in two
groups, those that keep the guest's data and the red ones that cut the
power, each group with one short caption.
Codex reviewed the power actions; these are fixed:
- Power Cycle and Force Off ask first, naming the VM and what is lost,
as the VM lists already did.
- The words say what happens: Power Off asks the guest to shut down;
Power Cycle cuts the power and starts the VM again, with its pending
settings; Force Off cuts the power.
- The tab follows states.allowed_actions, as the lists do; a running
VM's actions now include Force Off there too.
- A running template is not offered Power Cycle, which would start it
again; a shut-off template says to clone it instead of showing a
Power On button that only looked disabled.
- A paused VM tells its owner that an administrator can resume it; it
said an administrator had blocked it.
- Power On's please-wait dialog opens on the form's submit, from the
keyboard too.
The Resize tab's CPU, Memory and Disk panes were horizontal forms with
bold host figures. The owner chose the ledger layout from three
rendered options: each section with its heading, the VM's figures and
the host's on the left, its fields on the right, and one line on what
applies now and what at the next start.
Codex reviewed resizing end to end; these are fixed:
- A vCPU turned on or off while the VM runs is kept at its next start:
libvirt was asked without flags, which changes the running VM only.
- A paused VM offers to turn vCPUs on only: turning one off needs the
guest, which does not run.
- A smaller disk size is refused, and so is the rest of the form,
before anything grows; it was skipped without a word.
- vCPUs and memory below 1 are refused.
- The memory fields are numbers filled with the VM's values: the
"Custom value" box, its script and the preset list are gone; an empty
custom field silently used the preset.
- Units and words: MiB, not MB; a disk's field is its new size, with
its size now above it; the messages say what the VM gets.
Settings > Users was a bold title, an unlabelled "+" and a table of
names with a trash icon. The owner chose this layout from three
rendered options: one list of owners, each with what it may do
(change settings, delete the VM, change console settings, or power and
console only), Edit rights and Remove, an empty state, and a labelled
"Add owner".
- Remove and Edit rights come back to the VM's Users tab: the return
address carried #users unencoded, so it was lost, and Edit rights
went to the user's account page. Both accounts views now go to a
?next page of this site.
- The add-owner list offers only users who are not owners yet and not
administrators, who can use every VM anyway; a superuser owner also
took the only place when a VM may have one owner.
- The unused del_owner view and URL are removed: the list uses the
accounts page, which asks before removing an owner.
XML:
- An XML edit not saved yet is kept: the page no longer reloads itself
when the VM's state changes, and the browser asks before leaving it.
A state change lost the edit.
- The button that opens the review dialog reads "Review changes"; it
did not save, as "Change" suggested. Closing the dialog gives focus
back to the editor.
Two settings the owner chose to add to Settings > Options after the
page's review with Codex. Both change the persistent definition, so
they are saved while the VM runs and used from its next start.
- Random number generator: a switch for a virtio RNG device fed from
the host's /dev/urandom, so a new VM does not wait for entropy at
boot. Another RNG device, or the panel's with a rate or driver, is
shown as the VM's own and left to the XML editor.
- When the guest crashes: Restart or Stop (on_crash restart or
destroy); libvirt's default, Stop, when the definition has none.
Stop keeps a VM that crashes again and again down for a look. Any
other action is shown and left to the XML editor.
Settings > Options was four forms under bold sentences, most of them
disabled while the VM ran. The owner chose this layout from three
rendered options: one list, each row a setting with its description on
the left and its control on the right: VM details (title, description),
Template, primary video model, QEMU guest agent. vCPU hot plug moved to
Resize > CPU, next to the vCPU counts.
Codex reviewed the four forms; these are fixed:
- Title, description and the video model are saved while the VM runs:
they are in the persistent definition, used at the next start.
- The template flag has its own form and view, set_template, for
superusers and staff, and only for a shut-off VM: a running VM made a
template left the quota while it kept running. Turning a template
back into a VM checks the quota there, and each change is audited.
The title form no longer touches the flag.
- A new video model stays the primary one; the old model's memory and
head settings, which are its own, go with it.
- The agent and vCPU hot plug forms show the persistent definition; they
showed the running VM's, so a pending change looked undone.
- Enabling the guest agent adds its channel only where it is missing:
saving Enabled again failed, and a channel pending the next start kept
a running VM from getting one.
- The agent line tells a VM that is not running, one without a channel,
and a connected or not connected agent apart.
Settings > Migrate was a horizontal form of six switches without
explanations. The owner chose this layout from three rendered options:
the route on top as "From" (the source host) and "To" (the
destination select) side by side, a "Storage" card with
where each disk goes, a "Transfer settings" card with the switches in
two columns, each with a line saying what it does, and a footer.
Codex reviewed migration end to end; these are fixed:
- No Offline switch: the mode follows the VM's state, and one line says
which. A shut-off VM moved offline whether the switch was on or off.
- Live migration shows for a running or paused VM; Auto converge,
Compressed and Unsafe only for a running VM, the only state the
migration applies them to. Unsafe was offered for a paused or shut-off
VM and ignored.
- Unsafe says it skips libvirt's safety checks; Delete source files
says it deletes only the files of copied disks.
- A VM that cannot migrate anywhere (no other host, a state that does
not migrate, snapshots, host devices) gets the reason and a disabled
button instead of the disk plan and the switches; it learned that
after submitting.
- Migrate stays disabled while the destination did not answer or a disk
cannot go there; it was enabled after any plan.
Settings > Clone was a horizontal form with unlabelled checkboxes. The
owner chose this layout from three rendered options: the clone's name
on top, a "Network" card with a new MAC address per NIC, a "Storage"
card with a new volume name per disk, then the optional title and
description and a footer with Clone.
Codex reviewed cloning end to end; these are fixed:
- Every NIC of the clone gets a new random MAC when the form opens.
Only the first did, so a VM with two NICs gave its clone the second
NIC's MAC. Guess MAC is offered for the first NIC only: a name's DHCP
host entry holds one address.
- A VM without a NIC can be cloned: the first NIC's MAC was required.
- The disk name script uses the persistent disks, as the form does; a
disk added pending the next start kept the source's volume name.
- A running or paused VM is not cloned while a copied disk changes at
the next start: the backup copies the running disk, so the clone
would get the old contents under the new definition.
- A derived volume name that does not start with the source's name now
starts with the clone's name and the disk's device, so a second clone
or a second disk does not take the same name.
- Guess for the clone name says when the DHCP file has no free name; it
stopped with a script error.
- The qcow2 metadata preallocation box shows only for a shut-off VM: a
live clone's backup ignores it.
Other users no longer get hidden MAC and disk inputs or the scripts that
filled them: the server derives those values and ignored the posted ones.
Settings > Console was one horizontal form, and on a running VM its
Update button was disabled. The owner chose this layout from three
rendered options: the console type on top, then a "VNC connection" card
(listen address, keymap) and a "VNC password" card.
- The password is changed by an action: keep it, generate a new one,
set a new one or remove it. This replaces the Generate and Clear
boxes and their precedence, and an emptied field no longer removes
the password silently. The pane says whether a password is set; the
form no longer carries the password itself.
- A VNC password takes at most 8 bytes: libvirt refuses a longer one
("VNC password is 9 characters long, only 8 permitted"). Generate and
a new VM's random password now make 8 characters; they made 12, which
libvirt 11 refuses. A set password keeps its spaces.
- Every check is in the form, so a refused save changes nothing; before,
a refused password removal still saved the other settings.
- The settings can be saved while the VM runs: they are in the
persistent definition. A line says the running console keeps its
settings until the VM is shut down and started.
- The console type shown and compared is the persistent one; it was the
running VM's, so a pending switch to Serial showed VNC and a later
save could switch again.
- While the saved type is Serial, the VNC settings are not shown or
taken; choosing Serial ignores the posted VNC settings.
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.
- 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.
- 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 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.
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.
- 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 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.
- 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.
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.
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.
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).
- The VM's console settings have no "Clear keymap" box: it wrote an empty
keymap attribute into the VM, and choosing Auto already removes the
keymap; that change is logged as "remove console keymap".
- The "Clip viewport" switch was labelled "View Clipboard".
- The full console page no longer fills the password field with "None"
for a VM without a console password.
The console page is /console/<compute>/<uuid>/ with the view and display
options in the query. It was /console/?token=<compute>-<uuid>, a "token"
that was only the VM's address, parsed alongside a signed token that no
link ever sent. The list's console button takes the VM's UUID from the
database instead of asking libvirt for each row, and no longer defines a
global function per row.
- A VM delete reads delete_disk and delete_nvram as booleans from a JSON
or form body. The view read them as HTML checkboxes from the form body
only: JSON {"delete_disk": true} kept the disks while reporting success,
and a form "delete_disk=false" deleted them. Refused options stop
nothing; the API schema names both options.
- Power on and power cycle of a template answer 409 instead of reporting a
start that did not happen.
- The VM details leave out the host's ISO images for non-superusers, as
the VM page does; reading them also refreshed every pool of the host.
- Flavors take values of at least 1, as the web form does, and show their
id for later changes; a migration needs neither live nor offline; a VM
create on an unknown compute answers 404 instead of 500.
- On a narrow screen the grouped list hid the User, VCPU and Memory
headings but not the VMs' cells, so values sat under the wrong headings;
the cells now hide with their headings. Memory says MiB.
- A user with the view_instances permission sees each VM's host in the
list, as the host alerts already name them; the host page link stays a
superuser's.
- A flavor's memory and disk are labelled MiB and GiB, and each value is at
least 1, so "4" is no longer saved as a 4 MiB preset by mistake.
A boot device moved up or down was rebuilt without its click handler, so
unticking it afterwards left it in the boot order that was applied; the
moved label now keeps its checkbox. An empty root password did nothing
and said nothing; it is refused with a message. Changing a NIC of a
shut-off VM gave no confirmation; it now says the device changed.
The resource pattern was not anchored and took any letters after the VM's
name, so VM "web" also counted "webserver" and "myweb"; a VM with no
resource of its own got "Secondary/OK", stored on every page view. A
VM's resources are now its name or name_SUFFIX at the start of a line,
and a VM without one is "None DRBD".
When the guest agent failed, the Guest Info section stayed empty with no
word: the request's error answer was not handled. An agent without the
time zone or host name command lost the OS details it had answered too.
The section now shows the agent's error, and a missing command leaves out
only its own details.
Only external snapshots had their name checked. An internal one could be
taken with an empty name: libvirt named it, the job and its log row named
nothing, and a recovery looked for "" and marked the job failed though the
snapshot existed. Both kinds now pass the same check when queued, and the
internal form asks for a name as the external one does.
Snapshot dates showed no year, so last year's snapshot looked like this
year's in the list one reverts from; they now read 2026-10-13 23:04:00
under "Date (time zone)", as on the Jobs page.
The saved console form had no initial values, so every posted field
counted as changed: saving only a password also logged the keymap and the
listen address again, each time. A listen address the form does not list
(set in the XML editor) was not a choice, so the select showed localhost
and a save moved the console there. The form now takes its initial values
from the VM, on the page and on the save, and offers the VM's own address.
Only administrators suspend and resume, and the page tells an owner of a
paused VM "Administrator blocked your instance", but the power views let
the owner force it off or cycle it, on the page and the API, and start it
again: the block ended. The power permission now refuses a paused VM to
everyone but administrators.
Disk sizes were pre-filled rounded to whole GiB, so a 3.5 GiB disk showed
4 and was grown, for good, whenever another disk was resized; an empty
field or a disk outside a pool was a 500. A disk's field is now empty with
its size as the placeholder: an empty field leaves the disk alone, a
larger whole number grows it, anything else is refused with a message.
vCPUs change only on a shut-off VM, also on a direct request: a running
VM got them at its next start, past the quota counted against its live
count. Current vCPUs or memory above the maximum are refused instead of
failing (and dropping vCPU hot-plug) or being capped by libvirt. A running
VM's maximum memory select is disabled, since its maximum is kept, and a
custom memory value is sent only while its box is ticked.
A tombstone kept the VM's name and UUID but not its compute, so the page
could not say where the VM had been. It now stores the compute's name (as
text: the compute may be the one deleted) and the list shows it; records
made before this have no host. Deleting a record says so, and the admin
tables use scope="col" instead of the invalid span="col".
Closes#217
- A failed login writes the name it was tried with, as typed: a control
character in it made every download holding that row fail, as the
workbook cannot hold one. Workbooks now write it as \x01; the VM
report shares the code.
- A download held every matching row in memory: it now takes the newest
100000, and its About sheet says when the search should be narrowed.
The list pages' search (filter-table.js) read what was typed as a
regular expression: a "(" or "[" stopped it with an error. It also
needed jQuery, while the instance list had its own script for the same
search. Both are now static/js/table_filter.js: plain text in any case,
the instance list's state list and host groups where a page has them,
remembered for a day as before. A group's header row is marked as one
(data-group-header) instead of being any row without a state.
- An administrator could block, delete or demote (unset Active or
Superuser on) their own account, and with it the last one, leaving the
panel without anyone to undo it. These are now refused.
- The user form showed "No password set." for users who have one: its
password field never read the user's password. Its "Set password"
link went to ../password/, a page this panel does not have. The form
now says whether a password is set and links the page that sets it.
- "Staff status" said "can log into this admin site"; here it lets a
user change and delete the template VMs given to them, and is now
called Template editor.
- A quota of 0 counted as no limit, like -1, so a user's quota could not
be taken away. Now -1 has no limit and 0 allows none; the help says
so, and the labels give the units: memory in MiB, disk in GiB.
- A user's page for a user that does not exist answered with a server
error instead of not found.
- The user list said "None" for a user who never logged in; it says
"never", and dates fit their column.
- The password form's labels no longer break mid-word.
The Settings page saved whatever was posted, and some lists offered
values the panel cannot use:
- Quota Debug could not be turned off: "False" is a non-empty text and
counted as on.
- VM Clone Name Prefix showed as a True/False list, so no prefix could
be typed, and touching it set the prefix to True.
- Disk SCSI Controller listed " lsilogic" with a space, never shown as
chosen, and virtio-blk, which is no SCSI controller.
- VM Machine Type listed x86_64, an architecture; VM CD-ROM Device
listed virtio, a bus a CD-ROM cannot use.
- Logs per Page and Disk Owner UID/GID are read with int(): a letter
broke the logs page and VM create.
A migration fixes the lists and moves a value they no longer offer to
the default (the clone prefix keeps its text). A listed setting now
takes one of its choices, and every number setting a whole number in
its range, checked before it is saved.
The dialog filled its texts with String.replace, which reads $& and $'
in the replacement as patterns: a host named c$&1 showed as
c%(hosts)s1. The values now go in through a function, which inserts
them as they are.
Cloud images read their first-boot settings with cloud-init. The panel
served them only over HTTP, which a VM reaches only with the network
and a reverse DNS name for its address. A new VM can now carry them
itself: Cloud-init seed (Advanced) on the create form, or cloud_init in
the API.
The job writes a NoCloud seed, an ISO labelled cidata, into the disk's
directory pool as cidata-<uuid>.iso and puts it in the VM's CD-ROM
drive. Its meta-data holds the VM's UUID as instance-id and its name as
hostname, written as JSON so a VM named 123 or yes keeps a string
hostname; its user-data is what the HTTP datasource serves, with the
creating user's SSH keys. The ISO is built with pycdlib and written
through a libvirt stream, as the NVRAM copy is, now one helper for both.
A seed needs a CD-ROM bus and a directory pool, else the create is
refused. A failed create deletes the seed with the new disk, recovery
names one left behind, and deleting the VM with its disks deletes its
seed, found in the pools by name even when ejected. A clone's drive is
left empty: the seed is the source VM's identity.