mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-01 17:35:17 +00:00
qemu: backup: Rewrite backup bitmap handling to the new bitmap semantics
Reuse qemuBlockGetBitmapMergeActions which allows removal of the ad-hoc implementation of bitmap merging for backup. The new approach is simpler and also more robust in case some of the bitmaps break as they remove the dependency on the whole chain of bitmaps working. The new approach also allows backups if a snapshot is created outside of libvirt. Additionally the code is greatly simplified. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
parent
4fa8654ece
commit
e0d8d989e2
@ -173,199 +173,58 @@ qemuBackupDiskDataCleanup(virDomainObjPtr vm,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
int
|
||||||
* qemuBackupBeginCollectIncrementalCheckpoints:
|
qemuBackupDiskPrepareOneBitmapsChain(virStorageSourcePtr backingChain,
|
||||||
* @vm: domain object
|
virStorageSourcePtr targetsrc,
|
||||||
* @incrFrom: name of checkpoint representing starting point of incremental backup
|
const char *targetbitmap,
|
||||||
*
|
const char *incremental,
|
||||||
* Returns a NULL terminated list of pointers to checkpoint definitions in
|
virJSONValuePtr actions,
|
||||||
* chronological order starting from the 'current' checkpoint until reaching
|
virHashTablePtr blockNamedNodeData)
|
||||||
* @incrFrom.
|
|
||||||
*/
|
|
||||||
static virDomainMomentDefPtr *
|
|
||||||
qemuBackupBeginCollectIncrementalCheckpoints(virDomainObjPtr vm,
|
|
||||||
const char *incrFrom)
|
|
||||||
{
|
{
|
||||||
virDomainMomentObjPtr n = virDomainCheckpointGetCurrent(vm->checkpoints);
|
g_autoptr(virJSONValue) tmpactions = NULL;
|
||||||
g_autofree virDomainMomentDefPtr *incr = NULL;
|
|
||||||
size_t nincr = 0;
|
|
||||||
|
|
||||||
while (n) {
|
if (qemuBlockGetBitmapMergeActions(backingChain, NULL, targetsrc,
|
||||||
virDomainMomentDefPtr def = n->def;
|
incremental, targetbitmap, NULL,
|
||||||
|
&tmpactions,
|
||||||
if (VIR_APPEND_ELEMENT_COPY(incr, nincr, def) < 0)
|
blockNamedNodeData) < 0)
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (STREQ(def->name, incrFrom)) {
|
|
||||||
def = NULL;
|
|
||||||
if (VIR_APPEND_ELEMENT_COPY(incr, nincr, def) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return g_steal_pointer(&incr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!n->def->parent_name)
|
|
||||||
break;
|
|
||||||
|
|
||||||
n = virDomainCheckpointFindByName(vm->checkpoints, n->def->parent_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
||||||
_("could not locate checkpoint '%s' for incremental backup"),
|
|
||||||
incrFrom);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
|
||||||
qemuBackupGetBitmapMergeRange(virStorageSourcePtr from,
|
|
||||||
const char *bitmapname,
|
|
||||||
virJSONValuePtr *actions,
|
|
||||||
virStorageSourcePtr *to,
|
|
||||||
const char *diskdst,
|
|
||||||
virHashTablePtr blockNamedNodeData)
|
|
||||||
{
|
|
||||||
g_autoptr(virJSONValue) act = virJSONValueNewArray();
|
|
||||||
virStorageSourcePtr tmpsrc = NULL;
|
|
||||||
virStorageSourcePtr n;
|
|
||||||
bool foundbitmap = false;
|
|
||||||
|
|
||||||
for (n = from; virStorageSourceIsBacking(n); n = n->backingStore) {
|
|
||||||
qemuBlockNamedNodeDataBitmapPtr bitmap = NULL;
|
|
||||||
|
|
||||||
if (!(bitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
|
|
||||||
n,
|
|
||||||
bitmapname)))
|
|
||||||
break;
|
|
||||||
|
|
||||||
foundbitmap = true;
|
|
||||||
|
|
||||||
if (bitmap->inconsistent) {
|
|
||||||
virReportError(VIR_ERR_INVALID_ARG,
|
|
||||||
_("bitmap '%s' for image '%s%u' is inconsistent"),
|
|
||||||
bitmap->name, diskdst, n->id);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qemuMonitorTransactionBitmapMergeSourceAddBitmap(act,
|
|
||||||
n->nodeformat,
|
|
||||||
bitmapname) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
tmpsrc = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!foundbitmap) {
|
|
||||||
virReportError(VIR_ERR_INVALID_ARG,
|
|
||||||
_("failed to find bitmap '%s' in image '%s%u'"),
|
|
||||||
bitmapname, diskdst, from->id);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
*actions = g_steal_pointer(&act);
|
if (tmpactions &&
|
||||||
*to = tmpsrc;
|
virJSONValueArrayConcat(actions, tmpactions) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
virJSONValuePtr
|
|
||||||
qemuBackupDiskPrepareOneBitmapsChain(virDomainMomentDefPtr *incremental,
|
|
||||||
virStorageSourcePtr backingChain,
|
|
||||||
virHashTablePtr blockNamedNodeData,
|
|
||||||
const char *diskdst)
|
|
||||||
{
|
|
||||||
g_autoptr(virJSONValue) ret = NULL;
|
|
||||||
size_t incridx = 0;
|
|
||||||
virStorageSourcePtr n = backingChain;
|
|
||||||
|
|
||||||
ret = virJSONValueNewArray();
|
|
||||||
|
|
||||||
for (incridx = 0; incremental[incridx]; incridx++) {
|
|
||||||
g_autoptr(virJSONValue) tmp = virJSONValueNewArray();
|
|
||||||
virStorageSourcePtr tmpsrc = NULL;
|
|
||||||
virDomainCheckpointDefPtr chkdef = (virDomainCheckpointDefPtr) incremental[incridx];
|
|
||||||
bool checkpoint_has_disk = false;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < chkdef->ndisks; i++) {
|
|
||||||
if (STRNEQ_NULLABLE(diskdst, chkdef->disks[i].name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (chkdef->disks[i].type == VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
|
|
||||||
checkpoint_has_disk = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!checkpoint_has_disk) {
|
|
||||||
if (!incremental[incridx + 1]) {
|
|
||||||
virReportError(VIR_ERR_INVALID_ARG,
|
|
||||||
_("disk '%s' not found in checkpoint '%s'"),
|
|
||||||
diskdst, incremental[incridx]->name);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qemuBackupGetBitmapMergeRange(n, incremental[incridx]->name,
|
|
||||||
&tmp, &tmpsrc, diskdst,
|
|
||||||
blockNamedNodeData) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (virJSONValueArrayConcat(ret, tmp) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
n = tmpsrc;
|
|
||||||
}
|
|
||||||
|
|
||||||
return g_steal_pointer(&ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
qemuBackupDiskPrepareOneBitmaps(struct qemuBackupDiskData *dd,
|
qemuBackupDiskPrepareOneBitmaps(struct qemuBackupDiskData *dd,
|
||||||
virJSONValuePtr actions,
|
virJSONValuePtr actions,
|
||||||
virDomainMomentDefPtr *incremental,
|
|
||||||
virHashTablePtr blockNamedNodeData)
|
virHashTablePtr blockNamedNodeData)
|
||||||
{
|
{
|
||||||
g_autoptr(virJSONValue) mergebitmaps = NULL;
|
if (!qemuBlockBitmapChainIsValid(dd->domdisk->src,
|
||||||
g_autoptr(virJSONValue) mergebitmapsstore = NULL;
|
dd->backupdisk->incremental,
|
||||||
|
blockNamedNodeData)) {
|
||||||
|
virReportError(VIR_ERR_INVALID_ARG,
|
||||||
|
_("missing or broken bitmap '%s' for disk '%s'"),
|
||||||
|
dd->backupdisk->incremental, dd->domdisk->dst);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(mergebitmaps = qemuBackupDiskPrepareOneBitmapsChain(incremental,
|
if (qemuBackupDiskPrepareOneBitmapsChain(dd->domdisk->src,
|
||||||
dd->domdisk->src,
|
dd->domdisk->src,
|
||||||
blockNamedNodeData,
|
dd->incrementalBitmap,
|
||||||
dd->domdisk->dst)))
|
dd->backupdisk->incremental,
|
||||||
|
actions,
|
||||||
|
blockNamedNodeData) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!(mergebitmapsstore = virJSONValueCopy(mergebitmaps)))
|
if (qemuBackupDiskPrepareOneBitmapsChain(dd->domdisk->src,
|
||||||
return -1;
|
dd->store,
|
||||||
|
dd->incrementalBitmap,
|
||||||
if (qemuMonitorTransactionBitmapAdd(actions,
|
dd->backupdisk->incremental,
|
||||||
dd->domdisk->src->nodeformat,
|
actions,
|
||||||
dd->incrementalBitmap,
|
blockNamedNodeData) < 0)
|
||||||
false,
|
|
||||||
true, 0) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (qemuMonitorTransactionBitmapMerge(actions,
|
|
||||||
dd->domdisk->src->nodeformat,
|
|
||||||
dd->incrementalBitmap,
|
|
||||||
&mergebitmaps) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (qemuMonitorTransactionBitmapAdd(actions,
|
|
||||||
dd->store->nodeformat,
|
|
||||||
dd->incrementalBitmap,
|
|
||||||
false,
|
|
||||||
true, 0) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (qemuMonitorTransactionBitmapMerge(actions,
|
|
||||||
dd->store->nodeformat,
|
|
||||||
dd->incrementalBitmap,
|
|
||||||
&mergebitmapsstore) < 0)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -382,7 +241,6 @@ qemuBackupDiskPrepareDataOne(virDomainObjPtr vm,
|
|||||||
virQEMUDriverConfigPtr cfg)
|
virQEMUDriverConfigPtr cfg)
|
||||||
{
|
{
|
||||||
qemuDomainObjPrivatePtr priv = vm->privateData;
|
qemuDomainObjPrivatePtr priv = vm->privateData;
|
||||||
g_autofree virDomainMomentDefPtr *incremental = NULL;
|
|
||||||
|
|
||||||
/* set data structure */
|
/* set data structure */
|
||||||
dd->backupdisk = backupdisk;
|
dd->backupdisk = backupdisk;
|
||||||
@ -421,16 +279,12 @@ qemuBackupDiskPrepareDataOne(virDomainObjPtr vm,
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (dd->backupdisk->incremental) {
|
if (dd->backupdisk->incremental) {
|
||||||
if (!(incremental = qemuBackupBeginCollectIncrementalCheckpoints(vm, dd->backupdisk->incremental)))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (dd->backupdisk->exportbitmap)
|
if (dd->backupdisk->exportbitmap)
|
||||||
dd->incrementalBitmap = g_strdup(dd->backupdisk->exportbitmap);
|
dd->incrementalBitmap = g_strdup(dd->backupdisk->exportbitmap);
|
||||||
else
|
else
|
||||||
dd->incrementalBitmap = g_strdup_printf("backup-%s", dd->domdisk->dst);
|
dd->incrementalBitmap = g_strdup_printf("backup-%s", dd->domdisk->dst);
|
||||||
|
|
||||||
if (qemuBackupDiskPrepareOneBitmaps(dd, actions, incremental,
|
if (qemuBackupDiskPrepareOneBitmaps(dd, actions, blockNamedNodeData) < 0)
|
||||||
blockNamedNodeData) < 0)
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -884,8 +738,8 @@ qemuBackupBegin(virDomainObjPtr vm,
|
|||||||
if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, QEMU_ASYNC_JOB_BACKUP)))
|
if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, QEMU_ASYNC_JOB_BACKUP)))
|
||||||
goto endjob;
|
goto endjob;
|
||||||
|
|
||||||
if ((ndd = qemuBackupDiskPrepareData(vm, def, blockNamedNodeData,
|
if ((ndd = qemuBackupDiskPrepareData(vm, def, blockNamedNodeData, actions,
|
||||||
actions, cfg, &dd)) <= 0) {
|
cfg, &dd)) <= 0) {
|
||||||
if (ndd == 0) {
|
if (ndd == 0) {
|
||||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||||
_("no disks selected for backup"));
|
_("no disks selected for backup"));
|
||||||
|
@ -53,8 +53,10 @@ qemuBackupGetJobInfoStats(virQEMUDriverPtr driver,
|
|||||||
qemuDomainJobInfoPtr jobInfo);
|
qemuDomainJobInfoPtr jobInfo);
|
||||||
|
|
||||||
/* exported for testing */
|
/* exported for testing */
|
||||||
virJSONValuePtr
|
int
|
||||||
qemuBackupDiskPrepareOneBitmapsChain(virDomainMomentDefPtr *incremental,
|
qemuBackupDiskPrepareOneBitmapsChain(virStorageSourcePtr backingChain,
|
||||||
virStorageSourcePtr backingChain,
|
virStorageSourcePtr targetsrc,
|
||||||
virHashTablePtr blockNamedNodeData,
|
const char *targetbitmap,
|
||||||
const char *diskdst);
|
const char *incremental,
|
||||||
|
virJSONValuePtr actions,
|
||||||
|
virHashTablePtr blockNamedNodeData);
|
||||||
|
@ -716,65 +716,6 @@ testQemuBitmapGetFakeChainEntry(virStorageSourcePtr src,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
typedef virDomainMomentDefPtr testMomentList;
|
|
||||||
|
|
||||||
static void
|
|
||||||
testMomentListFree(testMomentList *list)
|
|
||||||
{
|
|
||||||
testMomentList *tmp = list;
|
|
||||||
|
|
||||||
if (!list)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (*tmp) {
|
|
||||||
virObjectUnref(*tmp);
|
|
||||||
tmp++;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(testMomentList, testMomentListFree);
|
|
||||||
|
|
||||||
static virDomainMomentDefPtr
|
|
||||||
testQemuBackupGetIncrementalMoment(const char *name)
|
|
||||||
{
|
|
||||||
virDomainCheckpointDefPtr checkpoint = NULL;
|
|
||||||
|
|
||||||
if (!(checkpoint = virDomainCheckpointDefNew()))
|
|
||||||
abort();
|
|
||||||
|
|
||||||
checkpoint->disks = g_new0(virDomainCheckpointDiskDef, 1);
|
|
||||||
checkpoint->ndisks = 1;
|
|
||||||
|
|
||||||
checkpoint->disks[0].name = g_strdup("testdisk");
|
|
||||||
checkpoint->disks[0].type = VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP;
|
|
||||||
|
|
||||||
checkpoint->parent.name = g_strdup(name);
|
|
||||||
|
|
||||||
return (virDomainMomentDefPtr) checkpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static virDomainMomentDefPtr *
|
|
||||||
testQemuBackupGetIncremental(const char *incFrom)
|
|
||||||
{
|
|
||||||
const char *checkpoints[] = {"current", "d", "c", "b", "a"};
|
|
||||||
virDomainMomentDefPtr *incr;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
incr = g_new0(virDomainMomentDefPtr, G_N_ELEMENTS(checkpoints) + 1);
|
|
||||||
|
|
||||||
for (i = 0; i < G_N_ELEMENTS(checkpoints); i++) {
|
|
||||||
incr[i] = testQemuBackupGetIncrementalMoment(checkpoints[i]);
|
|
||||||
|
|
||||||
if (STREQ(incFrom, checkpoints[i]))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return incr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *backupDataPrefix = "qemublocktestdata/backupmerge/";
|
static const char *backupDataPrefix = "qemublocktestdata/backupmerge/";
|
||||||
|
|
||||||
struct testQemuBackupIncrementalBitmapCalculateData {
|
struct testQemuBackupIncrementalBitmapCalculateData {
|
||||||
@ -791,10 +732,10 @@ testQemuBackupIncrementalBitmapCalculate(const void *opaque)
|
|||||||
const struct testQemuBackupIncrementalBitmapCalculateData *data = opaque;
|
const struct testQemuBackupIncrementalBitmapCalculateData *data = opaque;
|
||||||
g_autoptr(virJSONValue) nodedatajson = NULL;
|
g_autoptr(virJSONValue) nodedatajson = NULL;
|
||||||
g_autoptr(virHashTable) nodedata = NULL;
|
g_autoptr(virHashTable) nodedata = NULL;
|
||||||
g_autoptr(virJSONValue) mergebitmaps = NULL;
|
g_autoptr(virJSONValue) actions = virJSONValueNewArray();
|
||||||
g_autofree char *actual = NULL;
|
|
||||||
g_autofree char *expectpath = NULL;
|
g_autofree char *expectpath = NULL;
|
||||||
g_autoptr(testMomentList) incremental = NULL;
|
g_autoptr(virStorageSource) target = NULL;
|
||||||
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||||
|
|
||||||
expectpath = g_strdup_printf("%s/%s%s-out.json", abs_srcdir,
|
expectpath = g_strdup_printf("%s/%s%s-out.json", abs_srcdir,
|
||||||
backupDataPrefix, data->name);
|
backupDataPrefix, data->name);
|
||||||
@ -808,19 +749,24 @@ testQemuBackupIncrementalBitmapCalculate(const void *opaque)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
incremental = testQemuBackupGetIncremental(data->incremental);
|
if (!(target = virStorageSourceNew()))
|
||||||
|
return -1;
|
||||||
|
|
||||||
if ((mergebitmaps = qemuBackupDiskPrepareOneBitmapsChain(incremental,
|
target->nodeformat = g_strdup_printf("target_node");
|
||||||
data->chain,
|
|
||||||
nodedata,
|
if (qemuBackupDiskPrepareOneBitmapsChain(data->chain,
|
||||||
"testdisk"))) {
|
target,
|
||||||
if (!(actual = virJSONValueToString(mergebitmaps, true)))
|
"target-bitmap-name",
|
||||||
|
data->incremental,
|
||||||
|
actions,
|
||||||
|
nodedata) >= 0) {
|
||||||
|
if (virJSONValueToBuffer(actions, &buf, true) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
actual = g_strdup("NULL\n");
|
virBufferAddLit(&buf, "NULL\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return virTestCompareToFile(actual, expectpath);
|
return virTestCompareToFile(virBufferCurrentContent(&buf), expectpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1 +1,3 @@
|
|||||||
NULL
|
[
|
||||||
|
|
||||||
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user