With external snapshots we need to modify the metadata bit more then
what is required for internal snapshots. Mainly the storage source
location changes with every external snapshot.
This means that if we delete non-leaf snapshot we need to update all
children snapshots and modify the disk sources for all affected disks.
Signed-off-by: Pavel Hrdina <phrdina(a)redhat.com>
---
src/qemu/qemu_snapshot.c | 116 +++++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
index 64ee395230..37ae3f04d0 100644
--- a/src/qemu/qemu_snapshot.c
+++ b/src/qemu/qemu_snapshot.c
@@ -2380,6 +2380,109 @@ qemuSnapshotChildrenReparent(void *payload,
}
+typedef struct _qemuSnapshotUpdateDisksData qemuSnapshotUpdateDisksData;
+struct _qemuSnapshotUpdateDisksData {
+ virDomainMomentObj *snap;
+ virDomainObj *vm;
+ virQEMUDriver *driver;
+ int error;
+ int (*writeMetadata)(virDomainObj *, virDomainMomentObj *,
+ virDomainXMLOption *, const char *);
+};
+
+
+static int
+qemuSnapshotUpdateDisksSingle(virDomainMomentObj *snap,
+ virDomainDef *def,
+ virDomainDef *parentDef,
+ virDomainSnapshotDiskDef *snapDisk)
+{
+ virDomainDiskDef *disk = NULL;
+
+ if (!(disk = qemuDomainDiskByName(def, snapDisk->name)))
+ return -1;
+
+ if (virDomainSnapshotIsExternal(snap)) {
+ virDomainDiskDef *parentDisk = NULL;
+
+ if (!(parentDisk = qemuDomainDiskByName(parentDef, snapDisk->name)))
+ return -1;
+
+ if (virStorageSourceIsSameLocation(snapDisk->src, disk->src)) {
+ virObjectUnref(disk->src);
+ disk->src = virStorageSourceCopy(parentDisk->src, false);
+ }
+ }
+
+ if (disk->src->backingStore) {
+ virStorageSource *cur = disk->src;
+ virStorageSource *next = disk->src->backingStore;
+
+ while (next) {
+ if (virStorageSourceIsSameLocation(snapDisk->src, next)) {
+ cur->backingStore = next->backingStore;
+ next->backingStore = NULL;
+ virObjectUnref(next);
+ break;
+ }
+
+ cur = next;
+ next = cur->backingStore;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+qemuSnapshotDeleteUpdateDisks(void *payload,
+ const char *name G_GNUC_UNUSED,
+ void *opaque)
+{
+ virDomainMomentObj *snap = payload;
+ qemuSnapshotUpdateDisksData *data = opaque;
+ g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(data->driver);
+ virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(data->snap);
+ ssize_t i;
+
+ if (data->error < 0)
+ return 0;
+
+ for (i = 0; i < snapdef->ndisks; i++) {
+ virDomainSnapshotDiskDef *snapDisk = &(snapdef->disks[i]);
+
+ if (snapDisk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NO)
+ continue;
+
+ if (qemuSnapshotUpdateDisksSingle(snap, snap->def->dom,
+ data->snap->def->dom, snapDisk) <
0) {
+ data->error = -1;
+ return 0;
+ }
+
+ if (snap->def->inactiveDom) {
+ virDomainDef *dom = data->snap->def->inactiveDom;
+
+ if (!dom)
+ dom = data->snap->def->dom;
+
+ if (qemuSnapshotUpdateDisksSingle(snap, snap->def->inactiveDom,
+ dom, snapDisk) < 0) {
+ data->error = -1;
+ return 0;
+ }
+ }
+ }
+
+ data->error = data->writeMetadata(data->vm,
+ snap,
+ data->driver->xmlopt,
+ cfg->snapshotDir);
+ return 0;
+}
+
+
static int
qemuSnapshotJobRunning(virDomainObj *vm,
qemuBlockJobData *job)
@@ -2500,6 +2603,7 @@ qemuSnapshotDiscardMetadata(virDomainObj *vm,
if (update_parent && snap->nchildren) {
virQEMUMomentReparent rep;
+ qemuSnapshotUpdateDisksData data;
rep.dir = cfg->snapshotDir;
rep.parent = snap->parent;
@@ -2512,6 +2616,18 @@ qemuSnapshotDiscardMetadata(virDomainObj *vm,
&rep);
if (rep.err < 0)
return -1;
+
+ data.snap = snap;
+ data.driver = driver;
+ data.vm = vm;
+ data.error = 0;
+ data.writeMetadata = qemuDomainSnapshotWriteMetadata;
+ virDomainMomentForEachDescendant(snap,
+ qemuSnapshotDeleteUpdateDisks,
+ &data);
+ if (data.error < 0)
+ return -1;
+
virDomainMomentMoveChildren(snap, snap->parent);
}
--
2.37.2