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

61 commits

Author SHA1 Message Date
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
a95ec40f69 fix(virtsecrets): a secret in use is not deleted, and a password is stored as typed
- A secret is in use when a VM names it in a disk, a host device or its
  vTPM, or a pool in its source, by UUID or by its usage within the usage
  type, as they run and as saved. The vTPM, host devices and pools' saved
  definitions were not checked before.
- A Ceph key is taken in base64, as `ceph auth get-key` prints it; any other
  value is a password or passphrase stored as typed, spaces included.
- A new secret takes its value at once; a value refused leaves no secret.
- TLS and vTPM secrets can be created; the usage has no length limit.
- Every POST redirects; the list is read once and only with its users; a
  secret, VM or pool gone meanwhile is left out.
- domain_trees() is shared by the in-use checks of secrets, network filters,
  networks and pools; the storage page's Ceph secret picker reads the same
  listing.
2026-10-29 10:10:43 +03:00
catborise
680dd5dad4 fix(interfaces): interface changes roll back as one, a running one is not deleted, each interface is read once
Plan reviewed with Codex; tried on a udev host (AlmaLinux 10) and a
netcf one (AlmaLinux 8).

- Changes are one change of the host's configuration (libvirt's
  interface change transaction): a bridge that does not start used to
  be undefined, leaving its member without the addresses and start
  mode the bridge took from it; now the whole change rolls back, the
  member's own configuration too, and so does a commit libvirt refuses.
  A rollback that fails too is reported with the failure, saying the
  host's configuration may be changed.
  Deleting a bridge, which defines its
  members again on their own, is one change as well.
- A running interface is not deleted: the page hid Delete, but a post
  deleted it. A bridge with addresses and several members is refused
  (which member takes the addresses is the user's choice); the
  confirmation says that its members are defined again.
- A bridge takes only a device in no bridge, bond or VLAN as the host
  has them configured, at any depth (a bridge's bond's devices), not
  the loopback, a libvirt network's bridge or a VM's tap; an ethernet
  interface configures only such a device too.
- Whether the host can change interfaces: unsupported or refused by
  its access rules is a no, bad XML a yes, anything else (the
  connection, the login) is raised instead of taken for a yes. A page
  whose host could not be read offers no actions.
- Reads: the list reads each interface's XML once from one listing
  (listAllInterfaces: every state, not the active and the defined ones
  apart); a page reads the running and the saved definition once each
  (cached_property). netcf writes a link speed of -1 for a card that
  has none (a virtio one) and then refuses its own XML: the saved one
  is read instead, where virsh and the page failed. Name and MAC come
  from libvirt's object, without a call. Each family says how it is
  addressed (DHCP, autoconf, static or none) and every address, its
  prefix where it has one; no
  address is not "DHCP" any more. A bridge's members carry their link
  state and speed; an unknown speed (0, -1, 4294967295) is none.
- The interface API lists from the same reader. get_details,
  get_bridge, get_bridge_slave_ifaces and the per-family getters go.
2026-10-29 10:09:40 +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
887b807451 fix: a network, pool or secret a VM needs is not stopped or deleted
Stopping a network cut the running VMs on it off, deleting one or a
secret kept the VMs that name it from starting, and stopping a pool
took the disks of running VMs away, all without a word. Each is now
refused with the VMs (and, for a secret, the pools) that need it:
stopping while a running VM uses it, deleting while a VM's definition
names it. A disk given by its file path does not need its pool's
definition. The API's pool stop is refused the same way.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 08:34:55 +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
a35284bbc9 feat(operations): the job worker 2026-10-14 23:27:57 +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
0bb4337043 fix(interfaces): no create, start, stop or delete where libvirt cannot change interfaces
libvirt's udev interface backend (RHEL 9 and later, Debian 12) only reads
interfaces, so the create form and the start, stop and delete buttons
only failed there. The pages ask the host with an invalid definition,
which defines nothing: a backend without define refuses it as not
supported. There they show the interface state and a note instead, and
posts change nothing; the host's devices, needed only by the create
form, are not read. Any other answer keeps the pages as they were.
2026-10-10 21:38:22 +03:00
catborise
4ea002f5a3 perf(interfaces): list only the host's network devices
The interfaces page read the XML of every node device (183 on a test host)
to keep the network ones; libvirt now lists only those. Same names on both
test hosts, 756 -> 261 ms over TCP and 475 -> 173 ms over SSH.
2026-10-09 22:17:00 +03:00
catborise
0ea5f0ff18 fix(instances): a UEFI clone gets its own NVRAM without a new pool
Cloning a UEFI VM defined a persistent storage pool on the NVRAM's
directory when no pool held it, and dropped <nvram> from the clone,
which found its copy only because libvirt chose the same file name.
The copy is now made through the NVRAM directory's pool (a transient one
when there is none) or the pool that holds the file, with the original's
format, owner and mode, and the clone's definition names it. A failed
definition removes it with the copied disks. An NVRAM that is not a file
is refused before anything is copied.
2026-10-09 18:13:32 +03:00
catborise
11fe2409f9 fix(instances): device edits fit every definition and device type
A volume of an LVM or iSCSI pool can be attached: by its own path, with
the format its volume reports (raw when the pool knows none). Detaching
a disk takes each definition's own description of it, so a disk only in
the persistent definition goes too. Every NIC type can be a boot device;
bridge and direct NICs were dropped from the boot order. The video model
is a value, never parsed as XML. QoS changes clear the page's read cache.

The NIC model "default" leaves the choice to libvirt instead of writing
model "default", and rt18139 is rtl8139: a VM with either did not start
(checked on a RHEL 10 host). A migration corrects the default NIC type
setting.
2026-10-09 18:13:23 +03:00
catborise
57f592df70 perf(computes): the host's info is read once per request
The overview read the host's info four times, now once (12 libvirt calls).
A connection object keeps it next to the capabilities, and the VM page and
VM stats take it from there; the VM page's cached_reads block holds only
the VM's own data. Free memory is still read each time.
2026-10-09 15:52:11 +03:00
catborise
9adc808d26 perf(computes): the create page reads the host's capabilities once
Measured on the create page: 751 ms and 72 libvirt calls, now 123 ms and
13. Its getters parsed the host's capabilities some 60 times, each read
from libvirt. A connection object (one per request) now reads them once,
and the domain capabilities once per emulator, arch, machine and domain
type. The VM page's cached_reads block no longer needs its own copy.
2026-10-09 15:24:03 +03:00
catborise
975b7f0baf fix(migration): remove the NVRAM file a live migration leaves on the source
libvirt undefines the source domain after a migration but keeps its NVRAM
file. A VM created later on that host with the same name reuses the file
and starts with the old VM's UEFI variables (boot entries, enrolled keys):
checked on libvirt 11.10, where only VIR_DOMAIN_START_RESET_NVRAM
recreates it from the template.

After a successful migration the file is now deleted on the source, as a
volume of the pool on libvirt's NVRAM directory or of a transient one.
Only a file in /var/lib/libvirt/qemu/nvram, and only when the destination
cannot create a marker file the source just created there (an exclusive
create also fails on shared storage whose listing an NFS client caches).
A failed removal is logged with the migration, which stands and is
recorded.
2026-10-09 07:29:10 +03:00
catborise
802e90c79f fix: connection errors on VM actions name the host only to administrators
When a VM action failed because its host could not be reached (the
connection could not be opened, or it dropped during the call), the
error page showed libvirt's connection error to any user: it names the
host and how it is reached, e.g. "ssh: connect to host 10.x port 22".
The lists and the VM page already keep that to administrators.

- Opening a connection now fails with ConnectionFailed (a subclass of
  OperationError). A dropped connection is recognized by its error
  domain and code (RPC/remote/SSH layer with a transport code, measured:
  a keepalive timeout is VIR_FROM_RPC/VIR_ERR_INTERNAL_ERROR); an
  operation the daemon does not support (VIR_ERR_NO_SUPPORT) is not
  treated as one.
- ExceptionMiddleware (pages and API) and the views that report the
  error themselves (snapshot, revert, disk deletion, clone) show it to
  superusers and view_instances users, and to everyone else "The host
  of this virtual machine cannot be reached right now." Errors of the
  operation itself ("domain is not running") are shown as before.

Checked on the test setup: powering on a VM of an unreachable compute
shows the owner the generic text and the administrator the error. The
tests fail on the previous code.
2026-10-06 10:08:48 +03:00
catborise
9c3a7cdbec fix(vrtManager): request values are not written into XPath expressions
Four lookups built XPath expressions from values that come from
requests (CodeQL py/xpath-injection, plus get_capabilities, which
CodeQL did not flag; the pages are for superusers):

- The architecture in the create wizard's URLs went into
  arch[@name='...'] in get_emulator, get_machine_types and
  get_capabilities: "nope' or '1'='1" matched every architecture. It is
  now an XPath variable ($arch), which is never parsed as XPath.
- Network filter rules were found with a predicate built from the
  posted action, direction and priority: a quote raised SyntaxError (an
  error page). The rule is now found by comparing its attributes.
- unset_qos put the posted direction into the path, so "*" removed
  every bandwidth setting. It accepts inbound and outbound, as set_qos
  does; the network page shows an error for anything else.

No other XPath in vrtManager is built from a value; the remaining
expressions are constants. The tests fail on the previous code.
2026-10-06 08:55:41 +03:00
catborise
eab27f8aba fix(vrtManager): the libvirt event loop starts per process, on first use
The connection manager registered libvirt's event implementation and
started the event loop thread when vrtManager.connection was imported.
novncd imports it and then forks a child per console (websockify's
multiprocessing), so every child was forked from a process with a
running libvirt thread, which libvirt does not support, and the child
had no loop thread of its own: keepalive never ran there. Measured in
the test setup: in such a child, a call on a host that went silent
still waited after 70 s.

- The loop starts with the first get_connection of a process. The
  state is per process and shared by all managers; libvirt's event
  implementation is registered once and a child inherits it.
- After a fork, the child renews the lock and starts its own loop on
  first use. It stops using the inherited connections, which are the
  parent's sockets, but keeps them: releasing them would run libvirt
  destructors in the child. No process connects before forking today
  (novncd's parent does not connect, gunicorn does not preload); this
  keeps such a case safe.

With the change, novncd's parent has no libvirt thread, the console
child starts its own, and the same call fails after 29 s with a
keepalive timeout. A test forks a real child; it fails without the
fork handling.
2026-10-05 14:41:31 +03:00
catborise
10b46afd36 fix(vrtManager): libvirt connections open within a time limit
libvirt has no timeout for opening a connection. Measured in the test
setup: an unreachable host blocked qemu+tcp for 135 s and qemu+ssh for
133 s; a daemon that accepts and never answers blocks forever. Gunicorn
runs sync workers and the instance list connects to every compute, so
one unreachable compute held a worker for minutes per page load.

- A connection is opened in a helper thread, at most one per
  connection at a time. The request waits LIBVIRT_CONNECT_TIMEOUT
  (default 5 s). An open nobody waits for any more keeps running; until
  it ends, requests get its error at once, and if it succeeds later the
  connection is used. A failed open is not repeated for
  LIBVIRT_RETRY_AFTER (default 30 s). A stuck host holds one thread and
  does not keep other hosts from connecting.
- qemu+ssh runs ssh through vrtManager/libvirt-ssh (the URI's command
  parameter), which adds ConnectTimeout=10 and BatchMode=yes, so an
  abandoned ssh process ends and no prompt can hang. The user's ssh
  configuration (aliases, ProxyJump, keys) still applies.
- The close callback acts only for the connection it was registered
  for: a late callback of a replaced connection dropped its successor.
  The reason is reported as text. A connection that closed before it
  was published is an error.
- Compute.status is a bool (it returned the exception object, so the
  "host is down" check in refresh_instance_database never fired); the
  message is Compute.connection_error.

Unit tests cover the timeout, retry delay, late success, single open,
a stuck host next to a healthy one and the close callback. Live tests
(vrtManager/test_live_connection.py), for TCP and SSH computes of the
test setup: an unreachable address (no ssh left after 10 s), an endpoint
that never answers, a slow open completing later, a host that goes
silent on an established connection (keepalive ends calls within 45 s,
the next request reconnects), an ssh_config alias and a ProxyJump host.
2026-10-05 09:06:52 +03:00
catborise
39feb373ed fix(vrtManager): host_is_up returns a bool; slow hosts block no others
- host_is_up returned the exception object for a down host, which is
  truthy: check_user_quota then connected to it and failed, so a user
  with a VM on a down host could not open its page, clone or resize,
  and migrate_instance's "target is down" check never fired. It returns
  True or False now, closes its socket, and accepts IPv6 and host:port.
- The connection manager no longer holds its global lock while a new
  connection is opened, so one slow host does not delay new connections
  to the others.

Bounding the connection time itself (including SSH aliases and jump
hosts) is left for a separate change.
2026-10-04 01:08:11 +03:00
catborise
57cf49262a fix(instances): clones roll back their copies; own error messages stay
Clone:
- only shut-off VMs are cloned, as the UI already required: a running
  guest keeps writing while its disks are copied; the definition comes
  from the persistent XML;
- MAC addresses and disk destinations are checked before anything is
  allocated;
- every volume the clone creates (NVRAM and disks) is tracked; if a
  later copy or the definition fails, only those are deleted, and any
  that cannot be deleted are named in the error. Before, a failed clone
  left its earlier copies behind.

libvirtError(msg) reports the thread's last libvirt error instead of
msg when there is one, so WebVirtCloud's own errors could show an
earlier, unrelated failure. util.OperationError, a libvirtError
subclass that keeps its message, replaces those raises in vrtManager,
instances and nwfilters; 'except libvirtError' still catches it.
2026-10-04 01:08:11 +03:00
catborise
e5fda79f5a feat(console): VNC is the only console type
SPICE was removed earlier; its leftovers and the console type choice go
too:
- the console settings form, update_console and the backend no longer
  offer or change the type; an invalid console form now shows its
  errors instead of being ignored;
- the create form, the API serializer and create_instance no longer take
  a graphics type, new VMs get VNC; get_graphics_types and the graphics
  entries of the domain capabilities are gone;
- migration appsettings.0013 deletes QEMU_CONSOLE_DEFAULT_TYPE;
- a VM defined elsewhere with another graphics type gets "Console type
  ... is not supported".

The locale catalogs still reference the deleted SPICE templates in
source comments; regenerating them is left to the translation work.
2026-10-04 01:08:11 +03:00
cserma
7d1cfb69b0 network interface model selection added for new instance and new interface 2023-08-16 09:13:11 +03:00
catborise
2f1b11b3ca reorganize host interfaces. remove it from instance methods 2021-02-12 16:05:16 +03:00
catborise
508e3609be lint with black python. convert f style strings to old one. some small fixes 2020-11-05 12:34:31 +03:00
catborise
84a545dc61 fix divide remainder error for memory resize etc 2020-07-20 12:10:30 +03:00
Real-Gecko
47009d47ca Instances overhaul 2020-07-13 15:33:09 +06:00
catborise
11fcb6c0f3 show socket error & check with is True 2020-06-04 08:52:21 +03:00
catborise
0c72ccd066 add localhost is up checking 2020-06-04 08:51:54 +03:00
catborise
22cb8f702a fix code conventions for pylint 2020-06-04 08:48:43 +03:00
catborise
4d40de1b55 Python3 & Django 2.2 Migration - Fix & Updates 2020-03-16 16:59:45 +03:00
catborise
c62e6ba3bc Add changing option of network model. Show model on instance network list 2020-01-10 17:55:42 +03:00
catborise
30ed71e8ae add disk options choices 2020-01-08 11:24:22 +03:00
catborise
e87d7463fe check if host supports kvm, if it is, use kvm else qemu 2019-12-25 14:34:55 +03:00
catborise
592112590c add ability to show interface's ip address with qemu agent. Add checking agent if installed or not 2019-12-23 15:48:11 +03:00
catborise
1b0324e3e3 Add is_support_virtio function and is_qemu functions 2019-12-19 12:06:42 +03:00
catborise
dd16a5b2d5 Enrich Instance Create operation: -add capability to create arm, ppc, i686, aarch64 instances, -add option to choose firmware, -add options to choose chipset. -add capability to choose volume driver options. -add new default settings 2019-12-13 16:47:51 +03:00
catborise
74eb8004d7 changing video card model functionality added to options. 2019-11-20 16:24:01 +03:00
catborise
33407719b6 connection.py getting networking format information function 2019-11-19 14:18:36 +03:00
catborise
568ff92449 libvirt does not have the connection close reason contants anymore. pep8 conventions apply 2019-10-30 11:05:00 +03:00
catborise
828a271789 pep8 style code corrections 2019-09-10 09:48:31 +03:00
catborise
c98f0b1232 Add lib and qemu version info to compute overview 2018-12-05 11:08:21 +03:00
catborise
84bf04a137 add getting list of disk device types. Rename getting disk bus types function 2018-11-22 14:45:12 +03:00
catborise
7d21c138b7 calling hypervisor, machine, emulator operation is linked to arch variable. 2018-10-02 15:15:41 +03:00
catborise
4d14fdceea selection added to domain creation for nwfilters. Add nwfilter option while adding network card. NWfilters minor fixes 2018-09-24 14:41:13 +03:00
catborise
35bc8c67e4 Add NWFilters 2018-09-21 19:11:17 +03:00
catborise
f477dd6a11 add domain capabilities info. add getting list of hypervisor in detail. add getting bus types from dom cap. add getting graphics video type info. 2018-09-13 16:55:51 +03:00
catborise
9176cb6204 lxml conversion of get_info 2018-07-24 13:55:33 +03:00
catborise
e387c3a21d Host gets list of all storages active/inactive. If there is some inactive storages it gives error. But it coulde be inactive. It is normal. Changing the behaviour of getting list of storages. 2018-07-20 14:04:13 +03:00