[libvirt] [PATCH 2/3] blockjob: add virsh blockcommit

Eric Blake eblake at redhat.com
Mon Sep 17 22:08:44 UTC 2012


The wait loop logic borrows heavily from blockcommit.

* tools/virsh-domain.c (cmdBlockCommit): New function.
* tools/virsh.pod (blockcommit): Document it.
---
 tools/virsh-domain.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 tools/virsh.pod      |  30 ++++++++++
 2 files changed, 182 insertions(+), 1 deletion(-)

diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index b6de9a8..aa23011 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -1154,6 +1154,7 @@ typedef enum {
     VSH_CMD_BLOCK_JOB_SPEED = 2,
     VSH_CMD_BLOCK_JOB_PULL = 3,
     VSH_CMD_BLOCK_JOB_COPY = 4,
+    VSH_CMD_BLOCK_JOB_COMMIT = 5,
 } vshCmdBlockJobMode;

 static int
@@ -1166,6 +1167,7 @@ blockJobImpl(vshControl *ctl, const vshCmd *cmd,
     unsigned long bandwidth = 0;
     int ret = -1;
     const char *base = NULL;
+    const char *top = NULL;
     unsigned int flags = 0;

     if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
@@ -1180,7 +1182,7 @@ blockJobImpl(vshControl *ctl, const vshCmd *cmd,
     }

     switch ((vshCmdBlockJobMode) mode) {
-    case  VSH_CMD_BLOCK_JOB_ABORT:
+    case VSH_CMD_BLOCK_JOB_ABORT:
         if (vshCommandOptBool(cmd, "async"))
             flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
         if (vshCommandOptBool(cmd, "pivot"))
@@ -1201,6 +1203,16 @@ blockJobImpl(vshControl *ctl, const vshCmd *cmd,
         else
             ret = virDomainBlockPull(dom, path, bandwidth, 0);
         break;
+    case VSH_CMD_BLOCK_JOB_COMMIT:
+        if (vshCommandOptString(cmd, "base", &base) < 0 ||
+            vshCommandOptString(cmd, "top", &top) < 0)
+            goto cleanup;
+        if (vshCommandOptBool(cmd, "shallow"))
+            flags |= VIR_DOMAIN_BLOCK_COMMIT_SHALLOW;
+        if (vshCommandOptBool(cmd, "delete"))
+            flags |= VIR_DOMAIN_BLOCK_COMMIT_DELETE;
+        ret = virDomainBlockCommit(dom, path, base, top, bandwidth, flags);
+        break;
     case VSH_CMD_BLOCK_JOB_COPY:
         flags |= VIR_DOMAIN_BLOCK_REBASE_COPY;
         if (vshCommandOptBool(cmd, "shallow"))
@@ -1260,6 +1272,144 @@ static void vshCatchInt(int sig ATTRIBUTE_UNUSED,
 }

 /*
+ * "blockcommit" command
+ */
+static const vshCmdInfo info_block_commit[] = {
+    {"help", N_("Start a block commit operation.")},
+    {"desc", N_("Commit changes from a snapshot down to its backing image.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_block_commit[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
+    {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("bandwidth limit in MiB/s")},
+    {"base", VSH_OT_DATA, VSH_OFLAG_NONE,
+     N_("path of base file to commit into (default bottom of chain)")},
+    {"shallow", VSH_OT_BOOL, 0, N_("use backing file of top as base")},
+    {"top", VSH_OT_DATA, VSH_OFLAG_NONE,
+     N_("path of top file to commit from (default top of chain)")},
+    {"delete", VSH_OT_BOOL, 0,
+     N_("delete files that were successfully committed")},
+    {"wait", VSH_OT_BOOL, 0, N_("wait for job to complete")},
+    {"verbose", VSH_OT_BOOL, 0, N_("with --wait, display the progress")},
+    {"timeout", VSH_OT_INT, VSH_OFLAG_NONE,
+     N_("with --wait, abort if copy exceeds timeout (in seconds)")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdBlockCommit(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    bool ret = false;
+    bool blocking = vshCommandOptBool(cmd, "wait");
+    bool verbose = vshCommandOptBool(cmd, "verbose");
+    int timeout = 0;
+    struct sigaction sig_action;
+    struct sigaction old_sig_action;
+    sigset_t sigmask;
+    struct timeval start;
+    struct timeval curr;
+    const char *path = NULL;
+    bool quit = false;
+    int abort_flags = 0;
+
+    if (blocking) {
+        if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
+            if (timeout < 1) {
+                vshError(ctl, "%s", _("invalid timeout"));
+                return false;
+            }
+
+            /* Ensure that we can multiply by 1000 without overflowing. */
+            if (timeout > INT_MAX / 1000) {
+                vshError(ctl, "%s", _("timeout is too big"));
+                return false;
+            }
+        }
+        if (vshCommandOptString(cmd, "path", &path) < 0)
+            return false;
+        if (vshCommandOptBool(cmd, "async"))
+            abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
+
+        sigemptyset(&sigmask);
+        sigaddset(&sigmask, SIGINT);
+
+        intCaught = 0;
+        sig_action.sa_sigaction = vshCatchInt;
+        sig_action.sa_flags = SA_SIGINFO;
+        sigemptyset(&sig_action.sa_mask);
+        sigaction(SIGINT, &sig_action, &old_sig_action);
+
+        GETTIMEOFDAY(&start);
+    } else if (verbose || vshCommandOptBool(cmd, "timeout") ||
+               vshCommandOptBool(cmd, "async")) {
+        vshError(ctl, "%s", _("blocking control options require --wait"));
+        return false;
+    }
+
+    if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_COMMIT, &dom) < 0)
+        goto cleanup;
+
+    if (!blocking) {
+        vshPrint(ctl, "%s", _("Block Commit started"));
+        ret = true;
+        goto cleanup;
+    }
+
+    while (blocking) {
+        virDomainBlockJobInfo info;
+        int result = virDomainGetBlockJobInfo(dom, path, &info, 0);
+
+        if (result < 0) {
+            vshError(ctl, _("failed to query job for disk %s"), path);
+            goto cleanup;
+        }
+        if (result == 0)
+            break;
+
+        if (verbose)
+            print_job_progress(_("Block Commit"),
+                               info.end - info.cur, info.end);
+
+        GETTIMEOFDAY(&curr);
+        if (intCaught || (timeout &&
+                          (((int)(curr.tv_sec - start.tv_sec)  * 1000 +
+                            (int)(curr.tv_usec - start.tv_usec) / 1000) >
+                           timeout * 1000))) {
+            vshDebug(ctl, VSH_ERR_DEBUG,
+                     intCaught ? "interrupted" : "timeout");
+            intCaught = 0;
+            timeout = 0;
+            quit = true;
+            if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
+                vshError(ctl, _("failed to abort job for disk %s"), path);
+                goto cleanup;
+            }
+            if (abort_flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC)
+                break;
+        } else {
+            usleep(500 * 1000);
+        }
+    }
+
+    if (verbose && !quit) {
+        /* printf [100 %] */
+        print_job_progress(_("Block Commit"), 0, 1);
+    }
+    vshPrint(ctl, "\n%s", quit ? _("Commit aborted") : _("Commit complete"));
+
+    ret = true;
+cleanup:
+    if (dom)
+        virDomainFree(dom);
+    if (blocking)
+        sigaction(SIGINT, &old_sig_action, NULL);
+    return ret;
+}
+
+/*
  * "blockcopy" command
  */
 static const vshCmdInfo info_block_copy[] = {
@@ -8112,6 +8262,7 @@ const vshCmdDef domManagementCmds[] = {
     {"autostart", cmdAutostart, opts_autostart, info_autostart, 0},
     {"blkdeviotune", cmdBlkdeviotune, opts_blkdeviotune, info_blkdeviotune, 0},
     {"blkiotune", cmdBlkiotune, opts_blkiotune, info_blkiotune, 0},
+    {"blockcommit", cmdBlockCommit, opts_block_commit, info_block_commit, 0},
     {"blockcopy", cmdBlockCopy, opts_block_copy, info_block_copy, 0},
     {"blockjob", cmdBlockJob, opts_block_job, info_block_job, 0},
     {"blockpull", cmdBlockPull, opts_block_pull, info_block_pull, 0},
diff --git a/tools/virsh.pod b/tools/virsh.pod
index bb135da..4a79e12 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -694,6 +694,36 @@ currently in use by a running domain. Other contexts that require a MAC
 address of virtual interface (such as I<detach-interface> or
 I<domif-setlink>) will accept the MAC address printed by this command.

+=item B<blockcommit> I<domain> I<path> [I<bandwidth>]
+{[I<base>] | [I<--shallow>]} [I<top>] [I<--delete>]
+[I<--wait> [I<--verbose>] [I<--timeout> B<seconds>]]
+
+Reduce the length of a backing image chain, by committing changes at the
+top of the chain (snapshot or delta files) into backing images.  By
+default, this command attempts to flatten the entire chain.  If I<base>
+and/or I<top> are specified as files within the backing chain, then the
+operation is constrained to committing just that portion of the chain;
+I<--shallow> can be used instead of I<base> to specify the immediate
+backing file of the resulting top image to be committed.  The files
+being committed are rendered invalid, possibly as soon as the operation
+starts; using the I<--delete> flag will remove these files at the successful
+completion of the commit operation.
+
+By default, this command returns as soon as possible, and data for
+the entire disk is committed in the background; the progress of the
+operation can be checked with B<blockjob>.  However, if I<--wait> is
+specified, then this command will block until the operation completes,
+or cancel the operation if the optional I<timeout> in seconds elapses
+or SIGINT is sent (usually with C<Ctrl-C>).  Using I<--verbose> along
+with I<--wait> will produce periodic status updates.
+
+I<path> specifies fully-qualified path of the disk; it corresponds
+to a unique target name (<target dev='name'/>) or source file (<source
+file='name'/>) for one of the disk devices attached to I<domain> (see
+also B<domblklist> for listing these names).
+I<bandwidth> specifies copying bandwidth limit in MiB/s, although for
+qemu, it may be non-zero only for an online domain.
+
 =item B<blockcopy> I<domain> I<path> I<dest> [I<bandwidth>] [I<--shallow>]
 [I<--reuse-external>] [I<--raw>] [I<--wait> [I<--verbose]
 [{I<--pivot> | I<--finish>}] [I<--timeout> B<seconds>] [I<--async>]]
-- 
1.7.11.4




More information about the libvir-list mailing list