blockjob: add virsh blockpull --wait

I'm tired of shell-scripting to wait for completion of a block pull,
when virsh can be taught to do the same.  I couldn't quite reuse
vshWatchJob, as this is not a case of a long-running command where
a second thread must be used to probe job status (at least, not unless
I make virsh start doing blocking waits for an event to fire), but it
served as inspiration for my simpler single-threaded loop.  There is
up to a half-second delay between sending SIGINT and the job being
aborted, but I didn't think it worth the complexity of a second thread
and use of poll() just to minimize that delay.

* tools/virsh.c (cmdBlockPull): Add new options to wait for
completion.
(blockJobImpl): Add argument.
(cmdBlockJob): Adjust caller.
* tools/virsh.pod (blockjob): Document new mode.
This commit is contained in:
Eric Blake 2012-04-12 13:12:21 -06:00
parent 6fb8a64d93
commit 3b96a89242
2 changed files with 127 additions and 7 deletions

View File

@ -7521,7 +7521,8 @@ typedef enum {
static int
blockJobImpl(vshControl *ctl, const vshCmd *cmd,
virDomainBlockJobInfoPtr info, int mode)
virDomainBlockJobInfoPtr info, int mode,
virDomainPtr *pdom)
{
virDomainPtr dom = NULL;
const char *name, *path;
@ -7562,7 +7563,9 @@ blockJobImpl(vshControl *ctl, const vshCmd *cmd,
}
cleanup:
if (dom)
if (pdom && ret == 0)
*pdom = dom;
else if (dom)
virDomainFree(dom);
return ret;
}
@ -7582,15 +7585,122 @@ static const vshCmdOptDef opts_block_pull[] = {
{"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")},
{"verbose", VSH_OT_BOOL, 0, N_("with --wait, display the progress")},
{"timeout", VSH_OT_INT, VSH_OFLAG_NONE,
N_("with --wait, abort if pull exceeds timeout (in seconds)")},
{"async", VSH_OT_BOOL, 0,
N_("with --wait, don't wait for cancel to finish")},
{NULL, 0, 0, NULL}
};
static bool
cmdBlockPull(vshControl *ctl, const vshCmd *cmd)
{
if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_PULL) != 0)
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;
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;
return true;
}
if (blockJobImpl(ctl, cmd, NULL, VSH_CMD_BLOCK_JOB_PULL, &dom) < 0)
goto cleanup;
if (!blocking) {
vshPrint(ctl, "%s", _("Block Pull 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 Pull"), 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 Pull"), 0, 1);
}
vshPrint(ctl, "\n%s", quit ? _("Pull aborted") : _("Pull complete"));
ret = true;
cleanup:
if (dom)
virDomainFree(dom);
if (blocking)
sigaction(SIGINT, &old_sig_action, NULL);
return ret;
}
/*
@ -7641,7 +7751,7 @@ cmdBlockJob(vshControl *ctl, const vshCmd *cmd)
else
mode = VSH_CMD_BLOCK_JOB_INFO;
ret = blockJobImpl(ctl, cmd, &info, mode);
ret = blockJobImpl(ctl, cmd, &info, mode, NULL);
if (ret < 0)
return false;

View File

@ -640,6 +640,7 @@ address of virtual interface (such as I<detach-interface> or
I<domif-setlink>) will accept the MAC address printed by this command.
=item B<blockpull> I<domain> I<path> [I<bandwidth>] [I<base>]
[I<--wait> [I<--verbose>] [I<--timeout> B<seconds>] [I<--async]]
Populate a disk from its backing image chain. By default, this command
flattens the entire chain; but if I<base> is specified, containing the
@ -647,8 +648,17 @@ name of one of the backing files in the chain, then that file becomes
the new backing file and only the intermediate portion of the chain is
pulled. Once all requested data from the backing image chain has been
pulled, the disk no longer depends on that portion of the backing chain.
It pulls data for the entire disk in the background, the process of the
operation can be checked with B<blockjob>.
By default, this command returns as soon as possible, and data for
the entire disk is pulled 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. 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; it corresponds
to a unique target name (<target dev='name'/>) or source file (<source