blockjob: add virsh blockcommit

The new command 'virsh blockcommit $dom $disk' requests the start
of an asynchronous commit operation across the entire chain of
$disk.  Further arguments can fine-tune which portion of the
chain is committed.  Existing 'virsh blockjob' commands can then
track the status, change the bandwidth, or abort the commit job.

With a bit more on the command line, 'virsh blockcommit $dom $disk
--wait --verbose' can be used for blocking behavior, with visual
feedback on the overall status, and can be canceled with Ctrl-C.

The overall design, including the wait loop logic, borrows heavily
from the existing blockpull command.

* tools/virsh-domain.c (cmdBlockCommit): New function.
* tools/virsh.pod (blockcommit): Document it.
This commit is contained in:
Eric Blake 2012-09-17 14:56:01 -06:00
parent ef1e024df8
commit ed23b10660
2 changed files with 189 additions and 5 deletions

View File

@ -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"))
@ -1259,6 +1271,145 @@ static void vshCatchInt(int sig ATTRIBUTE_UNUSED,
intCaught = 1;
}
/*
* "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;
}
timeout *= 1000;
}
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", _("missing --wait option"));
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))) {
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
*/
@ -1322,6 +1473,7 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
vshError(ctl, "%s", _("migrate: Timeout is too big"));
return false;
}
timeout *= 1000;
}
if (vshCommandOptString(cmd, "path", &path) < 0)
return false;
@ -1340,7 +1492,7 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
GETTIMEOFDAY(&start);
} else if (verbose || vshCommandOptBool(cmd, "timeout") ||
vshCommandOptBool(cmd, "async") || pivot || finish) {
vshError(ctl, "%s", _("blocking control options require --wait"));
vshError(ctl, "%s", _("missing --wait option"));
return false;
}
@ -1370,7 +1522,7 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
if (intCaught || (timeout &&
(((int)(curr.tv_sec - start.tv_sec) * 1000 +
(int)(curr.tv_usec - start.tv_usec) / 1000) >
timeout * 1000))) {
timeout))) {
vshDebug(ctl, VSH_ERR_DEBUG,
intCaught ? "interrupted" : "timeout");
intCaught = 0;
@ -1541,6 +1693,7 @@ cmdBlockPull(vshControl *ctl, const vshCmd *cmd)
vshError(ctl, "%s", _("timeout is too big"));
return false;
}
timeout *= 1000;
}
if (vshCommandOptString(cmd, "path", &path) < 0)
return false;
@ -1559,7 +1712,7 @@ cmdBlockPull(vshControl *ctl, const vshCmd *cmd)
GETTIMEOFDAY(&start);
} else if (verbose || vshCommandOptBool(cmd, "timeout") ||
vshCommandOptBool(cmd, "async")) {
vshError(ctl, "%s", _("blocking control options require --wait"));
vshError(ctl, "%s", _("missing --wait option"));
return false;
}
@ -1590,7 +1743,7 @@ cmdBlockPull(vshControl *ctl, const vshCmd *cmd)
if (intCaught || (timeout &&
(((int)(curr.tv_sec - start.tv_sec) * 1000 +
(int)(curr.tv_usec - start.tv_usec) / 1000) >
timeout * 1000))) {
timeout))) {
vshDebug(ctl, VSH_ERR_DEBUG,
intCaught ? "interrupted" : "timeout");
intCaught = 0;
@ -8112,6 +8265,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},

View File

@ -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>]]