mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-10-22 14:09:22 +00:00
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:
parent
6fb8a64d93
commit
3b96a89242
120
tools/virsh.c
120
tools/virsh.c
@ -7521,7 +7521,8 @@ typedef enum {
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
blockJobImpl(vshControl *ctl, const vshCmd *cmd,
|
blockJobImpl(vshControl *ctl, const vshCmd *cmd,
|
||||||
virDomainBlockJobInfoPtr info, int mode)
|
virDomainBlockJobInfoPtr info, int mode,
|
||||||
|
virDomainPtr *pdom)
|
||||||
{
|
{
|
||||||
virDomainPtr dom = NULL;
|
virDomainPtr dom = NULL;
|
||||||
const char *name, *path;
|
const char *name, *path;
|
||||||
@ -7562,7 +7563,9 @@ blockJobImpl(vshControl *ctl, const vshCmd *cmd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (dom)
|
if (pdom && ret == 0)
|
||||||
|
*pdom = dom;
|
||||||
|
else if (dom)
|
||||||
virDomainFree(dom);
|
virDomainFree(dom);
|
||||||
return ret;
|
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")},
|
{"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")},
|
||||||
|
{"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}
|
{NULL, 0, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
cmdBlockPull(vshControl *ctl, const vshCmd *cmd)
|
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;
|
return false;
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
else
|
||||||
mode = VSH_CMD_BLOCK_JOB_INFO;
|
mode = VSH_CMD_BLOCK_JOB_INFO;
|
||||||
|
|
||||||
ret = blockJobImpl(ctl, cmd, &info, mode);
|
ret = blockJobImpl(ctl, cmd, &info, mode, NULL);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -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.
|
I<domif-setlink>) will accept the MAC address printed by this command.
|
||||||
|
|
||||||
=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]]
|
||||||
|
|
||||||
Populate a disk from its backing image chain. By default, this command
|
Populate a disk from its backing image chain. By default, this command
|
||||||
flattens the entire chain; but if I<base> is specified, containing the
|
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
|
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. Once all requested data from the backing image chain has been
|
||||||
pulled, the disk no longer depends on that portion of the backing chain.
|
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
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user