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

113 commits

Author SHA1 Message Date
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
catborise
8d7f28482f fix(api): VM API answers as the VM pages do
- 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.
2026-10-26 17:47:18 +03:00
catborise
b89f280076 fix(instances): VM list columns, hosts for viewers, flavor units
- 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.
2026-10-26 16:14:37 +03:00
catborise
c26e9a49c4 fix(instances): boot order, root password and NIC changes say what they did
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.
2026-10-26 13:49:37 +03:00
catborise
d696820120 fix(instances): a VM's DRBD status counts only its own resources
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".
2026-10-26 13:49:37 +03:00
catborise
e29a79c76d fix(instances): internal snapshot names are checked, snapshot dates carry the year
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.
2026-10-26 13:49:37 +03:00
catborise
fa84ce8113 fix(instances): a console save changes and logs only the fields edited
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.
2026-10-26 13:49:37 +03:00
catborise
182746c73e fix(instances): an owner cannot power a VM an administrator paused
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.
2026-10-26 13:49:37 +03:00
catborise
42a1783178 fix(instances): the Resize tab changes only what was asked
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.
2026-10-26 13:49:37 +03:00
catborise
e5c7dc338d fix(templates): the list pages search their rows as text, with one script
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.
2026-10-25 23:08:42 +03:00
catborise
0e8130f1b0 fix(admin): the Users pages keep an administrator in and say what their fields mean
- 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.
2026-10-25 22:38:40 +03:00
catborise
7392a5b4d2 fix(appsettings): settings take only the values the panel can use
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.
2026-10-25 22:02:53 +03:00
catborise
ad43352b09 fix(instances): the address search shows host names as they are
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.
2026-10-25 21:33:41 +03:00
catborise
f004552148 feat(instances): a new VM can get a cloud-init seed with its hostname and SSH keys
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.
2026-10-25 19:57:15 +03:00
catborise
890d82f44a fix(instances): a timed-out root password or SSH key change says it may still be made
When the guest agent does not answer a root password or SSH key change
in time, the command was already sent: the agent runs it when it
answers again. Measured: with the agent frozen the change failed with
"guest agent command timed out", and after it resumed, root's hash was
the one sent. The error now adds "The change may still be made when the
agent answers again." A failed synchronization sends no command and
gets no note.
2026-10-25 18:51:56 +03:00