On Mon, Apr 09, 2012 at 21:52:14 -0600, Eric Blake wrote:
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, and 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 | 94 ++++++++++++++++++++++++++++++++++++++----
src/util/virterror.c | 6 +++
4 files changed, 115 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 d44335a..753a2e0 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,7 +17930,8 @@ 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,
@@ -17925,6 +17943,15 @@ error:
* used); but since events can be missed, it is also possible to use
* virDomainBlockJobInfo() to poll if the job is still running.
*
+ * 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.
+ *
Oops, s/ABORT_SYNC/ABORT_ASYNC/
I notice that when reading the virsh patch where you have --async while I
remembered this ABORT_SYNC flag from here, which was of course wrong since the
enum contains ASYNC.
Jirka