Complete wiring up incremental backup, by adding in support for
creating a checkpoint at the same time as a backup (make the
transaction have a few more steps) as well as exposing the dirty
bitmap for a prior backup over NBD (requires creating a temporary
bitmap, merging all appropriate bitmaps in, then exposing that
bitmap over NBD).
Signed-off-by: Eric Blake <eblake@redhat.com>
---
src/qemu/qemu_driver.c | 164 ++++++++++++++++++++++++++++++++++-------
1 file changed, 139 insertions(+), 25 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index f8f05599ce..6b397ddf11 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -16785,6 +16785,24 @@ qemuDomainCheckpointPrepare(virQEMUDriverPtr driver, virCapsPtr caps,
if (disk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
continue;
+ /* We want to name temporary bitmap after disk name during
+ * incremental backup, which is not possible if that is a
+ * persistent bitmap name. We can also make life easier by
+ * enforcing bitmap names match checkpoint name, although this
+ * is not technically necessary. */
+ if (STREQ(disk->name, disk->bitmap)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("checkpoint for disk %s must have distinct bitmap name"),
+ disk->name);
+ goto cleanup;
+ }
+ if (STRNEQ(disk->bitmap, def->name)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("disk %s bitmap should match checkpoint name %s"),
+ disk->name, def->name);
+ goto cleanup;
+ }
+
if (vm->def->disks[i]->src->format > 0 &&
vm->def->disks[i]->src->format != VIR_STORAGE_FILE_QCOW2) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
@@ -17450,19 +17468,42 @@ qemuDomainCheckpointDelete(virDomainCheckpointPtr checkpoint,
static int
qemuDomainBackupPrepare(virQEMUDriverPtr driver, virDomainObjPtr vm,
- virDomainBackupDefPtr def)
+ virDomainBackupDefPtr def,
+ virDomainCheckpointObjPtr chk)
{
int ret = -1;
int i;
+ if (chk && def->ndisks != chk->def->ndisks) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("inconsistency between backup and checkpoint disks"));
+ goto cleanup;
+ }
if (qemuBlockNodeNamesDetect(driver, vm, QEMU_ASYNC_JOB_NONE) < 0)
goto cleanup;
for (i = 0; i < def->ndisks; i++) {
virDomainBackupDiskDef *disk = &def->disks[i];
virStorageSourcePtr src = vm->def->disks[disk->idx]->src;
- if (!disk->store)
+ /* For now, insist that atomic checkpoint affect same disks as
+ * those being backed up. */
+ if (!disk->store) {
+ if (chk &&
+ chk->def->disks[i].type != VIR_DOMAIN_CHECKPOINT_TYPE_NONE) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+ _("disk %s requested checkpoint without backup"),
+ disk->name);
+ goto cleanup;
+ }
continue;
+ }
+ if (chk &&
+ chk->def->disks[i].type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+ _("disk %s requested backup without checkpoint"),
+ disk->name);
+ goto cleanup;
+ }
if (virAsprintf(&disk->store->nodeformat, "tmp-%s", disk->name) < 0)
goto cleanup;
if (!disk->store->format)
@@ -17494,10 +17535,10 @@ qemuDomainBackupDiskCleanup(virQEMUDriverPtr driver, virDomainObjPtr vm,
return 0;
if (disk->state >= VIR_DOMAIN_BACKUP_DISK_STATE_EXPORT) {
/* No real need to use nbd-server-remove, since we will
- * shortly be calling nbd-server-stsop. */
+ * shortly be calling nbd-server-stop. */
}
if (incremental && disk->state >= VIR_DOMAIN_BACKUP_DISK_STATE_BITMAP &&
- qemuMonitorDeleteBitmap(priv->mon, node, disk->store->nodeformat) < 0) {
+ qemuMonitorDeleteBitmap(priv->mon, node, disk->name) < 0) {
VIR_WARN("Unable to remove temp bitmap for disk %s after backup",
disk->name);
ret = -1;
@@ -17538,23 +17579,14 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
char *suffix = NULL;
virCommandPtr cmd = NULL;
const char *qemuImgPath;
+ virDomainCheckpointDefPtr chkdef = NULL;
+ virDomainCheckpointObjPtr chk = NULL;
+ virDomainCheckpointObjPtr other = NULL;
+ virDomainCheckpointObjPtr parent = NULL;
virCheckFlags(VIR_DOMAIN_BACKUP_BEGIN_NO_METADATA, -1);
/* TODO: VIR_DOMAIN_BACKUP_BEGIN_QUIESCE */
- // FIXME: Support non-null checkpointXML for incremental - what
- // code can be shared with CheckpointCreateXML, then add to transaction
- // to create new checkpoint at same time as starting blockdev-backup
- if (checkpointXml) {
- virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
- _("cannot create incremental backups yet"));
- return -1;
- }
- // if (chk) VIR_STRDUP(suffix, chk->name);
- gettimeofday(&tv, NULL);
- if (virAsprintf(&suffix, "%lld", (long long)tv.tv_sec) < 0)
- goto cleanup;
-
if (!(vm = qemuDomObjFromDomain(domain)))
goto cleanup;
@@ -17580,6 +17612,17 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
if (!(def = virDomainBackupDefParseString(diskXml, driver->xmlopt, 0)))
goto cleanup;
+ if (checkpointXml) {
+ if (!(chkdef = qemuDomainCheckpointDefParseString(driver, caps,
+ checkpointXml, 0)) ||
+ VIR_STRDUP(suffix, chkdef->name) < 0)
+ goto cleanup;
+ } else {
+ gettimeofday(&tv, NULL);
+ if (virAsprintf(&suffix, "%lld", (long long)tv.tv_sec) < 0)
+ goto cleanup;
+ }
+
if (def->type == VIR_DOMAIN_BACKUP_TYPE_PULL &&
(!def->server ||
def->server->transport != VIR_STORAGE_NET_HOST_TRANS_TCP)) {
@@ -17588,9 +17631,18 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
goto cleanup;
}
if (def->incremental) {
- virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
- _("cannot create incremental backups yet"));
- goto cleanup;
+ for (other = vm->current_checkpoint; other;
+ other = other->def->parent ?
+ virDomainCheckpointFindByName(vm->checkpoints,
+ other->def->parent) : NULL)
+ if (STREQ(other->def->name, def->incremental))
+ break;
+ if (!other) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("could not locate checkpoint '%s' for incremental backup"),
+ def->incremental);
+ goto cleanup;
+ }
}
if (!(qemuImgPath = qemuFindQemuImgBinary(driver)))
@@ -17607,14 +17659,34 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
goto endjob;
}
+ if (chkdef) {
+ if (qemuDomainCheckpointPrepare(driver, caps, vm, chkdef) < 0)
+ goto endjob;
+ if (!(chk = virDomainCheckpointAssignDef(vm->checkpoints, chkdef)))
+ goto endjob;
+ chkdef = NULL;
+ chk->def->current = true;
+ if (vm->current_checkpoint) {
+ parent = vm->current_checkpoint;
+ if (VIR_STRDUP(chk->def->parent, parent->def->name) < 0)
+ goto endjob;
+ if (qemuDomainCheckpointWriteMetadata(vm, parent, driver->caps,
+ driver->xmlopt,
+ cfg->checkpointDir) < 0)
+ goto endjob;
+ vm->current_checkpoint = NULL;
+ }
+ }
+
if (virDomainBackupAlignDisks(def, vm->def, suffix) < 0 ||
- qemuDomainBackupPrepare(driver, vm, def) < 0)
+ qemuDomainBackupPrepare(driver, vm, def, chk) < 0)
goto endjob;
/* actually start the checkpoint. 2x2 array of push/pull, full/incr,
plus additional tweak if checkpoint requested */
qemuDomainObjEnterMonitor(driver, vm);
- /* - push/pull: blockdev-add per <disk> */
+ /* - push/pull: blockdev-add per <disk>
+ - incr: bitmap-add of tmp, x-bitmap-merge per <disk> */
for (i = 0; i < def->ndisks; i++) {
virDomainBackupDiskDef *disk = &def->disks[i];
virJSONValuePtr file;
@@ -17672,11 +17744,25 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
goto endmon;
json = NULL;
disk->state = VIR_DOMAIN_BACKUP_DISK_STATE_READY;
+
+ if (def->incremental) {
+ if (qemuMonitorAddBitmap(priv->mon, node, disk->name) < 0)
+ goto endmon;
+ disk->state = VIR_DOMAIN_BACKUP_DISK_STATE_BITMAP;
+ for (other = parent ?: vm->current_checkpoint; other;
+ other = other->def->parent ?
+ virDomainCheckpointFindByName(vm->checkpoints,
+ other->def->parent) : NULL) {
+ if (qemuMonitorMergeBitmaps(priv->mon, node, disk->name,
+ other->def->name) < 0)
+ goto endmon;
+ if (STREQ(other->def->name, def->incremental))
+ break;
+ }
+ }
}
- /* TODO:
- - incr: bitmap-add of tmp, x-bitmap-merge per <disk>
- - transaction, containing:
+ /* - transaction, containing:
- push+full: blockdev-backup sync:full
- push+incr: blockdev-backup sync:incremental bitmap:tmp
- pull+full: blockdev-backup sync:none
@@ -17699,10 +17785,37 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
"s:job-id", disk->store->nodeformat,
NULL) < 0)
goto endmon;
+ if (def->incremental && def->type == VIR_DOMAIN_BACKUP_TYPE_PULL &&
+ qemuMonitorJSONTransactionAdd(json,
+ "x-block-dirty-bitmap-disable",
+ "s:node", src->nodeformat,
+ "s:name", disk->name,
+ NULL) < 0)
+ goto endmon;
}
+ if (chk && qemuDomainCheckpointAddActions(json, parent, chk->def) < 0)
+ goto endmon;
if (qemuMonitorTransaction(priv->mon, &json) < 0)
goto endmon;
job_started = true;
+ if (chk) {
+ if (qemuDomainCheckpointWriteMetadata(vm, chk, driver->caps,
+ driver->xmlopt,
+ cfg->checkpointDir) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unable to save metadata for checkpoint %s"),
+ chk->def->name);
+ virDomainCheckpointObjListRemove(vm->checkpoints, chk);
+ goto endmon;
+ }
+ vm->current_checkpoint = chk;
+ other = virDomainCheckpointFindByName(vm->checkpoints,
+ chk->def->parent);
+ chk->parent = other;
+ other->nchildren++;
+ chk->sibling = other->first_child;
+ other->first_child = chk;
+ }
/*
- pull: nbd-server-start with <server> from user (or autogenerate server)
@@ -17769,6 +17882,7 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
qemuDomainObjEndJob(driver, vm);
cleanup:
+ virDomainCheckpointDefFree(chkdef);
virCommandFree(cmd);
VIR_FREE(suffix);
virJSONValueFree(json);
--
2.17.2
--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list