[libvirt] [PATCHv5 05/23] blockjob: add 'blockcopy' to virsh

Eric Blake eblake at redhat.com
Tue Apr 17 05:05:56 UTC 2012


Rather than further overloading 'blockpull', I decided to create a
new virsh command to expose the new flags of virDomainBlockRebase.

Blocking until the command completes naturally is pointless, since
the block copy job is intended to run indefinitely.  Instead, I
made the command support three --wait modes: by default, it runs until
mirroring is started; with --pivot, it pivots as soon as mirroring
is started; and with --finish, it aborts (for a clean copy) as
soon as mirroring is started.

* tools/virsh.c (VSH_CMD_BLOCK_JOB_COPY): New mode.
(blockJobImpl): Support new flags.
(cmdBlockCopy): New command.
(cmdBlockJob): Support new job info, new abort flag.
* tools/virsh.pod (blockcopy, blockjob): Document the new command
and flags.
---

was 6/18 in v4
v5: add --wait flag, consistent capitalization

 tools/virsh.c   |  212 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 tools/virsh.pod |   49 ++++++++++++-
 2 files changed, 243 insertions(+), 18 deletions(-)

diff --git a/tools/virsh.c b/tools/virsh.c
index 95ed7bc..0f79022 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -7517,7 +7517,8 @@ typedef enum {
     VSH_CMD_BLOCK_JOB_INFO = 1,
     VSH_CMD_BLOCK_JOB_SPEED = 2,
     VSH_CMD_BLOCK_JOB_PULL = 3,
-} VSH_CMD_BLOCK_JOB_MODE;
+    VSH_CMD_BLOCK_JOB_COPY = 4,
+} vshCmdBlockJobMode;

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

     if (!vshConnectionUsability(ctl, ctl->conn))
@@ -7544,22 +7546,39 @@ blockJobImpl(vshControl *ctl, const vshCmd *cmd,
         goto cleanup;
     }

-    if (mode == VSH_CMD_BLOCK_JOB_ABORT) {
+    switch ((vshCmdBlockJobMode) mode) {
+    case  VSH_CMD_BLOCK_JOB_ABORT:
         if (vshCommandOptBool(cmd, "async"))
             flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
+        if (vshCommandOptBool(cmd, "pivot"))
+            flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
         ret = virDomainBlockJobAbort(dom, path, flags);
-    } else if (mode == VSH_CMD_BLOCK_JOB_INFO) {
+        break;
+    case VSH_CMD_BLOCK_JOB_INFO:
         ret = virDomainGetBlockJobInfo(dom, path, info, 0);
-    } else if (mode == VSH_CMD_BLOCK_JOB_SPEED) {
+        break;
+    case VSH_CMD_BLOCK_JOB_SPEED:
         ret = virDomainBlockJobSetSpeed(dom, path, bandwidth, 0);
-    } else if (mode == VSH_CMD_BLOCK_JOB_PULL) {
-        const char *base = NULL;
+        break;
+    case VSH_CMD_BLOCK_JOB_PULL:
         if (vshCommandOptString(cmd, "base", &base) < 0)
             goto cleanup;
         if (base)
             ret = virDomainBlockRebase(dom, path, base, bandwidth, 0);
         else
             ret = virDomainBlockPull(dom, path, bandwidth, 0);
+        break;
+    case VSH_CMD_BLOCK_JOB_COPY:
+        flags |= VIR_DOMAIN_BLOCK_REBASE_COPY;
+        if (vshCommandOptBool(cmd, "shallow"))
+            flags |= VIR_DOMAIN_BLOCK_REBASE_SHALLOW;
+        if (vshCommandOptBool(cmd, "reuse-external"))
+            flags |= VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT;
+        if (vshCommandOptBool(cmd, "raw"))
+            flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_RAW;
+        if (vshCommandOptString(cmd, "dest", &base) < 0)
+            goto cleanup;
+        ret = virDomainBlockRebase(dom, path, base, bandwidth, flags);
     }

 cleanup:
@@ -7571,6 +7590,158 @@ cleanup:
 }

 /*
+ * "blockcopy" command
+ */
+static const vshCmdInfo info_block_copy[] = {
+    {"help", N_("Start a block copy operation.")},
+    {"desc", N_("Populate a disk from its backing image.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_block_copy[] = {
+    {"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")},
+    {"dest", VSH_OT_DATA, VSH_OFLAG_REQ, N_("path of the copy to create")},
+    {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("bandwidth limit in MB/s")},
+    {"shallow", VSH_OT_BOOL, 0, N_("make the copy share a backing chain")},
+    {"reuse-external", VSH_OT_BOOL, 0, N_("reuse existing destination")},
+    {"raw", VSH_OT_BOOL, 0, N_("use raw destination file")},
+    {"wait", VSH_OT_BOOL, 0, N_("wait for job to reach mirroring phase")},
+    {"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)")},
+    {"pivot", VSH_OT_BOOL, 0, N_("with --wait, pivot when mirroring starts")},
+    {"finish", VSH_OT_BOOL, 0, N_("with --wait, quit when mirroring starts")},
+    {"async", VSH_OT_BOOL, 0,
+     N_("with --wait, don't wait for cancel to finish")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
+{
+    virDomainPtr dom = NULL;
+    bool ret = false;
+    bool blocking = vshCommandOptBool(cmd, "wait");
+    bool verbose = vshCommandOptBool(cmd, "verbose");
+    bool pivot = vshCommandOptBool(cmd, "pivot");
+    bool finish = vshCommandOptBool(cmd, "finish");
+    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 (pivot && finish) {
+            vshError(ctl, "%s", _("cannot mix --pivot and --finish"));
+            return false;
+        }
+        if (vshCommandOptInt(cmd, "timeout", &timeout) > 0) {
+            if (timeout < 1) {
+                vshError(ctl, "%s", _("migrate: Invalid timeout"));
+                return false;
+            }
+
+            /* Ensure that we can multiply by 1000 without overflowing. */
+            if (timeout > INT_MAX / 1000) {
+                vshError(ctl, "%s", _("migrate: 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;
+        sigemptyset(&sig_action.sa_mask);
+        sigaction(SIGINT, &sig_action, &old_sig_action);
+
+        GETTIMEOFDAY(&start);
+    } else if (verbose || vshCommandOptBool(cmd, "timeout") ||
+               vshCommandOptBool(cmd, "async") || pivot || finish) {
+        vshError(ctl, "%s", _("blocking control options require --wait"));
+        return false;
+    }
+
+    if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_COPY, &dom) < 0)
+        goto cleanup;
+
+    if (!blocking) {
+        vshPrint(ctl, "%s", _("Block Copy 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 (verbose)
+            print_job_progress(_("Block Copy"), info.end - info.cur, info.end);
+        if (info.cur == info.end)
+            break;
+
+        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 (pivot) {
+        abort_flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
+        if (virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
+            vshError(ctl, _("failed to pivot job for disk %s"), path);
+            goto cleanup;
+        }
+    } else if (finish && virDomainBlockJobAbort(dom, path, abort_flags) < 0) {
+        vshError(ctl, _("failed to finish job for disk %s"), path);
+        goto cleanup;
+    }
+    vshPrint(ctl, "\n%s",
+             quit ? _("Copy aborted") :
+             pivot ? _("Successfully pivoted") :
+             finish ? _("Successfully copied") :
+             _("Now in mirroring phase"));
+
+    ret = true;
+cleanup:
+    if (dom)
+        virDomainFree(dom);
+    if (blocking)
+        sigaction(SIGINT, &old_sig_action, NULL);
+    return ret;
+}
+
+/*
  * "blockpull" command
  */
 static const vshCmdInfo info_block_pull[] = {
@@ -7581,8 +7752,8 @@ static const vshCmdInfo info_block_pull[] = {

 static const vshCmdOptDef opts_block_pull[] = {
     {"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 MB/s")},
+    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
+    {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("bandwidth limit in MB/s")},
     {"base", VSH_OT_DATA, VSH_OFLAG_NONE,
      N_("path of backing file in chain for a partial pull")},
     {"wait", VSH_OT_BOOL, 0, N_("wait for job to finish")},
@@ -7714,15 +7885,17 @@ static const vshCmdInfo info_block_job[] = {

 static const vshCmdOptDef opts_block_job[] = {
     {"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")},
+    {"path", VSH_OT_DATA, VSH_OFLAG_REQ, N_("fully-qualified path of disk")},
     {"abort", VSH_OT_BOOL, VSH_OFLAG_NONE,
-     N_("Abort the active job on the specified disk")},
+     N_("abort the active job on the specified disk")},
     {"async", VSH_OT_BOOL, VSH_OFLAG_NONE,
      N_("don't wait for --abort to complete")},
+    {"pivot", VSH_OT_BOOL, VSH_OFLAG_NONE,
+     N_("conclude and pivot a copy job")},
     {"info", VSH_OT_BOOL, VSH_OFLAG_NONE,
-     N_("Get active job information for the specified disk")},
+     N_("get active job information for the specified disk")},
     {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE,
-     N_("Set the Bandwidth limit in MB/s")},
+     N_("set the Bandwidth limit in MB/s")},
     {NULL, 0, 0, NULL}
 };

@@ -7734,7 +7907,8 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd)
     const char *type;
     int ret;
     bool abortMode = (vshCommandOptBool(cmd, "abort") ||
-                      vshCommandOptBool(cmd, "async"));
+                      vshCommandOptBool(cmd, "async") ||
+                      vshCommandOptBool(cmd, "pivot"));
     bool infoMode = vshCommandOptBool(cmd, "info");
     bool bandwidth = vshCommandOptBool(cmd, "bandwidth");

@@ -7758,10 +7932,17 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd)
     if (ret == 0 || mode != VSH_CMD_BLOCK_JOB_INFO)
         return true;

-    if (info.type == VIR_DOMAIN_BLOCK_JOB_TYPE_PULL)
+    switch (info.type) {
+    case VIR_DOMAIN_BLOCK_JOB_TYPE_PULL:
         type = _("Block Pull");
-    else
+        break;
+    case VIR_DOMAIN_BLOCK_JOB_TYPE_COPY:
+        type = _("Block Copy");
+        break;
+    default:
         type = _("Unknown job");
+        break;
+    }

     print_job_progress(type, info.end - info.cur, info.end);
     if (info.bandwidth != 0)
@@ -17240,6 +17421,7 @@ static const vshCmdDef domManagementCmds[] = {
     {"autostart", cmdAutostart, opts_autostart, info_autostart, 0},
     {"blkdeviotune", cmdBlkdeviotune, opts_blkdeviotune, info_blkdeviotune, 0},
     {"blkiotune", cmdBlkiotune, opts_blkiotune, info_blkiotune, 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},
     {"blockresize", cmdBlockResize, opts_block_resize, info_block_resize, 0},
diff --git a/tools/virsh.pod b/tools/virsh.pod
index 140d8e8..23324b2 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -639,6 +639,47 @@ 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<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>]]
+
+Copy a disk backing image chain to I<dest>. By default, this command
+flattens the entire chain; but if I<--shallow> is specified, the copy
+shares the backing chain.
+
+If I<--reuse-external> is specified, then I<dest> must exist and have
+contents identical to the resulting backing file (that is, it must
+start with contents matching the backing file I<disk> if I<--shallow>
+is used, otherwise it must start empty); this option is typically used
+to set up a relative backing file name in the destination.
+
+The format of the destination is determined by the first match in the
+following list: if I<--raw> is specified, it will be raw; if
+I<--reuse-external> is specified, the existing destination is probed
+for a format; and in all other cases, the destination format will
+match the source format.
+
+By default, the copy job runs in the background, and consists of two
+phases.  Initially, the job must copy all data from the source, and
+during this phase, the job can only be canceled to revert back to the
+source disk, with no guarantees about the destination.  After this phase
+completes, both the source and the destination remain mirrored until a
+call to B<blockjob> with the I<--abort> and I<--pivot> flags pivots over
+to the copy, or a call without I<--pivot> leaves the destination as a
+faithful copy of that point in time.  However, if I<--wait> is specified,
+then this command will block until the mirroring phase begins, 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.  Using I<--pivot> or I<--finish>
+along with I<--wait> will additionally end the job cleanly rather than
+leaving things in the mirroring phase.  If job cancellation is triggered,
+I<--async> will return control to the user as fast as possible, otherwise
+the command may continue to block a little while longer until the job
+is done cleaning up.
+
+I<path> specifies fully-qualified path of the disk.
+I<bandwidth> specifies copying bandwidth limit in Mbps.
+
 =item B<blockpull> I<domain> I<path> [I<bandwidth>] [I<base>]
 [I<--wait> [I<--verbose>] [I<--timeout> B<seconds>] [I<--async]]

@@ -700,12 +741,12 @@ Both I<--live> and I<--current> flags may be given, but I<--current> is
 exclusive. If no flag is specified, behavior is different depending
 on hypervisor.

-=item B<blockjob> I<domain> I<path> { [I<--abort>] [I<--async>] |
+=item B<blockjob> I<domain> I<path> { [I<--abort>] [I<--async>] [I<--pivot>] |
 [I<--info>] | [I<bandwidth>] }

 Manage active block operations.  There are three modes: I<--info>,
 I<bandwidth>, and I<--abort>; I<--info> is default except that I<--async>
-implies I<--abort>.
+or I<--pivot> implies I<--abort>.

 I<path> specifies fully-qualified path of the disk; it corresponds
 to a unique target name (<target dev='name'/>) or source file (<source
@@ -714,7 +755,9 @@ also B<domblklist> for listing these names).

 If I<--abort> is specified, the active job on the specified disk will
 be aborted.  If I<--async> is also specified, this command will return
-immediately, rather than waiting for the cancelation to complete.
+immediately, rather than waiting for the cancelation to complete.  If
+I<--pivot> is specified, this requests that an active copy job
+be pivoted over to the new copy.
 If I<--info> is specified, the active job information on the specified
 disk will be printed.
 I<bandwidth> can be used to set bandwidth limit for the active job.
-- 
1.7.7.6




More information about the libvir-list mailing list