render / rpms / libvirt

Forked from rpms/libvirt 9 months ago
Clone
d76c62
From 30377cd627a919e51cc4bb60a8a57e94e73f016c Mon Sep 17 00:00:00 2001
d76c62
Message-Id: <30377cd627a919e51cc4bb60a8a57e94e73f016c@dist-git>
d76c62
From: Peter Krempa <pkrempa@redhat.com>
d76c62
Date: Tue, 4 Feb 2020 15:08:12 +0100
d76c62
Subject: [PATCH] qemu: checkpoint: Introduce support for deleting checkpoints
d76c62
 accross snapshots
d76c62
MIME-Version: 1.0
d76c62
Content-Type: text/plain; charset=UTF-8
d76c62
Content-Transfer-Encoding: 8bit
d76c62
d76c62
Allow deleting of checkpoints when snapshots were created along. The
d76c62
code tracks and modifies the checkpoint list so that backups can still
d76c62
be taken with such a backing chain. This unfortunately requires to
d76c62
rename few bitmaps (by copying and deleting them) in some cases.
d76c62
d76c62
Signed-off-by: Peter Krempa <pkrempa@redhat.com>
d76c62
Reviewed-by: Ján Tomko <jtomko@redhat.com>
d76c62
(cherry picked from commit 30bc426071c0f5957af01181c063cd68ade97899)
d76c62
d76c62
https://bugzilla.redhat.com/show_bug.cgi?id=1207659
d76c62
Message-Id: <e9f416811658b2ba03968ae5fca783b7fb7649f7.1580824112.git.pkrempa@redhat.com>
d76c62
Reviewed-by: Ján Tomko <jtomko@redhat.com>
d76c62
---
d76c62
 src/qemu/qemu_checkpoint.c | 112 ++++++++++++++++++++++++++++---------
d76c62
 src/qemu/qemu_checkpoint.h |   5 +-
d76c62
 tests/qemublocktest.c      |  34 +++++++----
d76c62
 3 files changed, 111 insertions(+), 40 deletions(-)
d76c62
d76c62
diff --git a/src/qemu/qemu_checkpoint.c b/src/qemu/qemu_checkpoint.c
d76c62
index e75cdd0458..55061bbf76 100644
d76c62
--- a/src/qemu/qemu_checkpoint.c
d76c62
+++ b/src/qemu/qemu_checkpoint.c
d76c62
@@ -24,6 +24,7 @@
d76c62
 #include "qemu_capabilities.h"
d76c62
 #include "qemu_monitor.h"
d76c62
 #include "qemu_domain.h"
d76c62
+#include "qemu_block.h"
d76c62
 
d76c62
 #include "virerror.h"
d76c62
 #include "virlog.h"
d76c62
@@ -150,39 +151,92 @@ qemuCheckpointFindActiveDiskInParent(virDomainObjPtr vm,
d76c62
 
d76c62
 int
d76c62
 qemuCheckpointDiscardDiskBitmaps(virStorageSourcePtr src,
d76c62
+                                 virHashTablePtr blockNamedNodeData,
d76c62
                                  const char *delbitmap,
d76c62
                                  const char *parentbitmap,
d76c62
-                                 bool chkcurrent,
d76c62
-                                 virJSONValuePtr actions)
d76c62
+                                 virJSONValuePtr actions,
d76c62
+                                 const char *diskdst)
d76c62
 {
d76c62
-    if (parentbitmap) {
d76c62
-        g_autoptr(virJSONValue) arr = NULL;
d76c62
+    virStorageSourcePtr n = src;
d76c62
 
d76c62
-        if (!(arr = virJSONValueNewArray()))
d76c62
-            return -1;
d76c62
+    /* find the backing chain entry with bitmap named '@delbitmap' */
d76c62
+    while (n) {
d76c62
+        qemuBlockNamedNodeDataBitmapPtr tmp;
d76c62
 
d76c62
-        if (qemuMonitorTransactionBitmapMergeSourceAddBitmap(arr,
d76c62
-                                                             src->nodeformat,
d76c62
-                                                             delbitmap) < 0)
d76c62
-            return -1;
d76c62
+        if ((tmp = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
d76c62
+                                                         n, delbitmap))) {
d76c62
+            break;
d76c62
+        }
d76c62
 
d76c62
-        if (chkcurrent) {
d76c62
-            if (qemuMonitorTransactionBitmapEnable(actions,
d76c62
-                                                   src->nodeformat,
d76c62
-                                                   parentbitmap) < 0)
d76c62
+        n = n->backingStore;
d76c62
+    }
d76c62
+
d76c62
+    if (!n) {
d76c62
+        virReportError(VIR_ERR_INTERNAL_ERROR,
d76c62
+                       _("bitmap '%s' not found in backing chain of '%s'"),
d76c62
+                       delbitmap, diskdst);
d76c62
+        return -1;
d76c62
+    }
d76c62
+
d76c62
+    while (n) {
d76c62
+        qemuBlockNamedNodeDataBitmapPtr srcbitmap;
d76c62
+
d76c62
+        if (!(srcbitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
d76c62
+                                                                n, delbitmap)))
d76c62
+            break;
d76c62
+
d76c62
+        /* For the actual checkpoint deletion we will merge any bitmap into the
d76c62
+         * bitmap of the parent checkpoint (@parentbitmap) or for any image
d76c62
+         * where the parent checkpoint bitmap is not present we must rename
d76c62
+         * the bitmap of the deleted checkpoint into the bitmap of the parent
d76c62
+         * checkpoint as qemu can't currently take the allocation map and turn
d76c62
+         * it into a bitmap and thus we wouldn't be able to do a backup. */
d76c62
+        if (parentbitmap) {
d76c62
+            qemuBlockNamedNodeDataBitmapPtr dstbitmap;
d76c62
+            g_autoptr(virJSONValue) arr = NULL;
d76c62
+
d76c62
+            dstbitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
d76c62
+                                                              n, parentbitmap);
d76c62
+
d76c62
+            if (dstbitmap) {
d76c62
+                if (srcbitmap->recording && !dstbitmap->recording) {
d76c62
+                    if (qemuMonitorTransactionBitmapEnable(actions,
d76c62
+                                                           n->nodeformat,
d76c62
+                                                           dstbitmap->name) < 0)
d76c62
+                        return -1;
d76c62
+                }
d76c62
+
d76c62
+            } else {
d76c62
+                if (qemuMonitorTransactionBitmapAdd(actions,
d76c62
+                                                    n->nodeformat,
d76c62
+                                                    parentbitmap,
d76c62
+                                                    true,
d76c62
+                                                    !srcbitmap->recording,
d76c62
+                                                    srcbitmap->granularity) < 0)
d76c62
+                    return -1;
d76c62
+            }
d76c62
+
d76c62
+            if (!(arr = virJSONValueNewArray()))
d76c62
+                return -1;
d76c62
+
d76c62
+            if (qemuMonitorTransactionBitmapMergeSourceAddBitmap(arr,
d76c62
+                                                                 n->nodeformat,
d76c62
+                                                                 srcbitmap->name) < 0)
d76c62
+                return -1;
d76c62
+
d76c62
+            if (qemuMonitorTransactionBitmapMerge(actions,
d76c62
+                                                  n->nodeformat,
d76c62
+                                                  parentbitmap, &arr) < 0)
d76c62
                 return -1;
d76c62
         }
d76c62
 
d76c62
-        if (qemuMonitorTransactionBitmapMerge(actions,
d76c62
-                                              src->nodeformat,
d76c62
-                                              parentbitmap, &arr) < 0)
d76c62
+        if (qemuMonitorTransactionBitmapRemove(actions,
d76c62
+                                               n->nodeformat,
d76c62
+                                               srcbitmap->name) < 0)
d76c62
             return -1;
d76c62
-    }
d76c62
 
d76c62
-    if (qemuMonitorTransactionBitmapRemove(actions,
d76c62
-                                           src->nodeformat,
d76c62
-                                           delbitmap) < 0)
d76c62
-        return -1;
d76c62
+        n = n->backingStore;
d76c62
+    }
d76c62
 
d76c62
     return 0;
d76c62
 }
d76c62
@@ -191,11 +245,11 @@ qemuCheckpointDiscardDiskBitmaps(virStorageSourcePtr src,
d76c62
 static int
d76c62
 qemuCheckpointDiscardBitmaps(virDomainObjPtr vm,
d76c62
                              virDomainCheckpointDefPtr chkdef,
d76c62
-                             bool chkcurrent,
d76c62
                              virDomainMomentObjPtr parent)
d76c62
 {
d76c62
     qemuDomainObjPrivatePtr priv = vm->privateData;
d76c62
     virQEMUDriverPtr driver = priv->driver;
d76c62
+    g_autoptr(virHashTable) blockNamedNodeData = NULL;
d76c62
     int rc;
d76c62
     g_autoptr(virJSONValue) actions = NULL;
d76c62
     size_t i;
d76c62
@@ -203,6 +257,11 @@ qemuCheckpointDiscardBitmaps(virDomainObjPtr vm,
d76c62
     if (!(actions = virJSONValueNewArray()))
d76c62
         return -1;
d76c62
 
d76c62
+    qemuDomainObjEnterMonitor(driver, vm);
d76c62
+    blockNamedNodeData = qemuMonitorBlockGetNamedNodeData(priv->mon);
d76c62
+    if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || !blockNamedNodeData)
d76c62
+        return -1;
d76c62
+
d76c62
     for (i = 0; i < chkdef->ndisks; i++) {
d76c62
         virDomainCheckpointDiskDef *chkdisk = &chkdef->disks[i];
d76c62
         virDomainDiskDefPtr domdisk = virDomainDiskByTarget(vm->def, chkdisk->name);
d76c62
@@ -223,8 +282,9 @@ qemuCheckpointDiscardBitmaps(virDomainObjPtr vm,
d76c62
                                                                   chkdisk->name)))
d76c62
             parentbitmap = parentchkdisk->name;
d76c62
 
d76c62
-        if (qemuCheckpointDiscardDiskBitmaps(domdisk->src, chkdisk->bitmap,
d76c62
-                                             parentbitmap, chkcurrent, actions) < 0)
d76c62
+        if (qemuCheckpointDiscardDiskBitmaps(domdisk->src, blockNamedNodeData,
d76c62
+                                             chkdisk->bitmap, parentbitmap,
d76c62
+                                             actions, domdisk->dst) < 0)
d76c62
             return -1;
d76c62
     }
d76c62
 
d76c62
@@ -262,7 +322,7 @@ qemuCheckpointDiscard(virQEMUDriverPtr driver,
d76c62
         virDomainCheckpointDefPtr chkdef = virDomainCheckpointObjGetDef(chk);
d76c62
         parent = virDomainCheckpointFindByName(vm->checkpoints,
d76c62
                                                chk->def->parent_name);
d76c62
-        if (qemuCheckpointDiscardBitmaps(vm, chkdef, chkcurrent, parent) < 0)
d76c62
+        if (qemuCheckpointDiscardBitmaps(vm, chkdef, parent) < 0)
d76c62
             return -1;
d76c62
     }
d76c62
 
d76c62
diff --git a/src/qemu/qemu_checkpoint.h b/src/qemu/qemu_checkpoint.h
d76c62
index 85fd453d50..976b1eed0f 100644
d76c62
--- a/src/qemu/qemu_checkpoint.h
d76c62
+++ b/src/qemu/qemu_checkpoint.h
d76c62
@@ -74,7 +74,8 @@ qemuCheckpointRollbackMetadata(virDomainObjPtr vm,
d76c62
 
d76c62
 int
d76c62
 qemuCheckpointDiscardDiskBitmaps(virStorageSourcePtr src,
d76c62
+                                 virHashTablePtr blockNamedNodeData,
d76c62
                                  const char *delbitmap,
d76c62
                                  const char *parentbitmap,
d76c62
-                                 bool chkcurrent,
d76c62
-                                 virJSONValuePtr actions);
d76c62
+                                 virJSONValuePtr actions,
d76c62
+                                 const char *diskdst);
d76c62
diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c
d76c62
index edaf82053d..e56f813424 100644
d76c62
--- a/tests/qemublocktest.c
d76c62
+++ b/tests/qemublocktest.c
d76c62
@@ -704,6 +704,7 @@ struct testQemuCheckpointDeleteMergeData {
d76c62
     virStorageSourcePtr chain;
d76c62
     const char *deletebitmap;
d76c62
     const char *parentbitmap;
d76c62
+    const char *nodedatafile;
d76c62
 };
d76c62
 
d76c62
 
d76c62
@@ -714,22 +715,30 @@ testQemuCheckpointDeleteMerge(const void *opaque)
d76c62
     g_autofree char *actual = NULL;
d76c62
     g_autofree char *expectpath = NULL;
d76c62
     g_autoptr(virJSONValue) actions = NULL;
d76c62
-    bool currentcheckpoint;
d76c62
+    g_autoptr(virJSONValue) nodedatajson = NULL;
d76c62
+    g_autoptr(virHashTable) nodedata = NULL;
d76c62
 
d76c62
     expectpath = g_strdup_printf("%s/%s%s-out.json", abs_srcdir,
d76c62
                                  checkpointDeletePrefix, data->name);
d76c62
 
d76c62
+    if (!(nodedatajson = virTestLoadFileJSON(bitmapDetectPrefix, data->nodedatafile,
d76c62
+                                             ".json", NULL)))
d76c62
+        return -1;
d76c62
+
d76c62
+    if (!(nodedata = qemuMonitorJSONBlockGetNamedNodeDataJSON(nodedatajson))) {
d76c62
+        VIR_TEST_VERBOSE("failed to load nodedata JSON\n");
d76c62
+        return -1;
d76c62
+    }
d76c62
+
d76c62
     if (!(actions = virJSONValueNewArray()))
d76c62
         return -1;
d76c62
 
d76c62
-    /* hack to get the 'current' state until the function stops accepting it */
d76c62
-    currentcheckpoint = STREQ("current", data->deletebitmap);
d76c62
-
d76c62
     if (qemuCheckpointDiscardDiskBitmaps(data->chain,
d76c62
+                                         nodedata,
d76c62
                                          data->deletebitmap,
d76c62
                                          data->parentbitmap,
d76c62
-                                         currentcheckpoint,
d76c62
-                                         actions) < 0) {
d76c62
+                                         actions,
d76c62
+                                         "testdisk") < 0) {
d76c62
         VIR_TEST_VERBOSE("failed to generate checkpoint delete transaction\n");
d76c62
         return -1;
d76c62
     }
d76c62
@@ -988,22 +997,23 @@ mymain(void)
d76c62
     TEST_BACKUP_BITMAP_CALCULATE("snapshot-intermediate", bitmapSourceChain, "d", "snapshots");
d76c62
     TEST_BACKUP_BITMAP_CALCULATE("snapshot-deep", bitmapSourceChain, "a", "snapshots");
d76c62
 
d76c62
-#define TEST_CHECKPOINT_DELETE_MERGE(testname, delbmp, parbmp) \
d76c62
+#define TEST_CHECKPOINT_DELETE_MERGE(testname, delbmp, parbmp, named) \
d76c62
     do { \
d76c62
         checkpointdeletedata.name = testname; \
d76c62
         checkpointdeletedata.chain = bitmapSourceChain; \
d76c62
         checkpointdeletedata.deletebitmap = delbmp; \
d76c62
         checkpointdeletedata.parentbitmap = parbmp; \
d76c62
+        checkpointdeletedata.nodedatafile = named; \
d76c62
         if (virTestRun("checkpoint delete " testname, \
d76c62
                        testQemuCheckpointDeleteMerge, &checkpointdeletedata) < 0) \
d76c62
         ret = -1; \
d76c62
     } while (0)
d76c62
 
d76c62
-    TEST_CHECKPOINT_DELETE_MERGE("basic-noparent", "a", NULL);
d76c62
-    TEST_CHECKPOINT_DELETE_MERGE("basic-intermediate1", "b", "a");
d76c62
-    TEST_CHECKPOINT_DELETE_MERGE("basic-intermediate2", "c", "b");
d76c62
-    TEST_CHECKPOINT_DELETE_MERGE("basic-intermediate3", "d", "c");
d76c62
-    TEST_CHECKPOINT_DELETE_MERGE("basic-current", "current", "d");
d76c62
+    TEST_CHECKPOINT_DELETE_MERGE("basic-noparent", "a", NULL, "basic");
d76c62
+    TEST_CHECKPOINT_DELETE_MERGE("basic-intermediate1", "b", "a", "basic");
d76c62
+    TEST_CHECKPOINT_DELETE_MERGE("basic-intermediate2", "c", "b", "basic");
d76c62
+    TEST_CHECKPOINT_DELETE_MERGE("basic-intermediate3", "d", "c", "basic");
d76c62
+    TEST_CHECKPOINT_DELETE_MERGE("basic-current", "current", "d", "basic");
d76c62
 
d76c62
  cleanup:
d76c62
     virHashFree(diskxmljsondata.schema);
d76c62
-- 
d76c62
2.25.0
d76c62