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