1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 11:46:49 +00:00
Commit graph

127 commits

Author SHA1 Message Date
catborise
e35342bff1 feat(instances): a VM's TPM 2.0 is switched on and off on its Options tab
A VM made without a TPM, a Windows 10 to be upgraded to Windows 11, had
no way to get one but the XML editor. The Options tab has a TPM 2.0
switch, as the RNG one: it adds an emulated TPM of the host's model to
the definition for the next start, or removes it. Its state stays on the
host until the VM is undefined, so a TPM added again has it back. A TPM
the switch did not make (passthrough, 1.2, encrypted, with its own state
path) is left to the XML editor, and a host without swtpm says so.
2026-11-01 17:10:26 +03:00
catborise
2a05576e18 feat(instances): a CD-ROM is added on the bus chosen, with its ISO
A running VM got its new CD-ROM on SCSI, the one bus the panel could plug
in then, and Windows does not see a virtio-scsi drive before the driver
on that ISO is installed. Add CD-ROM is now a dialog that chooses the bus
and the ISO: a running VM takes USB, first, which every guest sees and
which hot-plugs, or SCSI; a shut-off one SATA (IDE on pc), SCSI or USB.
libvirt has no empty USB drive, so a USB CD-ROM takes its ISO when it is
added and is detached instead of ejected, also while the VM runs.

A USB CD-ROM is a CD drive on QEMU's usb-bot only. Where QEMU has none
(RHEL 9), libvirt took usb-storage, which shows the guest a hard disk
that Windows does not read; the drive now asks for usb-bot, and such a
host refuses it with libvirt's message.
2026-11-01 16:27:39 +03:00
catborise
005fa59bf0 feat(instances): a new VM can have UEFI Secure Boot and a TPM 2.0
Windows 11 does not install without Secure Boot and a TPM 2.0, and the new
VM form offered neither: its firmware list was BIOS and one UEFI loader
guessed from file names, which on RHEL never matched the Secure Boot one.

The firmware choices are now BIOS, UEFI and UEFI Secure Boot, as the
host's libvirt reports them (its firmware features from libvirt 12.1,
before that its loaders), and libvirt picks the firmware itself: UEFI
without Secure Boot, or with Microsoft's keys enrolled. A TPM 2.0 switch,
shown when the host has swtpm, adds an emulated TPM. The create job
refuses a firmware or a TPM the host does not offer, and the API takes
both. The path-guessing helpers are gone.
2026-11-01 16:18:29 +03:00
catborise
8b7b7bdc3e test(instances): the live LVM test confirms erasing the device it pools
The New pool form asks for the erase confirmation on a logical pool since
the storage pages were redone; the live test's pool request lacked it, so
the pool was never created and both LVM tests failed in setUp.
2026-10-31 23:23:26 +03:00
catborise
c401479cf9 feat(templates): every confirmation asks in the page's own dialog
Deleting or detaching a disk, deleting a network card, forcing a VM off,
reverting or deleting a snapshot, stopping or deleting a pool, a
network, an interface, a filter or a secret, and the other actions that
ask first opened the browser's yes/no box (confirm()), and the ISO
upload told its mistakes in the browser's alert().

One dialog in the base page now asks for all of them: its title and
confirming button carry the button's own words, its body the button's
question, the confirming button red for a destructive action. Confirmed,
the button is clicked again, so its form sends what it did before. A
button inside another dialog (the flavors) hides that one while asking
and shows it again after; Cancel gives the focus back, and the
confirming button acts once. The ISO upload's mistakes show in its
dialog's error box, announced.
2026-10-31 20:44:39 +03:00
catborise
95562544eb feat(instances): Add PCIe slots for a VM that has none free
An existing q35 VM whose PCIe root ports are all used takes no new
virtio disk or network device while it runs, and the only way to give
it more was editing its XML.

The Disks tab of a PCIe machine now counts its slots, "PCIe slots: 0
free of 7", in red when none is free, with what its next start will
have when that differs, and Add PCIe slots adds four root ports to its
persistent definition: libvirt numbers and places them, and a running
VM gets them at its next start. The no-slot advice of Add disk and Add
network device points at it.
2026-10-31 20:44:24 +03:00
catborise
fe4cffdc91 fix(instances): new q35 VMs have spare PCIe ports, and a full VM says what to do
On a q35 machine each PCIe device (a virtio disk, a network card) takes
a root port of its own, libvirt makes only as many ports as the VM is
defined with, and a running VM gets no new one: adding a virtio disk to
such a VM failed with libvirt's "No more available PCI slots".

A new q35 VM is now defined with the PCIe root and 14 root ports, as
virt-install makes them: libvirt puts the VM's devices into them and
leaves the rest free for devices added while it runs.

When a running VM has no free slot, Add disk and Add network device say
what to do instead of libvirt's words: for a disk, the scsi bus when
the VM has a SCSI controller (its disks need no slot of their own), or
adding it with the VM shut down, where libvirt makes the port; for a
network device, shutting the VM down. A new volume whose attach was
refused stays in its pool, as before, to attach as an existing volume.
2026-10-31 19:47:26 +03:00
catborise
f77439f398 fix(storages): a refused new volume comes back in its dialog
A New volume form the pool page refused, for a name with a space or a
character not allowed, redirected back with the reasons at the top of
the page and lost what was typed.

The form now posts to the pool page itself: a valid one makes the
volume and redirects as before, a refused one renders the page with
the dialog open on what was entered, the refused field marked with its
reason and focused. A pool stopped meanwhile has no dialog: its page
shows the reasons. The separate create_volume view and URL go.
2026-10-31 18:35:09 +03:00
catborise
0fd3e69c8e feat(instances): the VM page's add dialogs in the panel's dialog look
The Add disk, Add network device and bandwidth dialogs of the VM page
set their labels in a narrow column beside the fields and closed with
a grey Close: Add disk split "GB" over two lines and offered an
unexplained Metadata box, its existing-volume pool was a dropdown of
links that wrote a hidden field; the network list prefixed every
choice with "Network" or "Interface"; the bandwidth fields asked for
"kilobytes" where libvirt counts KiB/s and KiB.

Now their labels sit above the fields, tied to them, in two columns.
Add disk is one form whose source, New disk or Existing volume, is a
radio card as in the new VM form, the chosen source's fields shown and
sent and its action taken; the size has a GiB suffix and
"Preallocate metadata (qcow2)", the existing volume's pool is a select
that loads its volumes, the bus and the cache are shared. Add network
device picks its
source from Networks and Interfaces groups and says how its MAC is
filled; the bandwidth limit names its units and when a running VM gets
it. Move says when the host has no other pool and is disabled then.
Every dialog closes with Cancel and its action; the posted fields are
unchanged.
2026-10-31 18:35:09 +03:00
catborise
26a10d127a fix(instances): the disk Edit dialog on one page, and an empty serial stays empty
The disk Edit dialog showed a disk without a serial as "None" in its
Serial box, so saving it untouched gave the disk the serial "None".
The box and the format box are now empty for a missing value.

The dialog also hid most of its nine fields behind three tabs, said
nothing about a running VM, greyed the bus without a reason and its
switch labels did not toggle them. Now one body shows the disk's bus,
format and size under the title; a running VM's note that the changes
apply at its next start; the path and the Read-only and Shareable
switches in a Source card; the four performance modes in a grid; and
the bus, serial and image format folded under Device, with why the bus
is locked and that changing the format does not convert the image.
Cancel is outlined and Save the primary action.
2026-10-31 18:10:05 +03:00
catborise
2e17661390 fix(instances): a network device keeps its source unless another is chosen
The Edit dialog of a VM's network device selected the listed network
or interface whose name matched the device's source, and the first one
when none did: saving only a new model moved a macvtap device or a
bridge the host's lists did not show onto the first network, a bridge
named like a network became that network, and a macvtap device chosen
again lost its mode.

The dialog now offers the device's own source first, "<name>, as it
is", and posts nothing for it; the view passes no source and the
device's source element is left as it is. Choosing another source
changes it as before.
2026-10-31 18:10:05 +03:00
catborise
3d69dfe5d9 feat(instances): the network device Edit dialog as the disk one
The Edit dialog of a VM's network device listed its fields in an
order of its own, named the source list "Net Source" with every option
prefixed "Network" or "Interface", said nothing about a running VM and
closed with Close and Apply.

Now it reads like the disk Edit dialog: the device's model and MAC
under the title, a running VM's note that the changes apply at its
next start, the source chosen from Networks and Interfaces groups, the
model and the NWFilter side by side, the MAC in monospace, and Cancel
and Save. The posted fields are unchanged.
2026-10-31 18:10:05 +03:00
catborise
6e147ed8cc feat(instances): a VM's network devices as rows of one list
The Network tab gave each network device a card with its buttons in a
footer, two cards to a row, as the disks had.

Each device is now a row of the same kind of list as the disks: eth N
with its target, the source, the IPv4 addresses and the link switch,
and Edit, Add QoS and Delete at the row's end; the device details, the
QoS limits and the macvtap note still fold out below it. A row holds
its Edit and Add QoS dialogs, so only rows sit in the list.
2026-10-31 18:10:05 +03:00
catborise
68d0631e33 feat(instances): a VM's disks as rows of one list
The Disks tab gave each disk a card: its capacity in large type, its
pool, its path and its buttons stacked, two cards to a row, so a VM
with a few disks filled the screen.

Each disk is now a row of one list: the device with its bus and format,
the capacity and the space used with a thin bar, the pool and the path,
and Edit, Move, Detach and Delete at the row's end; the device details
still fold out below it. A qcow2 file whose metadata takes it past its
capacity shows a full bar.
2026-10-31 18:10:05 +03:00
catborise
2dab23cd9e fix(gstfsd): only the panel, with the host's key, can change a VM's disk
gstfsd, the root service on each compute that rewrites a shut-off VM's
/etc/shadow and authorized_keys, did what any client on its port asked.
Now it needs GSTFSD_KEY (64 hexadecimal characters, in the unit's
/etc/default/webvirt-gstfsd) and does not start without it. Each
connection gets a new challenge; the panel signs its request for it
with HMAC-SHA256 and gstfsd checks the signature before any change, so
a request recorded on one connection is refused on another. gstfsd
signs its reply for the same challenge and the panel's nonce, and the
panel accepts only that reply.

Each compute has a gstfsd key, the host's GSTFSD_KEY: the compute forms
and the API take it and never show it, and an empty value on an edit
keeps it. The bootstrap script makes the key file once, readable by
root only; the README shows how to make it and where the panel takes
it. The traffic is still not encrypted: a remote panel's port stays
open to the panel's address only.
2026-10-31 01:53:43 +03:00
catborise
a408cfdb2b feat(instances): the Migrate tab marks hosts it cannot use and files in the way
The Migrate tab offered every other host alike, and its plan showed
neither a file of the same name in a destination pool nor the disks of
the VM's next start, which the job then refused. Now:

- When the tab opens, the hosts are checked first: one that cannot be
  reached, or the VM's own host under another record (the same libvirt
  host UUID), is shown with why and cannot be chosen, and the disks of
  the first one left are checked. The job refuses the VM's own host
  under another name too.
- A pool of the destination that has a file of a disk's name is shown
  but cannot be chosen for it; a disk no pool can take refuses the form.
- The next start's file disks the migration does not carry are listed;
  one not on storage both hosts share refuses the form, as the job does.
2026-10-31 01:34:44 +03:00
catborise
54a1943e26 fix(instances): a VM's destruction checks its files on every host first
Destroying a VM with its disk files forced it off first and then
worked out which files to delete, so a check that failed left the VM
stopped, and only this host's VMs and volumes were checked: a file on
shared storage that a VM on another host uses was deleted.

The files are now worked out before the VM is stopped: the disks it
runs with and those of its next start, as before with their snapshot
files and seed. A file a VM or volume of another of the panel's hosts
uses at the same path (shared storage is mounted at one path on every
host, as migration needs) is kept like one used here; another record
of the VM's own host is skipped, as there the VM itself uses them. A
host that cannot be read stops the destruction with its name, before
anything is stopped or deleted.
2026-10-31 01:08:42 +03:00
catborise
6dc2e5851b fix(instances): the quota counts queued clones and pending values
A user's quota counted each VM's running vCPUs, memory and disks, so a
larger value set for the VM's next start was not counted, and a clone
counted only once its VM was recorded, so a user could queue clones
past the quota. Now:

- Each VM counts the larger of its running and its next-start values.
- A clone job counts as one VM with the source's persistent vCPUs,
  memory and disk size, which its plan stores, from its admission until
  its VM is recorded; a failed, cancelled or resolved job no longer
  counts. The clone's VM and its owner are recorded in one transaction,
  so the quota counts either the job or the VM, never neither.
- The quota reads each VM's definition once (cached_reads).
2026-10-31 00:51:31 +03:00
catborise
13d3a16ea5 fix(instances): a clone takes a free name the form offers
A user who is not a superuser chooses the clone's name from the free
names the clone form lists, or gets the first one with automatic names,
but the server took any posted name. It now refuses a name outside the
list, and with automatic names it takes the first free one whatever is
posted. A superuser still names a clone freely.

The panel checks VM names on every compute, but a job held a name only
on its compute, so two clones from VMs on two hosts could take the same
free name at once; a name claim now holds the name everywhere. A clone
job whose name a VM found on a host took while it was queued stops
before it copies anything.
2026-10-31 00:32:39 +03:00
catborise
cd2dd517b7 fix(instances): two owners added at once keep a VM to one owner
With one owner per VM, the VM page and a user's page counted the VM's
owners and then added one in separate steps, so two owners added at the
same moment both found the VM without one, and it got two. The check
and the insert now run in one transaction that holds the ownership lock
(PostgreSQL's advisory lock; SQLite's IMMEDIATE transactions already
serialize them): the second waits for the first and is refused. The
audit row is written after the transaction, so a failure keeps its row.
2026-10-31 00:21:06 +03:00
catborise
5e93920d78 feat(instances): a VM's stats in one grid, its logs a switch away
The Stats tab stacked one large card per chart, so a VM with a few
devices made a long page, and its "Real Time" and "Logs" sub-tabs were
shown to every user although only a superuser may read the logs. The
owner chose the layout from rendered options: a state line, CPU and
memory side by side, one chart per NIC and disk below, each with its
latest reading next to its title, and a small Graphs/Logs switch for
superusers.

Codex reviewed the tab; this is fixed:

- Memory is shown in MiB (it was labelled MB), and its axis grows to
  the RSS when the RSS passes the balloon size instead of clipping it.
- The CPU reading says it is of the host's CPUs.
- A NIC is titled by its MAC, not an invented "eth0", and its readings
  are matched to its chart by the MAC: they were matched by position, so
  removing a NIC while the page was open moved the next one's readings
  into its chart.
- A VM that is not running says nothing is measured.
- The logs load whenever the Logs view is shown, by keyboard too; an
  empty answer says "None" and a failed one says so, instead of an
  empty table. The tab shows the latest 50 rows, with a link to all of
  the VM's rows on the Logs page.
- The VM's header shows memory in MiB; it said MB.
2026-10-30 23:41:17 +03:00
catborise
b72e9b1442 feat(instances): a VM's destruction says what goes, and keeps what a kept file needs
The Destroy tab was a sub-tab with one sentence and a link; its
confirmation page said only "This action is irreversible!" over a
"Remove Instance's data" switch. The tab now shows the state and
"Review destruction". The owner chose the confirmation layout from
three rendered options: the VM and its host, that its definition,
saved state and snapshot records go and that a running VM is forced
off, two plans for the files (delete the disk files, or keep them), the
VM's disks, what is always kept, the NVRAM choice, and Cancel.

Codex reviewed destroying; this is fixed:

- A disk file that cannot be deleted keeps the files below it, which
  it needs: they were deleted after it, leaving it broken. The files
  are deleted overlays first (split_disk_paths_by_use orders them by
  how many files each grows from), and if the files a kept one needs
  cannot be read, none of the rest goes. A message names each file
  kept so, and a failed deletion says the VM is removed.
2026-10-30 22:19:31 +03:00
catborise
7fc55537e5 feat(instances): external snapshots first, internal ones a fallback
The Snapshots tab had three sub-tabs: internal, external and the
lists, with the two kinds side by side as equals. The owner set the
direction: external snapshots are the way; internal ones pause the VM
while its memory is saved and can take long, so they stay only as a
fallback for a VM that cannot take an external one.

The tab now has the external snapshot form on top, its name and
description on one line, with its memory and freeze options; the
internal snapshot under a folded "fallback" line with its warning; and
one list of all snapshots, newest first, each on one line with its
name, description, kind and date, and Revert and Delete.

Codex reviewed snapshots end to end; these are fixed:

- The pause and memory lines follow the VM's state and the kind.
- An internal snapshot is reverted to on a shut-off VM only, on the
  server too; the tab alone held that rule.
- A paused VM's revert says it comes back paused, not that it starts.
- An external snapshot waits, with the reason, while snapshots newer
  than the one reverted to exist; the server refused it unexplained.
- Deleting an external snapshot says its files are merged with the
  disks; "into the next one" was wrong for the last one.
- The description has no 45-character limit only the browser kept.
- Freezing is not shown ticked where it cannot be offered, and
  unticking memory gives back the earlier freeze choice.
- Internal create, revert and delete refuse a saved state or a running
  disk job, as external ones do.
2026-10-30 21:40:00 +03:00
catborise
60f12cefa1 feat(instances): a VM's access in one strip, its console password kept to its rule
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.
2026-10-30 20:39:40 +03:00
catborise
efb77dd866 feat(instances): a VM's power actions at a glance, a power cut asked first
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.
2026-10-30 19:21:30 +03:00
catborise
f8e30d7668 feat(instances): a VM's resize tab as a ledger, its vCPU changes kept
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.
2026-10-30 18:16:11 +03:00
catborise
3d632f1592 feat(instances): a VM's owners with their rights, and XML edits kept until saved
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.
2026-10-30 17:02:38 +03:00
catborise
1150b941be feat(instances): a VM's RNG device and crash action in its options
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.
2026-10-30 16:20:32 +03:00
catborise
a993c12db0 feat(instances): a VM's options in one list, each saved when it can be
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.
2026-10-30 16:03:43 +03:00
catborise
46188d3230 feat(instances): a VM's migration in a route, a disk plan and its transfer settings
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.
2026-10-30 14:55:57 +03:00
catborise
400ba1614f feat(instances): a VM's clone form in device cards, with fixes to what it clones
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.
2026-10-30 14:55:57 +03:00
catborise
301b73155d feat(instances): a VM's console settings in two cards, with a password action
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.
2026-10-30 14:55:57 +03:00
catborise
20cfc25367 feat(instances): a VM's boot settings as switches and its boot order as one list
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.
2026-10-30 14:55:57 +03:00
catborise
a8f445d580 feat(instances): a VM's network devices as cards, each with its QoS limits
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.
2026-10-30 14:55:57 +03:00
catborise
0f778fe9f0 feat(instances): a VM's disks as cards, its CD-ROM drives above them
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.
2026-10-30 11:19:55 +03:00
catborise
935a0e9bdc feat(instances): the top of a VM's page in one card
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.
2026-10-29 22:46:24 +03:00
catborise
d97ec5afd2 fix: a queued job is shown once, in the job panel
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.
2026-10-29 21:38:09 +03:00
catborise
7f8e5cd618 feat(instances): move a VM's disk to another storage pool of its host
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.
2026-10-29 21:30:26 +03:00
catborise
51d24fce30 feat(instances): migrate a VM with its disks copied into pools of the destination
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.
2026-10-29 19:46:31 +03:00
catborise
d43245d996 feat(instances): PCI devices hot-plugged into a running VM
- 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.
2026-10-29 14:54:36 +03:00
catborise
80dd145bcf feat(instances): PCI devices of the host given to a shut-off VM
- 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.
2026-10-29 14:20:34 +03:00
catborise
ac8ff62e7e feat(instances): USB devices of the host given to a VM
- 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.
2026-10-29 13:33:23 +03:00
catborise
2e1f29961c fix(instances): a new VM's CPU is a mode or a model the host offers, checked by libvirt
- 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.
2026-10-29 11:17:38 +03:00
catborise
050d071ff9 feat(interfaces): the interface pages on Bootstrap's cards, tables and dialogs
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.
2026-10-29 10:09:52 +03:00
catborise
3aac8f51f2 feat(instances): the VM list on Bootstrap's pills, badges and menus, its actions by the user's rights
- 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.
2026-10-28 13:55:46 +03:00
catborise
2d80f21a4a fix(instances): the VM list searches its VMs, collapses its hosts together and says when it is empty
- 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.
2026-10-27 22:40:49 +03:00
catborise
2f5e3ccf62 fix(instances): the VM lists show the VMs by name
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.
2026-10-27 16:32:44 +03:00
catborise
e6a6b7f3cf test(instances): the live suite expects vCPUs to wait for a shut-off VM
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.
2026-10-27 13:53:06 +03:00
catborise
0532471759 feat(console): one VNC console page, driven from the panel's bar
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).
2026-10-27 08:05:31 +03:00
catborise
c169ccf5fe fix(console): small console settings and page fixes
- 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.
2026-10-26 20:41:46 +03:00