|
|
d89b3e |
From babfc1d48c3a0f83592fa501b609fd839ff1a51b Mon Sep 17 00:00:00 2001
|
|
|
d89b3e |
Message-Id: <babfc1d48c3a0f83592fa501b609fd839ff1a51b@dist-git>
|
|
|
d89b3e |
From: Eric Blake <eblake@redhat.com>
|
|
|
d89b3e |
Date: Tue, 24 Feb 2015 11:59:52 +0100
|
|
|
d89b3e |
Subject: [PATCH] blockcopy: allow block device destination
|
|
|
d89b3e |
|
|
|
d89b3e |
https://bugzilla.redhat.com/show_bug.cgi?id=1196066
|
|
|
d89b3e |
|
|
|
d89b3e |
To date, anyone performing a block copy and pivot ends up with
|
|
|
d89b3e |
the destination being treated as <disk type='file'>. While this
|
|
|
d89b3e |
works for data access for a block device, it has at least one
|
|
|
d89b3e |
noticeable shortcoming: virDomainGetBlockInfo() reports allocation
|
|
|
d89b3e |
differently for block devices visited as files (the size of the
|
|
|
d89b3e |
device) than for block devices visited as <disk type='block'>
|
|
|
d89b3e |
(the maximum sector used, as reported by qemu); and this difference
|
|
|
d89b3e |
is significant when trying to manage qcow2 format on block devices
|
|
|
d89b3e |
that can be grown as needed.
|
|
|
d89b3e |
|
|
|
d89b3e |
Of course, the more powerful virDomainBlockCopy() API can already
|
|
|
d89b3e |
express the ability to set the <disk> type. But a new API can't
|
|
|
d89b3e |
be backported, while a new flag to an existing API can; and it is
|
|
|
d89b3e |
also rather inconvenient to have to resort to the full power of
|
|
|
d89b3e |
generating XML when just adding a flag to the older call will do
|
|
|
d89b3e |
the trick. So this patch enhances blockcopy to let the user flag
|
|
|
d89b3e |
when the resulting XML after the copy must list the device as
|
|
|
d89b3e |
type='block'.
|
|
|
d89b3e |
|
|
|
d89b3e |
* include/libvirt/libvirt.h.in (VIR_DOMAIN_BLOCK_REBASE_COPY_DEV):
|
|
|
d89b3e |
New flag.
|
|
|
d89b3e |
* src/libvirt.c (virDomainBlockRebase): Document it.
|
|
|
d89b3e |
* tools/virsh-domain.c (opts_block_copy, blockJobImpl): Add
|
|
|
d89b3e |
--blockdev option.
|
|
|
d89b3e |
* tools/virsh.pod (blockcopy): Document it.
|
|
|
d89b3e |
* src/qemu/qemu_driver.c (qemuDomainBlockRebase): Allow new flag.
|
|
|
d89b3e |
(qemuDomainBlockCopy): Remember the flag, and make sure it is only
|
|
|
d89b3e |
used on actual block devices.
|
|
|
d89b3e |
* tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml: Test it.
|
|
|
d89b3e |
|
|
|
d89b3e |
Signed-off-by: Eric Blake <eblake@redhat.com>
|
|
|
d89b3e |
(cherry picked from commit b7e73585a8d96677695a52bafb156f26cbd48fb5)
|
|
|
d89b3e |
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
|
|
|
d89b3e |
---
|
|
|
d89b3e |
include/libvirt/libvirt.h.in | 2 ++
|
|
|
d89b3e |
src/libvirt.c | 8 +++--
|
|
|
d89b3e |
src/qemu/qemu_driver.c | 36 ++++++++++++++--------
|
|
|
d89b3e |
.../qemuxml2argvdata/qemuxml2argv-disk-mirror.xml | 4 +--
|
|
|
d89b3e |
tools/virsh-domain.c | 6 ++++
|
|
|
d89b3e |
tools/virsh.pod | 7 +++--
|
|
|
d89b3e |
6 files changed, 45 insertions(+), 18 deletions(-)
|
|
|
d89b3e |
|
|
|
d89b3e |
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
|
|
|
d89b3e |
index 94de8a6..e086c8f 100644
|
|
|
d89b3e |
--- a/include/libvirt/libvirt.h.in
|
|
|
d89b3e |
+++ b/include/libvirt/libvirt.h.in
|
|
|
d89b3e |
@@ -2638,6 +2638,8 @@ typedef enum {
|
|
|
d89b3e |
VIR_DOMAIN_BLOCK_REBASE_RELATIVE = 1 << 4, /* Keep backing chain
|
|
|
d89b3e |
referenced using relative
|
|
|
d89b3e |
names */
|
|
|
d89b3e |
+ VIR_DOMAIN_BLOCK_REBASE_COPY_DEV = 1 << 5, /* Treat destination as block
|
|
|
d89b3e |
+ device instead of file */
|
|
|
d89b3e |
} virDomainBlockRebaseFlags;
|
|
|
d89b3e |
|
|
|
d89b3e |
int virDomainBlockRebase(virDomainPtr dom, const char *disk,
|
|
|
d89b3e |
diff --git a/src/libvirt.c b/src/libvirt.c
|
|
|
d89b3e |
index 5315881..e1c02dc 100644
|
|
|
d89b3e |
--- a/src/libvirt.c
|
|
|
d89b3e |
+++ b/src/libvirt.c
|
|
|
d89b3e |
@@ -19891,7 +19891,10 @@ virDomainBlockPull(virDomainPtr dom, const char *disk,
|
|
|
d89b3e |
* pre-create files with relative backing file names, rather than the default
|
|
|
d89b3e |
* of absolute backing file names; as a security precaution, you should
|
|
|
d89b3e |
* generally only use reuse_ext with the shallow flag and a non-raw
|
|
|
d89b3e |
- * destination file.
|
|
|
d89b3e |
+ * destination file. By default, the copy destination will be treated as
|
|
|
d89b3e |
+ * type='file', but using VIR_DOMAIN_BLOCK_REBASE_COPY_DEV treats the
|
|
|
d89b3e |
+ * destination as type='block' (affecting how virDomainGetBlockInfo() will
|
|
|
d89b3e |
+ * report allocation after pivoting).
|
|
|
d89b3e |
*
|
|
|
d89b3e |
* A copy job has two parts; in the first phase, the @bandwidth parameter
|
|
|
d89b3e |
* affects how fast the source is pulled into the destination, and the job
|
|
|
d89b3e |
@@ -19966,7 +19969,8 @@ virDomainBlockRebase(virDomainPtr dom, const char *disk,
|
|
|
d89b3e |
virCheckNonNullArgGoto(base, error);
|
|
|
d89b3e |
} else if (flags & (VIR_DOMAIN_BLOCK_REBASE_SHALLOW |
|
|
|
d89b3e |
VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
|
|
|
d89b3e |
- VIR_DOMAIN_BLOCK_REBASE_COPY_RAW)) {
|
|
|
d89b3e |
+ VIR_DOMAIN_BLOCK_REBASE_COPY_RAW |
|
|
|
d89b3e |
+ VIR_DOMAIN_BLOCK_REBASE_COPY_DEV)) {
|
|
|
d89b3e |
virReportInvalidArg(flags,
|
|
|
d89b3e |
_("use of flags in %s requires a copy job"),
|
|
|
d89b3e |
__FUNCTION__);
|
|
|
d89b3e |
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
|
|
|
d89b3e |
index 162e039..c25c5ac 100644
|
|
|
d89b3e |
--- a/src/qemu/qemu_driver.c
|
|
|
d89b3e |
+++ b/src/qemu/qemu_driver.c
|
|
|
d89b3e |
@@ -15781,7 +15781,8 @@ qemuDomainBlockCopy(virDomainObjPtr vm,
|
|
|
d89b3e |
|
|
|
d89b3e |
/* Preliminaries: find the disk we are editing, sanity checks */
|
|
|
d89b3e |
virCheckFlags(VIR_DOMAIN_BLOCK_REBASE_SHALLOW |
|
|
|
d89b3e |
- VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT, -1);
|
|
|
d89b3e |
+ VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
|
|
|
d89b3e |
+ VIR_DOMAIN_BLOCK_REBASE_COPY_DEV, -1);
|
|
|
d89b3e |
|
|
|
d89b3e |
priv = vm->privateData;
|
|
|
d89b3e |
cfg = virQEMUDriverGetConfig(driver);
|
|
|
d89b3e |
@@ -15842,25 +15843,34 @@ qemuDomainBlockCopy(virDomainObjPtr vm,
|
|
|
d89b3e |
virReportSystemError(errno, _("unable to stat for disk %s: %s"),
|
|
|
d89b3e |
disk->dst, dest);
|
|
|
d89b3e |
goto endjob;
|
|
|
d89b3e |
- } else if (flags & VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT) {
|
|
|
d89b3e |
+ } else if (flags & (VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
|
|
|
d89b3e |
+ VIR_DOMAIN_BLOCK_REBASE_COPY_DEV)) {
|
|
|
d89b3e |
virReportSystemError(errno,
|
|
|
d89b3e |
_("missing destination file for disk %s: %s"),
|
|
|
d89b3e |
disk->dst, dest);
|
|
|
d89b3e |
goto endjob;
|
|
|
d89b3e |
}
|
|
|
d89b3e |
- } else if (!S_ISBLK(st.st_mode) && st.st_size &&
|
|
|
d89b3e |
- !(flags & VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT)) {
|
|
|
d89b3e |
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
d89b3e |
- _("external destination file for disk %s already "
|
|
|
d89b3e |
- "exists and is not a block device: %s"),
|
|
|
d89b3e |
- disk->dst, dest);
|
|
|
d89b3e |
- goto endjob;
|
|
|
d89b3e |
+ } else if (!S_ISBLK(st.st_mode)) {
|
|
|
d89b3e |
+ if (st.st_size && !(flags & VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT)) {
|
|
|
d89b3e |
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
d89b3e |
+ _("external destination file for disk %s already "
|
|
|
d89b3e |
+ "exists and is not a block device: %s"),
|
|
|
d89b3e |
+ disk->dst, dest);
|
|
|
d89b3e |
+ goto endjob;
|
|
|
d89b3e |
+ }
|
|
|
d89b3e |
+ if (flags & VIR_DOMAIN_BLOCK_REBASE_COPY_DEV) {
|
|
|
d89b3e |
+ virReportError(VIR_ERR_INVALID_ARG,
|
|
|
d89b3e |
+ _("blockdev flag requested for disk %s, but file "
|
|
|
d89b3e |
+ "'%s' is not a block device"), disk->dst, dest);
|
|
|
d89b3e |
+ goto endjob;
|
|
|
d89b3e |
+ }
|
|
|
d89b3e |
}
|
|
|
d89b3e |
|
|
|
d89b3e |
if (VIR_ALLOC(mirror) < 0)
|
|
|
d89b3e |
goto endjob;
|
|
|
d89b3e |
/* XXX Allow non-file mirror destinations */
|
|
|
d89b3e |
- mirror->type = VIR_STORAGE_TYPE_FILE;
|
|
|
d89b3e |
+ mirror->type = flags & VIR_DOMAIN_BLOCK_REBASE_COPY_DEV ?
|
|
|
d89b3e |
+ VIR_STORAGE_TYPE_BLOCK : VIR_STORAGE_TYPE_FILE;
|
|
|
d89b3e |
|
|
|
d89b3e |
if (format) {
|
|
|
d89b3e |
if ((mirror->format = virStorageFileFormatTypeFromString(format)) <= 0) {
|
|
|
d89b3e |
@@ -15954,7 +15964,8 @@ qemuDomainBlockRebase(virDomainPtr dom, const char *path, const char *base,
|
|
|
d89b3e |
VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
|
|
|
d89b3e |
VIR_DOMAIN_BLOCK_REBASE_COPY |
|
|
|
d89b3e |
VIR_DOMAIN_BLOCK_REBASE_COPY_RAW |
|
|
|
d89b3e |
- VIR_DOMAIN_BLOCK_REBASE_RELATIVE, -1);
|
|
|
d89b3e |
+ VIR_DOMAIN_BLOCK_REBASE_RELATIVE |
|
|
|
d89b3e |
+ VIR_DOMAIN_BLOCK_REBASE_COPY_DEV, -1);
|
|
|
d89b3e |
|
|
|
d89b3e |
if (!(vm = qemuDomObjFromDomain(dom)))
|
|
|
d89b3e |
return -1;
|
|
|
d89b3e |
@@ -15982,7 +15993,8 @@ qemuDomainBlockRebase(virDomainPtr dom, const char *path, const char *base,
|
|
|
d89b3e |
}
|
|
|
d89b3e |
|
|
|
d89b3e |
flags &= (VIR_DOMAIN_BLOCK_REBASE_SHALLOW |
|
|
|
d89b3e |
- VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT);
|
|
|
d89b3e |
+ VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
|
|
|
d89b3e |
+ VIR_DOMAIN_BLOCK_REBASE_COPY_DEV);
|
|
|
d89b3e |
ret = qemuDomainBlockCopy(vm, dom->conn, path, base, format,
|
|
|
d89b3e |
bandwidth, flags);
|
|
|
d89b3e |
vm = NULL;
|
|
|
d89b3e |
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml b/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml
|
|
|
d89b3e |
index 46f2a3e..7495a45 100644
|
|
|
d89b3e |
--- a/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml
|
|
|
d89b3e |
+++ b/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml
|
|
|
d89b3e |
@@ -17,8 +17,8 @@
|
|
|
d89b3e |
<disk type='block' device='disk'>
|
|
|
d89b3e |
<source dev='/dev/HostVG/QEMUGuest1'/>
|
|
|
d89b3e |
<backingStore/>
|
|
|
d89b3e |
- <mirror type='file' file='/dev/HostVG/QEMUGuest1Copy' job='copy' ready='yes'>
|
|
|
d89b3e |
- <source file='/dev/HostVG/QEMUGuest1Copy'/>
|
|
|
d89b3e |
+ <mirror type='block' job='copy' ready='yes'>
|
|
|
d89b3e |
+ <source dev='/dev/HostVG/QEMUGuest1Copy'/>
|
|
|
d89b3e |
</mirror>
|
|
|
d89b3e |
<target dev='hda' bus='ide'/>
|
|
|
d89b3e |
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
|
|
|
d89b3e |
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
|
|
|
d89b3e |
index 28f5319..8f79b55 100644
|
|
|
d89b3e |
--- a/tools/virsh-domain.c
|
|
|
d89b3e |
+++ b/tools/virsh-domain.c
|
|
|
d89b3e |
@@ -1550,6 +1550,8 @@ blockJobImpl(vshControl *ctl, const vshCmd *cmd,
|
|
|
d89b3e |
flags |= VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT;
|
|
|
d89b3e |
if (vshCommandOptBool(cmd, "raw"))
|
|
|
d89b3e |
flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_RAW;
|
|
|
d89b3e |
+ if (vshCommandOptBool(cmd, "blockdev"))
|
|
|
d89b3e |
+ flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_DEV;
|
|
|
d89b3e |
if (vshCommandOptStringReq(ctl, cmd, "dest", &base) < 0)
|
|
|
d89b3e |
goto cleanup;
|
|
|
d89b3e |
ret = virDomainBlockRebase(dom, path, base, bandwidth, flags);
|
|
|
d89b3e |
@@ -1857,6 +1859,10 @@ static const vshCmdOptDef opts_block_copy[] = {
|
|
|
d89b3e |
.type = VSH_OT_BOOL,
|
|
|
d89b3e |
.help = N_("use raw destination file")
|
|
|
d89b3e |
},
|
|
|
d89b3e |
+ {.name = "blockdev",
|
|
|
d89b3e |
+ .type = VSH_OT_BOOL,
|
|
|
d89b3e |
+ .help = N_("copy destination is block device instead of regular file")
|
|
|
d89b3e |
+ },
|
|
|
d89b3e |
{.name = "wait",
|
|
|
d89b3e |
.type = VSH_OT_BOOL,
|
|
|
d89b3e |
.help = N_("wait for job to reach mirroring phase")
|
|
|
d89b3e |
diff --git a/tools/virsh.pod b/tools/virsh.pod
|
|
|
d89b3e |
index c5b176a..46ef01d 100644
|
|
|
d89b3e |
--- a/tools/virsh.pod
|
|
|
d89b3e |
+++ b/tools/virsh.pod
|
|
|
d89b3e |
@@ -959,7 +959,8 @@ unlimited. The hypervisor can choose whether to reject the value or
|
|
|
d89b3e |
convert it to the maximum value allowed.
|
|
|
d89b3e |
|
|
|
d89b3e |
=item B<blockcopy> I<domain> I<path> I<dest> [I<bandwidth>] [I<--shallow>]
|
|
|
d89b3e |
-[I<--reuse-external>] [I<--raw>] [I<--wait> [I<--async>] [I<--verbose>]]
|
|
|
d89b3e |
+[I<--reuse-external>] [I<--raw>] [I<--blockdev>]
|
|
|
d89b3e |
+[I<--wait> [I<--async>] [I<--verbose>]]
|
|
|
d89b3e |
[{I<--pivot> | I<--finish>}] [I<--timeout> B<seconds>]
|
|
|
d89b3e |
|
|
|
d89b3e |
Copy a disk backing image chain to I<dest>. By default, this command
|
|
|
d89b3e |
@@ -977,7 +978,9 @@ The format of the destination is determined by the first match in the
|
|
|
d89b3e |
following list: if I<--raw> is specified, it will be raw; if
|
|
|
d89b3e |
I<--reuse-external> is specified, the existing destination is probed
|
|
|
d89b3e |
for a format; and in all other cases, the destination format will
|
|
|
d89b3e |
-match the source format.
|
|
|
d89b3e |
+match the source format. The destination is treated as a regular
|
|
|
d89b3e |
+file unless I<--blockdev> is used to signal that it is a block
|
|
|
d89b3e |
+device.
|
|
|
d89b3e |
|
|
|
d89b3e |
By default, the copy job runs in the background, and consists of two
|
|
|
d89b3e |
phases. Initially, the job must copy all data from the source, and
|
|
|
d89b3e |
--
|
|
|
d89b3e |
2.3.0
|
|
|
d89b3e |
|