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 QMP equivalents of {save,load,del}vm commands are
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
it's HMP equivalent.
---
src/qemu/qemu_monitor_json.c | 204 +++++++++++++++++++++++++++++++++++-------
1 files changed, 170 insertions(+), 34 deletions(-)
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index e6903a1..09fef16 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -31,6 +31,7 @@
#include <string.h>
#include <sys/time.h>
+#include "qemu_monitor_text.h"
#include "qemu_monitor_json.h"
#include "qemu_command.h"
#include "memory.h"
@@ -675,6 +676,55 @@ static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon,
virJSONValueP
}
+static int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3)
+qemuMonitorJSONHumanCommand(qemuMonitorPtr mon,
+ char **reply_str,
+ const char *cmd_str)
+{
+ virJSONValuePtr cmd = NULL;
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr obj;
+ int ret = -1;
+
+ 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:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
+
int
qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon)
{
@@ -2389,6 +2439,34 @@ int qemuMonitorJSONSetDrivePassphrase(qemuMonitorPtr mon,
return ret;
}
+
+static int
+qemuMonitorJSONCreateSnapshotHMP(qemuMonitorPtr mon, const char *name)
+{
+ char *cmd;
+ char *reply = NULL;
+ int ret = -1;
+
+ if (!(cmd = qemuMonitorTextCreateSnapshotMakeCommand(name)))
+ return -1;
+
+ if (qemuMonitorJSONHumanCommand(mon, &reply, cmd) < 0) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("failed to take snapshot using HMP command"));
+ goto cleanup;
+ }
+
+ if (qemuMonitorTextCreateSnapshotCheckReply(reply) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(cmd);
+ VIR_FREE(reply);
+ return ret;
+}
+
int qemuMonitorJSONCreateSnapshot(qemuMonitorPtr mon, const char *name)
{
int ret;
@@ -2401,16 +2479,51 @@ 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;
}
+
+static int
+qemuMonitorJSONLoadSnapshotHMP(qemuMonitorPtr mon, const char *name)
+{
+ char *cmd;
+ char *reply = NULL;
+ int ret = -1;
+
+ if (!(cmd = qemuMonitorTextLoadSnapshotMakeCommand(name)))
+ return -1;
+
+ if (qemuMonitorJSONHumanCommand(mon, &reply, cmd) < 0) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("failed to load snapshot using HMP command"));
+ goto cleanup;
+ }
+
+ if (qemuMonitorTextLoadSnapshotCheckReply(reply, name) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(cmd);
+ VIR_FREE(reply);
+ return ret;
+}
+
int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon, const char *name)
{
int ret;
@@ -2423,16 +2536,51 @@ int qemuMonitorJSONLoadSnapshot(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("loadvm command not found, trying HMP");
+ ret = qemuMonitorJSONLoadSnapshotHMP(mon, name);
+ goto cleanup;
+ }
+
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
+
+static int
+qemuMonitorJSONDeleteSnapshotHMP(qemuMonitorPtr mon, const char *name)
+{
+ char *cmd;
+ char *reply = NULL;
+ int ret = -1;
+
+ if (!(cmd = qemuMonitorTextDeleteSnapshotMakeCommand(name)))
+ return -1;
+
+ if (qemuMonitorJSONHumanCommand(mon, &reply, cmd) < 0) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("failed to delete snapshot using HMP command"));
+ goto cleanup;
+ }
+
+ if (qemuMonitorTextDeleteSnapshotCheckReply(reply) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(cmd);
+ VIR_FREE(reply);
+ return ret;
+}
+
int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name)
{
int ret;
@@ -2445,11 +2593,18 @@ int qemuMonitorJSONDeleteSnapshot(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("loadvm command not found, trying HMP");
+ ret = qemuMonitorJSONDeleteSnapshotHMP(mon, name);
+ goto cleanup;
+ }
+
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
@@ -2464,36 +2619,17 @@ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
virJSONValuePtr reply = NULL;
int ret = -1;
- if (!hmp) {
- cmd = virJSONValueFromString(cmd_str);
+ if (hmp) {
+ return qemuMonitorJSONHumanCommand(mon, reply_str, 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