1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-11 12:46:49 +00:00

feat(instances): external snapshots use libvirt's snapshot API

Create, revert and delete are one libvirt call each. The pflash-to-rom
loader rewrite, the hand-written block commit and the s1./s2. names are
gone, so UEFI VMs can take external snapshots, a revert keeps disks added
later, and deleting an older snapshot keeps the newer one.

Create refuses what libvirt could not revert or delete later: internal
snapshots on the VM, a saved state, a running snapshot job, disks that
are not files, CD-ROM media, a branch after a revert, and memory without
a running VM or room in the first disk's pool. A disk-only revert keeps
the VM running, paused or shut off; one with memory resumes it as saved.
A revert in the same second as the previous one, which libvirt fails
after stopping the VM, is retried once.
A snapshot the old code took of a UEFI VM saved the loader as rom; its
revert is refused (deleting it still works).

The snapshots tab lists internal and external snapshots separately, can
save memory too, shows running snapshot jobs, and the revert confirmation
names the disks it detaches and that NVRAM is not restored.
This commit is contained in:
catborise 2026-10-12 14:07:33 +03:00
parent c55f52f025
commit ff1508b3c2
13 changed files with 994 additions and 311 deletions

View file

@ -35,7 +35,6 @@ from libvirt import (VIR_DOMAIN_UNDEFINE_KEEP_NVRAM,
VIR_DOMAIN_UNDEFINE_MANAGED_SAVE,
VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA,
VIR_DOMAIN_UNDEFINE_NVRAM,
VIR_DOMAIN_START_PAUSED,
libvirtError)
from logs.views import addlogmsg
from webvirtcloud.middleware import error_text
@ -1342,7 +1341,11 @@ def delete_snapshot(request, pk):
if not request.user.has_perm("instances.snapshot_instances"):
raise PermissionDenied
snap_name = request.POST.get("name", "")
instance.proxy.snapshot_delete(snap_name)
try:
instance.proxy.snapshot_delete(snap_name)
except libvirtError as err:
messages.error(request, _("Snapshot was not deleted: %(err)s") % {"err": error_text(request, err)})
return _back(request, pk, "managesnapshot")
msg = _("Delete snapshot: %(snap)s") % {"snap": snap_name}
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
return _back(request, pk, "managesnapshot")
@ -1376,9 +1379,14 @@ def create_external_snapshot(request, pk):
raise PermissionDenied
name = request.POST.get("name", "")
desc = request.POST.get("description", "")
instance.proxy.create_external_snapshot("s1." + name, instance, desc=desc)
msg = _("Create external snapshot: %(snap)s") % {"snap": name}
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
memory = request.POST.get("memory") == "on"
try:
instance.proxy.create_external_snapshot(name, desc, memory)
except libvirtError as err:
messages.error(request, _("Snapshot was not created: %(err)s") % {"err": error_text(request, err)})
else:
msg = _("Create external snapshot: %(snap)s") % {"snap": name}
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
return _back(request, pk, "managesnapshot")
@ -1388,15 +1396,15 @@ def revert_external_snapshot(request, pk):
instance = get_instance(request.user, pk, perm_type="change")
if not request.user.has_perm("instances.snapshot_instances"):
raise PermissionDenied
instance_state = True if instance.proxy.get_status() != 5 else False
name = request.POST.get("name", "")
date = request.POST.get("date", "")
desc = request.POST.get("desc", "")
instance.proxy.force_shutdown() if instance_state else None
instance.proxy.revert_external_snapshot(name, date, desc)
instance.proxy.start() if instance_state else None
msg = _("Revert external snapshot: %(snap)s") % {"snap": name}
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
try:
instance.proxy.revert_external_snapshot(name)
except libvirtError as err:
messages.error(request, _("Snapshot was not reverted: %(err)s") % {"err": error_text(request, err)})
else:
messages.success(request, _("Successful revert snapshot: ") + name)
msg = _("Revert external snapshot: %(snap)s") % {"snap": name}
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
return _back(request, pk, "managesnapshot")
@ -1406,18 +1414,14 @@ def delete_external_snapshot(request, pk):
instance = get_instance(request.user, pk, perm_type="change")
if not request.user.has_perm("instances.snapshot_instances"):
raise PermissionDenied
instance_state = True if instance.proxy.get_status() == 5 else False
name = request.POST.get("name", "")
instance.proxy.start(VIR_DOMAIN_START_PAUSED) if instance_state else None
try:
instance.proxy.delete_external_snapshot(name)
except libvirtError as err:
messages.error(request, _("Snapshot was not deleted: %(err)s") % {"err": error_text(request, err)})
else:
msg = _("Delete external snapshot: %(snap)s") % {"snap": name}
addlogmsg(request.user.username, instance.compute.name, instance.name, msg)
finally:
instance.proxy.force_shutdown() if instance_state else None
return _back(request, pk, "managesnapshot")