Upstream qemu 1.3 is adding two new monitor commands, 'drive-mirror'
and 'block-job-complete'[1], which can drive live block copy and
storage migration. Additionally, RHEL 6.3 had backported an earlier
version of most of the same functionality, but under the names
'__com.redhat_drive-mirror' and '__com.redhat_drive-reopen' and with
slightly different JSON arguments, which we can support without too
much extra effort.
The libvirt API virDomainBlockRebase as already committed for 0.9.12
is flexible enough to expose the basics of block copy, but some
additional features in the 'drive-mirror' qemu command, such as
setting granularity or using a persistent bitmap, may later require
a new libvirt API virDomainBlockCopy.
The use of two capability bits allows the bulk of libvirt code to
handle both RHEL 6.3 and upstream qemu 1.3 without caring about the
difference in monitor commands used; one bit that says whether the
commands are present, and another saying whether the RHEL 6.3
limitations are in effect. The choice of XML names for the
capability bits is essential for interoperability with the RHEL 6.3
version of libvirt.
[TODO - link to Paolo's v2 post in September, once he posts it]
[
1]https://lists.gnu.org/archive/html/qemu-devel/2012-07/msg03082.html
* src/qemu/qemu_capabilities.h (QEMU_CAPS_DRIVE_MIRROR)
(QEMU_CAPS_DRIVE_REOPEN): New bits.
* src/qemu/qemu_capabilities.c (qemuCaps): Name them.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONCheckCommands): Set
them.
(qemuMonitorJSONDriveMirror, qemuMonitorDrivePivot): New functions.
* src/qemu/qemu_monitor_json.h (qemuMonitorJSONDriveMirror)
(qemuMonitorDrivePivot): Declare them.
* src/qemu/qemu_monitor.c (qemuMonitorDriveMirror)
(qemuMonitorDrivePivot): New passthroughs.
* src/qemu/qemu_monitor.h (qemuMonitorDriveMirror)
(qemuMonitorDrivePivot): Declare them.
---
src/qemu/qemu_capabilities.c | 2 ++
src/qemu/qemu_capabilities.h | 2 ++
src/qemu/qemu_monitor.c | 52 ++++++++++++++++++++++++++++
src/qemu/qemu_monitor.h | 14 ++++++++
src/qemu/qemu_monitor_json.c | 82 ++++++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_monitor_json.h | 21 ++++++++++--
6 files changed, 171 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 43645bf..fcf1e47 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -177,6 +177,8 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST,
"disable-s4", /* 105 */
"usb-redir.filter",
+ "drive-mirror",
+ "drive-reopen",
);
struct _qemuCaps {
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index c1519ed..f04451f 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -142,6 +142,8 @@ enum qemuCapsFlags {
QEMU_CAPS_DISABLE_S3 = 104, /* S3 BIOS Advertisement on/off */
QEMU_CAPS_DISABLE_S4 = 105, /* S4 BIOS Advertisement on/off */
QEMU_CAPS_USB_REDIR_FILTER = 106, /* usb-redir.filter */
+ QEMU_CAPS_DRIVE_MIRROR = 107, /* drive-mirror monitor command */
+ QEMU_CAPS_DRIVE_REOPEN = 108, /* drive-reopen instead of block-job-complete */
QEMU_CAPS_LAST, /* this must always be the last item */
};
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index f8d717f..b7730fd 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -2749,6 +2749,39 @@ qemuMonitorDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr
actions,
return ret;
}
+/* Start a drive-mirror block job. bandwidth is in MB/sec. */
+int
+qemuMonitorDriveMirror(qemuMonitorPtr mon,
+ const char *device, const char *file,
+ const char *format, unsigned long bandwidth,
+ bool reopen, unsigned int flags)
+{
+ int ret = -1;
+ unsigned long long speed;
+
+ VIR_DEBUG("mon=%p, device=%s, file=%s, format=%s, bandwidth=%ld, "
+ "reopen=%d, flags=%x",
+ mon, device, file, NULLSTR(format), bandwidth, reopen, flags);
+
+ /* Convert bandwidth MiB to bytes */
+ speed = bandwidth;
+ if (speed > ULLONG_MAX / 1024 / 1024) {
+ virReportError(VIR_ERR_OVERFLOW,
+ _("bandwidth must be less than %llu"),
+ ULLONG_MAX / 1024 / 1024);
+ return -1;
+ }
+ speed = bandwidth * 1024ULL * 1024ULL;
+
+ if (mon->json)
+ ret = qemuMonitorJSONDriveMirror(mon, device, file, format, speed,
+ reopen, flags);
+ else
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("drive-mirror requires JSON monitor"));
+ return ret;
+}
+
/* Use the transaction QMP command to run atomic snapshot commands. */
int
qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr actions)
@@ -2765,6 +2798,25 @@ qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr
actions)
return ret;
}
+/* Use the block-job-complete or drive-reopen monitor command to pivot
+ * a block copy job. */
+int
+qemuMonitorDrivePivot(qemuMonitorPtr mon, const char *device,
+ const char *file, const char *format, bool reopen)
+{
+ int ret = -1;
+
+ VIR_DEBUG("mon=%p, device=%s, file=%s, format=%s, reopen=%d",
+ mon, device, file, NULLSTR(format), reopen);
+
+ if (mon->json)
+ ret = qemuMonitorJSONDrivePivot(mon, device, file, format, reopen);
+ else
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("drive pivot requires JSON monitor"));
+ return ret;
+}
+
int qemuMonitorArbitraryCommand(qemuMonitorPtr mon,
const char *cmd,
char **reply,
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index d44e93c..e37dac8 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -530,6 +530,20 @@ int qemuMonitorDiskSnapshot(qemuMonitorPtr mon,
bool reuse);
int qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr actions)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+int qemuMonitorDriveMirror(qemuMonitorPtr mon,
+ const char *device,
+ const char *file,
+ const char *format,
+ unsigned long bandwidth,
+ bool reopen,
+ unsigned int flags)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+int qemuMonitorDrivePivot(qemuMonitorPtr mon,
+ const char *device,
+ const char *file,
+ const char *format,
+ bool reopen)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
int qemuMonitorArbitraryCommand(qemuMonitorPtr mon,
const char *cmd,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index ed18a64..a223c08 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -1012,6 +1012,10 @@ qemuMonitorJSONCheckCommands(qemuMonitorPtr mon,
qemuCapsSet(caps, QEMU_CAPS_BLOCKJOB_ASYNC);
else if (STREQ(name, "dump-guest-memory"))
qemuCapsSet(caps, QEMU_CAPS_DUMP_GUEST_MEMORY);
+ else if (strstr(name, "drive-mirror"))
+ qemuCapsSet(caps, QEMU_CAPS_DRIVE_MIRROR);
+ else if (STREQ(name, "__com.redhat_drive-reopen"))
+ qemuCapsSet(caps, QEMU_CAPS_DRIVE_REOPEN);
}
ret = 0;
@@ -3304,6 +3308,52 @@ cleanup:
return ret;
}
+/* speed is in bytes/sec */
+int
+qemuMonitorJSONDriveMirror(qemuMonitorPtr mon,
+ const char *device, const char *file,
+ const char *format, unsigned long long speed,
+ bool reopen, unsigned int flags)
+{
+ int ret = -1;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+ bool shallow = (flags & VIR_DOMAIN_BLOCK_REBASE_SHALLOW) != 0;
+ bool reuse = (flags & VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT) != 0;
+
+ if (reopen)
+ cmd = qemuMonitorJSONMakeCommand("__com.redhat_drive-mirror",
+ "s:device", device,
+ "s:target", file,
+ "U:speed", speed,
+ "b:full", !shallow,
+ "s:mode",
+ reuse ? "existing" :
"absolute-paths",
+ format ? "s:format" : NULL, format,
+ NULL);
+ else
+ cmd = qemuMonitorJSONMakeCommand("drive-mirror",
+ "s:device", device,
+ "s:target", file,
+ "U:speed", speed,
+ "s:sync", shallow ? "top" :
"full",
+ "s:mode",
+ reuse ? "existing" :
"absolute-paths",
+ format ? "s:format" : NULL, format,
+ NULL);
+ if (!cmd)
+ return -1;
+
+ if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
+ goto cleanup;
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+
+cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
int
qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions)
{
@@ -3332,6 +3382,38 @@ cleanup:
return ret;
}
+int
+qemuMonitorJSONDrivePivot(qemuMonitorPtr mon, const char *device,
+ const char *file, const char *format, bool reopen)
+{
+ int ret;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+
+ if (reopen)
+ cmd = qemuMonitorJSONMakeCommand("__com.redhat_drive-reopen",
+ "s:device", device,
+ "s:new-image-file", file,
+ format ? "s:format" : NULL, format,
+ NULL);
+ else
+ cmd = qemuMonitorJSONMakeCommand("block-job-complete",
+ "s:device", device,
+ NULL);
+ if (!cmd)
+ return -1;
+
+ if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
+ goto cleanup;
+
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+
+cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
const char *cmd_str,
char **reply_str,
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index d092b88..bdcf819 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -237,8 +237,25 @@ int qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon,
const char *device,
const char *file,
const char *format,
- bool reuse);
-int qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions);
+ bool reuse)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3)
+ ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5);
+int qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+int qemuMonitorJSONDriveMirror(qemuMonitorPtr mon,
+ const char *device,
+ const char *file,
+ const char *format,
+ unsigned long long speed,
+ bool reopen,
+ unsigned int flags)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+int qemuMonitorJSONDrivePivot(qemuMonitorPtr mon,
+ const char *device,
+ const char *file,
+ const char *format,
+ bool reopen)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
const char *cmd_str,
--
1.7.11.4