render / rpms / libvirt

Forked from rpms/libvirt 9 months ago
Clone
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