This patch introduces a new block job, useful for live storage
migration using pre-copy streaming. If a live VM is using the
following backing chain:
base <- snap1 <- snap2
then virDomainBlockRebase(dom, disk, "/path/to/copy", 0,
VIR_DOMAIN_BLOCK_REBASE_COPY)
will create /path/to/copy with no backing file, and
virDomainBlockRebase(dom, disk, "/path/to/copy", 0,
VIR_DOMAIN_BLOCK_REBASE_COPY|VIR_DOMAIN_BLOCK_REBASE_SHALLOW)
will create /path/to/copy with snap1 as a backing file. An event
will be issued when the copy is finally in sync with ths source,
at which point the job remains alive until the user is ready to
break the mirroring and either abort to the source or pivot to
the destination with the new:
virDomainBlockJobAbort(dom, disk, VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT)
Normally, a shallow copy is created with a backing file being the
absolute path matching the backing file of the source, but a
management application 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.
(VIR_DOMAIN_BLOCK_JOB_MIRRORING): New block job event 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 | 90 ++++++++++++++++++++++++++++++++++++++----
src/util/virterror.c | 6 +++
4 files changed, 111 insertions(+), 10 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 97ad99d..9901a82 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,20 @@ 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 = 1 << 2, /* Start a copy job */
+} virDomainBlockRebaseFlags;
+
int virDomainBlockRebase(virDomainPtr dom, const char *disk,
const char *base, unsigned long bandwidth,
unsigned int flags);
@@ -3627,6 +3646,7 @@ typedef enum {
VIR_DOMAIN_BLOCK_JOB_COMPLETED = 0,
VIR_DOMAIN_BLOCK_JOB_FAILED = 1,
VIR_DOMAIN_BLOCK_JOB_CANCELED = 2,
+ VIR_DOMAIN_BLOCK_JOB_MIRRORING = 3,
#ifdef VIR_ENUM_SENTINELS
VIR_DOMAIN_BLOCK_JOB_LAST
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..9212c08 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
@@ -9424,6 +9428,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 +17132,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 +17925,22 @@ 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. In
+ * this usage, @flags must not contain VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC.
*
* Returns -1 in case of failure, 0 when successful.
*/
@@ -17949,6 +17970,12 @@ int virDomainBlockJobAbort(virDomainPtr dom, const char *disk,
_("disk is NULL"));
goto error;
}
+ if ((flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC) &&
+ (flags & VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT)) {
+ virLibDomainError(VIR_ERR_INVALID_ARG,
+ _("async and pivot flags are mutually exclusive"));
+ goto error;
+ }
if (conn->driver->domainBlockJobAbort) {
int ret;
@@ -18169,19 +18196,49 @@ 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. The
+ * destination file will have the same file format as the top of the source
+ * chain. By default, if @base exists as a non-empty regular file, the
+ * copy is rejected to avoid losing content of that file. However, if
+ * @flags additionally includes VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT, then
+ * the destination file must already exist and contain content identical
+ * to the source file (this allows a management app to pre-create files
+ * with relative backing file names, rather than the default of creating
+ * with absolute backing file names). 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. 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. An asynchronous event is sent when
+ * this phase ends, at which point the job remains alive to indicate
+ * that both source and destination files contain mirrored contents, and
+ * the user must call virDomainBlockJobAbort() to end the mirroring while
+ * choosing whether to revert to source or pivot to the destination.
+ * 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 +18252,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 +18286,22 @@ 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) {
+ virLibDomainError(VIR_ERR_INVALID_ARG,
+ _("shallow only permitted when requesting a copy"));
+ goto error;
+ } else if (flags & VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT) {
+ virLibDomainError(VIR_ERR_INVALID_ARG,
+ _("reuse_ext only permitted when requesting a
copy"));
+ 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