mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 21:55:25 +00:00
blockcopy: expose new API in virsh
Expose the new power of virDomainBlockCopy through virsh (well, all but the finer-grained bandwidth, as that is its own can of worms for a later patch). Continue to use the older API where possible, for maximum compatibility. The command now requires either --dest (with optional --format and --blockdev), to directly describe the file destination, or --xml, to name a file that contains an XML description such as: <disk type='network'> <driver type='raw'/> <source protocol='gluster' name='vol1/img'> <host name='red'/> </source> </disk> [well, it may be a while before the qemu driver is actually patched to act on that particular xml beyond just parsing it, but the virsh interface won't need changing at that time] Non-zero option parameters are converted into virTypedParameters, and if anything requires the new API, the command can synthesize appropriate XML even if the --dest option was used instead of --xml. The existing --raw flag remains for back-compat, but the preferred spelling is now --format=raw, since the new API now allows us to specify all formats rather than just a boolean raw to suppress probing. I hope I did justice in describing the effects of granularity and buf-size on how they get passed through to qemu. * tools/virsh-domain.c (cmdBlockCopy): Add new options --xml, --granularity, --buf-size, --format. Make --raw an alias for --format=raw. Call new API if new parameters are in use. * tools/virsh.pod (blockcopy): Document new options. Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
parent
0e8bed8177
commit
c1d75deea2
@ -1818,11 +1818,10 @@ static const vshCmdOptDef opts_block_copy[] = {
|
||||
{.name = "path",
|
||||
.type = VSH_OT_DATA,
|
||||
.flags = VSH_OFLAG_REQ,
|
||||
.help = N_("fully-qualified path of disk")
|
||||
.help = N_("fully-qualified path of source disk")
|
||||
},
|
||||
{.name = "dest",
|
||||
.type = VSH_OT_DATA,
|
||||
.flags = VSH_OFLAG_REQ,
|
||||
.help = N_("path of the copy to create")
|
||||
},
|
||||
{.name = "bandwidth",
|
||||
@ -1838,8 +1837,8 @@ static const vshCmdOptDef opts_block_copy[] = {
|
||||
.help = N_("reuse existing destination")
|
||||
},
|
||||
{.name = "raw",
|
||||
.type = VSH_OT_BOOL,
|
||||
.help = N_("use raw destination file")
|
||||
.type = VSH_OT_ALIAS,
|
||||
.help = "format=raw"
|
||||
},
|
||||
{.name = "blockdev",
|
||||
.type = VSH_OT_BOOL,
|
||||
@ -1869,6 +1868,22 @@ static const vshCmdOptDef opts_block_copy[] = {
|
||||
.type = VSH_OT_BOOL,
|
||||
.help = N_("with --wait, don't wait for cancel to finish")
|
||||
},
|
||||
{.name = "xml",
|
||||
.type = VSH_OT_DATA,
|
||||
.help = N_("filename containing XML description of the copy destination")
|
||||
},
|
||||
{.name = "format",
|
||||
.type = VSH_OT_DATA,
|
||||
.help = N_("format of the destination file")
|
||||
},
|
||||
{.name = "granularity",
|
||||
.type = VSH_OT_INT,
|
||||
.help = N_("power-of-two granularity to use during the copy")
|
||||
},
|
||||
{.name = "buf-size",
|
||||
.type = VSH_OT_INT,
|
||||
.help = N_("maximum amount of in-flight data during the copy")
|
||||
},
|
||||
{.name = NULL}
|
||||
};
|
||||
|
||||
@ -1876,14 +1891,18 @@ static bool
|
||||
cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
|
||||
{
|
||||
virDomainPtr dom = NULL;
|
||||
const char *dest;
|
||||
const char *dest = NULL;
|
||||
const char *format = NULL;
|
||||
unsigned long bandwidth = 0;
|
||||
unsigned int flags = VIR_DOMAIN_BLOCK_REBASE_COPY;
|
||||
unsigned int granularity = 0;
|
||||
unsigned long long buf_size = 0;
|
||||
unsigned int flags = 0;
|
||||
bool ret = false;
|
||||
bool blocking = vshCommandOptBool(cmd, "wait");
|
||||
bool verbose = vshCommandOptBool(cmd, "verbose");
|
||||
bool pivot = vshCommandOptBool(cmd, "pivot");
|
||||
bool finish = vshCommandOptBool(cmd, "finish");
|
||||
bool blockdev = vshCommandOptBool(cmd, "blockdev");
|
||||
int timeout = 0;
|
||||
struct sigaction sig_action;
|
||||
struct sigaction old_sig_action;
|
||||
@ -1893,9 +1912,23 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
|
||||
const char *path = NULL;
|
||||
bool quit = false;
|
||||
int abort_flags = 0;
|
||||
const char *xml = NULL;
|
||||
char *xmlstr = NULL;
|
||||
virTypedParameterPtr params = NULL;
|
||||
int nparams = 0;
|
||||
|
||||
if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
|
||||
return false;
|
||||
if (vshCommandOptString(cmd, "dest", &dest) < 0)
|
||||
return false;
|
||||
if (vshCommandOptString(cmd, "xml", &xml) < 0)
|
||||
return false;
|
||||
if (vshCommandOptString(cmd, "format", &format) < 0)
|
||||
return false;
|
||||
|
||||
VSH_EXCLUSIVE_OPTIONS_VAR(dest, xml);
|
||||
VSH_EXCLUSIVE_OPTIONS_VAR(format, xml);
|
||||
VSH_EXCLUSIVE_OPTIONS_VAR(blockdev, xml);
|
||||
|
||||
blocking |= vshCommandOptBool(cmd, "timeout") || pivot || finish;
|
||||
if (blocking) {
|
||||
@ -1926,24 +1959,100 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
|
||||
if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
|
||||
goto cleanup;
|
||||
|
||||
/* XXX: Parse bandwidth as scaled input, rather than forcing
|
||||
* MiB/s, and either reject negative input or treat it as 0 rather
|
||||
* than trying to guess which value will work well across both
|
||||
* APIs with their different sizes and scales. */
|
||||
if (vshCommandOptULWrap(cmd, "bandwidth", &bandwidth) < 0) {
|
||||
vshError(ctl, "%s", _("bandwidth must be a number"));
|
||||
goto cleanup;
|
||||
}
|
||||
if (vshCommandOptUInt(cmd, "granularity", &granularity) < 0) {
|
||||
vshError(ctl, "%s", _("granularity must be a number"));
|
||||
goto cleanup;
|
||||
}
|
||||
if (vshCommandOptULongLong(cmd, "buf-size", &buf_size) < 0) {
|
||||
vshError(ctl, "%s", _("buf-size must be a number"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml) {
|
||||
if (virFileReadAll(xml, 8192, &xmlstr) < 0) {
|
||||
vshReportError(ctl);
|
||||
goto cleanup;
|
||||
}
|
||||
} else if (!dest) {
|
||||
vshError(ctl, "%s", _("need either --dest or --xml"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Exploit that some VIR_DOMAIN_BLOCK_REBASE_* and
|
||||
* VIR_DOMAIN_BLOCK_COPY_* flags have the same values. */
|
||||
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 (vshCommandOptBool(cmd, "blockdev"))
|
||||
flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_DEV;
|
||||
if (vshCommandOptStringReq(ctl, cmd, "dest", &dest) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (virDomainBlockRebase(dom, path, dest, bandwidth, flags) < 0)
|
||||
goto cleanup;
|
||||
if (granularity || buf_size || (format && STRNEQ(format, "raw")) || xml) {
|
||||
/* New API */
|
||||
if (bandwidth || granularity || buf_size) {
|
||||
params = vshCalloc(ctl, 3, sizeof(*params));
|
||||
if (bandwidth) {
|
||||
/* bandwidth is ulong MiB/s, but the typed parameter is
|
||||
* ullong bytes/s; make sure we don't overflow */
|
||||
if (bandwidth > ULLONG_MAX >> 20) {
|
||||
virReportError(VIR_ERR_OVERFLOW,
|
||||
_("bandwidth must be less than %llu"),
|
||||
ULLONG_MAX >> 20);
|
||||
}
|
||||
if (virTypedParameterAssign(¶ms[nparams++],
|
||||
VIR_DOMAIN_BLOCK_COPY_BANDWIDTH,
|
||||
VIR_TYPED_PARAM_ULLONG,
|
||||
bandwidth << 20ULL) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
if (granularity &&
|
||||
virTypedParameterAssign(¶ms[nparams++],
|
||||
VIR_DOMAIN_BLOCK_COPY_GRANULARITY,
|
||||
VIR_TYPED_PARAM_UINT,
|
||||
granularity) < 0)
|
||||
goto cleanup;
|
||||
if (buf_size &&
|
||||
virTypedParameterAssign(¶ms[nparams++],
|
||||
VIR_DOMAIN_BLOCK_COPY_BUF_SIZE,
|
||||
VIR_TYPED_PARAM_ULLONG,
|
||||
buf_size) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!xmlstr) {
|
||||
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||
virBufferAsprintf(&buf, "<disk type='%s'>\n",
|
||||
blockdev ? "block" : "file");
|
||||
virBufferAdjustIndent(&buf, 2);
|
||||
virBufferAsprintf(&buf, "<source %s", blockdev ? "dev" : "file");
|
||||
virBufferEscapeString(&buf, "='%s'/>\n", dest);
|
||||
virBufferEscapeString(&buf, "<driver type='%s'/>\n", format);
|
||||
virBufferAdjustIndent(&buf, -2);
|
||||
virBufferAddLit(&buf, "</disk>\n");
|
||||
if (virBufferCheckError(&buf) < 0)
|
||||
goto cleanup;
|
||||
xmlstr = virBufferContentAndReset(&buf);
|
||||
}
|
||||
|
||||
if (virDomainBlockCopy(dom, path, xmlstr, params, nparams, flags) < 0)
|
||||
goto cleanup;
|
||||
} else {
|
||||
/* Old API */
|
||||
flags |= VIR_DOMAIN_BLOCK_REBASE_COPY;
|
||||
if (vshCommandOptBool(cmd, "blockdev"))
|
||||
flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_DEV;
|
||||
if (STREQ_NULLABLE(format, "raw"))
|
||||
flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_RAW;
|
||||
|
||||
if (virDomainBlockRebase(dom, path, dest, bandwidth, flags) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!blocking) {
|
||||
vshPrint(ctl, "%s", _("Block Copy started"));
|
||||
@ -2014,6 +2123,8 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
|
||||
|
||||
ret = true;
|
||||
cleanup:
|
||||
VIR_FREE(xmlstr);
|
||||
virTypedParamsFree(params, nparams);
|
||||
if (dom)
|
||||
virDomainFree(dom);
|
||||
if (blocking)
|
||||
|
@ -898,30 +898,31 @@ value is interpreted as an unsigned long long value or essentially
|
||||
unlimited. The hypervisor can choose whether to reject the value or
|
||||
convert it to the maximum value allowed.
|
||||
|
||||
=item B<blockcopy> I<domain> I<path> I<dest> [I<bandwidth>] [I<--shallow>]
|
||||
[I<--reuse-external>] [I<--raw>] [I<--blockdev>]
|
||||
[I<--wait> [I<--async>] [I<--verbose>]]
|
||||
[{I<--pivot> | I<--finish>}] [I<--timeout> B<seconds>]
|
||||
=item B<blockcopy> I<domain> I<path> { I<dest> [I<format>] [I<--blockdev>]
|
||||
| I<xml> } [I<--shallow>] [I<--reuse-external>] [I<bandwidth>]
|
||||
[I<--wait> [I<--async>] [I<--verbose>]] [{I<--pivot> | I<--finish>}]
|
||||
[I<--timeout> B<seconds>] [I<granularity>] [I<buf-size>]
|
||||
|
||||
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.
|
||||
Copy a disk backing image chain to a destination. Either I<dest> as
|
||||
the destination file name, or I<xml> as the name of an XML file containing
|
||||
a top-level <disk> element describing the destination, must be present.
|
||||
Additionally, if I<dest> is given, I<format> should be specified to declare
|
||||
the format of the destination (if I<format> is omitted, then libvirt
|
||||
will reuse the format of the source, or with I<--reuse-external> will
|
||||
be forced to probe the destination format, which could be a potential
|
||||
security hole). The command supports I<--raw> as a boolean flag synonym for
|
||||
I<--format=raw>. When using I<dest>, the destination is treated as a regular
|
||||
file unless I<--blockdev> is used to signal that it is a block device. 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
|
||||
If I<--reuse-external> is specified, then the destination must exist and have
|
||||
sufficient space to hold the copy. If I<--shallow> is used in
|
||||
conjunction with I<--reuse-external> then the pre-created image must have
|
||||
guest visible contents identical to guest visible contents of the backing
|
||||
file of the original image. This may be used to modify the backing file
|
||||
names on 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. The destination is treated as a regular
|
||||
file unless I<--blockdev> is used to signal that it is a block
|
||||
device.
|
||||
|
||||
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
|
||||
@ -943,9 +944,15 @@ while longer until the job has actually cancelled.
|
||||
|
||||
I<path> specifies fully-qualified path of the disk.
|
||||
I<bandwidth> specifies copying bandwidth limit in MiB/s. Specifying a negative
|
||||
value is interpreted as an unsigned long long value or essentially
|
||||
unlimited. The hypervisor can choose whether to reject the value or
|
||||
convert it to the maximum value allowed.
|
||||
value is interpreted as an unsigned long long value that might be essentially
|
||||
unlimited, but more likely would overflow; it is safer to use 0 for that
|
||||
purpose. Specifying I<granularity> allows fine-tuning of the granularity that
|
||||
will be copied when a dirty region is detected; larger values trigger less
|
||||
I/O overhead but may end up copying more data overall (the default value is
|
||||
usually correct); this value must be a power of two. Specifying I<buf-size>
|
||||
will control how much data can be simultaneously in-flight during the copy;
|
||||
larger values use more memory but may allow faster completion (the default
|
||||
value is usually correct).
|
||||
|
||||
=item B<blockpull> I<domain> I<path> [I<bandwidth>] [I<base>]
|
||||
[I<--wait> [I<--verbose>] [I<--timeout> B<seconds>] [I<--async>]]
|
||||
|
Loading…
Reference in New Issue
Block a user