提交 c1d75dee 编写于 作者: E Eric Blake

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: NEric Blake <eblake@redhat.com>
上级 0e8bed81
...@@ -1818,11 +1818,10 @@ static const vshCmdOptDef opts_block_copy[] = { ...@@ -1818,11 +1818,10 @@ static const vshCmdOptDef opts_block_copy[] = {
{.name = "path", {.name = "path",
.type = VSH_OT_DATA, .type = VSH_OT_DATA,
.flags = VSH_OFLAG_REQ, .flags = VSH_OFLAG_REQ,
.help = N_("fully-qualified path of disk") .help = N_("fully-qualified path of source disk")
}, },
{.name = "dest", {.name = "dest",
.type = VSH_OT_DATA, .type = VSH_OT_DATA,
.flags = VSH_OFLAG_REQ,
.help = N_("path of the copy to create") .help = N_("path of the copy to create")
}, },
{.name = "bandwidth", {.name = "bandwidth",
...@@ -1838,8 +1837,8 @@ static const vshCmdOptDef opts_block_copy[] = { ...@@ -1838,8 +1837,8 @@ static const vshCmdOptDef opts_block_copy[] = {
.help = N_("reuse existing destination") .help = N_("reuse existing destination")
}, },
{.name = "raw", {.name = "raw",
.type = VSH_OT_BOOL, .type = VSH_OT_ALIAS,
.help = N_("use raw destination file") .help = "format=raw"
}, },
{.name = "blockdev", {.name = "blockdev",
.type = VSH_OT_BOOL, .type = VSH_OT_BOOL,
...@@ -1869,6 +1868,22 @@ static const vshCmdOptDef opts_block_copy[] = { ...@@ -1869,6 +1868,22 @@ static const vshCmdOptDef opts_block_copy[] = {
.type = VSH_OT_BOOL, .type = VSH_OT_BOOL,
.help = N_("with --wait, don't wait for cancel to finish") .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} {.name = NULL}
}; };
...@@ -1876,14 +1891,18 @@ static bool ...@@ -1876,14 +1891,18 @@ static bool
cmdBlockCopy(vshControl *ctl, const vshCmd *cmd) cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
{ {
virDomainPtr dom = NULL; virDomainPtr dom = NULL;
const char *dest; const char *dest = NULL;
const char *format = NULL;
unsigned long bandwidth = 0; 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 ret = false;
bool blocking = vshCommandOptBool(cmd, "wait"); bool blocking = vshCommandOptBool(cmd, "wait");
bool verbose = vshCommandOptBool(cmd, "verbose"); bool verbose = vshCommandOptBool(cmd, "verbose");
bool pivot = vshCommandOptBool(cmd, "pivot"); bool pivot = vshCommandOptBool(cmd, "pivot");
bool finish = vshCommandOptBool(cmd, "finish"); bool finish = vshCommandOptBool(cmd, "finish");
bool blockdev = vshCommandOptBool(cmd, "blockdev");
int timeout = 0; int timeout = 0;
struct sigaction sig_action; struct sigaction sig_action;
struct sigaction old_sig_action; struct sigaction old_sig_action;
...@@ -1893,9 +1912,23 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd) ...@@ -1893,9 +1912,23 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
const char *path = NULL; const char *path = NULL;
bool quit = false; bool quit = false;
int abort_flags = 0; int abort_flags = 0;
const char *xml = NULL;
char *xmlstr = NULL;
virTypedParameterPtr params = NULL;
int nparams = 0;
if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0) if (vshCommandOptStringReq(ctl, cmd, "path", &path) < 0)
return false; 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; blocking |= vshCommandOptBool(cmd, "timeout") || pivot || finish;
if (blocking) { if (blocking) {
...@@ -1926,24 +1959,100 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd) ...@@ -1926,24 +1959,100 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
goto cleanup; 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) { if (vshCommandOptULWrap(cmd, "bandwidth", &bandwidth) < 0) {
vshError(ctl, "%s", _("bandwidth must be a number")); vshError(ctl, "%s", _("bandwidth must be a number"));
goto cleanup; 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")) if (vshCommandOptBool(cmd, "shallow"))
flags |= VIR_DOMAIN_BLOCK_REBASE_SHALLOW; flags |= VIR_DOMAIN_BLOCK_REBASE_SHALLOW;
if (vshCommandOptBool(cmd, "reuse-external")) if (vshCommandOptBool(cmd, "reuse-external"))
flags |= VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT; 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) if (granularity || buf_size || (format && STRNEQ(format, "raw")) || xml) {
goto cleanup; /* 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(&params[nparams++],
VIR_DOMAIN_BLOCK_COPY_BANDWIDTH,
VIR_TYPED_PARAM_ULLONG,
bandwidth << 20ULL) < 0)
goto cleanup;
}
if (granularity &&
virTypedParameterAssign(&params[nparams++],
VIR_DOMAIN_BLOCK_COPY_GRANULARITY,
VIR_TYPED_PARAM_UINT,
granularity) < 0)
goto cleanup;
if (buf_size &&
virTypedParameterAssign(&params[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) { if (!blocking) {
vshPrint(ctl, "%s", _("Block Copy started")); vshPrint(ctl, "%s", _("Block Copy started"));
...@@ -2014,6 +2123,8 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd) ...@@ -2014,6 +2123,8 @@ cmdBlockCopy(vshControl *ctl, const vshCmd *cmd)
ret = true; ret = true;
cleanup: cleanup:
VIR_FREE(xmlstr);
virTypedParamsFree(params, nparams);
if (dom) if (dom)
virDomainFree(dom); virDomainFree(dom);
if (blocking) if (blocking)
......
...@@ -898,30 +898,31 @@ value is interpreted as an unsigned long long value or essentially ...@@ -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 unlimited. The hypervisor can choose whether to reject the value or
convert it to the maximum value allowed. convert it to the maximum value allowed.
=item B<blockcopy> I<domain> I<path> I<dest> [I<bandwidth>] [I<--shallow>] =item B<blockcopy> I<domain> I<path> { I<dest> [I<format>] [I<--blockdev>]
[I<--reuse-external>] [I<--raw>] [I<--blockdev>] | I<xml> } [I<--shallow>] [I<--reuse-external>] [I<bandwidth>]
[I<--wait> [I<--async>] [I<--verbose>]] [I<--wait> [I<--async>] [I<--verbose>]] [{I<--pivot> | I<--finish>}]
[{I<--pivot> | I<--finish>}] [I<--timeout> B<seconds>] [I<--timeout> B<seconds>] [I<granularity>] [I<buf-size>]
Copy a disk backing image chain to I<dest>. By default, this command Copy a disk backing image chain to a destination. Either I<dest> as
flattens the entire chain; but if I<--shallow> is specified, the copy the destination file name, or I<xml> as the name of an XML file containing
shares the backing chain. a top-level <disk> element describing the destination, must be present.
Additionally, if I<dest> is given, I<format> should be specified to declare
If I<--reuse-external> is specified, then I<dest> must exist and have 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 the destination must exist and have
sufficient space to hold the copy. If I<--shallow> is used in sufficient space to hold the copy. If I<--shallow> is used in
conjunction with I<--reuse-external> then the pre-created image must have conjunction with I<--reuse-external> then the pre-created image must have
guest visible contents identical to guest visible contents of the backing 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 file of the original image. This may be used to modify the backing file
names on the destination. 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 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 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 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. ...@@ -943,9 +944,15 @@ while longer until the job has actually cancelled.
I<path> specifies fully-qualified path of the disk. I<path> specifies fully-qualified path of the disk.
I<bandwidth> specifies copying bandwidth limit in MiB/s. Specifying a negative I<bandwidth> specifies copying bandwidth limit in MiB/s. Specifying a negative
value is interpreted as an unsigned long long value or essentially value is interpreted as an unsigned long long value that might be essentially
unlimited. The hypervisor can choose whether to reject the value or unlimited, but more likely would overflow; it is safer to use 0 for that
convert it to the maximum value allowed. 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>] =item B<blockpull> I<domain> I<path> [I<bandwidth>] [I<base>]
[I<--wait> [I<--verbose>] [I<--timeout> B<seconds>] [I<--async>]] [I<--wait> [I<--verbose>] [I<--timeout> B<seconds>] [I<--async>]]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册