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.
The memory given read "24.0 GB given of 32.0 GB": long, and the share
of the host's memory, what one looks for, had to be worked out. It is
now told as the vCPUs are: the share large ("75 %", amber over 90 %,
red over 100 %), then "24.0 GB / 32.0 GB", the bar and "12.0 GB
running". The Computes list's memory column shares it.
Every card of the overview has a Bootstrap card-header with an icon:
VMs, vCPUs, Memory, Storage, CPU in use and Memory in use (with the
current reading at its right), Hardware, Software and connection. The
last two were label and value lists under indented headings; they are
list-group-flush rows now, the label at the left, the value at the
right: counts in semibold, the hypervisor's and libvirt's versions as
badges, the guest architectures as light badges, the emulator and the
connection as code. The section headings, indented by the panel's
page-header margin unlike the cards, are gone: each card names itself.
The status line and notes are not paragraphs, which the panel indents.
A host the panel could not reach made its overview the 500 page,
without the host's sections, and the Computes list gave such a host an
Overview button that led nowhere. The page now keeps the host's name,
sections, "Edit connection" and "Remove from panel" (the existing
confirmation), and says "Not connected" with the reason and a "Try
again". Details that cannot be read are said in an alert; the VMs,
capacity and load still show, as they are read on their own.
A reachable host's page leads with what it runs and what it gave:
- its VMs by state (running, shut off, paused, other), linked to
Instances;
- the vCPUs, memory and storage pools given, as on the Computes list,
which now shares those parts (computes/capacity_*.html) and their
preparation (views.shown_capacity) with it;
- CPU and memory in use, side by side, with the current reading in
words, a note on what the charts hold and when they were last
updated, or since when not; memory in GB like the rest of the page;
- Hardware and Software and connection: logical CPUs and clock from
virNodeGetInfo, but physical cores, sockets and NUMA nodes counted
from the CPUs of the capabilities' NUMA cells, as virNodeGetInfo
makes a topology up for hosts it cannot describe (asymmetric NUMA);
the hypervisor's own name and version, libvirt, emulator, the guest
architectures as text instead of menus of dead links. A host without
sysinfo shows no processor instead of failing.
Empty notes no longer show "None"; labels stack over their values on a
phone instead of running into them.
Pools that cannot be read no longer drop a host's vCPUs and memory from
its capacity: the storage part says so. get_capacity also counts the
VMs by state.
- The host graphs and the machine/bus lists connect inside their libvirt
error handling, so a down host gives empty answers instead of a 500 page
the polling script drops.
- The Secrets page lists vTPM secrets instead of failing on them, and a
secret without usage data shows an empty cell instead of "None".
- A TCP/TLS host takes a SASL password longer than 14 characters and a
login up to the Linux limit of 32.
- The compute edit page says "Edit Compute"; a host without VMs no longer
closes an extra page element; the memory graph is labelled MiB.
Under each host's CPU and memory load, the dialog shows one line:
Given to VMs: 24 vCPUs (1.5:1), 48.0 GB memory.
Pool default: 120.0 GB free.
The line is read when the dialog opens, from computes/<id>/capacity/,
as plain text, so the VM list itself reads no VM definitions. The CPU
bars and the lines share one queue, host by host and two reads at a
time, as each read holds a worker. A host that cannot be read says so.
Compute.capacity now also gives the vCPUs per CPU and its pools with
the default one first, so the Computes page and the dialog share them.
The capacity read lived in its own module with raw libvirt calls and a
thread pool with a deadline, beside helpers that already read the same
things. It now uses them:
- wvmHostDetails.get_capacity() sums the vCPUs and memory of all and of
the running VMs from get_host_instances(), against the host's info.
- get_storages_info() also gives each pool's free space.
- Compute.capacity joins the two, like cpu_count and ram_size, and is
None when the host is down or the read fails.
The page reads the hosts one after another, as it already connected to
each of them; a host that cannot be reached is given up after the
connect timeout. computes/capacity.py and its thread handling are gone.
The page looks and sorts as before.
Each host is a row of what it has committed, read live from the host (in
parallel, by a 10 s deadline):
- Host: a status dot, the name, its address and details; when it cannot
be reached, why.
- vCPUs: the overcommit ratio, large (warning above 2:1, red above
4:1), over "N vCPUs on M CPUs" and the running VMs' share.
- Memory: memory given to all VMs against the host's memory, as one bar:
running VMs dark, shut-off VMs light (they take their memory again
when they start), and a line where the host's memory ends; red when
the VMs were given more than the host has.
- Storage: the default pool (or the first) with its used share and free
space; the others, a host may have dozens, open on a click.
A host that cannot be reached, or does not answer in time, says so in
one line instead. vCPUs and Memory sort by ratio and share. A host whose
read hangs is not read again until that read ends, so a stuck host keeps
at most one thread. The table's hidden status texts stay inside it, so
the page does not widen on a phone.
The Logs page shows who added, changed or deleted a compute, from web
or API, and every login, failed login, lockout and logout, with the
client address and a Result column (Done, Failed, Unknown for older
rows). A failed change is recorded with its reason and the error still
reaches the user; a refused one is recorded too.
- logs.audit.write saves each row in its own savepoint and never
raises, so a log write cannot break or undo the action it records.
The audit() context manager writes one row when the change ends.
- Logs gains success and ip; user, host and instance are wider and
date is indexed. The page orders by date then id.
- A compute edit names the changed fields; an edit that changes
nothing writes no row, and an empty password field is no change.
- A compute's VMs leaving the panel get a system row each: owners
kept on the Removed VMs page, moved to another compute, restored,
left without owners, or the removed record expired.
- Job rows carry the result. A job's log entry that cannot be
computed is logged and skipped instead of rolling back the finish
(test_service.py changes this on purpose).
- Logins are recorded by signal receivers; CustomLoginView is removed
and the plain LoginView is used. A wrong OTP code is a failed login,
counted by django-axes toward the lockout.
Panel records and host-side objects follow in the next layers.
Deleting a compute removed its VMs from the panel together with their
owners, while the VMs kept running on the host: added again, the host's
VMs came back with no owners. A compute's delete now treats each VM as
one that left its compute: its owners follow the same UUID on another
compute or wait in a tombstone for the retention period. One function,
forget(), does this for the refresh and for the delete; the API's
compute delete goes through the same model delete.
The delete holds the compute exclusive and locks its row before its
VMs', as the reconcile does: a reconcile, VM operation or job on the
compute makes it busy instead of losing owners to a concurrent restore
or deadlocking with it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The shared delete page asked only whether to delete the object. Each
view now gives its consequence: a deleted compute's VMs keep running
but the panel forgets them and their owners, a deleted user's VMs
stay, a group's members keep their accounts, a VM's owner loses only
the access, a removed VM's record no longer brings its owners back,
an SSH key stays on the VMs that have it, a flavor only goes as a
size template.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
No page or script called it, and it answered with the disk device
types for both videos and buses. The create view keeps reading the
domain capabilities through wvmConnect.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The bus list of a disk on the VM create page came from the host's disk
device types: choosing "disk" for an added image offered cdrom, disk and
lun as its buses. The disk branch of get_disk_buses now returns the
host's disk bus types, the list the page starts each disk with.
Each poll of a host's overview graph held a worker for one second between
two CPU samples. The answer now carries its sample, signed with the time
and the compute; the next poll sends it back and the usage is taken since
then, so a point is the average since the previous one (normally 5 s, at
most 15 s). Without a sample, or with one that is altered, older or from
another host, or when the host restarted, it is measured over one second
as before.
A sample is one reading; the busy share was mixed from separate readings,
counted guest time twice (it is part of user) and added half a percent.
A host that cannot be read leaves a gap in both graphs instead of a zero
CPU point and a broken memory axis.
The overview page decided whether the host is up with a TCP probe of
the compute's hostname. For an ssh compute named by an ssh_config alias
or reached through ProxyJump, that name and port cannot be reached from
WebVirtCloud directly, so the probe failed while the connection worked:
the page showed the host details and never started the CPU and memory
graphs. The overview now uses Compute.status, the connection
WebVirtCloud uses for everything else.
The compute list shows why a host is not connected (Compute.connection_error)
under "Not Connected", for example a host key verification failure.
Checked against the ssh compute of the test setup through an
ssh_config alias: the previous code renders the overview with the
graphs off, the new one with them on. Both new tests fail on the
previous code.
computes.views.get_compute_video_models had no URL and no caller, and
ComputeManager.get_video_models was used only by it. The video models
of the create page and the VM settings come from wvmConnect directly.
Super-linter ran from the unpinned :latest image and, because of
branches-ignore: [master], never on pull requests to master. Ruff now
checks syntax errors and pyflakes rules (E9, F) on every push and PR.
Fixes the existing findings: unused imports and variables, an unused
duplicate import in instances/tests.py, and the star import in
accounts/views.py. settings-dev.py keeps its star import of the base
settings.
compute_graph (text/javascript) and the machine type, disk bus, video
model and domain capability views (text/html) return JSON; send it as
application/json. create_instance_w1.html builds the chipset <option>
elements with .val().text() instead of string concatenation.