Follow-ups from the review of the audit log layers:
- Blocking a user who is already blocked, or unblocking one who is
not, changes nothing and now writes no row, like any other edit
that changes nothing.
- write_chunk takes the request instead of the user and an optional
request; the view always had one, so the branch only tests used
is gone and every chunk 0 goes through the same audited submit.
- Tests: a busy clone creates no job and its row carries the page's
message; a later chunk that fails writes no submit row; libvirt
refusing a network or pool delete is a failed row.
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 upload view sent the error of an unexpected failure to the browser
but left nothing in the server log, so whoever runs the server had no
trace of it. It is logged with its traceback now; the superuser still
sees the message.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The upload session lives in a job instead of flocks and .meta/.done
files; the job panel shows and cancels it; an upload idle for an hour is
ended and its partial file deleted; the page no longer reloads after an
upload.