From: Peter Krempa Date: Thu, 9 Jan 2020 13:19:07 +0000 (+0100) Subject: qemu: checkpoint: Introduce support for deleting checkpoints accross snapshots X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=30bc426071c0f5957af01181c063cd68ade97899;p=libvirt.git qemu: checkpoint: Introduce support for deleting checkpoints accross snapshots Allow deleting of checkpoints when snapshots were created along. The code tracks and modifies the checkpoint list so that backups can still be taken with such a backing chain. This unfortunately requires to rename few bitmaps (by copying and deleting them) in some cases. Signed-off-by: Peter Krempa Reviewed-by: Ján Tomko --- diff --git a/src/qemu/qemu_checkpoint.c b/src/qemu/qemu_checkpoint.c index e75cdd0458..55061bbf76 100644 --- a/src/qemu/qemu_checkpoint.c +++ b/src/qemu/qemu_checkpoint.c @@ -24,6 +24,7 @@ #include "qemu_capabilities.h" #include "qemu_monitor.h" #include "qemu_domain.h" +#include "qemu_block.h" #include "virerror.h" #include "virlog.h" @@ -150,39 +151,92 @@ qemuCheckpointFindActiveDiskInParent(virDomainObjPtr vm, int qemuCheckpointDiscardDiskBitmaps(virStorageSourcePtr src, + virHashTablePtr blockNamedNodeData, const char *delbitmap, const char *parentbitmap, - bool chkcurrent, - virJSONValuePtr actions) + virJSONValuePtr actions, + const char *diskdst) { - if (parentbitmap) { - g_autoptr(virJSONValue) arr = NULL; + virStorageSourcePtr n = src; - if (!(arr = virJSONValueNewArray())) - return -1; + /* find the backing chain entry with bitmap named '@delbitmap' */ + while (n) { + qemuBlockNamedNodeDataBitmapPtr tmp; - if (qemuMonitorTransactionBitmapMergeSourceAddBitmap(arr, - src->nodeformat, - delbitmap) < 0) - return -1; + if ((tmp = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData, + n, delbitmap))) { + break; + } + + n = n->backingStore; + } + + if (!n) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("bitmap '%s' not found in backing chain of '%s'"), + delbitmap, diskdst); + return -1; + } - if (chkcurrent) { - if (qemuMonitorTransactionBitmapEnable(actions, - src->nodeformat, - parentbitmap) < 0) + while (n) { + qemuBlockNamedNodeDataBitmapPtr srcbitmap; + + if (!(srcbitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData, + n, delbitmap))) + break; + + /* For the actual checkpoint deletion we will merge any bitmap into the + * bitmap of the parent checkpoint (@parentbitmap) or for any image + * where the parent checkpoint bitmap is not present we must rename + * the bitmap of the deleted checkpoint into the bitmap of the parent + * checkpoint as qemu can't currently take the allocation map and turn + * it into a bitmap and thus we wouldn't be able to do a backup. */ + if (parentbitmap) { + qemuBlockNamedNodeDataBitmapPtr dstbitmap; + g_autoptr(virJSONValue) arr = NULL; + + dstbitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData, + n, parentbitmap); + + if (dstbitmap) { + if (srcbitmap->recording && !dstbitmap->recording) { + if (qemuMonitorTransactionBitmapEnable(actions, + n->nodeformat, + dstbitmap->name) < 0) + return -1; + } + + } else { + if (qemuMonitorTransactionBitmapAdd(actions, + n->nodeformat, + parentbitmap, + true, + !srcbitmap->recording, + srcbitmap->granularity) < 0) + return -1; + } + + if (!(arr = virJSONValueNewArray())) + return -1; + + if (qemuMonitorTransactionBitmapMergeSourceAddBitmap(arr, + n->nodeformat, + srcbitmap->name) < 0) + return -1; + + if (qemuMonitorTransactionBitmapMerge(actions, + n->nodeformat, + parentbitmap, &arr) < 0) return -1; } - if (qemuMonitorTransactionBitmapMerge(actions, - src->nodeformat, - parentbitmap, &arr) < 0) + if (qemuMonitorTransactionBitmapRemove(actions, + n->nodeformat, + srcbitmap->name) < 0) return -1; - } - if (qemuMonitorTransactionBitmapRemove(actions, - src->nodeformat, - delbitmap) < 0) - return -1; + n = n->backingStore; + } return 0; } @@ -191,11 +245,11 @@ qemuCheckpointDiscardDiskBitmaps(virStorageSourcePtr src, static int qemuCheckpointDiscardBitmaps(virDomainObjPtr vm, virDomainCheckpointDefPtr chkdef, - bool chkcurrent, virDomainMomentObjPtr parent) { qemuDomainObjPrivatePtr priv = vm->privateData; virQEMUDriverPtr driver = priv->driver; + g_autoptr(virHashTable) blockNamedNodeData = NULL; int rc; g_autoptr(virJSONValue) actions = NULL; size_t i; @@ -203,6 +257,11 @@ qemuCheckpointDiscardBitmaps(virDomainObjPtr vm, if (!(actions = virJSONValueNewArray())) return -1; + qemuDomainObjEnterMonitor(driver, vm); + blockNamedNodeData = qemuMonitorBlockGetNamedNodeData(priv->mon); + if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || !blockNamedNodeData) + return -1; + for (i = 0; i < chkdef->ndisks; i++) { virDomainCheckpointDiskDef *chkdisk = &chkdef->disks[i]; virDomainDiskDefPtr domdisk = virDomainDiskByTarget(vm->def, chkdisk->name); @@ -223,8 +282,9 @@ qemuCheckpointDiscardBitmaps(virDomainObjPtr vm, chkdisk->name))) parentbitmap = parentchkdisk->name; - if (qemuCheckpointDiscardDiskBitmaps(domdisk->src, chkdisk->bitmap, - parentbitmap, chkcurrent, actions) < 0) + if (qemuCheckpointDiscardDiskBitmaps(domdisk->src, blockNamedNodeData, + chkdisk->bitmap, parentbitmap, + actions, domdisk->dst) < 0) return -1; } @@ -262,7 +322,7 @@ qemuCheckpointDiscard(virQEMUDriverPtr driver, virDomainCheckpointDefPtr chkdef = virDomainCheckpointObjGetDef(chk); parent = virDomainCheckpointFindByName(vm->checkpoints, chk->def->parent_name); - if (qemuCheckpointDiscardBitmaps(vm, chkdef, chkcurrent, parent) < 0) + if (qemuCheckpointDiscardBitmaps(vm, chkdef, parent) < 0) return -1; } diff --git a/src/qemu/qemu_checkpoint.h b/src/qemu/qemu_checkpoint.h index 85fd453d50..976b1eed0f 100644 --- a/src/qemu/qemu_checkpoint.h +++ b/src/qemu/qemu_checkpoint.h @@ -74,7 +74,8 @@ qemuCheckpointRollbackMetadata(virDomainObjPtr vm, int qemuCheckpointDiscardDiskBitmaps(virStorageSourcePtr src, + virHashTablePtr blockNamedNodeData, const char *delbitmap, const char *parentbitmap, - bool chkcurrent, - virJSONValuePtr actions); + virJSONValuePtr actions, + const char *diskdst); diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c index 4b243915f6..5d0b0c228e 100644 --- a/tests/qemublocktest.c +++ b/tests/qemublocktest.c @@ -708,6 +708,7 @@ struct testQemuCheckpointDeleteMergeData { virStorageSourcePtr chain; const char *deletebitmap; const char *parentbitmap; + const char *nodedatafile; }; @@ -718,22 +719,30 @@ testQemuCheckpointDeleteMerge(const void *opaque) g_autofree char *actual = NULL; g_autofree char *expectpath = NULL; g_autoptr(virJSONValue) actions = NULL; - bool currentcheckpoint; + g_autoptr(virJSONValue) nodedatajson = NULL; + g_autoptr(virHashTable) nodedata = NULL; expectpath = g_strdup_printf("%s/%s%s-out.json", abs_srcdir, checkpointDeletePrefix, data->name); - if (!(actions = virJSONValueNewArray())) + if (!(nodedatajson = virTestLoadFileJSON(bitmapDetectPrefix, data->nodedatafile, + ".json", NULL))) return -1; - /* hack to get the 'current' state until the function stops accepting it */ - currentcheckpoint = STREQ("current", data->deletebitmap); + if (!(nodedata = qemuMonitorJSONBlockGetNamedNodeDataJSON(nodedatajson))) { + VIR_TEST_VERBOSE("failed to load nodedata JSON\n"); + return -1; + } + + if (!(actions = virJSONValueNewArray())) + return -1; if (qemuCheckpointDiscardDiskBitmaps(data->chain, + nodedata, data->deletebitmap, data->parentbitmap, - currentcheckpoint, - actions) < 0) { + actions, + "testdisk") < 0) { VIR_TEST_VERBOSE("failed to generate checkpoint delete transaction\n"); return -1; } @@ -992,22 +1001,23 @@ mymain(void) TEST_BACKUP_BITMAP_CALCULATE("snapshot-intermediate", bitmapSourceChain, "d", "snapshots"); TEST_BACKUP_BITMAP_CALCULATE("snapshot-deep", bitmapSourceChain, "a", "snapshots"); -#define TEST_CHECKPOINT_DELETE_MERGE(testname, delbmp, parbmp) \ +#define TEST_CHECKPOINT_DELETE_MERGE(testname, delbmp, parbmp, named) \ do { \ checkpointdeletedata.name = testname; \ checkpointdeletedata.chain = bitmapSourceChain; \ checkpointdeletedata.deletebitmap = delbmp; \ checkpointdeletedata.parentbitmap = parbmp; \ + checkpointdeletedata.nodedatafile = named; \ if (virTestRun("checkpoint delete " testname, \ testQemuCheckpointDeleteMerge, &checkpointdeletedata) < 0) \ ret = -1; \ } while (0) - TEST_CHECKPOINT_DELETE_MERGE("basic-noparent", "a", NULL); - TEST_CHECKPOINT_DELETE_MERGE("basic-intermediate1", "b", "a"); - TEST_CHECKPOINT_DELETE_MERGE("basic-intermediate2", "c", "b"); - TEST_CHECKPOINT_DELETE_MERGE("basic-intermediate3", "d", "c"); - TEST_CHECKPOINT_DELETE_MERGE("basic-current", "current", "d"); + TEST_CHECKPOINT_DELETE_MERGE("basic-noparent", "a", NULL, "basic"); + TEST_CHECKPOINT_DELETE_MERGE("basic-intermediate1", "b", "a", "basic"); + TEST_CHECKPOINT_DELETE_MERGE("basic-intermediate2", "c", "b", "basic"); + TEST_CHECKPOINT_DELETE_MERGE("basic-intermediate3", "d", "c", "basic"); + TEST_CHECKPOINT_DELETE_MERGE("basic-current", "current", "d", "basic"); cleanup: virHashFree(diskxmljsondata.schema);