On Tue, Jun 27, 2023 at 17:07:15 +0200, Pavel Hrdina wrote: > When reverting to external snapshot we need to create new overlay qcow2 > files from the disk files the VM had when the snapshot was taken. > > There are some specifics and limitations when reverting to a snapshot: > > 1) When reverting to last snapshot we need to first create new overlay > files before we can safely delete the old overlay files in case the > creation fails so we have still recovery option when we error out. > > These new files will not have the suffix as when the snapshot was > created as renaming the original files in order to use the same file > names as when the snapshot was created would add unnecessary > complexity to the code. > > 2) When reverting to any snapshot we will always create overlay files > for every disk the VM had when the snapshot was done. Otherwise we > would have to figure out if there is any other qcow2 image already > using any of the VM disks as backing store and that itself might be > extremely complex and in some cases impossible. > > 3) When reverting from any state the current overlay files will be > always removed as that VM state is not meant to be saved. It's the > same as with internal snapshots. If user want's to keep the current > state before reverting they need to create a new snapshot. For now > this will only work if the current snapshot is the last. > > Signed-off-by: Pavel Hrdina <phrdina@xxxxxxxxxx> > --- > src/qemu/qemu_snapshot.c | 232 ++++++++++++++++++++++++++++++++++++++- > 1 file changed, 228 insertions(+), 4 deletions(-) > > diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c > index 1cb0ea55de..dbf2cdd5db 100644 > --- a/src/qemu/qemu_snapshot.c > +++ b/src/qemu/qemu_snapshot.c > @@ -18,6 +18,8 @@ > > #include <config.h> > > +#include <fcntl.h> > + > #include "qemu_snapshot.h" > > #include "qemu_monitor.h" > @@ -1975,6 +1977,183 @@ qemuSnapshotRevertWriteMetadata(virDomainObj *vm, > } Please document all new functions both any non-obvious parameter and what it is supposed to do. > +static int > +qemuSnapshotRevertExternalPrepare(virDomainObj *vm, > + virDomainSnapshotDef *tmpsnapdef, > + virDomainMomentObj *snap, > + virDomainDef *config, > + virDomainDef *inactiveConfig, > + int *memsnapFD, > + char **memsnapPath) > +{ > + ssize_t i; Normally we declare 'i' as unsigned. > + bool active = virDomainObjIsActive(vm); > + virDomainDef *domdef = NULL; > + virDomainSnapshotLocation location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL; This constant is used in one place only. > + virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap); > + > + if (config) { > + domdef = config; > + } else { > + domdef = inactiveConfig; > + } > + > + /* We need this to generate creation timestamp that is used as default > + * snapshot name. */ > + if (virDomainMomentDefPostParse(&tmpsnapdef->parent) < 0) > + return -1; > + > + if (virDomainSnapshotAlignDisks(tmpsnapdef, domdef, location, false, true) < 0) > + return -1; > + > + for (i = 0; i < tmpsnapdef->ndisks; i++) { > + virDomainSnapshotDiskDef *snapdisk = &tmpsnapdef->disks[i]; > + virDomainDiskDef *domdisk = domdef->disks[i]; > + > + if (qemuSnapshotPrepareDiskExternal(domdisk, snapdisk, active, false) < 0) > + return -1; > + } > + > + if (memsnapFD && memsnapPath && snapdef->memorysnapshotfile) { > + virQEMUDriver *driver = ((qemuDomainObjPrivate *) vm->privateData)->driver; > + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); > + > + *memsnapPath = snapdef->memorysnapshotfile; > + *memsnapFD = qemuDomainOpenFile(cfg, NULL, *memsnapPath, O_RDONLY, NULL); Since the caller inherits the FD this must be documented. > + } > + > + return 0; > +} > + > + > +static int > +qemuSnapshotRevertExternalActive(virDomainObj *vm, > + virDomainSnapshotDef *tmpsnapdef) > +{ > + ssize_t i; 'size_t' > + g_autoptr(GHashTable) blockNamedNodeData = NULL; > + g_autoptr(qemuSnapshotDiskContext) snapctxt = NULL; > + > + snapctxt = qemuSnapshotDiskContextNew(tmpsnapdef->ndisks, vm, VIR_ASYNC_JOB_SNAPSHOT); > + > + if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, VIR_ASYNC_JOB_SNAPSHOT))) > + return -1; > + > + for (i = 0; i < tmpsnapdef->ndisks; i++) { > + if (qemuSnapshotDiskPrepareOne(snapctxt, > + vm->def->disks[i], > + tmpsnapdef->disks + i, > + blockNamedNodeData, > + false, > + true) < 0) > + return -1; > + } > + > + if (qemuSnapshotDiskCreate(snapctxt) < 0) > + return -1; > + > + return 0; > +} > + > + > +static int > +qemuSnapshotRevertExternalInactive(virDomainObj *vm, > + virDomainSnapshotDef *tmpsnapdef, > + virDomainDef *config, > + virDomainDef *inactiveConfig) This function is named '..Inactive' > +{ > + virDomainDef *domdef = NULL; > + g_autoptr(virBitmap) created = NULL; > + > + created = virBitmapNew(tmpsnapdef->ndisks); > + > + if (config) { > + domdef = config; > + } else { > + domdef = inactiveConfig; > + } So this logic is weird. Inactive VMs have only one definition. > + > + if (config) { > + if (qemuSnapshotDomainDefUpdateDisk(config, tmpsnapdef, false) < 0) > + return -1; > + } > + > + if (qemuSnapshotDomainDefUpdateDisk(inactiveConfig, tmpsnapdef, false) < 0) > + return -1; > + > + if (qemuSnapshotCreateQcow2Files(vm, domdef, tmpsnapdef, created, false) < 0) { > + ssize_t bit = -1; Consider storing the error here ... > + > + while ((bit = virBitmapNextSetBit(created, bit)) >= 0) { > + virDomainSnapshotDiskDef *snapdisk = &(tmpsnapdef->disks[bit]); > + > + if (virStorageSourceInit(snapdisk->src) < 0 || > + virStorageSourceUnlink(snapdisk->src) < 0) { ... so that this doesn't overwrite it. > + VIR_WARN("Failed to remove snapshot image '%s'", > + snapdisk->src->path); > + } > + } > + > + return -1; > + } > + > + return 0; > +} The rest seems to make sense: Reviewed-by: Peter Krempa <pkrempa@xxxxxxxxxx>