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(a)redhat.com>
---
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(a)redhat.com>