mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-23 06:05:27 +00:00
snapshot: add support for qemu transaction command
QEmu 1.1 is adding a 'transaction' command to the JSON monitor. Each element of a transaction corresponds to a top-level command, with the additional guarantee that the transaction flushes all pending I/O, then guarantees that all actions will be successful as a group or that failure will roll back the state to what it was before the monitor command. The difference between a top-level command: { "execute": "blockdev-snapshot-sync", "arguments": { "device": "virtio0", ... } } and a transaction: { "execute": "transaction", "arguments": { "actions": [ { "type": "blockdev-snapshot-sync", "data": { "device": "virtio0", ... } } ] } } is just a couple of changed key names and nesting the shorter command inside a JSON array to the longer command. This patch just adds the framework; the next patch will actually use a transaction. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONMakeCommand): Move guts... (qemuMonitorJSONMakeCommandRaw): ...into new helper. Add support for array element. (qemuMonitorJSONTransaction): New command. (qemuMonitorJSONDiskSnapshot): Support use in a transaction. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskSnapshot): Add argument. (qemuMonitorJSONTransaction): New declaration. * src/qemu/qemu_monitor.h (qemuMonitorTransaction): Likewise. (qemuMonitorDiskSnapshot): Add argument. * src/qemu/qemu_monitor.c (qemuMonitorTransaction): New wrapper. (qemuMonitorDiskSnapshot): Pass argument on. * src/qemu/qemu_driver.c (qemuDomainSnapshotCreateSingleDiskActive): Update caller.
This commit is contained in:
parent
4c4cc1b96d
commit
64d5e815b7
@ -9883,7 +9883,7 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver,
|
|||||||
origdriver = NULL;
|
origdriver = NULL;
|
||||||
|
|
||||||
/* create the actual snapshot */
|
/* create the actual snapshot */
|
||||||
ret = qemuMonitorDiskSnapshot(priv->mon, device, source);
|
ret = qemuMonitorDiskSnapshot(priv->mon, NULL, device, source);
|
||||||
virDomainAuditDisk(vm, disk->src, source, "snapshot", ret >= 0);
|
virDomainAuditDisk(vm, disk->src, source, "snapshot", ret >= 0);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
@ -2656,12 +2656,13 @@ int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name)
|
|||||||
* device into a read-only backing file of a new qcow2 image located
|
* device into a read-only backing file of a new qcow2 image located
|
||||||
* at file. */
|
* at file. */
|
||||||
int
|
int
|
||||||
qemuMonitorDiskSnapshot(qemuMonitorPtr mon, const char *device,
|
qemuMonitorDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions,
|
||||||
const char *file)
|
const char *device, const char *file)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
VIR_DEBUG("mon=%p, device=%s, file=%s", mon, device, file);
|
VIR_DEBUG("mon=%p, actions=%p, device=%s, file=%s",
|
||||||
|
mon, actions, device, file);
|
||||||
|
|
||||||
if (!mon) {
|
if (!mon) {
|
||||||
qemuReportError(VIR_ERR_INVALID_ARG, "%s",
|
qemuReportError(VIR_ERR_INVALID_ARG, "%s",
|
||||||
@ -2669,10 +2670,32 @@ qemuMonitorDiskSnapshot(qemuMonitorPtr mon, const char *device,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mon->json)
|
if (mon->json) {
|
||||||
ret = qemuMonitorJSONDiskSnapshot(mon, device, file);
|
ret = qemuMonitorJSONDiskSnapshot(mon, actions, device, file);
|
||||||
else
|
} else {
|
||||||
|
if (actions) {
|
||||||
|
qemuReportError(VIR_ERR_INVALID_ARG, "%s",
|
||||||
|
_("actions not supported with text monitor"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
ret = qemuMonitorTextDiskSnapshot(mon, device, file);
|
ret = qemuMonitorTextDiskSnapshot(mon, device, file);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use the transaction QMP command to run atomic snapshot commands. */
|
||||||
|
int
|
||||||
|
qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr actions)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
VIR_DEBUG("mon=%p, actions=%p", mon, actions);
|
||||||
|
|
||||||
|
if (mon->json)
|
||||||
|
ret = qemuMonitorJSONTransaction(mon, actions);
|
||||||
|
else
|
||||||
|
qemuReportError(VIR_ERR_INVALID_ARG, "%s",
|
||||||
|
_("transaction requires JSON monitor"));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
# include "qemu_conf.h"
|
# include "qemu_conf.h"
|
||||||
# include "bitmap.h"
|
# include "bitmap.h"
|
||||||
# include "virhash.h"
|
# include "virhash.h"
|
||||||
|
# include "json.h"
|
||||||
|
|
||||||
typedef struct _qemuMonitor qemuMonitor;
|
typedef struct _qemuMonitor qemuMonitor;
|
||||||
typedef qemuMonitor *qemuMonitorPtr;
|
typedef qemuMonitor *qemuMonitorPtr;
|
||||||
@ -502,8 +503,11 @@ int qemuMonitorLoadSnapshot(qemuMonitorPtr mon, const char *name);
|
|||||||
int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name);
|
int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name);
|
||||||
|
|
||||||
int qemuMonitorDiskSnapshot(qemuMonitorPtr mon,
|
int qemuMonitorDiskSnapshot(qemuMonitorPtr mon,
|
||||||
|
virJSONValuePtr actions,
|
||||||
const char *device,
|
const char *device,
|
||||||
const char *file);
|
const char *file);
|
||||||
|
int qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr actions)
|
||||||
|
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
|
||||||
|
|
||||||
int qemuMonitorArbitraryCommand(qemuMonitorPtr mon,
|
int qemuMonitorArbitraryCommand(qemuMonitorPtr mon,
|
||||||
const char *cmd,
|
const char *cmd,
|
||||||
|
@ -375,9 +375,10 @@ qemuMonitorJSONHasError(virJSONValuePtr reply,
|
|||||||
return STREQ(klass, thisklass);
|
return STREQ(klass, thisklass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Top-level commands and nested transaction list elements share a
|
||||||
|
* common structure for everything except the dictionary names. */
|
||||||
static virJSONValuePtr ATTRIBUTE_SENTINEL
|
static virJSONValuePtr ATTRIBUTE_SENTINEL
|
||||||
qemuMonitorJSONMakeCommand(const char *cmdname,
|
qemuMonitorJSONMakeCommandRaw(bool wrap, const char *cmdname, ...)
|
||||||
...)
|
|
||||||
{
|
{
|
||||||
virJSONValuePtr obj;
|
virJSONValuePtr obj;
|
||||||
virJSONValuePtr jargs = NULL;
|
virJSONValuePtr jargs = NULL;
|
||||||
@ -389,7 +390,8 @@ qemuMonitorJSONMakeCommand(const char *cmdname,
|
|||||||
if (!(obj = virJSONValueNewObject()))
|
if (!(obj = virJSONValueNewObject()))
|
||||||
goto no_memory;
|
goto no_memory;
|
||||||
|
|
||||||
if (virJSONValueObjectAppendString(obj, "execute", cmdname) < 0)
|
if (virJSONValueObjectAppendString(obj, wrap ? "type" : "execute",
|
||||||
|
cmdname) < 0)
|
||||||
goto no_memory;
|
goto no_memory;
|
||||||
|
|
||||||
while ((key = va_arg(args, char *)) != NULL) {
|
while ((key = va_arg(args, char *)) != NULL) {
|
||||||
@ -411,8 +413,7 @@ qemuMonitorJSONMakeCommand(const char *cmdname,
|
|||||||
!(jargs = virJSONValueNewObject()))
|
!(jargs = virJSONValueNewObject()))
|
||||||
goto no_memory;
|
goto no_memory;
|
||||||
|
|
||||||
/* This doesn't supports maps/arrays. This hasn't
|
/* This doesn't support maps, but no command uses those. */
|
||||||
* proved to be a problem..... yet :-) */
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 's': {
|
case 's': {
|
||||||
char *val = va_arg(args, char *);
|
char *val = va_arg(args, char *);
|
||||||
@ -450,6 +451,10 @@ qemuMonitorJSONMakeCommand(const char *cmdname,
|
|||||||
case 'n': {
|
case 'n': {
|
||||||
ret = virJSONValueObjectAppendNull(jargs, key);
|
ret = virJSONValueObjectAppendNull(jargs, key);
|
||||||
} break;
|
} break;
|
||||||
|
case 'a': {
|
||||||
|
virJSONValuePtr val = va_arg(args, virJSONValuePtr);
|
||||||
|
ret = virJSONValueObjectAppend(jargs, key, val);
|
||||||
|
} break;
|
||||||
default:
|
default:
|
||||||
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
_("unsupported data type '%c' for arg '%s'"), type, key - 2);
|
_("unsupported data type '%c' for arg '%s'"), type, key - 2);
|
||||||
@ -460,7 +465,7 @@ qemuMonitorJSONMakeCommand(const char *cmdname,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (jargs &&
|
if (jargs &&
|
||||||
virJSONValueObjectAppend(obj, "arguments", jargs) < 0)
|
virJSONValueObjectAppend(obj, wrap ? "data" : "arguments", jargs) < 0)
|
||||||
goto no_memory;
|
goto no_memory;
|
||||||
|
|
||||||
va_end(args);
|
va_end(args);
|
||||||
@ -476,6 +481,8 @@ error:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define qemuMonitorJSONMakeCommand(cmdname, ...) \
|
||||||
|
qemuMonitorJSONMakeCommandRaw(false, cmdname, __VA_ARGS__)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
qemuFreeKeywords(int nkeywords, char **keywords, char **values)
|
qemuFreeKeywords(int nkeywords, char **keywords, char **values)
|
||||||
@ -3097,30 +3104,69 @@ cleanup:
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, const char *device,
|
qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions,
|
||||||
const char *file)
|
const char *device, const char *file)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
virJSONValuePtr cmd;
|
virJSONValuePtr cmd;
|
||||||
virJSONValuePtr reply = NULL;
|
virJSONValuePtr reply = NULL;
|
||||||
|
|
||||||
cmd = qemuMonitorJSONMakeCommand("blockdev-snapshot-sync",
|
cmd = qemuMonitorJSONMakeCommandRaw(actions != NULL,
|
||||||
"s:device", device,
|
"blockdev-snapshot-sync",
|
||||||
"s:snapshot-file", file,
|
"s:device", device,
|
||||||
NULL);
|
"s:snapshot-file", file,
|
||||||
|
NULL);
|
||||||
if (!cmd)
|
if (!cmd)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
if (actions) {
|
||||||
|
if (virJSONValueArrayAppend(actions, cmd) < 0) {
|
||||||
|
virReportOOMError();
|
||||||
|
ret = -1;
|
||||||
|
} else {
|
||||||
|
ret = 0;
|
||||||
|
cmd = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (qemuMonitorJSONHasError(reply, "CommandNotFound") &&
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound") &&
|
||||||
qemuMonitorCheckHMP(mon, "snapshot_blkdev")) {
|
qemuMonitorCheckHMP(mon, "snapshot_blkdev")) {
|
||||||
VIR_DEBUG("blockdev-snapshot-sync command not found, trying HMP");
|
VIR_DEBUG("blockdev-snapshot-sync command not found, trying HMP");
|
||||||
ret = qemuMonitorTextDiskSnapshot(mon, device, file);
|
ret = qemuMonitorTextDiskSnapshot(mon, device, file);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
virJSONValueFree(cmd);
|
||||||
|
virJSONValueFree(reply);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note that this call frees actions regardless of whether the call
|
||||||
|
* succeeds. */
|
||||||
|
int
|
||||||
|
qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
virJSONValuePtr cmd;
|
||||||
|
virJSONValuePtr reply = NULL;
|
||||||
|
|
||||||
|
cmd = qemuMonitorJSONMakeCommand("transaction",
|
||||||
|
"a:actions", actions,
|
||||||
|
NULL);
|
||||||
|
if (!cmd) {
|
||||||
|
virJSONValueFree(actions);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
ret = qemuMonitorJSONCheckError(cmd, reply);
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
@ -226,8 +226,10 @@ int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon, const char *name);
|
|||||||
int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name);
|
int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name);
|
||||||
|
|
||||||
int qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon,
|
int qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon,
|
||||||
|
virJSONValuePtr actions,
|
||||||
const char *device,
|
const char *device,
|
||||||
const char *file);
|
const char *file);
|
||||||
|
int qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions);
|
||||||
|
|
||||||
int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
|
int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
|
||||||
const char *cmd_str,
|
const char *cmd_str,
|
||||||
|
Loading…
Reference in New Issue
Block a user