This patch introduces a new block job, useful for live storage
migration using pre-copy streaming.
Using a live VM with the backing chain:
base <- snap1 <- snap2
as the starting point, we have:
- virDomainBlockRebase(dom, disk, "/path/to/copy", 0,
VIR_DOMAIN_BLOCK_REBASE_COPY)
creates /path/to/copy with the same format as snap2, with no backing
file, so entire chain is copied and flattened
- virDomainBlockRebase(dom, disk, "/path/to/copy", 0,
VIR_DOMAIN_BLOCK_REBASE_COPY|VIR_DOMAIN_BLOCK_REBASE_COPY_RAW)
creates /path/to/copy as a raw file, so entire chain is copied and
flattened
- virDomainBlockRebase(dom, disk, "/path/to/copy", 0,
VIR_DOMAIN_BLOCK_REBASE_COPY|VIR_DOMAIN_BLOCK_REBASE_SHALLOW)
creates /path/to/copy with the same format as snap2, but with snap1 as
a backing file, so only snap2 is copied.
- virDomainBlockRebase(dom, disk, "/path/to/copy", 0,
VIR_DOMAIN_BLOCK_REBASE_COPY|VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT)
reuse existing /path/to/copy (must have empty contents, and format is
probed from the metadata), and copy the full chain
- virDomainBlockRebase(dom, disk, "/path/to/copy", 0,
VIR_DOMAIN_BLOCK_REBASE_COPY|VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT|
VIR_DOMAIN_BLOCK_REBASE_SHALLOW)
reuse existing /path/to/copy (contents must be identical to snap1,
and format is probed from the metadata), and copy only the contents
of snap2
- virDomainBlockRebase(dom, disk, "/path/to/copy", 0,
VIR_DOMAIN_BLOCK_REBASE_COPY|VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT|
VIR_DOMAIN_BLOCK_REBASE_SHALLOW|VIR_DOMAIN_BLOCK_REBASE_COPY_RAW)
reuse existing /path/to/copy (must be raw volume with contents
identical to snap1), and copy only the contents of snap2
Less useful combinations:
- virDomainBlockRebase(dom, disk, "/path/to/copy", 0,
VIR_DOMAIN_BLOCK_REBASE_COPY|VIR_DOMAIN_BLOCK_REBASE_SHALLOW|
VIR_DOMAIN_BLOCK_REBASE_COPY_RAW)
fail if source is not raw, otherwise create /path/to/copy as raw and
the single file is copied (no chain involved)
- virDomainBlockRebase(dom, disk, "/path/to/copy", 0,
VIR_DOMAIN_BLOCK_REBASE_COPY|VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT|
VIR_DOMAIN_BLOCK_REBASE_COPY_RAW)
makes little sense: the destination must be raw but have no contents,
meaning that it is an empty file, so there is nothing to reuse
The other three flags are rejected without VIR_DOMAIN_BLOCK_COPY.
It would be nice if we could issue an event when pivoting from phase 1
to phase 2, but qemu hasn't implemented that, so we would have to poll
in order to synthesize it ourselves. Meanwhile, qemu will give us a
distinct job info and completion event when we either cancel or pivot
to end the job. Pivoting is accomplished via the new:
virDomainBlockJobAbort(dom, disk, VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT)
Management applications can pre-create the copy with a relative
backing file name, and use the VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT
flag to have qemu reuse the metadata; if the management application
also copies the backing files to a new location, this can be used
to perform live storage migration of an entire backing chain.
* include/libvirt/libvirt.h.in (VIR_DOMAIN_BLOCK_JOB_TYPE_COPY):
New block job type.
(virDomainBlockJobAbortFlags, virDomainBlockRebaseFlags): New enums.
* src/libvirt.c (virDomainBlockRebase): Document the new flags,
and implement general restrictions on flag combinations.
(virDomainBlockJobAbort): Document the new flag.
(virDomainSaveFlags, virDomainSnapshotCreateXML)
(virDomainRevertToSnapshot, virDomainDetachDeviceFlags): Document
restrictions.
* include/libvirt/virterror.h (VIR_ERR_BLOCK_COPY_ACTIVE): New
error.
* src/util/virterror.c (virErrorMsg): Define it.
---
include/libvirt/libvirt.h.in | 24 ++++++++++-
include/libvirt/virterror.h | 1 +
src/libvirt.c | 95 ++++++++++++++++++++++++++++++++++++++----
src/util/virterror.c | 6 +++
4 files changed, 116 insertions(+), 10 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 97ad99d..ac5df95 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -1934,12 +1934,15 @@ int virDomainUpdateDeviceFlags(virDomainPtr domain,
/**
* virDomainBlockJobType:
*
- * VIR_DOMAIN_BLOCK_JOB_TYPE_PULL: Block Pull (virDomainBlockPull or
- * virDomainBlockRebase)
+ * 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
*/
typedef enum {
VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN = 0,
VIR_DOMAIN_BLOCK_JOB_TYPE_PULL = 1,
+ VIR_DOMAIN_BLOCK_JOB_TYPE_COPY = 2,
#ifdef VIR_ENUM_SENTINELS
VIR_DOMAIN_BLOCK_JOB_TYPE_LAST
@@ -1950,9 +1953,11 @@ typedef enum {
* virDomainBlockJobAbortFlags:
*
* VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC: Request only, do not wait for completion
+ * VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT: Pivot to mirror when ending a copy job
*/
typedef enum {
VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC = 1 << 0,
+ VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT = 1 << 1,
} virDomainBlockJobAbortFlags;
/* An iterator for monitoring block job operations */
@@ -1983,6 +1988,21 @@ int virDomainBlockJobSetSpeed(virDomainPtr dom, const char
*disk,
int virDomainBlockPull(virDomainPtr dom, const char *disk,
unsigned long bandwidth, unsigned int flags);
+
+/**
+ * virDomainBlockRebaseFlags:
+ *
+ * Flags available for virDomainBlockRebase().
+ */
+typedef enum {
+ 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
+ file for a copy */
+ VIR_DOMAIN_BLOCK_REBASE_COPY_RAW = 1 << 2, /* Make destination file raw */
+ VIR_DOMAIN_BLOCK_REBASE_COPY = 1 << 3, /* Start a copy job */
+} virDomainBlockRebaseFlags;
+
int virDomainBlockRebase(virDomainPtr dom, const char *disk,
const char *base, unsigned long bandwidth,
unsigned int flags);
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index e04d29e..070fdb5 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -249,6 +249,7 @@ typedef enum {
VIR_ERR_NO_DOMAIN_METADATA = 80, /* The metadata is not present */
VIR_ERR_MIGRATE_UNSAFE = 81, /* Migration is not safe */
VIR_ERR_OVERFLOW = 82, /* integer overflow */
+ VIR_ERR_BLOCK_COPY_ACTIVE = 83, /* action prevented by block copy job */
} virErrorNumber;
/**
diff --git a/src/libvirt.c b/src/libvirt.c
index af22232..d4b648c 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -2696,6 +2696,10 @@ error:
* A save file can be inspected or modified slightly with
* virDomainSaveImageGetXMLDesc() and virDomainSaveImageDefineXML().
*
+ * Some hypervisors may prevent this operation if there is a current
+ * block copy operation; in that case, use virDomainBlockJobAbort()
+ * to stop the block copy first.
+ *
* Returns 0 in case of success and -1 in case of failure.
*/
int
@@ -7891,6 +7895,11 @@ error:
* virDomainUndefine(). A previous definition for this domain would be
* overriden if it already exists.
*
+ * Some hypervisors may prevent this operation if there is a current
+ * block copy operation on a transient domain with the same id as the
+ * domain being defined; in that case, use virDomainBlockJobAbort() to
+ * stop the block copy first.
+ *
* Returns NULL in case of error, a pointer to the domain otherwise
*/
virDomainPtr
@@ -9424,6 +9433,10 @@ error:
* return failure if LIVE is specified but it only supports removing the
* persisted device allocation.
*
+ * Some hypervisors may prevent this operation if there is a current
+ * block copy operation on the device being detached; in that case,
+ * use virDomainBlockJobAbort() to stop the block copy first.
+ *
* Returns 0 in case of success, -1 in case of failure.
*/
int
@@ -17124,6 +17137,10 @@ virDomainSnapshotGetConnect(virDomainSnapshotPtr snapshot)
* that it is still possible to fail after disks have changed, but only
* in the much rarer cases of running out of memory or disk space).
*
+ * Some hypervisors may prevent this operation if there is a current
+ * block copy operation; in that case, use virDomainBlockJobAbort()
+ * to stop the block copy first.
+ *
* Returns an (opaque) virDomainSnapshotPtr on success, NULL on failure.
*/
virDomainSnapshotPtr
@@ -17913,13 +17930,24 @@ error:
* can be found by calling virDomainGetXMLDesc() and inspecting
* elements within //domain/devices/disk.
*
- * By default, this function performs a synchronous operation and the caller
+ * If the current block job for @disk is VIR_DOMAIN_BLOCK_JOB_TYPE_PULL, then
+ * by default, this function performs a synchronous operation and the caller
* may assume that the operation has completed when 0 is returned. However,
* BlockJob operations may take a long time to complete, and during this time
* further domain interactions may be unresponsive. To avoid this problem,
* pass VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC in the @flags argument to enable
* asynchronous behavior. Either way, when the job has been cancelled, a
* BlockJob event will be emitted, with status VIR_DOMAIN_BLOCK_JOB_CANCELLED.
+ * In this usage, @flags must not contain VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT.
+ *
+ * If the current block job for @disk is VIR_DOMAIN_BLOCK_JOB_TYPE_COPY, then
+ * the default is to abort the mirroring and revert to the source disk;
+ * adding @flags of VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT causes this call to
+ * fail with VIR_ERR_BLOCK_COPY_ACTIVE if the copy is not fully populated,
+ * otherwise it will swap the disk over to the copy to end the mirroring. An
+ * event will be issued when the job is ended, and it is possible to use
+ * VIR_DOMAIN_BLOCK_JOB_ABORT_SYNC to control whether this command waits
+ * for the completion of the job.
*
* Returns -1 in case of failure, 0 when successful.
*/
@@ -18169,19 +18197,55 @@ error:
* @disk: path to the block device, or device shorthand
* @base: path to backing file to keep, or NULL for no backing file
* @bandwidth: (optional) specify copy bandwidth limit in Mbps
- * @flags: extra flags; not used yet, so callers should always pass 0
+ * @flags: bitwise-OR of virDomainBlockRebaseFlags
*
* Populate a disk image with data from its backing image chain, and
- * setting the backing image to @base. @base must be the absolute
+ * setting the backing image to @base, or alternatively copy an entire
+ * backing chain to a new file @base.
+ *
+ * When @flags is 0, this starts a pull, where @base must be the absolute
* path of one of the backing images further up the chain, or NULL to
* convert the disk image so that it has no backing image. Once all
* data from its backing image chain has been pulled, the disk no
* longer depends on those intermediate backing images. This function
* pulls data for the entire device in the background. Progress of
- * the operation can be checked with virDomainGetBlockJobInfo() and
- * the operation can be aborted with virDomainBlockJobAbort(). When
- * finished, an asynchronous event is raised to indicate the final
- * status.
+ * the operation can be checked with virDomainGetBlockJobInfo() with a
+ * job type of VIR_DOMAIN_BLOCK_JOB_TYPE_PULL, and the operation can be
+ * aborted with virDomainBlockJobAbort(). When finished, an asynchronous
+ * event is raised to indicate the final status, and the job no longer
+ * exists.
+ *
+ * When @flags includes VIR_DOMAIN_BLOCK_REBASE_COPY, this starts a copy,
+ * where @base must be 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_REBASE_SHALLOW, then
+ * only the top of the source chain will be copied (the source and
+ * destination have a common backing file). By default, @base will be
+ * created with the same file format as the source, but this can be altered
+ * by adding VIR_DOMAIN_BLOCK_REBASE_COPY_RAW to force the copy to be raw
+ * (does not make sense with the shallow flag unless the source is also raw),
+ * or by using VIR_DOMAIN_BLOCK_REBASE_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
@@ -18195,7 +18259,8 @@ error:
* suitable default. Some hypervisors do not support this feature and will
* return an error if bandwidth is not 0.
*
- * When @base is NULL, this is identical to virDomainBlockPull().
+ * When @base is NULL and @flags is 0, this is identical to
+ * virDomainBlockPull().
*
* Returns 0 if the operation has started, -1 on failure.
*/
@@ -18228,6 +18293,20 @@ int virDomainBlockRebase(virDomainPtr dom, const char *disk,
goto error;
}
+ if (flags & VIR_DOMAIN_BLOCK_REBASE_COPY) {
+ if (!base) {
+ virLibDomainError(VIR_ERR_INVALID_ARG,
+ _("base is required when starting a copy"));
+ goto error;
+ }
+ } else if (flags & (VIR_DOMAIN_BLOCK_REBASE_SHALLOW |
+ VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
+ VIR_DOMAIN_BLOCK_REBASE_COPY_RAW)) {
+ virLibDomainError(VIR_ERR_INVALID_ARG,
+ _("use of flags requires a copy job"));
+ goto error;
+ }
+
if (conn->driver->domainBlockRebase) {
int ret;
ret = conn->driver->domainBlockRebase(dom, disk, base, bandwidth,
diff --git a/src/util/virterror.c b/src/util/virterror.c
index ff9a36f..845081e 100644
--- a/src/util/virterror.c
+++ b/src/util/virterror.c
@@ -1250,6 +1250,12 @@ virErrorMsg(virErrorNumber error, const char *info)
else
errmsg = _("numerical overflow: %s");
break;
+ case VIR_ERR_BLOCK_COPY_ACTIVE:
+ if (!info)
+ errmsg = _("block copy still active");
+ else
+ errmsg = _("block copy still active: %s");
+ break;
}
return errmsg;
}
--
1.7.7.6