[PATCH v1 2/4] qemu: Support active disk internal snapshots

Or Ozeri oro at il.ibm.com
Thu Jan 12 08:44:37 UTC 2023


libvirt supports taking external disk snapshots on a running VM,
using qemu's "blockdev-snapshot" command.
qemu also supports "blockdev-snapshot-internal-sync" to do the
same for internal snapshots.
This commit wraps this (old) qemu capability to allow libvirt users
to take internal disk snapshots on a running VM.
This will only work for disk types which support internal snapshots,
and thus we require the disk type to be part of a white list of known
types (only RBD disks for start).

Signed-off-by: Or Ozeri <oro at il.ibm.com>
---
 src/qemu/qemu_monitor.c                       |  9 ++
 src/qemu/qemu_monitor.h                       |  5 ++
 src/qemu/qemu_monitor_json.c                  | 14 ++++
 src/qemu/qemu_monitor_json.h                  |  5 ++
 src/qemu/qemu_snapshot.c                      | 83 +++++++++++--------
 .../disk_snapshot.xml                         |  2 +-
 .../disk_snapshot.xml                         |  2 +-
 .../disk_snapshot_redefine.xml                |  2 +-
 tests/qemumonitorjsontest.c                   |  1 +
 9 files changed, 84 insertions(+), 39 deletions(-)

diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 38f89167e0..f6dab34243 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -4225,6 +4225,15 @@ qemuMonitorTransactionSnapshotBlockdev(virJSONValue *actions,
 }
 
 
+int
+qemuMonitorTransactionInternalSnapshotBlockdev(virJSONValue *actions,
+                                               const char *device,
+                                               const char *name)
+{
+    return qemuMonitorJSONTransactionInternalSnapshotBlockdev(actions, device, name);
+}
+
+
 int
 qemuMonitorTransactionBackup(virJSONValue *actions,
                              const char *device,
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 2d16214ba2..1bfd1ccbc2 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -1411,6 +1411,11 @@ qemuMonitorTransactionSnapshotBlockdev(virJSONValue *actions,
                                        const char *node,
                                        const char *overlay);
 
+int
+qemuMonitorTransactionInternalSnapshotBlockdev(virJSONValue *actions,
+                                               const char *device,
+                                               const char *name);
+
 typedef enum {
     QEMU_MONITOR_TRANSACTION_BACKUP_SYNC_MODE_NONE = 0,
     QEMU_MONITOR_TRANSACTION_BACKUP_SYNC_MODE_INCREMENTAL,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index db99017555..002a6caa52 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -8307,6 +8307,20 @@ qemuMonitorJSONTransactionSnapshotBlockdev(virJSONValue *actions,
                                          NULL);
 }
 
+
+int
+qemuMonitorJSONTransactionInternalSnapshotBlockdev(virJSONValue *actions,
+                                                   const char *device,
+                                                   const char *name)
+{
+    return qemuMonitorJSONTransactionAdd(actions,
+                                         "blockdev-snapshot-internal-sync",
+                                         "s:device", device,
+                                         "s:name", name,
+                                         NULL);
+}
+
+
 VIR_ENUM_DECL(qemuMonitorTransactionBackupSyncMode);
 VIR_ENUM_IMPL(qemuMonitorTransactionBackupSyncMode,
               QEMU_MONITOR_TRANSACTION_BACKUP_SYNC_MODE_LAST,
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 6f376cf9b7..313004f327 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -779,6 +779,11 @@ qemuMonitorJSONTransactionSnapshotBlockdev(virJSONValue *actions,
                                            const char *node,
                                            const char *overlay);
 
+int
+qemuMonitorJSONTransactionInternalSnapshotBlockdev(virJSONValue *actions,
+                                                   const char *device,
+                                                   const char *name);
+
 int
 qemuMonitorJSONTransactionBackup(virJSONValue *actions,
                                  const char *device,
diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
index b8416808b3..9146ecae2f 100644
--- a/src/qemu/qemu_snapshot.c
+++ b/src/qemu/qemu_snapshot.c
@@ -639,20 +639,13 @@ qemuSnapshotPrepare(virDomainObj *vm,
         case VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL:
             found_internal = true;
 
-            if (def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT && active) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("active qemu domains require external disk "
-                                 "snapshots; disk %s requested internal"),
-                               disk->name);
-                return -1;
-            }
-
             if (qemuSnapshotPrepareDiskInternal(dom_disk,
                                                 active) < 0)
                 return -1;
 
             if (dom_disk->src->format > 0 &&
-                dom_disk->src->format != VIR_STORAGE_FILE_QCOW2) {
+                dom_disk->src->format != VIR_STORAGE_FILE_QCOW2 &&
+                dom_disk->src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD) {
                 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                _("internal snapshot for disk %s unsupported "
                                  "for storage type %s"),
@@ -727,7 +720,7 @@ qemuSnapshotPrepare(virDomainObj *vm,
     }
 
     /* disk snapshot requires at least one disk */
-    if (def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT && !external) {
+    if (def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT && !external && !found_internal) {
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                        _("disk-only snapshots require at least "
                          "one disk to be selected for snapshot"));
@@ -843,6 +836,7 @@ qemuSnapshotDiskCleanup(qemuSnapshotDiskData *data,
 struct _qemuSnapshotDiskContext {
     qemuSnapshotDiskData *dd;
     size_t ndd;
+    bool has_internal;
 
     virJSONValue *actions;
 
@@ -1061,17 +1055,17 @@ qemuSnapshotDiskPrepareOne(qemuSnapshotDiskContext *snapctxt,
 
 
 /**
- * qemuSnapshotDiskPrepareActiveExternal:
+ * qemuSnapshotDiskPrepareActive:
  *
  * Collects and prepares a list of structures that hold information about disks
  * that are selected for the snapshot.
  */
 static qemuSnapshotDiskContext *
-qemuSnapshotDiskPrepareActiveExternal(virDomainObj *vm,
-                                      virDomainMomentObj *snap,
-                                      bool reuse,
-                                      GHashTable *blockNamedNodeData,
-                                      virDomainAsyncJob asyncJob)
+qemuSnapshotDiskPrepareActive(virDomainObj *vm,
+                              virDomainMomentObj *snap,
+                              bool reuse,
+                              GHashTable *blockNamedNodeData,
+                              virDomainAsyncJob asyncJob)
 {
     g_autoptr(qemuSnapshotDiskContext) snapctxt = NULL;
     size_t i;
@@ -1080,16 +1074,33 @@ qemuSnapshotDiskPrepareActiveExternal(virDomainObj *vm,
     snapctxt = qemuSnapshotDiskContextNew(snapdef->ndisks, vm, asyncJob);
 
     for (i = 0; i < snapdef->ndisks; i++) {
-        if (snapdef->disks[i].snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL)
-            continue;
+        switch (snapdef->disks[i].snapshot) {
+            case VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL: {
+                if (qemuSnapshotDiskPrepareOne(snapctxt,
+                                               vm->def->disks[i],
+                                               snapdef->disks + i,
+                                               blockNamedNodeData,
+                                               reuse,
+                                               true) < 0)
+                    return NULL;
+                break;
+            }
 
-        if (qemuSnapshotDiskPrepareOne(snapctxt,
-                                       vm->def->disks[i],
-                                       snapdef->disks + i,
-                                       blockNamedNodeData,
-                                       reuse,
-                                       true) < 0)
-            return NULL;
+            case VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL: {
+                snapctxt->has_internal = true;
+                if (qemuMonitorTransactionInternalSnapshotBlockdev(snapctxt->actions,
+                                                                   vm->def->disks[i]->src->nodeformat,
+                                                                   snapdef->disks[i].snapshot_name) < 0)
+                    return NULL;
+                break;
+            }
+
+            case VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT:
+            case VIR_DOMAIN_SNAPSHOT_LOCATION_NO:
+            case VIR_DOMAIN_SNAPSHOT_LOCATION_MANUAL:
+            case VIR_DOMAIN_SNAPSHOT_LOCATION_LAST:
+                continue;
+        }
     }
 
     return g_steal_pointer(&snapctxt);
@@ -1173,7 +1184,7 @@ qemuSnapshotDiskCreate(qemuSnapshotDiskContext *snapctxt)
     int rc;
 
     /* check whether there's anything to do */
-    if (snapctxt->ndd == 0)
+    if (snapctxt->ndd == 0 && !snapctxt->has_internal)
         return 0;
 
     if (qemuDomainObjEnterMonitorAsync(snapctxt->vm, snapctxt->asyncJob) < 0)
@@ -1206,11 +1217,11 @@ qemuSnapshotDiskCreate(qemuSnapshotDiskContext *snapctxt)
 
 /* The domain is expected to be locked and active. */
 static int
-qemuSnapshotCreateActiveExternalDisks(virDomainObj *vm,
-                                      virDomainMomentObj *snap,
-                                      GHashTable *blockNamedNodeData,
-                                      unsigned int flags,
-                                      virDomainAsyncJob asyncJob)
+qemuSnapshotCreateActiveDisks(virDomainObj *vm,
+                              virDomainMomentObj *snap,
+                              GHashTable *blockNamedNodeData,
+                              unsigned int flags,
+                              virDomainAsyncJob asyncJob)
 {
     bool reuse = (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT) != 0;
     g_autoptr(qemuSnapshotDiskContext) snapctxt = NULL;
@@ -1220,8 +1231,8 @@ qemuSnapshotCreateActiveExternalDisks(virDomainObj *vm,
 
     /* prepare a list of objects to use in the vm definition so that we don't
      * have to roll back later */
-    if (!(snapctxt = qemuSnapshotDiskPrepareActiveExternal(vm, snap, reuse,
-                                                           blockNamedNodeData, asyncJob)))
+    if (!(snapctxt = qemuSnapshotDiskPrepareActive(vm, snap, reuse,
+                                                   blockNamedNodeData, asyncJob)))
         return -1;
 
     if (qemuSnapshotDiskCreate(snapctxt) < 0)
@@ -1361,9 +1372,9 @@ qemuSnapshotCreateActiveExternal(virQEMUDriver *driver,
 
     /* the domain is now paused if a memory snapshot was requested */
 
-    if ((ret = qemuSnapshotCreateActiveExternalDisks(vm, snap,
-                                                     blockNamedNodeData, flags,
-                                                     VIR_ASYNC_JOB_SNAPSHOT)) < 0)
+    if ((ret = qemuSnapshotCreateActiveDisks(vm, snap,
+                                             blockNamedNodeData, flags,
+                                             VIR_ASYNC_JOB_SNAPSHOT)) < 0)
         goto cleanup;
 
     /* the snapshot is complete now */
diff --git a/tests/qemudomainsnapshotxml2xmlin/disk_snapshot.xml b/tests/qemudomainsnapshotxml2xmlin/disk_snapshot.xml
index cf5ea0814e..87b6251a7f 100644
--- a/tests/qemudomainsnapshotxml2xmlin/disk_snapshot.xml
+++ b/tests/qemudomainsnapshotxml2xmlin/disk_snapshot.xml
@@ -4,7 +4,7 @@
   <disks>
     <disk name='/dev/HostVG/QEMUGuest1'/>
     <disk name='hdb' snapshot='no'/>
-    <disk name='hdc' snapshot='internal'/>
+    <disk name='hdc' snapshot='internal' snapshotName='snap1'/>
     <disk name='hdd' snapshot='external'>
       <source/>
       <driver type='qed'/>
diff --git a/tests/qemudomainsnapshotxml2xmlout/disk_snapshot.xml b/tests/qemudomainsnapshotxml2xmlout/disk_snapshot.xml
index 76c543d25c..6cf93183d5 100644
--- a/tests/qemudomainsnapshotxml2xmlout/disk_snapshot.xml
+++ b/tests/qemudomainsnapshotxml2xmlout/disk_snapshot.xml
@@ -5,7 +5,7 @@
   <disks>
     <disk name='/dev/HostVG/QEMUGuest1'/>
     <disk name='hdb' snapshot='no'/>
-    <disk name='hdc' snapshot='internal'/>
+    <disk name='hdc' snapshot='internal' snapshotName='snap1'/>
     <disk name='hdd' snapshot='external' type='file'>
       <driver type='qed'/>
     </disk>
diff --git a/tests/qemudomainsnapshotxml2xmlout/disk_snapshot_redefine.xml b/tests/qemudomainsnapshotxml2xmlout/disk_snapshot_redefine.xml
index 24b41ba7c5..f574793edf 100644
--- a/tests/qemudomainsnapshotxml2xmlout/disk_snapshot_redefine.xml
+++ b/tests/qemudomainsnapshotxml2xmlout/disk_snapshot_redefine.xml
@@ -10,7 +10,7 @@
   <disks>
     <disk name='hda' snapshot='no'/>
     <disk name='hdb' snapshot='no'/>
-    <disk name='hdc' snapshot='internal'/>
+    <disk name='hdc' snapshot='internal' snapshotName='snap1'/>
     <disk name='hdd' snapshot='external' type='file'>
       <driver type='qed'/>
       <source file='/path/to/generated4'/>
diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c
index 1db1f2b949..1269c74e43 100644
--- a/tests/qemumonitorjsontest.c
+++ b/tests/qemumonitorjsontest.c
@@ -2587,6 +2587,7 @@ testQemuMonitorJSONTransaction(const void *opaque)
         qemuMonitorTransactionBitmapDisable(actions, "node4", "bitmap4") < 0 ||
         qemuMonitorTransactionBitmapMerge(actions, "node5", "bitmap5", &mergebitmaps) < 0 ||
         qemuMonitorTransactionSnapshotBlockdev(actions, "node7", "overlay7") < 0 ||
+        qemuMonitorTransactionInternalSnapshotBlockdev(actions, "device1", "snapshot1") < 0 ||
         qemuMonitorTransactionBackup(actions, "dev8", "job8", "target8", "bitmap8",
                                      QEMU_MONITOR_TRANSACTION_BACKUP_SYNC_MODE_NONE) < 0 ||
         qemuMonitorTransactionBackup(actions, "dev9", "job9", "target9", "bitmap9",
-- 
2.25.1



More information about the libvir-list mailing list