blockjob: add 'blockcopy' to virsh

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.
This commit is contained in:
Eric Blake 2012-04-12 14:31:52 -06:00
parent 3648469258
commit 1f06c007fe
2 changed files with 244 additions and 18 deletions

View File

@ -7515,7 +7515,8 @@ typedef enum {
VSH_CMD_BLOCK_JOB_INFO = 1, VSH_CMD_BLOCK_JOB_INFO = 1,
VSH_CMD_BLOCK_JOB_SPEED = 2, VSH_CMD_BLOCK_JOB_SPEED = 2,
VSH_CMD_BLOCK_JOB_PULL = 3, VSH_CMD_BLOCK_JOB_PULL = 3,
} VSH_CMD_BLOCK_JOB_MODE; VSH_CMD_BLOCK_JOB_COPY = 4,
} vshCmdBlockJobMode;
static int static int
blockJobImpl(vshControl *ctl, const vshCmd *cmd, blockJobImpl(vshControl *ctl, const vshCmd *cmd,
@ -7526,6 +7527,7 @@ blockJobImpl(vshControl *ctl, const vshCmd *cmd,
const char *name, *path; const char *name, *path;
unsigned long bandwidth = 0; unsigned long bandwidth = 0;
int ret = -1; int ret = -1;
const char *base = NULL;
unsigned int flags = 0; unsigned int flags = 0;
if (!vshConnectionUsability(ctl, ctl->conn)) if (!vshConnectionUsability(ctl, ctl->conn))
@ -7542,22 +7544,39 @@ blockJobImpl(vshControl *ctl, const vshCmd *cmd,
goto cleanup; goto cleanup;
} }
if (mode == VSH_CMD_BLOCK_JOB_ABORT) { switch ((vshCmdBlockJobMode) mode) {
case VSH_CMD_BLOCK_JOB_ABORT:
if (vshCommandOptBool(cmd, "async")) if (vshCommandOptBool(cmd, "async"))
flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC; flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC;
if (vshCommandOptBool(cmd, "pivot"))
flags |= VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT;
ret = virDomainBlockJobAbort(dom, path, flags); 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); 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); ret = virDomainBlockJobSetSpeed(dom, path, bandwidth, 0);
} else if (mode == VSH_CMD_BLOCK_JOB_PULL) { break;
const char *base = NULL; case VSH_CMD_BLOCK_JOB_PULL:
if (vshCommandOptString(cmd, "base", &base) < 0) if (vshCommandOptString(cmd, "base", &base) < 0)
goto cleanup; goto cleanup;
if (base) if (base)
ret = virDomainBlockRebase(dom, path, base, bandwidth, 0); ret = virDomainBlockRebase(dom, path, base, bandwidth, 0);
else else
ret = virDomainBlockPull(dom, path, bandwidth, 0); 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: cleanup:
@ -7568,6 +7587,159 @@ cleanup:
return ret; return ret;
} }
/*
* "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;
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") || 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 * "blockpull" command
*/ */
@ -7579,8 +7751,8 @@ static const vshCmdInfo info_block_pull[] = {
static const vshCmdOptDef opts_block_pull[] = { static const vshCmdOptDef opts_block_pull[] = {
{"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, {"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")},
{"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("Bandwidth limit in MB/s")}, {"bandwidth", VSH_OT_DATA, VSH_OFLAG_NONE, N_("bandwidth limit in MB/s")},
{"base", VSH_OT_DATA, VSH_OFLAG_NONE, {"base", VSH_OT_DATA, VSH_OFLAG_NONE,
N_("path of backing file in chain for a partial pull")}, N_("path of backing file in chain for a partial pull")},
{"wait", VSH_OT_BOOL, 0, N_("wait for job to finish")}, {"wait", VSH_OT_BOOL, 0, N_("wait for job to finish")},
@ -7713,15 +7885,17 @@ static const vshCmdInfo info_block_job[] = {
static const vshCmdOptDef opts_block_job[] = { static const vshCmdOptDef opts_block_job[] = {
{"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, {"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, {"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, {"async", VSH_OT_BOOL, VSH_OFLAG_NONE,
N_("don't wait for --abort to complete")}, 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, {"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, {"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} {NULL, 0, 0, NULL}
}; };
@ -7733,7 +7907,8 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd)
const char *type; const char *type;
int ret; int ret;
bool abortMode = (vshCommandOptBool(cmd, "abort") || bool abortMode = (vshCommandOptBool(cmd, "abort") ||
vshCommandOptBool(cmd, "async")); vshCommandOptBool(cmd, "async") ||
vshCommandOptBool(cmd, "pivot"));
bool infoMode = vshCommandOptBool(cmd, "info"); bool infoMode = vshCommandOptBool(cmd, "info");
bool bandwidth = vshCommandOptBool(cmd, "bandwidth"); bool bandwidth = vshCommandOptBool(cmd, "bandwidth");
@ -7757,10 +7932,17 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd)
if (ret == 0 || mode != VSH_CMD_BLOCK_JOB_INFO) if (ret == 0 || mode != VSH_CMD_BLOCK_JOB_INFO)
return true; return true;
if (info.type == VIR_DOMAIN_BLOCK_JOB_TYPE_PULL) switch (info.type) {
case VIR_DOMAIN_BLOCK_JOB_TYPE_PULL:
type = _("Block Pull"); type = _("Block Pull");
else break;
case VIR_DOMAIN_BLOCK_JOB_TYPE_COPY:
type = _("Block Copy");
break;
default:
type = _("Unknown job"); type = _("Unknown job");
break;
}
print_job_progress(type, info.end - info.cur, info.end); print_job_progress(type, info.end - info.cur, info.end);
if (info.bandwidth != 0) if (info.bandwidth != 0)
@ -17239,6 +17421,7 @@ static const vshCmdDef domManagementCmds[] = {
{"autostart", cmdAutostart, opts_autostart, info_autostart, 0}, {"autostart", cmdAutostart, opts_autostart, info_autostart, 0},
{"blkdeviotune", cmdBlkdeviotune, opts_blkdeviotune, info_blkdeviotune, 0}, {"blkdeviotune", cmdBlkdeviotune, opts_blkdeviotune, info_blkdeviotune, 0},
{"blkiotune", cmdBlkiotune, opts_blkiotune, info_blkiotune, 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}, {"blockjob", cmdBlockJob, opts_block_job, info_block_job, 0},
{"blockpull", cmdBlockPull, opts_block_pull, info_block_pull, 0}, {"blockpull", cmdBlockPull, opts_block_pull, info_block_pull, 0},
{"blockresize", cmdBlockResize, opts_block_resize, info_block_resize, 0}, {"blockresize", cmdBlockResize, opts_block_resize, info_block_resize, 0},

View File

@ -641,6 +641,47 @@ currently in use by a running domain. Other contexts that require a MAC
address of virtual interface (such as I<detach-interface> or address of virtual interface (such as I<detach-interface> or
I<domif-setlink>) will accept the MAC address printed by this command. 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>] =item B<blockpull> I<domain> I<path> [I<bandwidth>] [I<base>]
[I<--wait> [I<--verbose>] [I<--timeout> B<seconds>] [I<--async]] [I<--wait> [I<--verbose>] [I<--timeout> B<seconds>] [I<--async]]
@ -702,12 +743,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 exclusive. If no flag is specified, behavior is different depending
on hypervisor. 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>] } [I<--info>] | [I<bandwidth>] }
Manage active block operations. There are three modes: I<--info>, Manage active block operations. There are three modes: I<--info>,
I<bandwidth>, and I<--abort>; I<--info> is default except that I<--async> 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 I<path> specifies fully-qualified path of the disk; it corresponds
to a unique target name (<target dev='name'/>) or source file (<source to a unique target name (<target dev='name'/>) or source file (<source
@ -716,7 +757,9 @@ also B<domblklist> for listing these names).
If I<--abort> is specified, the active job on the specified disk will 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 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 If I<--info> is specified, the active job information on the specified
disk will be printed. disk will be printed.
I<bandwidth> can be used to set bandwidth limit for the active job. I<bandwidth> can be used to set bandwidth limit for the active job.