[libvirt RFC 20/24] qemu_snapshot: prepare data for external snapshot deletion

Pavel Hrdina phrdina at redhat.com
Tue Aug 23 16:32:23 UTC 2022


In order to save some CPU cycles we will collect all the necessary data
to delete external snapshot before we even start. They will be later
used by code that deletes the snapshots and updates metadata when
needed.

With external snapshots we need data that libvirt gets from running QEMU
process so if the VM is not running we need to start paused QEMU process
for the snapshot deletion and kill at afterwards.

Signed-off-by: Pavel Hrdina <phrdina at redhat.com>
---
 src/qemu/qemu_snapshot.c | 144 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 142 insertions(+), 2 deletions(-)

diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
index da9b4c30f0..dade5dcea4 100644
--- a/src/qemu/qemu_snapshot.c
+++ b/src/qemu/qemu_snapshot.c
@@ -2232,6 +2232,120 @@ qemuSnapshotRevert(virDomainObj *vm,
 }
 
 
+typedef struct {
+    virDomainMomentObj *parentSnap;
+    virDomainSnapshotDiskDef *snapDisk;
+    virDomainDiskDef *domDisk;
+    virDomainDiskDef *parentDomDisk;
+    virStorageSource *diskSrc;
+    virStorageSource *parentDiskSrc;
+    virStorageSource *prevDiskSrc;
+    qemuBlockJobData *job;
+} qemuSnapshotDeleteExternalData;
+
+
+static virDomainMomentObj*
+qemuSnapshotFindParentSnapForDisk(virDomainMomentObj *snap,
+                                  virDomainSnapshotDiskDef *snapDisk)
+{
+    virDomainMomentObj *parentSnap = snap->parent;
+
+    while (parentSnap) {
+        ssize_t i;
+        virDomainSnapshotDef *parentSnapdef = virDomainSnapshotObjGetDef(parentSnap);
+
+        if (!parentSnapdef)
+            break;
+
+        for (i = 0; i < parentSnapdef->ndisks; i++) {
+            virDomainSnapshotDiskDef *parentSnapDisk = &(parentSnapdef->disks[i]);
+
+            if (parentSnapDisk->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_NO &&
+                STREQ(snapDisk->name, parentSnapDisk->name)) {
+                return parentSnap;
+            }
+        }
+
+        parentSnap = parentSnap->parent;
+    }
+
+    return NULL;
+}
+
+
+static GPtrArray*
+qemuSnapshotDeleteExternalPrepare(virDomainObj *vm,
+                                  virDomainMomentObj *snap)
+{
+    ssize_t i;
+    virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap);
+    g_autoptr(GPtrArray) ret = g_ptr_array_new_full(0, g_free);
+
+    for (i = 0; i < snapdef->ndisks; i++) {
+        g_autofree qemuSnapshotDeleteExternalData *data = NULL;
+        virDomainSnapshotDiskDef *snapDisk = &(snapdef->disks[i]);
+
+        if (snapDisk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NO)
+            continue;
+
+        data = g_new0(qemuSnapshotDeleteExternalData, 1);
+        data->snapDisk = snapDisk;
+
+        data->domDisk = qemuDomainDiskByName(vm->def, snapDisk->name);
+        if (!data->domDisk)
+            return NULL;
+
+        data->diskSrc = virStorageSourceChainLookup(data->domDisk->src, NULL,
+                                                    data->snapDisk->src->path,
+                                                    NULL, &data->prevDiskSrc);
+        if (!data->diskSrc)
+            return NULL;
+
+        if (!virStorageSourceIsSameLocation(data->diskSrc, data->snapDisk->src)) {
+            virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                           _("VM disk source and snapshot disk source are not the same"));
+            return NULL;
+        }
+
+        data->parentDomDisk = virDomainDiskByTarget(snapdef->parent.dom,
+                                                    data->snapDisk->name);
+        if (!data->parentDomDisk) {
+            virReportError(VIR_ERR_OPERATION_FAILED,
+                           _("failed to find disk '%s' in snapshot VM XML"),
+                           snapDisk->name);
+            return NULL;
+        }
+
+        data->parentDiskSrc = data->diskSrc->backingStore;
+        if (!data->parentDiskSrc) {
+            virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                           _("failed to find parent disk source in backing chain"));
+            return NULL;
+        }
+
+        if (!virStorageSourceIsSameLocation(data->parentDiskSrc, data->parentDomDisk->src)) {
+            virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                           _("snapshot VM disk source and parent disk source are not the same"));
+            return NULL;
+        }
+
+        data->parentSnap = qemuSnapshotFindParentSnapForDisk(snap, data->snapDisk);
+
+        if (data->parentSnap && !virDomainSnapshotIsExternal(data->parentSnap)) {
+            virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+                           _("parent snapshot '%s' for disk '%s' is internal snapshot"),
+                           snapDisk->name,
+                           data->parentSnap->def->name);
+            return NULL;
+        }
+
+        g_ptr_array_add(ret, g_steal_pointer(&data));
+    }
+
+    return g_steal_pointer(&ret);
+}
+
+
 typedef struct _virQEMUMomentReparent virQEMUMomentReparent;
 struct _virQEMUMomentReparent {
     const char *dir;
@@ -2504,9 +2618,9 @@ qemuSnapshotDeleteValidate(virDomainMomentObj *snap,
     }
 
     if (virDomainSnapshotIsExternal(snap) &&
-        !(flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY)) {
+        (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN)) {
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                       _("deletion of external disk snapshots not supported"));
+                       _("deletion of external disk snapshot with internal children disk snapshots not supported"));
         return -1;
     }
 
@@ -2523,6 +2637,8 @@ qemuSnapshotDelete(virDomainObj *vm,
     int ret = -1;
     virDomainMomentObj *snap = NULL;
     bool metadata_only = !!(flags & VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY);
+    bool stop_qemu = false;
+    g_autoptr(GPtrArray) externalData = NULL;
 
     virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN |
                   VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY |
@@ -2537,6 +2653,25 @@ qemuSnapshotDelete(virDomainObj *vm,
     if (!metadata_only) {
         if (qemuSnapshotDeleteValidate(snap, flags) < 0)
             goto endjob;
+
+        if (virDomainSnapshotIsExternal(snap) &&
+            !(flags & (VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN |
+                       VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY))) {
+            if (!virDomainObjIsActive(vm)) {
+                if (qemuProcessStart(NULL, driver, vm, NULL, VIR_ASYNC_JOB_NONE,
+                                     NULL, -1, NULL, NULL,
+                                     VIR_NETDEV_VPORT_PROFILE_OP_CREATE,
+                                     VIR_QEMU_PROCESS_START_PAUSED) < 0) {
+                    goto endjob;
+                }
+
+                stop_qemu = true;
+            }
+
+            externalData = qemuSnapshotDeleteExternalPrepare(vm, snap);
+            if (!externalData)
+                goto endjob;
+        }
     }
 
     if (flags & (VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN |
@@ -2547,6 +2682,11 @@ qemuSnapshotDelete(virDomainObj *vm,
     }
 
  endjob:
+    if (stop_qemu) {
+        qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN,
+                        VIR_ASYNC_JOB_NONE, 0);
+    }
+
     qemuDomainObjEndJob(vm);
 
     return ret;
-- 
2.37.2



More information about the libvir-list mailing list