mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 05:35:25 +00:00
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:
parent
ef1e024df8
commit
ed23b10660
@ -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},
|
||||
|
@ -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>]]
|
||||
|
Loading…
Reference in New Issue
Block a user