1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
Commit graph

579 commits

Author SHA1 Message Date
catborise
90c148d287 fix: a form libvirt refuses goes back to its page with the reason
A libvirt error that a view left to ExceptionMiddleware showed the "500
Internal Server Error" page, which blames the server, at the form's
action URL; its only way on was the browser's Back. Adding a NIC, a new
or existing disk, editing a disk, setting a NIC's QoS and creating a
volume end there when libvirt refuses, as do the other VM actions.

For a form (any method but GET or HEAD) the middleware now goes back to
the page the form was on, with the error in the messages, as it already
does for a busy compute; a page that cannot be shown keeps the error
page. same_origin_referer moves from the instances views to the
middleware module, next to error_text, for both to use.
2026-10-20 22:37:40 +03:00
catborise
ce7c772aae fix(instances): the image, network and template selects of the create page have names
Three selects of the VM create page had no accessible name. TomSelect
points the label of a select it enhances at its own input, so the
network select lost its label and the image select never had one; the
template select sits beside the template pool's label with none of its
own. Each now has an aria-label (Images, Networks, Template), which
TomSelect also copies to its input for the image select.
2026-10-20 21:24:41 +03:00
catborise
96141c3ef7 fix(instances): a refused custom VM form comes back with its disks and networks
A refused custom create form came back with its image pool unselected
and its disk and network lists empty: every disk's device and bus and
every MAC had to be chosen again. Adding a disk or a network also reset
the rows already there: each addition rebuilt all rows from an HTML
string, so the earlier disks fell back to the default device and bus
and every NIC got a new random MAC, a typed one included.

The view sends the submitted pool, disks (volume, device, bus) and
networks (name, MAC) back with json_script, built field by field from
the POST (submitted_custom_lists). The rows are cloned from two
<template> elements and filled with textContent and listeners; one
function, sync_custom_lists, derives the images, networks and mac
fields and the device<i>/bus<i> names from the rows in order. Adding or
removing a row leaves the others as they are, and a random MAC that
arrives after one was typed is dropped. On load the page selects the
pool, waits for its volumes, rebuilds each disk with its device, waits
for that device's bus list and selects its bus, rebuilds the networks
with their MACs, and then focuses the first error. A submitted value the
loaded lists no longer have stays selected. The bus and volume loaders
return their requests so that the page can wait on them.
2026-10-20 21:24:36 +03:00
catborise
dba41f97f3 fix(instances): a refused VM create form comes back with what was entered
When the server refused a VM create submission (a field error, a busy
resource, a reused submission key, an existing VM name, a missing MAC
for a new disk), the page came back on its first tab with the flavor
dialog closed and every entered value gone: the hand-written fields
showed fixed defaults, and the errors sat in a block at the top of the
page, outside #messages and tied to no field.

The view builds one NewVMForm per create form on the page (custom,
template, one per flavor; create_forms), each with its own defaults; a
hidden form_id, which is also the prefix of the form's field ids, names
the submitted one, and it comes back bound. Every field renders from its
own form and is marked by two small template tags (form_fields:
error_attrs, field_error) when refused; a summary at the top of the
form lists all of its errors, also those of fields it keeps hidden. The
submitted tab or flavor dialog, marked data-form-id, reopens with any
"Advanced" section that holds an error; a reopened dialog shows the
messages too; the template form gets its pool, template and storage
back. base.html's error focus is a function that walks the places an
error can be, and a page can call it once it is set up, as the create
page does. form_id changes nothing in how a POST is processed; one of
an unknown form (a flavor deleted since) has its errors in the
messages. The default video now applies to every create form.

The custom form's disks and networks are not restored yet.
2026-10-20 08:12:25 +03:00
catborise
35468bc0fd fix(instances): a custom VM gets the MAC addresses its create form shows
The custom create form shows a MAC per network (random, or typed in),
but had no mac field: the script wrote the joined list into every
name="mac" input of the page, the other six-plus forms' own MAC fields,
so a flavor form then sent one MAC per custom network and was refused,
and the custom VM got other MACs from libvirt. The custom form gets its
own hidden mac field and the script writes only there. The field took
17 characters, one MAC, so the API also refused a VM with two NICs and
their MACs; it takes 255. The serializer's test sent no submission_key,
so its check for more MACs than networks never ran; it does now, next
to the case of two networks with two MACs.
2026-10-19 22:24:57 +03:00
catborise
d14a311862 fix(instances): the disk bus list follows the device type on the create page
Changing a disk's device type in the custom create form asked for its
buses at /archs/undefined/machines/undefined/...: the script read the
architecture and machine from a select[name=arch] and #machine-control
the page does not have. The request failed (500) and the bus list kept
the disk's buses, so a CD-ROM could be put on virtio. The script takes
both from the page's own URL.
2026-10-19 22:14:41 +03:00
catborise
c58d323e21 refactor(instances): the instance page drops scripts for console fields it no longer has
The console settings form lost its password, keymap and listen address
fields long ago (47009d4), but its scripts stayed: show_console(), which
nothing calls, change handlers for #console_passwd_gen, #console_passwd_clear
and #console_keymap_clear, and two blocks that preselected
#console_select_keymap and #console_select_listener_address. No template
defines any of these ids, so the code matched nothing. The instance list
drops a commented-out line that read #compute_select.
2026-10-19 20:12:26 +03:00
catborise
ca74c02c4f fix(templates): the fields of the create modals are named by their labels
An accessibility scan with every modal opened found 49 fields without a
name: the labels of the instance create forms (custom, template and each
flavor's modal), the interface, network, secret and storage pool modals
did not point at their fields. Each label now has a for= and its field an
id, prefixed per form (and per flavor), so a click on the label focuses
the field too. The "Advanced" collapse buttons and the memory and
processing bars are named.

The VCPU config select had id="vcpu_mode" in each of these forms, nine
times on the create page; the unique-ID test did not see it as the test
driver reports no CPU modes. The id goes (nothing used it), and the test
now renders that branch.
2026-10-19 20:01:36 +03:00
catborise
64057f8d05 fix(templates): icon-only buttons, selects and delete pages have accessible names
An accessibility scan (axe-core, WCAG 2 A/AA) of every page found 87
elements without a name. Icon-only buttons and links (the navbar's
administration menu on every page, the add/create buttons, the flavor
delete and back buttons) get a title, which is also their tooltip; the
labels of the language, architecture, chipset and filter selects point
at them; the memory bar is named. The shared delete confirmation page
had an empty <title> and heading, as no view passes "title": it says
"Delete <object>". The power-off form's aria-label was misspelled.
2026-10-19 16:15:06 +03:00
catborise
ea7030d2a1 fix(instances): the grouped instance list without computes has a table body
The grouped list opens one tbody per compute, so with no compute the
sortable table had none, and sortable.js threw on page load ("Cannot read
properties of undefined (reading 'rows')"). An empty tbody is rendered
in that case.
2026-10-19 15:57:09 +03:00
catborise
3a4488d34f fix(templates): pages parse without markup errors, element IDs are unique
Rendered through every URL on libvirt's test driver, the pages had
duplicate IDs and markup browsers repaired silently:

- Each flavor modal's "Advanced" button toggled #flav-add-collapse, the
  first modal's section; every modal now has its own. The flavor and
  template copies of the custom tab's control IDs, and the template
  tab's unused images input, are gone.
- The VM page's action icons and the XML editors were styled by
  repeated IDs; they use classes.
- Modal forms opened inside modal-body and closed after modal-footer;
  they wrap both. Stray end tags and duplicate attributes are removed,
  paragraphs no longer hold modals or rows, table heads have a row, and
  the create page closes its row and column.
- html lang is the active language instead of "en".

A test walks every URL as a superuser and fails on a repeated ID. The
console page still has no doctype: its full-height layout relies on
quirks mode and needs its own check.
2026-10-19 07:49:25 +03:00
catborise
8d175bbd54 test: a LUN of an iSCSI pool attaches live as a block disk
The Vagrant computes serve a 1 GiB LUN on 127.0.0.1 (targetcli). A live
test, run with TEST_LIBVIRT_ISCSI_TARGET, starts a transient iSCSI pool
on it and attaches the LUN from the VM page to a stopped and a running
VM: both definitions get a raw block disk on its /dev/disk/by-path link.
2026-10-18 21:32:52 +03:00
catborise
c46212bb38 fix(instances): a disk on an LVM pool is not resized, the page says so instead of failing
libvirt's logical pool cannot change a volume's capacity, so every
resize of such a disk ended in a 500. Disks are resized one at a time;
VIR_ERR_NO_SUPPORT becomes a message naming the disk, and the other
disks still grow.

A live test creates an LVM pool from the storage page on a spare disk
(TEST_LIBVIRT_LVM_DEVICE; the Vagrant computes get /dev/vdb), attaches
new and existing volumes to stopped and running VMs as block disks, and
checks the refused resize in every state.
2026-10-18 21:10:06 +03:00
catborise
0f9bbd3e86 feat(instances): choose VNC or the serial console on the VM page
Settings > Console gets a Console type field. Serial removes the VM's
graphics device (adding a pty console when there is none); VNC adds the
graphics device new VMs get, on localhost, and then applies the form's
VNC fields, so an administrator can open it to all interfaces for a TCP
compute. Serial is offered only while SERIAL_CONSOLE_ENABLED is on.
2026-10-17 19:48:58 +03:00
catborise
d37aa42392 feat(console): a working serial console
The xterm console never connected: socketiod expected a different token
format, its worker had no compute credentials, one global console was
shared by every client and its output broadcast to all of them, and the
Docker image did not run socketiod at all.

- console/tokens.py: one authorize() for noVNC and socketiod; the signed
  token is the only credential, checked at connect time.
- socketiod: one pty worker per client (consolecallback with the
  instance id), one session per VM, a session cap, input and resize
  checked; the parent never opens libvirt. The engine.io same-origin
  check is on.
- consolecallback: opens the console with the compute's credentials and
  exits with a message when it cannot.
- Docker runs socketiod as a runit service; nginx proxies /socket.io/
  with Host and the scheme for the origin check. Supervisor and systemd
  start it as well; WEBVIRTCLOUD_SERIAL_CONSOLE=1 enables it.
- Tests: token and session unit tests, socketiod as a process, and a
  live test that reads SeaBIOS from a wvc-test VM on the TCP and SSH
  hosts.
2026-10-17 19:43:09 +03:00
catborise
f5ec757d1a build: Chart.js 4.5.1 2026-10-17 13:19:43 +03:00
catborise
61ec138cfa fix(instances): the create wizard's pickers keep their height
Tom Select showed each chosen image and network as a chip inside its picker, which grew with every one and pushed the form down; the wizard lists them below the pickers already, so the chips are hidden.
2026-10-16 23:41:08 +03:00
catborise
057aabca1f build: Tom Select replaces bootstrap-multiselect
The create wizard's image and network pickers use Tom Select 2.6.2 (its
Bootstrap 5 theme and checkbox list), which does not need jQuery;
bootstrap-multiselect calls $.isArray, which jQuery 4 removed. The
selected images and networks still fill the wizard's own lists.
2026-10-16 23:25:15 +03:00
catborise
d3e4ef9132 feat(instances): a shut-off UEFI VM can be migrated
The move copies the VM's NVRAM file to the destination with libvirt
streams (unless both hosts share the NVRAM directory) and removes the
copy if the move fails; the source's file goes with the existing
cleanup. The undefine keeps the file, as libvirt refuses to undefine a
VM with NVRAM otherwise. A VM with TPM state is still refused.
2026-10-16 21:05:23 +03:00
catborise
c93bcc5d04 docs: a running VM can be cloned 2026-10-16 16:52:38 +03:00
catborise
46a611941e feat(instances): a running VM can be cloned
libvirt's backup copies the disks of a running or paused VM from one
instant into new volumes while it keeps running; the clone is defined
shut off as before. The VM is held for the copy, the copy can be
cancelled, and recovery reads the job's records and libvirt.
2026-10-16 16:52:38 +03:00
catborise
06f03d65fc docs: an ISO upload is a job 2026-10-16 11:53:59 +03:00
catborise
41e1fcd4b9 docs: migration runs as a job 2026-10-16 07:57:02 +03:00
catborise
c2d3285e0f feat(instances): a running migration can be cancelled; its time limit goes 2026-10-16 07:57:02 +03:00
catborise
31cbe60aa4 feat(instances): migration runs as a job 2026-10-16 07:57:02 +03:00
catborise
149c14f1bb docs: snapshot create and revert run as jobs
Live tests that expected the snapshot or clone result at once now call the
backend or run the queued job.
2026-10-15 14:21:32 +03:00
catborise
8d2e88f4bb feat(instances): snapshot revert runs as a job 2026-10-15 14:21:32 +03:00
catborise
6f81043438 feat(instances): snapshot create runs as a job 2026-10-15 14:21:32 +03:00
catborise
9d3d218fbb docs: clone and create run as jobs 2026-10-15 09:28:43 +03:00
catborise
6f0c730071 feat(instances): VM create runs as a job, also in the API 2026-10-15 09:28:43 +03:00
catborise
fbf82da5a9 feat(instances): VM clone runs as a job 2026-10-15 09:28:43 +03:00
catborise
49232c6e1d feat(storages): volume clone runs as a job 2026-10-15 09:28:43 +03:00
catborise
0fb0eb2bac feat(operations): jobs page 2026-10-14 23:27:57 +03:00
catborise
9c7943a0c8 feat(operations): job panel on the VM page 2026-10-14 23:27:57 +03:00
catborise
079e2ca5ca feat(instances): external snapshot delete runs as a job 2026-10-14 23:27:57 +03:00
catborise
68812ddbdf refactor(computes): the lock helpers take claims instead of file locks 2026-10-14 23:27:57 +03:00
catborise
5f8f7a8643 fix(instances): a snapshot needs free space in the VM's storage pools
A disk-only snapshot was taken without any space check, though its
overlays grow as the guest writes and a later delete merges them down;
a full pool pauses the guest and can leave the snapshot undeletable.
Only a memory snapshot was checked, for the memory alone.

Before a snapshot, each storage pool with a disk of the VM must have
free a share of those disks' virtual size (10%, at least 1 GiB), and
the pool of the memory file the VM's memory on top. Both values are on
the Settings page (Snapshot Free Percent, Snapshot Free Min GiB; 0
turns a rule off, an invalid value keeps the default). The pools are
refreshed first, and a failed refresh or an unreadable volume refuses
the snapshot instead of skipping the check; a disk in no pool is not
checked. Nothing is reserved: later writes can still fill a pool.
2026-10-13 13:27:39 +03:00
catborise
f766890825 feat(instances): external snapshots can freeze the guest file systems
A disk-only snapshot of a running VM is crash-consistent: data the
guest has not written yet is missing from it. With a connected QEMU
guest agent, the snapshot form now offers to freeze the guest's file
systems while the disks are snapshotted (libvirt's QUIESCE flag); the
box is checked by default and can be cleared. It is offered only for a
running VM whose agent is connected, and not together with memory:
libvirt accepts quiesce only for disk-only snapshots, and a memory
snapshot keeps the whole guest state anyway. A request that does not
fit is refused before libvirt is called.
2026-10-13 10:55:24 +03:00
catborise
64ea12b0ce fix(instances): the revert confirmation says the VM restarts
Reverting a running or paused VM to a disk-only external snapshot
powers it off: libvirt starts it again from the snapshot's disks, as
after a power cut. The confirmation now says so for such a snapshot; a
snapshot with memory resumes the VM as it was saved.
2026-10-13 09:36:36 +03:00
catborise
097a91592c fix(instances): a snapshot file another VM uses is not called left behind
After a snapshot revert or delete, files libvirt could not remove are
named in a warning that says nothing uses them and asks to remove them
by hand. A file another VM on the same host uses (as a disk, a backing
file or a snapshot file) is no longer named: removing it would break
that VM. The warning now says no VM on this host uses the files.
2026-10-13 09:21:51 +03:00
catborise
c970f8e853 fix(storages): keep volumes a VM still needs
libvirt deletes any volume it is asked to. The storage page and the API
deleted a VM's disk, the base image of another volume or the file a
snapshot was taken on without a check, breaking the VM or its
snapshots.

wvmStorage.del_volume now refuses a volume a domain on the compute uses
as a disk or a backing file of one, a file of a domain's snapshots, or
the backing file of another volume. The check is the one destroy
already used, moved to wvmConnect and extended to snapshot files, so
destroy also keeps the files of another VM's snapshots. The storage
page shows the refusal; the API answers 400 with it, as for other
refusals. Deleting a disk from a VM refuses after the detach when the
VM's own snapshots need the volume, and says the volume was kept.

Network disks count by their source name, pool volume disks by their
volume's path. A volume whose metadata cannot be read stops the check
instead of counting as unused; only one removed meanwhile is skipped.
The check and the delete hold the compute lock, so no VM operation
attaches the volume in between; a busy compute is a message on the page
and 409 in the API.
2026-10-13 09:13:02 +03:00
catborise
08cad04468 fix(instances): deleting a VM's data takes its snapshot files too
Deleting a VM with its data deleted only the images its disks pointed
at. With external snapshots that is the newest overlay: the image the
snapshots were taken on, the older overlays and the memory files stayed
on disk, with nothing referring to them once the VM and its snapshot
metadata were gone.

The files the VM's snapshots were taken on, down each disk's backing
chain, the overlays of snapshots kept off the chain after a revert, and
the snapshots' memory files are now the VM's own too. A template the
VM grew from ends the chain and stays, as does an overlay of a disk
detached since. The backing references of the VM's own volumes no
longer count as another domain's use, and a file another VM uses keeps
the files below it.

A backing file that cannot be read (other than one in no pool) stops the
split before anything is deleted, instead of ending the chain there.
2026-10-13 09:03:05 +03:00
catborise
ff4ffab382 fix(instances): snapshot tab reads less and fails less
The snapshot tab listed the VM's snapshots three times per page: once
per kind and once more for all of them. The list of all is now made
from the two kinds, so the combined flag is no longer used anywhere.

The revert confirm named only the disks the persistent definition
would lose. A disk hot-plugged into the running VM alone is detached
too, so the live definition is compared with what ran when the
snapshot was taken.

A storage pool refresh error after libvirt had already made, reverted
or deleted the snapshot was shown as if the operation had failed. It
is now logged, and the left-behind check is skipped then, since a stale
pool still lists the removed files. A volume removed while that check
reads it is skipped too.

An error reading the block jobs no longer breaks the instance page;
create, revert and delete still query the jobs again and refuse.
2026-10-13 08:30:22 +03:00
catborise
3fd422db7e fix(instances): name snapshot files libvirt could not remove
When libvirt cannot remove a snapshot file it merged or discarded, it
only logs a warning and the delete or revert still succeeds. On NFS it
removes them as the qemu user, so an image directory the qemu user may
not write to left every merged overlay on disk without a word.

Revert and delete now compare the files in use before and after and
show a warning naming the ones still on disk. Only a snapshot's own
files (overlays, memory) and overlays on a snapshot point count, and a
disk the revert detached is never named, so a kept disk is not offered
for removal.
2026-10-12 21:41:20 +03:00
catborise
ff1508b3c2 feat(instances): external snapshots use libvirt's snapshot API
Create, revert and delete are one libvirt call each. The pflash-to-rom
loader rewrite, the hand-written block commit and the s1./s2. names are
gone, so UEFI VMs can take external snapshots, a revert keeps disks added
later, and deleting an older snapshot keeps the newer one.

Create refuses what libvirt could not revert or delete later: internal
snapshots on the VM, a saved state, a running snapshot job, disks that
are not files, CD-ROM media, a branch after a revert, and memory without
a running VM or room in the first disk's pool. A disk-only revert keeps
the VM running, paused or shut off; one with memory resumes it as saved.
A revert in the same second as the previous one, which libvirt fails
after stopping the VM, is retried once.
A snapshot the old code took of a UEFI VM saved the loader as rom; its
revert is refused (deleting it still works).

The snapshots tab lists internal and external snapshots separately, can
save memory too, shows running snapshot jobs, and the revert confirmation
names the disks it detaches and that NVRAM is not restored.
2026-10-12 15:25:44 +03:00
catborise
c55f52f025 test: each app keeps its tests in a tests package
131 test modules sat next to the code they test; instances alone had
51 of them beside views.py and utils.py. They move into a
tests/ package per app, and the JavaScript checks into
webvirtcloud/tests/js. Each non-empty tests.py becomes tests/test_views.py;
the empty generated stubs are removed.

Imports of the app's own modules become absolute, paths built from
__file__ go one level up, the live helper is instances.tests.livetest,
and coverage omits */tests/*. The suite discovers the same 960 tests as
before.
2026-10-12 15:17:30 +03:00
catborise
87ffed66b9 fix(instances): attach a disk, NIC or guest agent to both definitions in one call
On an active VM a device was attached in two calls, running VM first.
When the persistent attach was refused, for example because a disk target
or an agent channel was already pending there, the device stayed in the
running VM only and was gone at the next start. A NIC added without a
MAC got a different generated MAC in each definition. One call with
AFFECT_LIVE|AFFECT_CONFIG lets QEMU check the persistent copy before the
hotplug and keeps one MAC. Detach and update still go per definition,
each from its own XML.
2026-10-11 20:12:24 +03:00
catborise
e924f3e7a0 fix(instances): a new disk on a running VM does not take a pending disk's name
The device name of a new disk or CD-ROM was chosen from the running VM
only. A name used only in the persistent definition, such as a disk
renamed by a pending bus change, could be chosen again: the disk was
attached to the running VM, the persistent attach was refused, and the
disk was gone after the next start. Names either definition uses are now
taken.
2026-10-11 17:58:19 +03:00
catborise
c1f6620c4b feat(instances): a running VM's settings tab says most settings apply at the next start
On an active VM most edits in the settings tab change the persistent
definition only. The tab now says so above its panes.
2026-10-11 17:47:44 +03:00
catborise
d0bd2ac32b fix(instances): a running VM's boot order, video and console forms show the pending settings
On an active VM the boot order editor, the video model select and the
console form showed the running VM: a pending boot order, video model or
console setting looked unset, and the boot order editor offered devices
of the running VM only. Choosing one of those cleared the pending order,
since the setter dropped the old order and skipped devices it could not
find. They now read the persistent definition (get_bootmenu,
get_bootorder, get_media_devices, get_video_model and the console
getters take config=), and set_bootorder refuses a device that is not in
it before it changes anything.
2026-10-11 14:56:51 +03:00