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

2055 commits

Author SHA1 Message Date
catborise
d15842a250 perf: the Logs and VM report downloads are written row by row
The workbook kept every cell as an object: a 100 000-row Logs download
took some 400 MB in a web worker. openpyxl's write-only mode writes the
rows as they come (85 MB for the same file, measured), and the Logs rows
are read as tuples, not model objects. The bold, frozen, filtered header,
the column widths and the text cells stay as they were.
2026-11-05 18:58:13 +03:00
catborise
d106a4f7ba perf(instances): the Stats tab reads a VM's counters in one libvirt call
Each poll called info(), then blockStats() for every disk and
interfaceStats() for every NIC: nine calls for a VM with four disks and
two NICs, every 5 s for each open Stats tab. domainListGetStats now gives
the CPU time and every disk's and NIC's counters at once; a counter the
host does not report is -1, as before.
2026-11-05 18:58:13 +03:00
catborise
34dd61c80c perf(console): a serial console asks for its VM's state once a second
Its worker asked libvirt for the VM's state five times a second, up to
100 calls a second with 20 consoles open, only to notice a start or a
stop. Once a second is enough: output and keys do not wait for it.
2026-11-05 18:58:13 +03:00
catborise
51aa712802 fix(console): a serial console whose VM was deleted meanwhile says so
consolecallback read the VM's record with .get(): one deleted between
socketiod's accept and the worker's start ended with a Python traceback
in the user's terminal, which is the worker's stderr. It now says the VM
is no longer in the panel.
2026-11-05 18:58:13 +03:00
catborise
c8fb778d08 fix(instances): recording a migration holds the ownership lock
record_migration moved or merged a VM's owners without lock_ownership(),
which every other step that moves owners takes; on PostgreSQL a sync of
a third compute with the same UUID could move them at the same time, and
owners moved onto the source record went with its delete.
2026-11-05 18:58:13 +03:00
catborise
1a8edafd14 fix(instances): a snapshot job checks its name again in the worker
The name was checked only when the job was queued; recovery counts any
snapshot of that name as the job's, so one taken outside the panel
meanwhile made a failed job succeed. precheck now refuses the name
before any effect.
2026-11-05 18:58:13 +03:00
catborise
076804be15 fix(instances): a DRBD status the compute did not tell is Unknown, not None DRBD
A drbdadm query that timed out, could not start ssh or failed showed
"None DRBD", as for a VM without DRBD resources. It now shows Unknown,
in the same red badge as a DRBD status that is not OK.
2026-11-05 18:58:13 +03:00
catborise
89035038e1 fix(gstfsd): at most 4 connections at a time; one more is told it is busy
Every connection got a thread before any authentication, and every
signed request a guestfs appliance, with no limit: with remote access
allowed, anyone on that network could make the host run out of threads
or memory. A connection over MAX_CLIENTS (4) now gets an unsigned busy
line and is closed before it gets a thread; the panel shows it as the
gstfsd error.
2026-11-05 18:58:13 +03:00
catborise
2cbcb8ab2c fix: the views that take a password or a key keep it out of error reports
No view used sensitive_post_parameters, so once ADMINS is set the error
mail of a failing POST would carry the password or key in plain text.
The user create, edit and password views, the password change, the
compute forms, the host secrets, the VM's root password and its console
settings now mark their POST data sensitive.
2026-11-05 18:17:00 +03:00
catborise
d3178154c3 fix(console): novncd waits at most 5 s for a VNC server to answer
websockify's socket() connects without a timeout, so a console address
that does not answer held the console's process for the OS's TCP timeout
(some 2 minutes) before the page heard of it. novncd now connects with
CONNECT_TIMEOUT, then waits on the open connection as before.
2026-11-05 18:17:00 +03:00
catborise
c42f74b4ee fix(accounts): an OTP mail links to the QR code instead of holding its secret
The Lost OTP? page, open without a login, and the admin's Email OTP QR
code mailed the QR code itself: the TOTP secret then stayed in the
mailbox and on every server the mail passed. The mail now carries a
signed link, good for an hour, to a page that shows the QR code (not
cached); the page shows nothing for an old or altered link or a user
without a device. tr and zh too.
2026-11-05 18:17:00 +03:00
catborise
c62de11147 docs(gstfsd): GSTFSD_PORT is for the tests; the panel always connects to 16510
gstfsd read its port from the environment while the panel has 16510 as a
constant, so a host that set GSTFSD_PORT could no longer be reached. The
documentation already gives 16510 everywhere; the variable now says it
exists for the tests.
2026-11-05 18:08:03 +03:00
catborise
2e4e9c78f8 fix(console): a VNC token works only in its user's session; refusals reach the Logs page
The VNC page's token travels in the websocket URL, so nginx's access log
and anyone who saw the URL had it for 120 s, and it opened the console as
a bearer token. novncd now also wants the session cookie of the token's
user. Its refusals were a warning without a user or an address: they now
name the client address (nginx sends X-Real-IP to novncd too) and leave
a Logs row "VNC console refused: reason".
2026-11-05 18:08:03 +03:00
catborise
6e3abd11ea fix(instances): a stuck host cannot hold a disk move job after the move
After the pivot the job asks every compute whether the old file is used;
libvirt has no call timeout, so one stuck host held the job, and a cancel
sent meanwhile held the VM host's control slot, for as long as the host
stayed stuck. The check now waits at most 60 s; a host that has not
answered by then might use the file, which is left behind.
2026-11-05 18:08:03 +03:00
catborise
a6f9e4b573 fix(instances): the page of a VM removed outside the panel sends the user to the list
The page checked only that the VM's host answers; a VM undefined with
virsh then failed at its first read and the page was a server error. It
now says the VM is no longer on its host and goes back to the list, whose
sync forgets the record (tr and zh too).
2026-11-05 18:08:03 +03:00
catborise
623e9874cb fix(instances): the VM page reads the user's rights from the VM's own record
It looked the user's ownership up by the VM's name on its compute; names
are not unique there (a VM renamed outside the panel and a migration can
leave two), which gave a server error or another VM's rights on the page.
2026-11-05 18:08:03 +03:00
catborise
d97e44b77f fix(instances): a request refused by the permission check takes no claim
The VM and compute claim decorators checked only that the user sees the
VM, took the claim, and left the action's own right to the view inside:
a read-only viewer, or an owner who may not delete the VM, briefly held
the VM's or the whole compute's claim and could make other requests
there busy. They now check the least right first: power for a VM change,
delete for the compute-wide claim (destroy; its other views are
superuser-only).
2026-11-05 18:08:03 +03:00
catborise
7d82cc7d6b fix(operations): a job refused by its checks in the worker says why
The worker's checks refuse with PermissionDenied or Http404, which carry
no text: the job failed with an empty error and its Logs row gave no
reason. They now read "You may no longer do this." and "The virtual
machine is no longer in the panel." (tr and zh too).
2026-11-05 16:28:29 +03:00
catborise
80b8d7fc91 fix(instances): a failed disk check on another host names it only to administrators
Deleting a VM with its disks checks every other compute for the files. A
compute that could not be read raised an OperationError whose text already
held that compute's name and libvirt's error, so owners who do not see
hosts got both. It is now a ConnectionFailed, which users who do not see
hosts get as "cannot be reached"; libvirt's text comes from the error
itself, so a connection that never opened no longer reads "None".
2026-11-05 16:28:29 +03:00
catborise
3fed1d04b4 fix(api): a VM action answers with what the page would have shown after it
The API's VM actions call the web views and only looked at the status
code; the views report a partial result (a disk that was not deleted or
was kept, a template that was not started) as a message for the page
they redirect to. The API reported plain success and left the messages in
the session. Its answer now carries them in "messages".
2026-11-05 16:28:29 +03:00
catborise
ff2182524f fix(api): a VM create without mac or nwfilter is accepted and runs
The API's create serializer leaves mac and nwfilter optional, but the
job's normalize gave them no default: without mac the request ended in an
HTML 500 (KeyError), without nwfilter it was queued and failed in the
worker. Both now default to empty, as the web form sends them.
2026-11-05 16:28:29 +03:00
catborise
cad392f79c fix(jobs): VM create and volume clone check the user and the name again in the worker
Every other job re-checks its permission and state in precheck before any
effect; these two checked only when queued. A queued create of a user who
is no longer a superuser, or whose VM name a host took meanwhile, ran; so
did a volume clone whose name a volume made outside the panel took.
2026-11-05 16:16:47 +03:00
catborise
1089b914ed fix(instances): every end of a migration job leaves a Logs row
Only migrate_instance, inside the job's run, wrote the migration's row:
a migration refused by precheck, cancelled while queued, or ended by
recovery after a worker restart left none. The job now has a log entry
for the ends migrate_instance did not log; run marks those it did.
2026-11-05 16:16:47 +03:00
catborise
62976cbd55 fix(instances): reverting a running VM to an internal snapshot fails before any effect
The internal revert's shut-off check ran in the job's run, after its
intent was recorded, so the refusal went to recovery: the job ended
uncertain and kept the VM's claim until a superuser resolved it. The check
(check_internal_revert) now runs in precheck too, so a page older than the
VM's start, or a direct POST, fails the job with the reason.
2026-11-05 16:16:47 +03:00
catborise
fe102f98c8 perf(instances): a user's VM list reads only the hosts of their VMs
The list page synchronised and connected to every compute for every user,
while the poll and the API list read only the hosts of the user's own VMs.
Hosts a user has no VMs on no longer get a listing per page load, which
also took one of their request slots.
2026-11-05 16:16:28 +03:00
catborise
f3c44f18c4 fix(instances): a busy host is reported as busy, not as unreachable
A web request refused because this process's requests to the host
(HOST_REQUESTS) all still wait for it raised the same ConnectionFailed as
a host that is down, so the VM page said the host cannot be reached. It
now raises HostBusy; the VM page and the error users who do not see hosts
get say the host is busy and to try again in a moment.
2026-11-05 16:16:28 +03:00
catborise
458074df82 fix(console): the console daemons hold to what the console page allowed
The token now names the console type it was made for: novncd accepts only
a VNC page's token and socketiod only a serial page's, so a VNC page's
token no longer opens the serial console without the VNC password.

A VNC session re-checks every 10 s, as the serial one did, that its user
may still open the console; a deactivated user or a removed owner loses
it. The check (may_still_open) moves to console.tokens for both daemons.
2026-11-05 16:16:22 +03:00
catborise
f670ee7333 fix(logs): libvirt prints no errors on the processes' stderr
libvirt's default handler printed each error on stderr without saying
where it came from: in Docker those lines went to docker logs, and a
VM page read of a shut-off VM left one there. Every process that talks to
libvirt imports vrtManager.connection, which now registers a silent
handler; the errors still reach their callers as exceptions. The
console callback's own registration is dropped.
2026-11-05 15:34:51 +03:00
catborise
5d90d3fcf9 fix(docker): gunicorn's and the job worker's output go to docker logs
They went to files in the container that were never rotated and were lost
on each recreate. docker logs is rotated (10 MB x 5) and outlives a
restart.
2026-11-05 14:47:37 +03:00
catborise
9ac1a29516 feat(logs): a refused page or API call leaves a Logs row
Each 401 or 403 answered to a signed-in user or to a request with
credentials (an API token) writes "Access denied (status): method path"
with the user and the address. Anonymous refusals are left out, so a
scanner cannot fill the table.
2026-11-05 14:47:37 +03:00
catborise
bc79c0c88a feat(logs): a host's connects, failures, closes and refused requests are logged
A run of failed opens is logged once, at its first failure, and the next
successful open ends it; a close callback and a request refused because the
host's requests still wait are logged each time. The lines go to the app log
of the process (web or job worker).
2026-11-05 14:47:37 +03:00
catborise
fdd7310297 feat(logs): each log line names its request, user and client address
A request gets nginx's $request_id (or a new one when it comes without a
valid one) and sends it back in X-Request-ID. The app log's lines carry
the logger, process and thread, the request ID, the signed-in user and the
client address; gunicorn's access lines carry the client address from
X-Real-IP and the same request ID, so the two logs can be joined.
2026-11-05 14:47:37 +03:00
catborise
9bae63f8c1 chore(console): the console pages load no jQuery
The VNC and serial console pages loaded jQuery, which neither their
scripts nor Bootstrap 5 use.
2026-11-05 12:37:51 +03:00
catborise
3baa93048b chore(deps): a list's search is kept in sessionStorage, without js-cookie
js-cookie served only table_filter.js, which kept a list's search in a
cookie for a day; the cookie went to the server with every request.
The search is now kept in the tab's sessionStorage: it stays across
reloads and pages in that tab, a new tab starts empty, and the server
never sees it. The library goes.
2026-11-05 12:37:51 +03:00
catborise
a81eb79e51 chore(deps): django-otp 1.7.5, paramiko 5.1.0, python-socketio 5.18.1, python-engineio 4.15.1
The updates on the login, SSH and serial console paths:
- django-otp 1.7.5 revokes the session of a device that is no longer
  confirmed.
- python-engineio 4.15.1 and python-socketio 5.18.1 refuse requests and
  events before the connection completes, HTTP requests after the
  upgrade to WebSocket, and ../ in static file paths.
- paramiko 5.1.0 fixes agent keys and the default RSA signature
  algorithm.

django-rest-knox 5.1.1 changes only its project's references, and
django-auth-ldap 5.4.0 drops Python 3.10, which the panel supports;
both stay.
2026-11-05 12:37:51 +03:00
catborise
2573228569 chore(deps): the icons come from the committed cache, not django-bootstrap-icons
django-bootstrap-icons (last release July 2024) only read the SVG of
each {% bs_icon %} from static/icon_cache; for an icon not there it
fetched the SVG from a CDN while rendering the page, and it brought
defusedxml along. A 20-line template tag in webvirtcloud reads the
same files; the package, its CDN path and the BS_ICONS_CACHE setting
go. requests stays: websockify needs it.
2026-11-05 12:37:51 +03:00
catborise
74483fc5f3 fix(instances): a disk or NIC form sent twice adds the device once
libvirt takes the same disk file or MAC twice in a persistent
definition. A double click or a resent request then attached an
existing volume twice, and a shut-off VM did not start again (QEMU
opens no file twice for writing), or added a second NIC with the MAC
the form had filled in. Attaching a volume that is already a disk of
the VM, live or in its next start, and adding a NIC with a MAC the VM
has are now refused with the reason.
2026-11-05 11:52:04 +03:00
catborise
42a3ba71fd fix(instances): a gstfsd host that is down is told in 5 seconds
gstfsd_request connected with the 120 seconds it waits for the change:
a host that was down held the request's thread two minutes before its
page said so, a wait no host bound counts. It now connects within
libvirt's CONNECT_TIMEOUT and gives the change its 120 seconds after.
2026-11-05 11:04:08 +03:00
catborise
e117345e56 fix(webvirtcloud): a PostgreSQL connection waits at most 5 seconds
libpq connects without a timeout of its own: a database server that
does not answer held each request for the operating system's TCP
timeout, minutes. The settings template sets connect_timeout to 5
seconds.
2026-11-05 11:04:08 +03:00
catborise
245ed12aad fix(accounts): an LDAP login waits a bounded time for the directory
python-ldap waits for a server without end, to connect and for each
answer: a directory that stopped answering held every login's thread.
The settings template's LDAP block sets 5 seconds to connect and 10 for
each answer.
2026-11-05 11:04:08 +03:00
catborise
cbd4374f4f fix(accounts): mailing an OTP QR code waits at most 10 seconds for the SMTP server
EMAIL_TIMEOUT was not set, and Django's SMTP connection then waits for
a server without end: one that accepts the connection and stops
answering held the request's thread for good, also on the login page's
Email OTP form, which anyone may post. The settings template sets 10
seconds. An administrator whose mail fails is told why on the account
page instead of getting an error page.
2026-11-05 11:04:08 +03:00
catborise
686c926a30 chore(docker): the container's log is rotated
gunicorn writes a line of its access log for every request to the
container's output, and Docker's json-file log driver keeps all of it
by default: an open VM list alone adds a line every 5 seconds until the
disk is full. The compose file now keeps 5 files of 10 MB, as the
panel's own log.
2026-11-05 10:36:38 +03:00
catborise
b2deb042e8 feat(operations): the Logs page's rows and expired sessions are removed
Nothing removed the Logs page's rows, which every action and every
failed login adds, or the database sessions past their expiry. The job
worker's housekeeping now deletes the rows older than Log Retention
Days, a new setting under Lists and pages (1-3650, 365 by default), and
the expired sessions.
2026-11-05 10:36:34 +03:00
catborise
93b420ae81 chore(accounts): django-axes keeps no record of successful logins
django-axes wrote an AccessLog row at every successful login and nothing
removed them; the Logs page already records each login. Its access log
is now off; failed logins are still counted for the lock.
2026-11-05 10:36:34 +03:00
catborise
ae03679b0e perf(instances): a clone request reads its source VM once
The clone form's checks read the source's NICs from the live and the
persistent definition, and each NIC read the running VM's addresses
again since b2b2386d: a guest agent that is connected but hung held the
request for its timeout once a NIC and definition. The checks now run
in one cached_reads block, as the VM page does.
2026-11-05 08:59:08 +03:00
catborise
3c51602652 fix(vrtManager): a request whose read of other hosts fails counts on its host again
other_hosts counted the request on its own host again only when the
block ended normally: a quota check that raised left the request
counted on the other host while it went on with its own. The count is
now restored however the block ends.
2026-11-05 08:59:08 +03:00
catborise
d4b9edc3e5 fix(vrtManager): a volume upload aborts its stream when the volume cannot be made
upload_volume opens the libvirt stream before it creates the volume, but
created the volume outside the block that aborts the stream: a pool that
refused the volume (full, a name taken) left an unfinished stream to be
freed. The creation is now inside that block.
2026-11-05 08:59:08 +03:00
catborise
297f9a8a53 fix(instances): a recovered clone gets the size and start asked
When the worker stopped after it defined a clone and before it resized
and started it, recovery recorded the clone at the source's size and
shut off, and the job read as a plain success. Recovery now finishes a
defined clone that is shut off as the run does; one that runs went
through the last step, starting, and is left as it is.
2026-11-05 08:59:08 +03:00
catborise
02ab78fe94 fix(instances): a clone size of a digit that is no number gets the form's refusal
clone_data checked the clone's vCPUs and memory with str.isdigit(), which
takes characters such as a superscript two that int() refuses: the user
saw Python's "invalid literal for int()" instead of the refusal. It now
checks with str.isdecimal(), whose characters int() reads.
2026-11-05 08:59:08 +03:00
catborise
337063bf7b perf(instances): a VM list reads only the VMs it shows
#394 read each host's states in one call and every domain's XML once,
for the title, memory and vCPUs. A user's list shows only their own VMs,
but it read the XML of every VM on those hosts: a user with two VMs on a
host of 300 made about 301 libvirt calls a list load, where the old
per-VM reads made 6.

get_domain_rows takes the UUIDs the list shows and reads only their XML;
the states still come in one call. The capacity reads every domain, as
before.
2026-11-05 07:33:13 +03:00