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

31 commits

Author SHA1 Message Date
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
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
4dff1008cc fix(instances): only superusers and staff owners change a template VM
The documented rule held only for snapshots: an owner with is_change could
still resize a template, set its root password and SSH keys, change its
options and console settings. get_instance(perm_type="change") now
refuses a template to owners who are not staff, which covers every change
view and the API; the per-view snapshot checks it replaces are removed,
and the page offers those controls by the same rule. Viewing, the console,
power (a no-op on a template) and cloning are unchanged.
2026-10-07 21:04:23 +03:00
catborise
043c24a208 fix(instances): a migration that does not finish in time is cancelled, not cut off
A VM that changes its memory faster than the network carries it never
finishes a live migration without auto converge: measured with a guest
rewriting about 1.2 GB/s over an 8.5 Gbit/s link. The migration then
ran until gunicorn ended the request after 600 s. libvirt cancelled it
when the request went away and the VM stayed on the source, but the
user got an error page and nothing was logged.

The app now cancels a migration that has not finished after 540 s
(MIGRATION_TIME_LIMIT, below the 600 s of gunicorn.conf.py; a test
keeps it below). The failure is reported and logged like any other:
the migration was cancelled, the VM stays on the source host, and a
VM like that needs auto converge. A timer that fires as the migration
ends finishes before the compute locks are released, and an error
the cancel did not cause keeps its own message.
2026-10-06 16:56:22 +03:00
catborise
5c22fc8436 fix(instances): a finished migration is recorded, and a stale request does not migrate
What migrate_instance did around the migration itself:

- It checked the destination with a 1 s TCP probe of the libvirt port,
  which passes while libvirt is down or refuses the login. It now uses
  the compute's connection status, as the rest of the app does.
- Two requests for the same VM waited for the compute locks one after
  the other; the second then migrated with the VM's old location. The
  VM's record is read again under the locks, and the request is
  refused when the VM is no longer on the source.
- Restoring autostart on the destination came after the migration but
  before the database move; when it failed, the VM ran on the
  destination while the app recorded it on the source. Autostart is
  now read from libvirt and a failure to restore it is only logged.
- A database failure after a successful migration was logged as
  nothing; the log now says the VM migrated and recording it failed.
- When the destination's list had already made a record for the VM,
  the merge kept the owners but dropped the template mark.
2026-10-06 15:52:32 +03:00
catborise
5061cc7f2c fix(instances): a migration runs in the mode the VM's state allows, and is logged as it ran
moveto read the VM state for each flag and dropped options that did
not fit it, so a request could run as something else than asked; the
log then named the mode from the form ("live" or "offline"), not the
one used. The API did not log at all and answered "instance migrate is
started" after the migration had finished.

- moveto reads the state once. A running VM migrates live, or without
  live paused for the transfer (non-live); a paused VM stays paused; a
  shut-off VM moves offline. Live for a shut-off VM, offline for a
  running or paused one, and any other state are refused before
  anything changes. unsafe, auto converge (live only) and compressed
  apply to a running VM.
- migrate_instance logs the outcome for the VM page and the API: the
  mode and options used, or the error. The API answers
  "instance is migrated (<mode>)" and accepts requests that leave out
  an optional option.
- Post copy is gone from the form, the view and the API: only the
  flag was set, and without migrateStartPostCopy, which the app never
  called, the migration never switched to post copy.
2026-10-06 15:26:49 +03:00
catborise
5a523befbd feat(computes): a compute names the address migrations reach it on
Migration is client-driven: libvirt sends the VM's memory to the
destination's own host name unless a migration URI is given, and the
app never gave one. Where the hosts cannot resolve each other's names
(or the names point at a management network) every migration failed
with "Unable to resolve address '<host name>' service '49152'".

A compute now has an optional migration address (an IP address or a
host name, no scheme or port), editable in the compute form and the
API. A VM migrating to that compute is sent to tcp://<address>
(bracketed for IPv6). Empty, the default for existing computes, keeps
the previous behavior. The compute's host name is not reused, because
for SSH computes it can be an ssh_config alias.

The migration log entry names the address when one is set.
2026-10-06 15:05:40 +03:00
catborise
25b3671c33 fix(instances): a migrated VM's source definition is always removed
Migration left the VM defined on the source host unless "Delete
original" was ticked (VIR_MIGRATE_UNDEFINE_SOURCE). With the shared
storage migration needs, that definition points at the disks the
destination VM is running on: the VM showed on both hosts and could be
started (or autostarted) a second time against the same disks; only a
configured lock manager stops that.

moveto now always passes VIR_MIGRATE_UNDEFINE_SOURCE with
VIR_MIGRATE_PERSIST_DEST. The option is gone from the form, the view,
the API (MigrateSerializer.xml_del) and the clone auto-migration. It
removes the definition, not the disk files.
2026-10-06 13:58:59 +03:00
catborise
fb3f686532 fix(instances): the quota counts every VM and refuses what it cannot verify
check_user_quota added up the vCPUs, memory and disks of the user's
VMs, but skipped a VM whose host failed a TCP probe. A host that was
down therefore let users exceed their quota, and the probe always failed
for ssh computes named by an ssh_config alias or reached through
ProxyJump, so their VMs were never counted. A VM that could not be read
turned the page into the error page instead.

- Hosts are checked with Compute.status, the connection WebVirtCloud
  uses, once per host. A VM that cannot be read (host down, VM gone)
  makes the usage unknown: clone and resize up are refused with their
  own message (fail closed).
- A request that adds nothing is not checked, so a user over quota can
  still shrink a VM. Users without limits, and an exceeded instance
  count (from the database), need no host either.
- Memory resize of a running VM changes only the current memory, but
  the quota was charged with the submitted maximum. It is now charged
  with no change, and resize_mem gets the current maximum, so a VM that
  shuts down meanwhile cannot take an unchecked one.
- The VM page no longer computes the quota on every load (it read every
  VM of the user on every host for a "quota reached" banner). The limit
  is reported when a change is refused.

The test that expected a down host to pass the quota now expects it to
be refused. Checked against the test hosts: with a VM behind an
ssh_config alias the previous code counted 29 vCPUs, the new one 31;
with a VM on an unreachable host the previous code allowed +1 vCPU,
the new one refuses it after 5.1 s and allows -1 at once. The new
tests fail on the previous code, except the ones guarding unchanged
behaviour.
2026-10-05 12:26:57 +03:00
catborise
5c4eb97410 fix(instances): clone name and MAC read the same dhcpd.conf
An optional dhcpd.conf next to the application supplies clone names and
their MAC addresses. get_dhcp_mac_address read it from BASE_DIR, but
guess_clone_name from a fixed /srv/webvirtcloud/dhcpd.conf, so on any
other install path the Guess button found no name while the MAC lookup
still worked. Both use utils.dhcpd_conf() (BASE_DIR) now.
2026-10-04 19:39:29 +03:00
catborise
03ac93ea53 fix(instances): CD-ROM bus, USB controller and MACs of a new VM
Creating each option on a RHEL qemu-kvm host showed:

- CD-ROM: the create page offered every disk bus, but the empty CD-ROM
  of a new VM works only on ide, sata and scsi. fdc and virtio failed
  ("Invalid bus type", "does not support ejectable media"), usb failed
  with a device named 'Nonea' because vol_dev_type() had no prefix for
  usb, and after that libvirt refuses an empty usb disk. Only ide, sata
  and scsi are offered now, and another value is refused before any
  storage is allocated. vol_dev_type() names usb disks sdX, which also
  fixes data volumes on the usb bus.
- USB controller: any input device added a bare <controller
  type='usb'/>, which libvirt turns into piix3-uhci (USB 1.1) on q35
  instead of its default qemu-xhci. The bare controller is gone; libvirt
  adds the machine's default for USB inputs.
- MACs: with more networks than MAC addresses, create_instance indexed
  past the list and the request failed with a 500. NICs without a given
  MAC now get one from libvirt. MAC addresses are validated (format, not
  more than networks) by the page and the API before anything is
  created; they went into the XML unchecked.

Unit tests cover the refused values, the MAC rule and the usb prefix.
Live tests create a VM with every offered CD-ROM bus and start it,
check the USB controller with a USB input device and create a VM with
two networks and one MAC.
2026-10-04 18:33:28 +03:00
catborise
792487e65e fix(instances): failures reach the user; no redirect to other sites
Web views:
- 34 views returned to request.META["HTTP_REFERER"] + "#tab": without
  a Referer they failed with a TypeError (500), and a Referer from
  another site was followed (open redirect). They return through
  _back(), which keeps a same-site Referer and otherwise opens the VM
  page at the tab. get_safe_redirect uses the same check. Rejected disk
  options also return to the VM's disks tab instead of the VM list.
- migrate_instance returned silently when the target was the current
  compute or unreachable, so the web view logged "Instance is migrated"
  and the API answered "instance migrate is started". It now raises;
  the view reports the error and logs only a completed migration, and
  an unknown compute id is a 404 instead of a 500. The automatic
  migration after a clone skips a target that is the current compute.
- add_owner and del_owner failed with a 500 without their ids, add_owner
  also when the user was already an owner, and del_owner deleted the
  given ownership record even when it belonged to another VM. Ids are
  validated, a record of another VM is a 404, and a repeated owner is
  reported.
- Broad handlers that hid programming errors are narrowed: clone catches
  libvirt, value and OS errors (a lock timeout included), ownership
  lookups catch DoesNotExist. The DRBD status no longer needs its bare
  except: the VM name is escaped in the regex, which was the only thing
  that could fail there.

API:
- A libvirt error under /api/ is JSON with status 400, and a lock
  timeout 409, instead of the HTML error page. This lives in the
  existing ExceptionMiddleware, so existing settings need no change.
- Volume create/delete on an inactive pool answer 409, and invalid
  volume data 400 with the serializer errors, instead of 200.

The new tests cover every view that returns with _back() without a
Referer and with a foreign one, the migrate failures on web and API,
the owner ids, the API error format and the volume statuses; nine of
the ten fail on the previous code.
2026-10-04 12:23:54 +03:00
catborise
f94b3cee7a chore: drop references to the local roadmap from code and tests
Comments, docstrings and live test names cited item IDs of a roadmap
that is not part of the repository. They now say what is meant, or
nothing where the sentence stands without them. Test assertions and
behavior are unchanged.
2026-10-04 11:09:25 +03:00
catborise
3e427a41b8 fix(instances): find known VMs by UUID, not by name
Instance.proxy looked the domain up by its stored name. After a rename
or a name swap outside WebVirtCloud, an operation on one VM changed
another; after a VM was replaced under the same name, operations hit
the new VM.

wvmInstance takes the UUID and uses lookupByUUIDString, with no
fallback to the name: a missing UUID fails instead of picking another
domain. Every caller that has an Instance passes it: proxy, console,
quota, post-migration autostart and the VDI URL. Migration uses the
domain the proxy already resolved. getvvfile and the gstfsd requests
use the host's current name of that UUID.

Live tests cover a name swap and a VM replaced under the same name;
both fail on the previous code.
2026-10-04 01:08:11 +03:00
catborise
e626da5002 fix(instances): do not refuse clones for volume-less disks or unusual names
Clone refused VMs that cloned fine before: a disk without a volume got a
None disk name, and a source image with characters such as a space or "@"
produced a derived name outside the allowed pattern. Disks without a
volume (which clone_instance does not copy) are no longer validated, and
derived disk names are sanitized to the allowed charset. Names typed by a
superuser are still validated strictly.
2026-10-02 23:14:28 +03:00
catborise
34851b8dc2 Merge branch 'fix/wave0-staff-console-password' into wave0-integration 2026-10-02 22:14:40 +03:00
catborise
8e23699987 fix(instances): show console settings and the VNC password only to console managers
The console settings form on the VM detail page renders the VNC password
(PasswordInput(render_value=True)) and was shown to every is_staff user.
A staff user with the global view_instances permission, or a staff owner
without is_vnc, could read the VNC password of every VM they could see.

Add instances.utils.can_manage_console (active superuser, or an active
owner with is_change and is_vnc), which matches what update_console
already enforced. The detail view only builds the form, and so only puts
the password into the page, when it returns True; the template and
update_console use the same rule.
2026-10-02 22:04:05 +03:00
catborise
4d34d9d07c fix: add an authorization matrix test and close the endpoints it finds
Add webvirtcloud/test_authorization_matrix.py, which walks every named URL
and checks that a user without any VM/compute relation only reaches an
explicit allowlist, and that the global view_instances permission does not
open consoles. It found:

- /computes/<id>/... views without any check (compute_graph,
  get_compute_*, get_dom_capabilities, storages.get_volumes): the whole
  computes URL namespace is now wrapped with superuser_only, so a view
  added there later cannot be exposed by a missing decorator.
- guess_mac_address (leaks dhcpd.conf host/MAC pairs), random_mac_address
  and guess_clone_name: superuser-only, like the buttons that use them.
- check_instance: cross-tenant VM name lookup with no caller; removed.
- getvvfile and get_vdi_url: reachable with only view_instances, and the
  .vv file contains the VNC password.

Console access now follows one rule, instances.utils.can_open_console:
an active superuser or an active owner of the VM. The global
view_instances permission is read-only. It is used by the console page,
novncd, getvvfile and get_vdi_url (getvvfile and the VDI tab are kept for
virt-viewer users). The console page answers 403 instead of 500 when
access is denied, with the same answer for a missing VM.
2026-10-02 20:33:12 +03:00
catborise
2cdf5dfca1 fix(reconciliation): serialize compute sync and enforce UUID identity 2026-09-28 14:55:49 +03:00
catborise
3f8eeb04bd fix(reconciliation): implement UUID-based domain reconciliation and connection safety 2026-09-27 13:13:43 +03:00
MisterBlueBear
5a211c0c56 Get MAC OUI from settings.py 2023-10-18 17:34:40 -04:00
Amélie Krejčí
1b2b3a3bce
Get UserAttributes object using get_or_create
This is done to automatically create the UserAttributes object in case LDAP User Backend didn't create it.
2023-09-23 09:52:48 +02:00
catborise
217e106c8b format python code with black 2022-11-02 08:54:35 +03:00
catborise
073b7b6717
Bootstrap5 (#17)
* Bootstrap5 migration
2021-07-07 14:12:38 +03:00
catborise
596d55d481
fix uuid and name changes and update them on db correctly. (#404) 2020-12-31 11:30:33 +03:00
catborise
993a247228 update uuid number if it is changed to fix console view error 2020-12-15 11:39:58 +03:00
catborise
85d4c5f132 fix to prevent not opened connection close error 2020-10-21 12:10:42 +03:00
Real-Gecko
0f66187e80 More instances tests 2020-10-15 11:58:42 +06:00
Real-Gecko
1b3fd9d05e replaced ugettext_lazy with gettext_lazy 2020-10-14 18:27:57 +06:00
Real-Gecko
47009d47ca Instances overhaul 2020-07-13 15:33:09 +06:00