[libvirt RFC 21/24] qemu_snapshot: implement deletion of external snapshot

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


When deleting snapshot we are starting block-commit job over all disks
that are part of the snapshot.

This operation may fail as it writes data changes to the backing qcow2
image so we need to wait for all the disks to finish the operation and
wait for correct signal from QEMU. If deleting active snapshot we will
get `ready` signal and for inactive snapshots we need to disable
autofinalize in order to get `pending` signal.

At this point if commit for any disk fails for some reason and we abort
the VM is still in consistent state and user can fix the reason why the
deletion failed.

After that we do `pivot` or `finalize` if it's active snapshot or not to
finish the block job. It still may fail but there is nothing else we can
do about it.

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

diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
index dade5dcea4..64ee395230 100644
--- a/src/qemu/qemu_snapshot.c
+++ b/src/qemu/qemu_snapshot.c
@@ -2380,6 +2380,114 @@ qemuSnapshotChildrenReparent(void *payload,
 }
 
 
+static int
+qemuSnapshotJobRunning(virDomainObj *vm,
+                       qemuBlockJobData *job)
+{
+    qemuBlockJobUpdate(vm, job, VIR_ASYNC_JOB_NONE);
+
+    while (job->state != QEMU_BLOCKJOB_STATE_READY &&
+           job->state != QEMU_BLOCKJOB_STATE_PENDING &&
+           job->state != QEMU_BLOCKJOB_STATE_FAILED &&
+           job->state != QEMU_BLOCKJOB_STATE_CANCELLED &&
+           job->state != QEMU_BLOCKJOB_STATE_COMPLETED) {
+        if (qemuDomainObjWait(vm) < 0)
+            return -1;
+        qemuBlockJobUpdate(vm, job, VIR_ASYNC_JOB_NONE);
+    }
+
+    return 0;
+}
+
+
+static int
+qemuSnapshotJobFinishing(virDomainObj *vm,
+                         qemuBlockJobData *job)
+{
+    qemuBlockJobUpdate(vm, job, VIR_ASYNC_JOB_NONE);
+
+    while (job->state != QEMU_BLOCKJOB_STATE_READY &&
+           job->state != QEMU_BLOCKJOB_STATE_FAILED &&
+           job->state != QEMU_BLOCKJOB_STATE_CANCELLED &&
+           job->state != QEMU_BLOCKJOB_STATE_COMPLETED) {
+        if (qemuDomainObjWait(vm) < 0)
+            return -1;
+        qemuBlockJobUpdate(vm, job, VIR_ASYNC_JOB_NONE);
+    }
+
+    return 0;
+}
+
+
+static int
+qemuSnapshotDiscardExternal(virDomainObj *vm,
+                            virQEMUDriver *driver,
+                            GPtrArray *externalData)
+{
+    ssize_t i;
+
+    for (i = 0; i < externalData->len; i++) {
+        qemuSnapshotDeleteExternalData *data = g_ptr_array_index(externalData, i);
+        virTristateBool autofinalize = VIR_TRISTATE_BOOL_NO;
+        unsigned int commitFlags = VIR_DOMAIN_BLOCK_COMMIT_DELETE;
+
+        if (data->domDisk->src == data->diskSrc) {
+            commitFlags |= VIR_DOMAIN_BLOCK_COMMIT_ACTIVE;
+            autofinalize = VIR_TRISTATE_BOOL_YES;
+        }
+
+        if (qemuBlockCommitImpl(vm, driver,
+                                data->domDisk,
+                                data->parentDiskSrc,
+                                data->diskSrc,
+                                data->prevDiskSrc,
+                                0, true, autofinalize, commitFlags) < 0) {
+            return -1;
+        }
+
+        data->job = qemuBlockJobDiskGetJob(data->domDisk);
+        if (!data->job) {
+            virReportError(VIR_ERR_OPERATION_INVALID,
+                           _("disk '%s' does not have an active block job"),
+                           data->domDisk->dst);
+            return -1;
+        }
+    }
+
+    for (i = 0; i < externalData->len; i++) {
+        qemuSnapshotDeleteExternalData *data = g_ptr_array_index(externalData, i);
+
+        if (qemuSnapshotJobRunning(vm, data->job) < 0)
+            return -1;
+
+        if (data->job->state == QEMU_BLOCKJOB_STATE_FAILED)
+            return -1;
+    }
+
+    for (i = 0; i < externalData->len; i++) {
+        qemuSnapshotDeleteExternalData *data = g_ptr_array_index(externalData, i);
+
+        if (data->job->state == QEMU_BLOCKJOB_STATE_READY) {
+            if (qemuBlockPivot(vm, data->job, NULL) < 0)
+                return -1;
+        } else if (data->job->state == QEMU_BLOCKJOB_STATE_PENDING) {
+            if (qemuBlockFinalize(vm, data->job) < 0)
+                return -1;
+        }
+
+        if (qemuSnapshotJobFinishing(vm, data->job) < 0)
+            return -1;
+
+        if (data->job->state == QEMU_BLOCKJOB_STATE_FAILED)
+            return -1;
+
+        qemuBlockJobSyncEnd(vm, data->job, VIR_ASYNC_JOB_NONE);
+    }
+
+    return 0;
+}
+
+
 static int
 qemuSnapshotDiscardMetadata(virDomainObj *vm,
                             virDomainMomentObj *snap,
@@ -2443,11 +2551,12 @@ qemuSnapshotDiscardMetadata(virDomainObj *vm,
 
 /* Discard one snapshot (or its metadata), without reparenting any children.  */
 static int
-qemuSnapshotDiscard(virQEMUDriver *driver,
-                    virDomainObj *vm,
-                    virDomainMomentObj *snap,
-                    bool update_parent,
-                    bool metadata_only)
+qemuSnapshotDiscardImpl(virQEMUDriver *driver,
+                        virDomainObj *vm,
+                        virDomainMomentObj *snap,
+                        GPtrArray *externalData,
+                        bool update_parent,
+                        bool metadata_only)
 {
     qemuDomainObjPrivate *priv;
 
@@ -2469,14 +2578,24 @@ qemuSnapshotDiscard(virQEMUDriver *driver,
                     return -1;
             }
 
-            if (qemuDomainSnapshotForEachQcow2(driver, def, snap, "-d", true) < 0)
-                return -1;
+            if (virDomainSnapshotIsExternal(snap)) {
+                if (qemuSnapshotDiscardExternal(vm, driver, externalData) < 0)
+                    return -1;
+            } else {
+                if (qemuDomainSnapshotForEachQcow2(driver, def, snap, "-d", true) < 0)
+                    return -1;
+            }
         } else {
-            priv = vm->privateData;
-            qemuDomainObjEnterMonitor(vm);
-            /* we continue on even in the face of error */
-            qemuMonitorDeleteSnapshot(priv->mon, snap->def->name);
-            qemuDomainObjExitMonitor(vm);
+            if (virDomainSnapshotIsExternal(snap)) {
+                if (qemuSnapshotDiscardExternal(vm, driver, externalData) < 0)
+                    return -1;
+            } else {
+                priv = vm->privateData;
+                qemuDomainObjEnterMonitor(vm);
+                /* we continue on even in the face of error */
+                qemuMonitorDeleteSnapshot(priv->mon, snap->def->name);
+                qemuDomainObjExitMonitor(vm);
+            }
         }
     }
 
@@ -2487,6 +2606,17 @@ qemuSnapshotDiscard(virQEMUDriver *driver,
 }
 
 
+static int
+qemuSnapshotDiscard(virQEMUDriver *driver,
+                    virDomainObj *vm,
+                    virDomainMomentObj *snap,
+                    bool update_parent,
+                    bool metadata_only)
+{
+    return qemuSnapshotDiscardImpl(driver, vm, snap, NULL, update_parent, metadata_only);
+}
+
+
 int
 qemuSnapshotDiscardAllMetadata(virQEMUDriver *driver,
                                virDomainObj *vm)
@@ -2509,9 +2639,10 @@ static int
 qemuSnapshotDeleteSingle(virDomainObj *vm,
                          virDomainMomentObj *snap,
                          virQEMUDriver *driver,
+                         GPtrArray *externalData,
                          bool metadata_only)
 {
-    return qemuSnapshotDiscard(driver, vm, snap, true, metadata_only);
+    return qemuSnapshotDiscardImpl(driver, vm, snap, externalData, true, metadata_only);
 }
 
 
@@ -2532,7 +2663,7 @@ qemuSnapshotDeleteAll(void *payload,
     virDomainMomentObj *snap = payload;
     struct qemuSnapshotDeleteAllData *data = opaque;
 
-    error = qemuSnapshotDeleteSingle(data->vm, snap, data->driver,
+    error = qemuSnapshotDeleteSingle(data->vm, snap, data->driver, NULL,
                                      data->metadata_only);
 
     if (error != 0 && data->error != 0)
@@ -2561,7 +2692,7 @@ qemuSnapshotDeleteChildren(virDomainObj *vm,
         return -1;
 
     if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN) {
-        return qemuSnapshotDeleteSingle(vm, snap, driver, metadata_only);
+        return qemuSnapshotDeleteSingle(vm, snap, driver, NULL, metadata_only);
     }
 
     return 0;
@@ -2678,7 +2809,7 @@ qemuSnapshotDelete(virDomainObj *vm,
                  VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY)) {
         ret = qemuSnapshotDeleteChildren(vm, snap, driver, metadata_only, flags);
     } else {
-        ret = qemuSnapshotDeleteSingle(vm, snap, driver, metadata_only);
+        ret = qemuSnapshotDeleteSingle(vm, snap, driver, externalData, metadata_only);
     }
 
  endjob:
-- 
2.37.2



More information about the libvir-list mailing list