On Tue, Aug 23, 2022 at 18:32:25 +0200, Pavel Hrdina wrote:
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;
'driver' is present in private data of 'vm'
+ int error;
+ int (*writeMetadata)(virDomainObj *, virDomainMomentObj *,
+ virDomainXMLOption *, const char *);
The only function pointer that is ever populated here is
qemuDomainSnapshotWriteMetadata. So you can use that directly from the
function that uses this.
+};
+
+
+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)) {
So IIUC, this bit is done only for external snapshots ...
+ 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) {
... but this even for internal ones? That doesn't seem right.
+ 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;
+ }
So you are looking up the image corresponding to the snapshot disk and
trying to move it one level up, right?
+
+ 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;
At this point I'm afraid we can't afford to deal with errors the usual
way but have to try to update as much metadata as possible.
If the first error breaks up everything then the state of the disk
images will not correspond to the metadata.
+
+ 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) {
The failure of qemuDomainDiskByName, in this case probably should not be
fatal, if the next-boot config diverges from the running config.
+ data->error = -1;
+ return 0;
+ }
+ }
+ }
+
+ data->error = data->writeMetadata(data->vm,
+ snap,
+ data->driver->xmlopt,
+ cfg->snapshotDir);
+ return 0;
+}