1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 03:36:48 +00:00

feat(jobs): deleting a VM is a job that recovery finishes

The delete ran in the request: undefine, then the VM's record, then its
disk files. A request that died in between (a gunicorn worker past its
timeout, OOM, a restart), or a database write that failed after the
undefine, left a record that later showed as removed outside the panel,
and the disk files the user asked to delete were never deleted.

Deleting a VM is now an instance.delete job. It holds the compute
exclusively, as the request did, so no other VM attaches a file it is
about to delete. The job chooses the files before it stops the VM (a host
that cannot be checked still stops nothing) and records them first. It
then stops and undefines the VM, removes its record, and deletes the
files, each with its own Logs row. Recovery decides from libvirt: a VM
still on its host means the delete failed; a VM gone means libvirt did
it, so recovery removes the record and deletes the recorded files that
are still there.

The page queues the job and returns to the VM page, where the job panel
follows it and opens the VM list when it ends; disks that were kept are
the result's warning. The API's delete answers 202 with the job and
needs a submission_key. backing_path moves to wvmConnect, as recovery
has no domain to ask.
This commit is contained in:
catborise 2026-11-06 09:49:31 +03:00
parent ce066052d9
commit 02412b7ea5
19 changed files with 923 additions and 764 deletions

View file

@ -1833,18 +1833,6 @@ class wvmInstance(wvmConnect):
grew = True
return files[len(tops):] + memory
def backing_path(self, path):
"""The backing file of a volume; None if it has none or is in no pool.
Any other error is raised: a chain cut short would let a file a kept
one needs count as deletable."""
try:
vol = self.get_volume_by_path(path)
except libvirtError as err:
if err.get_error_code() != VIR_ERR_NO_STORAGE_VOL:
raise
return None
return util.get_xml_path(vol.XMLDesc(0), "/volume/backingStore/path")
def _snapshotCreateXML(self, xml, flag):
self.instance.snapshotCreateXML(xml, flag)