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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list