Blame SOURCES/libvirt-qemu-checkpoint-Introduce-support-for-deleting-checkpoints-accross-snapshots.patch

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