[PATCH v7 23/23] backup: implement qemu incremental pull backup

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxx>
---
 src/qemu/qemu_driver.c | 197 +++++++++++++++++++++++++++++++++++------
 1 file changed, 169 insertions(+), 28 deletions(-)

diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 9c2bc8175a..238de51dc5 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -17068,6 +17068,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->common.name)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("disk %s bitmap should match checkpoint name %s"),
+                           disk->name, def->common.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,
@@ -17724,19 +17742,43 @@ qemuDomainCheckpointDelete(virDomainCheckpointPtr checkpoint,

 static int
 qemuDomainBackupPrepare(virQEMUDriverPtr driver, virDomainObjPtr vm,
-                        virDomainBackupDefPtr def)
+                        virDomainBackupDefPtr def,
+                        virDomainMomentObjPtr chk)
 {
     int ret = -1;
     size_t i;
+    virDomainCheckpointDefPtr chkdef = virDomainCheckpointObjGetDef(chk);

+    if (chk && def->ndisks != chkdef->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 &&
+                chkdef->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 &&
+            chkdef->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)
@@ -17770,7 +17812,7 @@ qemuDomainBackupDiskCleanup(virQEMUDriverPtr driver, virDomainObjPtr vm,
          * 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;
@@ -17808,28 +17850,22 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
     bool job_started = false;
     bool nbd_running = false;
     bool push;
+    const char *mode;
     size_t i;
     struct timeval tv;
     char *suffix = NULL;
     virCommandPtr cmd = NULL;
     const char *qemuImgPath;
+    virDomainCheckpointDefPtr chkdef = NULL;
+    virDomainMomentObjPtr chk = NULL;
+    virDomainMomentObjPtr other = NULL;
+    virDomainMomentObjPtr parent = NULL;
+    virDomainMomentObjPtr current;
+    virJSONValuePtr arr = 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;

@@ -17856,6 +17892,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->common.name) < 0)
+            goto cleanup;
+    } else {
+        gettimeofday(&tv, NULL);
+        if (virAsprintf(&suffix, "%lld", (long long)tv.tv_sec) < 0)
+            goto cleanup;
+    }
+
     push = def->type == VIR_DOMAIN_BACKUP_TYPE_PUSH;
     if (!push) {
         if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_NBD_BITMAP)) {
@@ -17870,15 +17917,25 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
             goto cleanup;
         }
     }
+    current = virDomainCheckpointGetCurrent(vm->checkpoints);
     if (def->incremental) {
         if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BITMAP_MERGE)) {
             virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                            _("qemu binary lacks persistent bitmaps support"));
             goto cleanup;
         }
-        virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
-                       _("cannot create incremental backups yet"));
-        goto cleanup;
+        for (other = current; 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)))
@@ -17894,14 +17951,38 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
         goto endjob;
     }

+    if (chkdef) {
+        if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BITMAP_MERGE)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("qemu binary lacks persistent bitmaps support"));
+            goto endjob;
+        }
+
+        if (qemuDomainCheckpointPrepare(driver, caps, vm, chkdef) < 0)
+            goto endjob;
+        if (!(chk = virDomainCheckpointAssignDef(vm->checkpoints, chkdef)))
+            goto endjob;
+        chkdef = NULL;
+        if (current) {
+            parent = current;
+            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;
+        }
+    }
+
     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, bitmap-merge per <disk> */
     for (i = 0; i < def->ndisks; i++) {
         virDomainBackupDiskDef *disk = &def->disks[i];
         virJSONValuePtr file;
@@ -17967,11 +18048,32 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
             goto endmon;
         json = NULL;
         disk->state = VIR_DOMAIN_BACKUP_DISK_STATE_READY;
+
+        if (def->incremental) {
+            if (!(arr = virJSONValueNewArray()))
+                goto endmon;
+            if (qemuMonitorAddBitmap(priv->mon, node, disk->name, false) < 0) {
+                virJSONValueFree(arr);
+                goto endmon;
+            }
+            disk->state = VIR_DOMAIN_BACKUP_DISK_STATE_BITMAP;
+            for (other = parent ? parent : current; other;
+                 other = other->def->parent ?
+                     virDomainCheckpointFindByName(vm->checkpoints,
+                                                   other->def->parent) : NULL) {
+                if (virJSONValueArrayAppendString(arr, other->def->name) < 0) {
+                    virJSONValueFree(arr);
+                    goto endmon;
+                }
+                if (STREQ(other->def->name, def->incremental))
+                    break;
+            }
+            if (qemuMonitorMergeBitmaps(priv->mon, node, disk->name, &arr) < 0)
+                goto endmon;
+        }
     }

-    /* TODO:
-       - incr: bitmap-add of tmp, 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
@@ -17980,24 +18082,61 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
     */
     if (!(json = virJSONValueNewArray()))
         goto endmon;
+    if (push)
+        mode = def->incremental ? "incremental" : "full";
+    else
+        mode = "none";
     for (i = 0; i < def->ndisks; i++) {
         virDomainBackupDiskDef *disk = &def->disks[i];
-        virStorageSourcePtr src = vm->def->disks[disk->idx]->src;
+        const char *node;
+        const char *push_bitmap = NULL;

         if (!disk->store)
             continue;
+        node = qemuBlockNodeLookup(vm, disk->name);
+        if (push && def->incremental)
+            push_bitmap = disk->name;
         if (qemuMonitorJSONTransactionAdd(json,
                                           "blockdev-backup",
-                                          "s:device", src->nodeformat,
+                                          "s:device", node,
                                           "s:target", disk->store->nodeformat,
-                                          "s:sync", push ? "full" : "none",
+                                          "s:sync", mode,
+                                          "S:bitmap", push_bitmap,
                                           "s:job-id", disk->name,
                                           NULL) < 0)
             goto endmon;
+        if (def->incremental && !push &&
+            qemuMonitorJSONTransactionAdd(json,
+                                          "block-dirty-bitmap-disable",
+                                          "s:node", node,
+                                          "s:name", disk->name,
+                                          NULL) < 0)
+            goto endmon;
     }
+    if (chk && qemuDomainCheckpointAddActions(vm, json, parent,
+                                              virDomainCheckpointObjGetDef(chk)) < 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;
+        }
+        virDomainCheckpointSetCurrent(vm->checkpoints, 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)
@@ -18040,7 +18179,7 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
             if (job_started &&
                 qemuMonitorBlockJobCancel(priv->mon, disk->name) < 0)
                 VIR_WARN("Unable to stop backup job %s on vm %s after failure",
-                         disk->store->nodeformat, vm->def->name);
+                         disk->name, vm->def->name);
             qemuDomainBackupDiskCleanup(driver, vm, disk, push,
                                         !!def->incremental, false);
         }
@@ -18063,6 +18202,8 @@ qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml,
     qemuDomainObjEndJob(driver, vm);

  cleanup:
+    virJSONValueFree(arr);
+    virDomainCheckpointDefFree(chkdef);
     virCommandFree(cmd);
     VIR_FREE(suffix);
     virJSONValueFree(json);
-- 
2.20.1

--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list



[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux