qemu driver in libvirt gained support for creating domain snapshots
almost a year ago in libvirt 0.8.0. Since then we enabled QMP support
for qemu >= 0.13.0 but a QMP equivalent of savevm command is not
implemented in current qemu (0.14.0) so the domain snapshot support is
not very useful.
This patch detects when the appropriate QMP command is not implemented
and tries to use human-monitor-command (aka HMP passthrough) to run
savevm HMP command.
---
src/qemu/qemu_monitor_json.c | 144 +++++++++++++++++++++++++++++++++--------
1 files changed, 116 insertions(+), 28 deletions(-)
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index e6903a1..6dd7980 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -675,6 +675,65 @@ static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon,
virJSONValueP
}
+static int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_FMT_PRINTF(3, 4)
+qemuMonitorJSONHumanCommand(qemuMonitorPtr mon,
+ char **reply_str,
+ const char *fmt, ...)
+{
+ virJSONValuePtr cmd = NULL;
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr obj;
+ char *cmd_str = NULL;
+ va_list ap;
+ int ret = -1;
+
+ va_start(ap, fmt);
+ if (virVasprintf(&cmd_str, fmt, ap) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ cmd = qemuMonitorJSONMakeCommand("human-monitor-command",
+ "s:command-line", cmd_str,
+ NULL);
+
+ if (!cmd || qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
+ goto cleanup;
+
+ if (qemuMonitorJSONCheckError(cmd, reply))
+ goto cleanup;
+
+ if (!(obj = virJSONValueObjectGet(reply, "return"))) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("human monitor command was missing return data"));
+ goto cleanup;
+ }
+
+ if (reply_str) {
+ const char *data;
+
+ if ((data = virJSONValueGetString(obj)))
+ *reply_str = strdup(data);
+ else
+ *reply_str = strdup("");
+
+ if (!*reply_str) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+
+cleanup:
+ va_end(ap);
+ VIR_FREE(cmd_str);
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
+
int
qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon)
{
@@ -2389,6 +2448,47 @@ int qemuMonitorJSONSetDrivePassphrase(qemuMonitorPtr mon,
return ret;
}
+
+static int
+qemuMonitorJSONCreateSnapshotHMP(qemuMonitorPtr mon, const char *name)
+{
+ char *reply = NULL;
+ int ret = -1;
+
+ if (qemuMonitorJSONHumanCommand(mon, &reply, "savevm \"%s\"",
name) < 0) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("failed to take snapshot using HMP command"));
+ goto cleanup;
+ }
+
+ if (strstr(reply, "Error while creating snapshot")) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("failed to take snapshot: %s"), reply);
+ goto cleanup;
+ }
+ else if (strstr(reply, "No block device can accept snapshots")) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("this domain does not have a device to"
+ " take snapshots"));
+ goto cleanup;
+ }
+ else if (strstr(reply, "Could not open VM state file")) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", reply);
+ goto cleanup;
+ }
+ else if (strstr(reply, "Error")
+ && strstr(reply, "while writing VM")) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", reply);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(reply);
+ return ret;
+}
+
int qemuMonitorJSONCreateSnapshot(qemuMonitorPtr mon, const char *name)
{
int ret;
@@ -2401,11 +2501,18 @@ int qemuMonitorJSONCreateSnapshot(qemuMonitorPtr mon, const char
*name)
if (!cmd)
return -1;
- ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+ if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
+ goto cleanup;
- if (ret == 0)
- ret = qemuMonitorJSONCheckError(cmd, reply);
+ if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
+ VIR_DEBUG0("savevm command not found, trying HMP");
+ ret = qemuMonitorJSONCreateSnapshotHMP(mon, name);
+ goto cleanup;
+ }
+
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
@@ -2464,36 +2571,17 @@ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
virJSONValuePtr reply = NULL;
int ret = -1;
- if (!hmp) {
- cmd = virJSONValueFromString(cmd_str);
+ if (hmp) {
+ return qemuMonitorJSONHumanCommand(mon, reply_str, "%s", cmd_str);
} else {
- cmd = qemuMonitorJSONMakeCommand("human-monitor-command",
- "s:command-line", cmd_str,
- NULL);
- }
-
- if (!cmd)
- return -1;
+ if (!(cmd = virJSONValueFromString(cmd_str)))
+ goto cleanup;
- if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
- goto cleanup;
+ if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
+ goto cleanup;
- if (!hmp) {
if (!(*reply_str = virJSONValueToString(reply)))
goto cleanup;
- } else if (qemuMonitorJSONCheckError(cmd, reply)) {
- goto cleanup;
- } else {
- const char *data;
- if (!(data = virJSONValueObjectGetString(reply, "return"))) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("human monitor command was missing return
data"));
- goto cleanup;
- }
- if (!(*reply_str = strdup(data))) {
- virReportOOMError();
- goto cleanup;
- }
}
ret = 0;
--
1.7.4.1