render / rpms / libvirt

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