[libvirt] [PATCHv5 15/23] blockjob: add virDomainBlockCopy

Eric Blake eblake at redhat.com
Tue Apr 17 05:06:06 UTC 2012


This new API provides additional flexibility over what can be
crammed on top of virDomainBlockRebase (namely, the ability to
specify an arbitrary destination format, for things like copying
qcow2 into qed without having to pre-create the destination), at
the expense that it cannot be backported without bumping the .so
version.  We rely on {REBASE,COPY}{_SHALLOW,_REUSE_EXT} having
the same values, for easier sharing of code.

* include/libvirt/libvirt.h.in (virDomainBlockCopy): New API.
* src/libvirt.c (virDomainBlockCopy): Implement it.
* src/libvirt_public.syms (LIBVIRT_0.9.12): Export it.
* src/driver.h (virDrvDomainBlockCopy): New driver callback.
* docs/apibuild.py (CParser.parseSignature): Add exception.
---
 docs/apibuild.py             |    1 +
 include/libvirt/libvirt.h.in |   22 +++++++-
 src/driver.h                 |    5 ++
 src/libvirt.c                |  119 +++++++++++++++++++++++++++++++++++++++++-
 src/libvirt_public.syms      |    5 ++
 5 files changed, 149 insertions(+), 3 deletions(-)

diff --git a/docs/apibuild.py b/docs/apibuild.py
index 1ac0281..bf06f3b 100755
--- a/docs/apibuild.py
+++ b/docs/apibuild.py
@@ -1650,6 +1650,7 @@ class CParser:
         "virDomainBlockJobSetSpeed"      : (False, ("bandwidth")),
         "virDomainBlockPull"             : (False, ("bandwidth")),
         "virDomainBlockRebase"           : (False, ("bandwidth")),
+        "virDomainBlockCopy"             : (False, ("bandwidth")),
         "virDomainMigrateGetMaxSpeed"    : (False, ("bandwidth")) }

     def checkLongLegacyFunction(self, name, return_type, signature):
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index ac5df95..aaee218 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -1937,7 +1937,7 @@ int virDomainUpdateDeviceFlags(virDomainPtr domain,
  * VIR_DOMAIN_BLOCK_JOB_TYPE_PULL: Block Pull (virDomainBlockPull, or
  * virDomainBlockRebase without flags), job ends on completion
  * VIR_DOMAIN_BLOCK_JOB_TYPE_COPY: Block Copy (virDomainBlockRebase with
- * flags), job exists as long as mirroring is active
+ * flags, or virDomainBlockCopy), job exists as long as mirroring is active
  */
 typedef enum {
     VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN = 0,
@@ -1995,6 +1995,8 @@ int           virDomainBlockPull(virDomainPtr dom, const char *disk,
  * Flags available for virDomainBlockRebase().
  */
 typedef enum {
+    /* Note: the first two bits intentionally match values from
+     * virDomainBlockCopyFlags.  */
     VIR_DOMAIN_BLOCK_REBASE_SHALLOW   = 1 << 0, /* Limit copy to top of source
                                                    backing chain */
     VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT = 1 << 1, /* Reuse existing external
@@ -2007,6 +2009,24 @@ int           virDomainBlockRebase(virDomainPtr dom, const char *disk,
                                    const char *base, unsigned long bandwidth,
                                    unsigned int flags);

+/**
+ * virDomainBlockCopyFlags:
+ *
+ * Flags available for virDomainBlockCopy().
+ */
+typedef enum {
+    /* Note: the first two bits intentionally match values from
+     * virDomainBlockRebaseFlags.  */
+    VIR_DOMAIN_BLOCK_COPY_SHALLOW   = 1 << 0, /* Limit copy to top of source
+                                                 backing chain */
+    VIR_DOMAIN_BLOCK_COPY_REUSE_EXT = 1 << 1, /* Reuse existing external
+                                                 file for a copy */
+} virDomainBlockCopyFlags;
+
+int virDomainBlockCopy(virDomainPtr dom, const char *disk,  const char *dest,
+                       const char *format, unsigned long bandwidth,
+                       unsigned int flags);
+

 /* Block I/O throttling support */

diff --git a/src/driver.h b/src/driver.h
index 03d249b..e10ba71 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -788,6 +788,10 @@ typedef int
     (*virDrvDomainBlockRebase)(virDomainPtr dom, const char *path,
                                const char *base, unsigned long bandwidth,
                                unsigned int flags);
+typedef int
+    (*virDrvDomainBlockCopy)(virDomainPtr dom, const char *path,
+                             const char *dest, const char *format,
+                             unsigned long bandwidth, unsigned int flags);

 typedef int
     (*virDrvSetKeepAlive)(virConnectPtr conn,
@@ -1005,6 +1009,7 @@ struct _virDriver {
     virDrvDomainBlockJobSetSpeed domainBlockJobSetSpeed;
     virDrvDomainBlockPull domainBlockPull;
     virDrvDomainBlockRebase domainBlockRebase;
+    virDrvDomainBlockCopy domainBlockCopy;
     virDrvSetKeepAlive setKeepAlive;
     virDrvConnectIsAlive isAlive;
     virDrvNodeSuspendForDuration nodeSuspendForDuration;
diff --git a/src/libvirt.c b/src/libvirt.c
index 17c7576..ad50815 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -18274,7 +18274,11 @@ error:
  * condition where the job may end before the speed can be set or queried.
  *
  * When @base is NULL and @flags is 0, this is identical to
- * virDomainBlockPull().
+ * virDomainBlockPull().  Conversely, when @flags includes
+ * VIR_DOMAIN_BLOCK_REBASE_COPY, this is shorthand for
+ * virDomainBlockCopy(dom, disk, base,
+ * flags & VIR_DOMAIN_BLOCK_COPY_RAW ? "raw" : NULL, bandwidth,
+ * flags & (VIR_DOMAIN_BLOCK_REBASE_SHALLOW|VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT)).
  *
  * Returns 0 if the operation has started, -1 on failure.
  */
@@ -18284,7 +18288,7 @@ int virDomainBlockRebase(virDomainPtr dom, const char *disk,
 {
     virConnectPtr conn;

-    VIR_DOMAIN_DEBUG(dom, "disk=%s, base=%s bandwidth=%lu, flags=%x",
+    VIR_DOMAIN_DEBUG(dom, "disk=%s, base=%s, bandwidth=%lu, flags=%x",
                      disk, NULLSTR(base), bandwidth, flags);

     virResetLastError();
@@ -18339,6 +18343,117 @@ error:


 /**
+ * virDomainBlockCopy:
+ * @dom: pointer to domain object
+ * @disk: path to the block device, or device shorthand
+ * @dest: path to the copy destination
+ * @format: format of the destination
+ * @bandwidth: (optional) specify copy bandwidth limit in Mbps
+ * @flags: bitwise-OR of virDomainBlockCopyFlags
+ *
+ * Start a block copy job, where @dest is the name of a new file to copy
+ * the chain to.  By default, the copy will pull the entire source chain
+ * into the destination file, but if @flags also contains
+ * VIR_DOMAIN_BLOCK_COPY_SHALLOW, then only the top of the source chain
+ * will be copied (the source and destination have a common backing file).
+ * By default, @dest will be created with the same file format as the
+ * source, but this can be altered by passing a non-NULL @format argument,
+ * or by using VIR_DOMAIN_BLOCK_COPY_REUSE_EXT to reuse an existing file
+ * with initial contents identical to the backing file of the source (this
+ * allows a management app to pre-create files with relative backing file
+ * names, rather than the default of absolute backing file names; it is
+ * generally used with the shallow flag, since otherwise the destination
+ * file must start with empty contents).
+ *
+ * A copy job has two parts; in the first phase, the @bandwidth parameter
+ * affects how fast the source is pulled into the destination, and the job
+ * can only be canceled by reverting to the source file; progress in this
+ * phase can be tracked via the virDomainBlockJobInfo() command, with a
+ * job type of VIR_DOMAIN_BLOCK_JOB_TYPE_COPY.  The job transitions to the
+ * second phase when the job info states cur == end, and remains alive to
+ * mirror all further changes to both source and destination.  The user
+ * must call virDomainBlockJobAbort() to end the mirroring while choosing
+ * whether to revert to source or pivot to the destination.  An event is
+ * issued when the job ends, and in the future, an event may be added when
+ * the job transitions from pulling to mirroring.
+ *
+ * Some hypervisors will restrict certain actions, such as virDomainSave()
+ * or virDomainDetachDevice(), while a copy job is active; they may
+ * also restrict a copy job to transient domains.
+ *
+ * The @disk parameter is either an unambiguous source name of the
+ * block device (the <source file='...'/> sub-element, such as
+ * "/path/to/image"), or the device target shorthand (the
+ * <target dev='...'/> sub-element, such as "xvda").  Valid names
+ * can be found by calling virDomainGetXMLDesc() and inspecting
+ * elements within //domain/devices/disk.
+ *
+ * The maximum bandwidth (in Mbps) that will be used to do the copy can be
+ * specified with the bandwidth parameter.  If set to 0, libvirt will choose a
+ * suitable default.  Some hypervisors do not support this feature and will
+ * return an error if bandwidth is not 0.
+ *
+ * When @format is NULL, this is equivalent to calling
+ * virDomainBlockRebase() with the VIR_DOMAIN_BLOCK_REBASE_COPY flag added
+ * to @flags.  Additionally, if @format is "raw", this is equivalent to
+ * calling virDomainBlockRebase() with the VIR_DOMAIN_BLOCK_REBASE_COPY
+ * and VIR_DOMAIN_BLOCK_REBASE_COPY_RAW flags added to @flags.
+ *
+ * Returns 0 if the operation has started, -1 on failure.
+ */
+int virDomainBlockCopy(virDomainPtr dom, const char *disk,
+                       const char *dest, const char *format,
+                       unsigned long bandwidth, unsigned int flags)
+{
+    virConnectPtr conn;
+
+    VIR_DOMAIN_DEBUG(dom,
+                     "disk=%s, dest=%s, format=%s, bandwidth=%lu, flags=%x",
+                     disk, dest, NULLSTR(format), bandwidth, flags);
+
+    virResetLastError();
+
+    if (!VIR_IS_CONNECTED_DOMAIN (dom)) {
+        virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+        virDispatchError(NULL);
+        return -1;
+    }
+    conn = dom->conn;
+
+    if (dom->conn->flags & VIR_CONNECT_RO) {
+        virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__);
+        goto error;
+    }
+
+    if (!disk) {
+        virLibDomainError(VIR_ERR_INVALID_ARG,
+                          _("disk is NULL"));
+        goto error;
+    }
+    if (!dest) {
+        virLibDomainError(VIR_ERR_INVALID_ARG,
+                          _("dest is NULL"));
+        goto error;
+    }
+
+    if (conn->driver->domainBlockCopy) {
+        int ret;
+        ret = conn->driver->domainBlockCopy(dom, disk, dest, format, bandwidth,
+                                            flags);
+        if (ret < 0)
+            goto error;
+        return ret;
+    }
+
+    virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+    virDispatchError(dom->conn);
+    return -1;
+}
+
+
+/**
  * virDomainOpenGraphics:
  * @dom: pointer to domain object
  * @idx: index of graphics config to open
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index 46c13fb..d152ab9 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -534,4 +534,9 @@ LIBVIRT_0.9.11 {
         virDomainPMWakeup;
 } LIBVIRT_0.9.10;

+LIBVIRT_0.9.12 {
+    global:
+        virDomainBlockCopy;
+} LIBVIRT_0.9.11;
+
 # .... define new API here using predicted next version number ....
-- 
1.7.7.6




More information about the libvir-list mailing list