libvirt/src/qemu/qemu_monitor_json.c

9228 lines
273 KiB
C
Raw Normal View History

/*
* qemu_monitor_json.c: interaction with QEMU monitor console
*
* Copyright (C) 2006-2016 Red Hat, Inc.
* Copyright (C) 2006 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <poll.h>
#include <unistd.h>
#include <sys/time.h>
#include "qemu_monitor_json.h"
#include "qemu_alias.h"
#include "qemu_capabilities.h"
2012-12-12 18:06:53 +00:00
#include "viralloc.h"
2012-12-12 17:59:27 +00:00
#include "virlog.h"
#include "driver.h"
#include "datatypes.h"
#include "virerror.h"
2012-12-12 17:53:50 +00:00
#include "virjson.h"
#include "virprobe.h"
#include "virstring.h"
#include "cpu/cpu_x86.h"
#include "virenum.h"
#include "virsocket.h"
#ifdef WITH_DTRACE_PROBES
# include "libvirt_qemu_probes.h"
#endif
#define VIR_FROM_THIS VIR_FROM_QEMU
VIR_LOG_INIT("qemu.qemu_monitor_json");
#define QOM_CPU_PATH "/machine/unattached/device[0]"
#define LINE_ENDING "\r\n"
VIR_ENUM_IMPL(qemuMonitorJob,
QEMU_MONITOR_JOB_TYPE_LAST,
"",
"commit",
"stream",
"mirror",
"backup",
"create");
VIR_ENUM_IMPL(qemuMonitorJobStatus,
QEMU_MONITOR_JOB_STATUS_LAST,
"",
"created",
"running",
"paused",
"ready",
"standby",
"waiting",
"pending",
"aborting",
"concluded",
"undefined",
"null");
static void qemuMonitorJSONHandleShutdown(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleReset(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleStop(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleResume(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleRTCChange(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleWatchdog(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleIOError(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleVNCConnect(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleVNCInitialize(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleSPICEConnect(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleSPICEInitialize(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleSPICEDisconnect(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleTrayChange(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandlePMWakeup(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandlePMSuspend(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleBlockJobCompleted(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleBlockJobCanceled(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleBlockJobReady(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleJobStatusChange(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleBalloonChange(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandlePMSuspendDisk(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleGuestCrashloaded(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleGuestPanic(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleDeviceDeleted(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleSerialChange(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleSpiceMigrated(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleMigrationStatus(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleMigrationPass(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleAcpiOstInfo(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleBlockThreshold(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleDumpCompleted(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandlePRManagerStatusChanged(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleMemoryFailure(qemuMonitor *mon, virJSONValue *data);
static void qemuMonitorJSONHandleMemoryDeviceSizeChange(qemuMonitor *mon, virJSONValue *data);
typedef struct {
const char *type;
void (*handler)(qemuMonitor *mon, virJSONValue *data);
} qemuEventHandler;
static qemuEventHandler eventHandlers[] = {
{ "ACPI_DEVICE_OST", qemuMonitorJSONHandleAcpiOstInfo, },
{ "BALLOON_CHANGE", qemuMonitorJSONHandleBalloonChange, },
{ "BLOCK_IO_ERROR", qemuMonitorJSONHandleIOError, },
{ "BLOCK_JOB_CANCELLED", qemuMonitorJSONHandleBlockJobCanceled, },
{ "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJobCompleted, },
{ "BLOCK_JOB_READY", qemuMonitorJSONHandleBlockJobReady, },
{ "BLOCK_WRITE_THRESHOLD", qemuMonitorJSONHandleBlockThreshold, },
{ "DEVICE_DELETED", qemuMonitorJSONHandleDeviceDeleted, },
{ "DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange, },
{ "DUMP_COMPLETED", qemuMonitorJSONHandleDumpCompleted, },
{ "GUEST_CRASHLOADED", qemuMonitorJSONHandleGuestCrashloaded, },
{ "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, },
{ "JOB_STATUS_CHANGE", qemuMonitorJSONHandleJobStatusChange, },
{ "MEMORY_DEVICE_SIZE_CHANGE", qemuMonitorJSONHandleMemoryDeviceSizeChange, },
{ "MEMORY_FAILURE", qemuMonitorJSONHandleMemoryFailure, },
{ "MIGRATION", qemuMonitorJSONHandleMigrationStatus, },
{ "MIGRATION_PASS", qemuMonitorJSONHandleMigrationPass, },
{ "NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged, },
{ "PR_MANAGER_STATUS_CHANGED", qemuMonitorJSONHandlePRManagerStatusChanged, },
{ "RDMA_GID_STATUS_CHANGED", qemuMonitorJSONHandleRdmaGidStatusChanged, },
{ "RESET", qemuMonitorJSONHandleReset, },
{ "RESUME", qemuMonitorJSONHandleResume, },
{ "RTC_CHANGE", qemuMonitorJSONHandleRTCChange, },
{ "SHUTDOWN", qemuMonitorJSONHandleShutdown, },
{ "SPICE_CONNECTED", qemuMonitorJSONHandleSPICEConnect, },
{ "SPICE_DISCONNECTED", qemuMonitorJSONHandleSPICEDisconnect, },
{ "SPICE_INITIALIZED", qemuMonitorJSONHandleSPICEInitialize, },
{ "SPICE_MIGRATE_COMPLETED", qemuMonitorJSONHandleSpiceMigrated, },
{ "STOP", qemuMonitorJSONHandleStop, },
{ "SUSPEND", qemuMonitorJSONHandlePMSuspend, },
{ "SUSPEND_DISK", qemuMonitorJSONHandlePMSuspendDisk, },
{ "VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect, },
{ "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, },
{ "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, },
{ "VSERPORT_CHANGE", qemuMonitorJSONHandleSerialChange, },
{ "WAKEUP", qemuMonitorJSONHandlePMWakeup, },
{ "WATCHDOG", qemuMonitorJSONHandleWatchdog, },
/* We use bsearch, so keep this list sorted. */
};
static int
qemuMonitorEventCompare(const void *key, const void *elt)
{
const char *type = key;
const qemuEventHandler *handler = elt;
return strcmp(type, handler->type);
}
static int
qemuMonitorJSONIOProcessEvent(qemuMonitor *mon,
virJSONValue *obj)
{
const char *type;
qemuEventHandler *handler;
virJSONValue *data;
char *details = NULL;
virJSONValue *timestamp;
long long seconds = -1;
unsigned int micros = 0;
VIR_DEBUG("mon=%p obj=%p", mon, obj);
type = virJSONValueObjectGetString(obj, "event");
if (!type) {
VIR_WARN("missing event type in message");
errno = EINVAL;
return -1;
}
/* Not all events have data; and event reporting is best-effort only */
if ((data = virJSONValueObjectGet(obj, "data")))
details = virJSONValueToString(data, false);
if ((timestamp = virJSONValueObjectGet(obj, "timestamp"))) {
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
ignore_value(virJSONValueObjectGetNumberLong(timestamp, "seconds",
&seconds));
ignore_value(virJSONValueObjectGetNumberUint(timestamp, "microseconds",
&micros));
}
qemuMonitorEmitEvent(mon, type, seconds, micros, details);
VIR_FREE(details);
handler = bsearch(type, eventHandlers, G_N_ELEMENTS(eventHandlers),
sizeof(eventHandlers[0]), qemuMonitorEventCompare);
if (handler) {
VIR_DEBUG("handle %s handler=%p data=%p", type,
handler->handler, data);
(handler->handler)(mon, data);
}
return 0;
}
int
qemuMonitorJSONIOProcessLine(qemuMonitor *mon,
const char *line,
qemuMonitorMessage *msg)
{
virJSONValue *obj = NULL;
int ret = -1;
VIR_DEBUG("Line [%s]", line);
if (!(obj = virJSONValueFromString(line)))
goto cleanup;
if (virJSONValueGetType(obj) != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Parsed JSON reply '%s' isn't an object"), line);
goto cleanup;
}
if (virJSONValueObjectHasKey(obj, "QMP") == 1) {
ret = 0;
} else if (virJSONValueObjectHasKey(obj, "event") == 1) {
PROBE(QEMU_MONITOR_RECV_EVENT,
"mon=%p event=%s", mon, line);
ret = qemuMonitorJSONIOProcessEvent(mon, obj);
} else if (virJSONValueObjectHasKey(obj, "error") == 1 ||
virJSONValueObjectHasKey(obj, "return") == 1) {
PROBE(QEMU_MONITOR_RECV_REPLY,
"mon=%p reply=%s", mon, line);
if (msg) {
msg->rxObject = g_steal_pointer(&obj);
msg->finished = 1;
ret = 0;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected JSON reply '%s'"), line);
}
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unknown JSON reply '%s'"), line);
}
cleanup:
virJSONValueFree(obj);
return ret;
}
int qemuMonitorJSONIOProcess(qemuMonitor *mon,
const char *data,
size_t len,
qemuMonitorMessage *msg)
{
int used = 0;
/*VIR_DEBUG("Data %d bytes [%s]", len, data);*/
while (used < len) {
char *nl = strstr(data + used, LINE_ENDING);
if (nl) {
int got = nl - (data + used);
char *line;
line = g_strndup(data + used, got);
used += got + strlen(LINE_ENDING);
line[got] = '\0'; /* kill \n */
if (qemuMonitorJSONIOProcessLine(mon, line, msg) < 0) {
VIR_FREE(line);
return -1;
}
VIR_FREE(line);
} else {
break;
}
}
#if DEBUG_IO
VIR_DEBUG("Total used %d bytes out of %zd available in buffer", used, len);
#endif
return used;
}
static int
qemuMonitorJSONCommandWithFd(qemuMonitor *mon,
virJSONValue *cmd,
int scm_fd,
virJSONValue **reply)
{
int ret = -1;
qemuMonitorMessage msg;
g_auto(virBuffer) cmdbuf = VIR_BUFFER_INITIALIZER;
*reply = NULL;
memset(&msg, 0, sizeof(msg));
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
if (virJSONValueObjectHasKey(cmd, "execute") == 1) {
g_autofree char *id = qemuMonitorNextCommandID(mon);
if (virJSONValueObjectAppendString(cmd, "id", id) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to append command 'id' string"));
goto cleanup;
}
}
if (virJSONValueToBuffer(cmd, &cmdbuf, false) < 0)
goto cleanup;
virBufferAddLit(&cmdbuf, "\r\n");
msg.txLength = virBufferUse(&cmdbuf);
msg.txBuffer = virBufferCurrentContent(&cmdbuf);
msg.txFD = scm_fd;
ret = qemuMonitorSend(mon, &msg);
if (ret == 0) {
if (!msg.rxObject) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing monitor reply object"));
ret = -1;
} else {
*reply = msg.rxObject;
}
}
cleanup:
return ret;
}
static int
qemuMonitorJSONCommand(qemuMonitor *mon,
virJSONValue *cmd,
virJSONValue **reply)
{
return qemuMonitorJSONCommandWithFd(mon, cmd, -1, reply);
}
/* Ignoring OOM in this method, since we're already reporting
* a more important error
*
* XXX see qerror.h for different klasses & fill out useful params
*/
static const char *
qemuMonitorJSONStringifyError(virJSONValue *error)
{
const char *klass = virJSONValueObjectGetString(error, "class");
const char *detail = NULL;
/* The QMP 'desc' field is usually sufficient for our generic
* error reporting needs.
*/
if (klass)
detail = virJSONValueObjectGetString(error, "desc");
if (!detail)
detail = "unknown QEMU command error";
return detail;
}
static const char *
qemuMonitorJSONCommandName(virJSONValue *cmd)
{
const char *name = virJSONValueObjectGetString(cmd, "execute");
if (name)
return name;
else
return "<unknown>";
}
static int
qemuMonitorJSONCheckErrorFull(virJSONValue *cmd,
virJSONValue *reply,
bool report)
{
if (virJSONValueObjectHasKey(reply, "error")) {
virJSONValue *error = virJSONValueObjectGet(reply, "error");
g_autofree char *cmdstr = virJSONValueToString(cmd, false);
g_autofree char *replystr = virJSONValueToString(reply, false);
/* Log the full JSON formatted command & error */
VIR_DEBUG("unable to execute QEMU command %s: %s",
NULLSTR(cmdstr), NULLSTR(replystr));
if (!report)
return -1;
/* Only send the user the command name + friendly error */
if (!error)
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unable to execute QEMU command '%s'"),
qemuMonitorJSONCommandName(cmd));
else
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unable to execute QEMU command '%s': %s"),
qemuMonitorJSONCommandName(cmd),
qemuMonitorJSONStringifyError(error));
return -1;
} else if (!virJSONValueObjectHasKey(reply, "return")) {
g_autofree char *cmdstr = virJSONValueToString(cmd, false);
g_autofree char *replystr = virJSONValueToString(reply, false);
VIR_DEBUG("Neither 'return' nor 'error' is set in the JSON reply %s: %s",
NULLSTR(cmdstr), NULLSTR(replystr));
if (!report)
return -1;
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unable to execute QEMU command '%s'"),
qemuMonitorJSONCommandName(cmd));
return -1;
}
return 0;
}
static int
qemuMonitorJSONCheckError(virJSONValue *cmd,
virJSONValue *reply)
{
return qemuMonitorJSONCheckErrorFull(cmd, reply, true);
}
static int
qemuMonitorJSONCheckReply(virJSONValue *cmd,
virJSONValue *reply,
virJSONType type)
{
virJSONValue *data;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
data = virJSONValueObjectGet(reply, "return");
if (virJSONValueGetType(data) != type) {
g_autofree char *cmdstr = virJSONValueToString(cmd, false);
g_autofree char *retstr = virJSONValueToString(data, false);
VIR_DEBUG("Unexpected return type %d (expecting %d) for command %s: %s",
virJSONValueGetType(data), type, cmdstr, retstr);
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected type returned by QEMU command '%s'"),
qemuMonitorJSONCommandName(cmd));
return -1;
}
return 0;
}
static bool
qemuMonitorJSONErrorIsClass(virJSONValue *error,
const char *klass)
{
return STREQ_NULLABLE(virJSONValueObjectGetString(error, "class"), klass);
}
static bool
qemuMonitorJSONHasError(virJSONValue *reply,
const char *klass)
{
virJSONValue *error;
if (!(error = virJSONValueObjectGet(reply, "error")))
return false;
return qemuMonitorJSONErrorIsClass(error, klass);
}
/**
* qemuMonitorJSONTransactionAdd:
* @actions: array of actions for the 'transaction' command
* @cmdname: command to add to @actions
* @...: arguments for @cmdname (see virJSONValueObjectAddVArgs for formatting)
*
* Add a new command with arguments to the existing ones. The resulting array
* is intended to be used as argument for the 'transaction' command.
*
* Returns 0 on success and -1 on error.
*/
static int
qemuMonitorJSONTransactionAdd(virJSONValue *actions,
const char *cmdname,
...)
{
g_autoptr(virJSONValue) entry = NULL;
g_autoptr(virJSONValue) data = NULL;
va_list args;
va_start(args, cmdname);
if (virJSONValueObjectCreateVArgs(&data, args) < 0) {
va_end(args);
return -1;
}
va_end(args);
if (virJSONValueObjectCreate(&entry,
"s:type", cmdname,
"A:data", &data, NULL) < 0)
return -1;
if (virJSONValueArrayAppend(actions, &entry) < 0)
return -1;
return 0;
}
/**
* qemuMonitorJSONMakeCommandInternal:
* @cmdname: QMP command name
* @arguments: a JSON object containing command arguments or NULL
*
* Create a JSON object used on the QMP monitor to call a command.
*
* Note that @arguments is consumed and cleared.
*/
static virJSONValue *
qemuMonitorJSONMakeCommandInternal(const char *cmdname,
virJSONValue **arguments)
{
virJSONValue *ret = NULL;
ignore_value(virJSONValueObjectCreate(&ret,
"s:execute", cmdname,
"A:arguments", arguments, NULL));
return ret;
}
static virJSONValue *G_GNUC_NULL_TERMINATED
qemuMonitorJSONMakeCommand(const char *cmdname,
...)
{
virJSONValue *obj = NULL;
g_autoptr(virJSONValue) jargs = NULL;
va_list args;
va_start(args, cmdname);
if (virJSONValueObjectCreateVArgs(&jargs, args) < 0)
goto cleanup;
obj = qemuMonitorJSONMakeCommandInternal(cmdname, &jargs);
cleanup:
va_end(args);
return obj;
}
static void qemuMonitorJSONHandleShutdown(qemuMonitor *mon, virJSONValue *data)
{
bool guest = false;
virTristateBool guest_initiated = VIR_TRISTATE_BOOL_ABSENT;
if (data && virJSONValueObjectGetBoolean(data, "guest", &guest) == 0)
guest_initiated = guest ? VIR_TRISTATE_BOOL_YES : VIR_TRISTATE_BOOL_NO;
qemuMonitorEmitShutdown(mon, guest_initiated);
}
static void qemuMonitorJSONHandleReset(qemuMonitor *mon, virJSONValue *data G_GNUC_UNUSED)
{
qemuMonitorEmitReset(mon);
}
static void qemuMonitorJSONHandleStop(qemuMonitor *mon, virJSONValue *data G_GNUC_UNUSED)
{
qemuMonitorEmitStop(mon);
}
static void qemuMonitorJSONHandleResume(qemuMonitor *mon, virJSONValue *data G_GNUC_UNUSED)
{
qemuMonitorEmitResume(mon);
}
static qemuMonitorEventPanicInfo *
qemuMonitorJSONGuestPanicExtractInfoHyperv(virJSONValue *data)
{
qemuMonitorEventPanicInfo *ret;
ret = g_new0(qemuMonitorEventPanicInfo, 1);
ret->type = QEMU_MONITOR_EVENT_PANIC_INFO_TYPE_HYPERV;
if (virJSONValueObjectGetNumberUlong(data, "arg1", &ret->data.hyperv.arg1) < 0 ||
virJSONValueObjectGetNumberUlong(data, "arg2", &ret->data.hyperv.arg2) < 0 ||
virJSONValueObjectGetNumberUlong(data, "arg3", &ret->data.hyperv.arg3) < 0 ||
virJSONValueObjectGetNumberUlong(data, "arg4", &ret->data.hyperv.arg4) < 0 ||
virJSONValueObjectGetNumberUlong(data, "arg5", &ret->data.hyperv.arg5) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed hyperv panic data"));
goto error;
}
return ret;
error:
qemuMonitorEventPanicInfoFree(ret);
return NULL;
}
static qemuMonitorEventPanicInfo *
qemuMonitorJSONGuestPanicExtractInfoS390(virJSONValue *data)
{
qemuMonitorEventPanicInfo *ret;
int core;
unsigned long long psw_mask, psw_addr;
const char *reason = NULL;
ret = g_new0(qemuMonitorEventPanicInfo, 1);
ret->type = QEMU_MONITOR_EVENT_PANIC_INFO_TYPE_S390;
if (virJSONValueObjectGetNumberInt(data, "core", &core) < 0 ||
virJSONValueObjectGetNumberUlong(data, "psw-mask", &psw_mask) < 0 ||
virJSONValueObjectGetNumberUlong(data, "psw-addr", &psw_addr) < 0 ||
!(reason = virJSONValueObjectGetString(data, "reason"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed s390 panic data"));
goto error;
}
ret->data.s390.core = core;
ret->data.s390.psw_mask = psw_mask;
ret->data.s390.psw_addr = psw_addr;
ret->data.s390.reason = g_strdup(reason);
return ret;
error:
qemuMonitorEventPanicInfoFree(ret);
return NULL;
}
static qemuMonitorEventPanicInfo *
qemuMonitorJSONGuestPanicExtractInfo(virJSONValue *data)
{
const char *type = virJSONValueObjectGetString(data, "type");
if (STREQ_NULLABLE(type, "hyper-v"))
return qemuMonitorJSONGuestPanicExtractInfoHyperv(data);
else if (STREQ_NULLABLE(type, "s390"))
return qemuMonitorJSONGuestPanicExtractInfoS390(data);
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown panic info type '%s'"), NULLSTR(type));
return NULL;
}
static void
qemuMonitorJSONHandleGuestPanic(qemuMonitor *mon,
virJSONValue *data)
{
virJSONValue *infojson = virJSONValueObjectGetObject(data, "info");
qemuMonitorEventPanicInfo *info = NULL;
if (infojson)
info = qemuMonitorJSONGuestPanicExtractInfo(infojson);
qemuMonitorEmitGuestPanic(mon, info);
}
static void qemuMonitorJSONHandleRTCChange(qemuMonitor *mon, virJSONValue *data)
{
long long offset = 0;
if (virJSONValueObjectGetNumberLong(data, "offset", &offset) < 0) {
VIR_WARN("missing offset in RTC change event");
offset = 0;
}
qemuMonitorEmitRTCChange(mon, offset);
}
VIR_ENUM_DECL(qemuMonitorWatchdogAction);
VIR_ENUM_IMPL(qemuMonitorWatchdogAction,
VIR_DOMAIN_EVENT_WATCHDOG_LAST,
"none", "pause", "reset", "poweroff", "shutdown", "debug", "inject-nmi",
);
static void qemuMonitorJSONHandleWatchdog(qemuMonitor *mon, virJSONValue *data)
{
const char *action;
int actionID;
if (!(action = virJSONValueObjectGetString(data, "action")))
VIR_WARN("missing action in watchdog event");
if (action) {
if ((actionID = qemuMonitorWatchdogActionTypeFromString(action)) < 0) {
VIR_WARN("unknown action %s in watchdog event", action);
actionID = VIR_DOMAIN_EVENT_WATCHDOG_NONE;
}
} else {
actionID = VIR_DOMAIN_EVENT_WATCHDOG_NONE;
}
qemuMonitorEmitWatchdog(mon, actionID);
}
VIR_ENUM_DECL(qemuMonitorIOErrorAction);
VIR_ENUM_IMPL(qemuMonitorIOErrorAction,
VIR_DOMAIN_EVENT_IO_ERROR_LAST,
"ignore", "stop", "report",
);
Add support for an explicit IO error event This introduces a new event type VIR_DOMAIN_EVENT_ID_IO_ERROR This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction; In addition it has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file) Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque); This is currently wired up to the QEMU block IO error events * daemon/remote.c: Dispatch IO error events to client * examples/domain-events/events-c/event-test.c: Watch for IO error events * include/libvirt/libvirt.h.in: Define new IO error event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle IO error events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block IO errors and emit a libvirt IO error event * src/remote/remote_driver.c: Receive and dispatch IO error events to application * src/remote/remote_protocol.x: Wire protocol definition for IO error events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_IO_ERROR event from QEMU monitor
2010-03-18 19:37:44 +00:00
qemu: support nospace reason in io error event Aeons ago (commit 34dcbbb4, v0.8.2), we added a new libvirt event (VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON) in order to tell the user WHY the guest halted. This is because at least VDSM wants to react differently to ENOSPC events (resize the lvm partition to be larger, and resume the guest as if nothing had happened) from all other events (I/O is hosed, throw up our hands and flag things as broken). At the time this was done, downstream RHEL qemu added a vendor extension '__com.redhat_reason', which would be exactly one of these strings: "enospc", "eperm", "eio", and "eother". In our stupidity, we exposed those exact strings to clients, rather than an enum, and we also return "" if we did not have access to a reason (which was the case for upstream qemu). Fast forward to now: upstream qemu commit c7c2ff0c (will be qemu 2.2) FINALLY adds a 'nospace' boolean, after discussion with multiple projects determined that VDSM really doesn't care about distinction between any other error types. So this patch converts 'nospace' into the string "enospc" for compatibility with RHEL clients that were already used to the downstream extension, while leaving the reason blank for all other cases (no change from the status quo). See also https://bugzilla.redhat.com/show_bug.cgi?id=1119784 * src/qemu/qemu_monitor_json.c (qewmuMonitorJSONHandleIOError): Parse reason field from modern qemu. * include/libvirt/libvirt.h.in (virConnectDomainEventIOErrorReasonCallback): Document it. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-10-03 14:46:25 +00:00
static void
qemuMonitorJSONHandleIOError(qemuMonitor *mon, virJSONValue *data)
Add support for an explicit IO error event This introduces a new event type VIR_DOMAIN_EVENT_ID_IO_ERROR This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction; In addition it has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file) Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque); This is currently wired up to the QEMU block IO error events * daemon/remote.c: Dispatch IO error events to client * examples/domain-events/events-c/event-test.c: Watch for IO error events * include/libvirt/libvirt.h.in: Define new IO error event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle IO error events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block IO errors and emit a libvirt IO error event * src/remote/remote_driver.c: Receive and dispatch IO error events to application * src/remote/remote_protocol.x: Wire protocol definition for IO error events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_IO_ERROR event from QEMU monitor
2010-03-18 19:37:44 +00:00
{
const char *device;
const char *nodename;
Add support for an explicit IO error event This introduces a new event type VIR_DOMAIN_EVENT_ID_IO_ERROR This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction; In addition it has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file) Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque); This is currently wired up to the QEMU block IO error events * daemon/remote.c: Dispatch IO error events to client * examples/domain-events/events-c/event-test.c: Watch for IO error events * include/libvirt/libvirt.h.in: Define new IO error event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle IO error events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block IO errors and emit a libvirt IO error event * src/remote/remote_driver.c: Receive and dispatch IO error events to application * src/remote/remote_protocol.x: Wire protocol definition for IO error events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_IO_ERROR event from QEMU monitor
2010-03-18 19:37:44 +00:00
const char *action;
qemu: support nospace reason in io error event Aeons ago (commit 34dcbbb4, v0.8.2), we added a new libvirt event (VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON) in order to tell the user WHY the guest halted. This is because at least VDSM wants to react differently to ENOSPC events (resize the lvm partition to be larger, and resume the guest as if nothing had happened) from all other events (I/O is hosed, throw up our hands and flag things as broken). At the time this was done, downstream RHEL qemu added a vendor extension '__com.redhat_reason', which would be exactly one of these strings: "enospc", "eperm", "eio", and "eother". In our stupidity, we exposed those exact strings to clients, rather than an enum, and we also return "" if we did not have access to a reason (which was the case for upstream qemu). Fast forward to now: upstream qemu commit c7c2ff0c (will be qemu 2.2) FINALLY adds a 'nospace' boolean, after discussion with multiple projects determined that VDSM really doesn't care about distinction between any other error types. So this patch converts 'nospace' into the string "enospc" for compatibility with RHEL clients that were already used to the downstream extension, while leaving the reason blank for all other cases (no change from the status quo). See also https://bugzilla.redhat.com/show_bug.cgi?id=1119784 * src/qemu/qemu_monitor_json.c (qewmuMonitorJSONHandleIOError): Parse reason field from modern qemu. * include/libvirt/libvirt.h.in (virConnectDomainEventIOErrorReasonCallback): Document it. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-10-03 14:46:25 +00:00
const char *reason = "";
bool nospc = false;
Add support for an explicit IO error event This introduces a new event type VIR_DOMAIN_EVENT_ID_IO_ERROR This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction; In addition it has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file) Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque); This is currently wired up to the QEMU block IO error events * daemon/remote.c: Dispatch IO error events to client * examples/domain-events/events-c/event-test.c: Watch for IO error events * include/libvirt/libvirt.h.in: Define new IO error event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle IO error events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block IO errors and emit a libvirt IO error event * src/remote/remote_driver.c: Receive and dispatch IO error events to application * src/remote/remote_protocol.x: Wire protocol definition for IO error events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_IO_ERROR event from QEMU monitor
2010-03-18 19:37:44 +00:00
int actionID;
/* Throughout here we try our best to carry on upon errors,
since it's important to get as much info as possible out
Add support for an explicit IO error event This introduces a new event type VIR_DOMAIN_EVENT_ID_IO_ERROR This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction; In addition it has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file) Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque); This is currently wired up to the QEMU block IO error events * daemon/remote.c: Dispatch IO error events to client * examples/domain-events/events-c/event-test.c: Watch for IO error events * include/libvirt/libvirt.h.in: Define new IO error event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle IO error events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block IO errors and emit a libvirt IO error event * src/remote/remote_driver.c: Receive and dispatch IO error events to application * src/remote/remote_protocol.x: Wire protocol definition for IO error events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_IO_ERROR event from QEMU monitor
2010-03-18 19:37:44 +00:00
to the application */
if ((action = virJSONValueObjectGetString(data, "action")) == NULL) {
VIR_WARN("Missing action in disk io error event");
Add support for an explicit IO error event This introduces a new event type VIR_DOMAIN_EVENT_ID_IO_ERROR This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction; In addition it has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file) Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque); This is currently wired up to the QEMU block IO error events * daemon/remote.c: Dispatch IO error events to client * examples/domain-events/events-c/event-test.c: Watch for IO error events * include/libvirt/libvirt.h.in: Define new IO error event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle IO error events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block IO errors and emit a libvirt IO error event * src/remote/remote_driver.c: Receive and dispatch IO error events to application * src/remote/remote_protocol.x: Wire protocol definition for IO error events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_IO_ERROR event from QEMU monitor
2010-03-18 19:37:44 +00:00
action = "ignore";
}
if ((device = virJSONValueObjectGetString(data, "device")) == NULL)
VIR_WARN("missing device in disk io error event");
Add support for an explicit IO error event This introduces a new event type VIR_DOMAIN_EVENT_ID_IO_ERROR This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction; In addition it has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file) Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque); This is currently wired up to the QEMU block IO error events * daemon/remote.c: Dispatch IO error events to client * examples/domain-events/events-c/event-test.c: Watch for IO error events * include/libvirt/libvirt.h.in: Define new IO error event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle IO error events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block IO errors and emit a libvirt IO error event * src/remote/remote_driver.c: Receive and dispatch IO error events to application * src/remote/remote_protocol.x: Wire protocol definition for IO error events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_IO_ERROR event from QEMU monitor
2010-03-18 19:37:44 +00:00
nodename = virJSONValueObjectGetString(data, "node-name");
qemu: support nospace reason in io error event Aeons ago (commit 34dcbbb4, v0.8.2), we added a new libvirt event (VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON) in order to tell the user WHY the guest halted. This is because at least VDSM wants to react differently to ENOSPC events (resize the lvm partition to be larger, and resume the guest as if nothing had happened) from all other events (I/O is hosed, throw up our hands and flag things as broken). At the time this was done, downstream RHEL qemu added a vendor extension '__com.redhat_reason', which would be exactly one of these strings: "enospc", "eperm", "eio", and "eother". In our stupidity, we exposed those exact strings to clients, rather than an enum, and we also return "" if we did not have access to a reason (which was the case for upstream qemu). Fast forward to now: upstream qemu commit c7c2ff0c (will be qemu 2.2) FINALLY adds a 'nospace' boolean, after discussion with multiple projects determined that VDSM really doesn't care about distinction between any other error types. So this patch converts 'nospace' into the string "enospc" for compatibility with RHEL clients that were already used to the downstream extension, while leaving the reason blank for all other cases (no change from the status quo). See also https://bugzilla.redhat.com/show_bug.cgi?id=1119784 * src/qemu/qemu_monitor_json.c (qewmuMonitorJSONHandleIOError): Parse reason field from modern qemu. * include/libvirt/libvirt.h.in (virConnectDomainEventIOErrorReasonCallback): Document it. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-10-03 14:46:25 +00:00
if (virJSONValueObjectGetBoolean(data, "nospace", &nospc) == 0 && nospc)
reason = "enospc";
Add support for an explicit IO error event This introduces a new event type VIR_DOMAIN_EVENT_ID_IO_ERROR This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction; In addition it has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file) Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque); This is currently wired up to the QEMU block IO error events * daemon/remote.c: Dispatch IO error events to client * examples/domain-events/events-c/event-test.c: Watch for IO error events * include/libvirt/libvirt.h.in: Define new IO error event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle IO error events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block IO errors and emit a libvirt IO error event * src/remote/remote_driver.c: Receive and dispatch IO error events to application * src/remote/remote_protocol.x: Wire protocol definition for IO error events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_IO_ERROR event from QEMU monitor
2010-03-18 19:37:44 +00:00
if ((actionID = qemuMonitorIOErrorActionTypeFromString(action)) < 0) {
VIR_WARN("unknown disk io error action '%s'", action);
actionID = VIR_DOMAIN_EVENT_IO_ERROR_NONE;
}
qemuMonitorEmitIOError(mon, device, nodename, actionID, reason);
Add support for an explicit IO error event This introduces a new event type VIR_DOMAIN_EVENT_ID_IO_ERROR This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction; In addition it has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file) Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque); This is currently wired up to the QEMU block IO error events * daemon/remote.c: Dispatch IO error events to client * examples/domain-events/events-c/event-test.c: Watch for IO error events * include/libvirt/libvirt.h.in: Define new IO error event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle IO error events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block IO errors and emit a libvirt IO error event * src/remote/remote_driver.c: Receive and dispatch IO error events to application * src/remote/remote_protocol.x: Wire protocol definition for IO error events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_IO_ERROR event from QEMU monitor
2010-03-18 19:37:44 +00:00
}
VIR_ENUM_DECL(qemuMonitorGraphicsAddressFamily);
VIR_ENUM_IMPL(qemuMonitorGraphicsAddressFamily,
VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_LAST,
"ipv4", "ipv6", "unix",
);
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
static void
qemuMonitorJSONHandleGraphicsVNC(qemuMonitor *mon,
virJSONValue *data,
int phase)
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
{
const char *localNode, *localService, *localFamily;
const char *remoteNode, *remoteService, *remoteFamily;
const char *authScheme, *saslUsername, *x509dname;
int localFamilyID, remoteFamilyID;
virJSONValue *client;
virJSONValue *server;
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
if (!(client = virJSONValueObjectGetObject(data, "client"))) {
VIR_WARN("missing client info in VNC event");
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
return;
}
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
if (!(server = virJSONValueObjectGetObject(data, "server"))) {
VIR_WARN("missing server info in VNC event");
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
return;
}
if (!(authScheme = virJSONValueObjectGetString(server, "auth"))) {
/* not all events are required to contain auth scheme */
VIR_DEBUG("missing auth scheme in VNC event");
authScheme = "";
}
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
if (!(localFamily = virJSONValueObjectGetString(server, "family"))) {
VIR_WARN("missing local address family in VNC event");
return;
}
if (!(localNode = virJSONValueObjectGetString(server, "host"))) {
VIR_WARN("missing local hostname in VNC event");
return;
}
if (!(localService = virJSONValueObjectGetString(server, "service"))) {
VIR_WARN("missing local service in VNC event");
return;
}
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
if (!(remoteFamily = virJSONValueObjectGetString(client, "family"))) {
VIR_WARN("missing remote address family in VNC event");
return;
}
if (!(remoteNode = virJSONValueObjectGetString(client, "host"))) {
VIR_WARN("missing remote hostname in VNC event");
return;
}
if (!(remoteService = virJSONValueObjectGetString(client, "service"))) {
VIR_WARN("missing remote service in VNC event");
return;
}
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
saslUsername = virJSONValueObjectGetString(client, "sasl_username");
x509dname = virJSONValueObjectGetString(client, "x509_dname");
if ((localFamilyID = qemuMonitorGraphicsAddressFamilyTypeFromString(localFamily)) < 0) {
VIR_WARN("unknown address family '%s'", localFamily);
localFamilyID = VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4;
}
if ((remoteFamilyID = qemuMonitorGraphicsAddressFamilyTypeFromString(remoteFamily)) < 0) {
VIR_WARN("unknown address family '%s'", remoteFamily);
remoteFamilyID = VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4;
}
qemuMonitorEmitGraphics(mon, phase,
localFamilyID, localNode, localService,
remoteFamilyID, remoteNode, remoteService,
authScheme, x509dname, saslUsername);
}
static void qemuMonitorJSONHandleVNCConnect(qemuMonitor *mon, virJSONValue *data)
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
{
qemuMonitorJSONHandleGraphicsVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_CONNECT);
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
}
static void qemuMonitorJSONHandleVNCInitialize(qemuMonitor *mon, virJSONValue *data)
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
{
qemuMonitorJSONHandleGraphicsVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE);
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
}
static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitor *mon, virJSONValue *data)
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
{
qemuMonitorJSONHandleGraphicsVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT);
}
static void
qemuMonitorJSONHandleGraphicsSPICE(qemuMonitor *mon,
virJSONValue *data,
int phase)
{
const char *lhost, *lport, *lfamily;
const char *rhost, *rport, *rfamily;
const char *auth = "";
int lfamilyID, rfamilyID;
virJSONValue *client;
virJSONValue *server;
if (!(client = virJSONValueObjectGetObject(data, "client")) ||
!(server = virJSONValueObjectGetObject(data, "server"))) {
VIR_WARN("missing server or client info in SPICE event");
return;
}
if (phase == VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE &&
!(auth = virJSONValueObjectGetString(server, "auth"))) {
VIR_DEBUG("missing auth scheme in SPICE event");
auth = "";
}
if (!(lfamily = virJSONValueObjectGetString(server, "family"))) {
VIR_WARN("missing local address family in SPICE event");
return;
}
if (!(lhost = virJSONValueObjectGetString(server, "host"))) {
VIR_WARN("missing local hostname in SPICE event");
return;
}
if (!(lport = virJSONValueObjectGetString(server, "port"))) {
VIR_WARN("missing local port in SPICE event");
return;
}
if (!(rfamily = virJSONValueObjectGetString(client, "family"))) {
VIR_WARN("missing remote address family in SPICE event");
return;
}
if (!(rhost = virJSONValueObjectGetString(client, "host"))) {
VIR_WARN("missing remote hostname in SPICE event");
return;
}
if (!(rport = virJSONValueObjectGetString(client, "port"))) {
VIR_WARN("missing remote service in SPICE event");
return;
}
if ((lfamilyID = qemuMonitorGraphicsAddressFamilyTypeFromString(lfamily)) < 0) {
VIR_WARN("unknown address family '%s'", lfamily);
lfamilyID = VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4;
}
if ((rfamilyID = qemuMonitorGraphicsAddressFamilyTypeFromString(rfamily)) < 0) {
VIR_WARN("unknown address family '%s'", rfamily);
rfamilyID = VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4;
}
qemuMonitorEmitGraphics(mon, phase, lfamilyID, lhost, lport, rfamilyID,
rhost, rport, auth, NULL, NULL);
}
static void qemuMonitorJSONHandleSPICEConnect(qemuMonitor *mon, virJSONValue *data)
{
qemuMonitorJSONHandleGraphicsSPICE(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_CONNECT);
}
static void qemuMonitorJSONHandleSPICEInitialize(qemuMonitor *mon, virJSONValue *data)
{
qemuMonitorJSONHandleGraphicsSPICE(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE);
}
static void qemuMonitorJSONHandleSPICEDisconnect(qemuMonitor *mon, virJSONValue *data)
{
qemuMonitorJSONHandleGraphicsSPICE(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT);
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
}
static void
qemuMonitorJSONHandleBlockJobImpl(qemuMonitor *mon,
virJSONValue *data,
int event)
{
const char *device;
const char *type_str;
const char *error = NULL;
int type = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
unsigned long long offset, len;
if ((device = virJSONValueObjectGetString(data, "device")) == NULL) {
VIR_WARN("missing device in block job event");
goto out;
}
if (virJSONValueObjectGetNumberUlong(data, "offset", &offset) < 0) {
VIR_WARN("missing offset in block job event");
goto out;
}
if (virJSONValueObjectGetNumberUlong(data, "len", &len) < 0) {
VIR_WARN("missing len in block job event");
goto out;
}
if ((type_str = virJSONValueObjectGetString(data, "type")) == NULL) {
VIR_WARN("missing type in block job event");
goto out;
}
if (STREQ(type_str, "stream"))
type = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL;
else if (STREQ(type_str, "commit"))
type = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT;
else if (STREQ(type_str, "mirror"))
type = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY;
else if (STREQ(type_str, "backup"))
type = VIR_DOMAIN_BLOCK_JOB_TYPE_BACKUP;
switch ((virConnectDomainEventBlockJobStatus) event) {
case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
error = virJSONValueObjectGetString(data, "error");
/* Make sure the whole device has been processed */
if (offset != len || error)
event = VIR_DOMAIN_BLOCK_JOB_FAILED;
break;
case VIR_DOMAIN_BLOCK_JOB_CANCELED:
case VIR_DOMAIN_BLOCK_JOB_READY:
break;
case VIR_DOMAIN_BLOCK_JOB_FAILED:
case VIR_DOMAIN_BLOCK_JOB_LAST:
VIR_DEBUG("should not get here");
break;
}
out:
qemuMonitorEmitBlockJob(mon, device, type, event, error);
}
static void
qemuMonitorJSONHandleJobStatusChange(qemuMonitor *mon,
virJSONValue *data)
{
const char *jobname = virJSONValueObjectGetString(data, "id");
const char *statusstr = virJSONValueObjectGetString(data, "status");
int status;
if (!jobname) {
VIR_WARN("missing job name in JOB_STATUS_CHANGE event");
return;
}
if ((status = qemuMonitorJobStatusTypeFromString(statusstr)) < 0) {
VIR_WARN("unknown job status '%s' for job '%s' in JOB_STATUS_CHANGE event",
statusstr, jobname);
return;
}
qemuMonitorEmitJobStatusChange(mon, jobname, status);
}
static void
qemuMonitorJSONHandleTrayChange(qemuMonitor *mon,
virJSONValue *data)
{
const char *devAlias = virJSONValueObjectGetString(data, "device");
const char *devid = virJSONValueObjectGetString(data, "id");
bool trayOpened;
int reason;
/* drive alias is always reported but empty for -blockdev */
if (*devAlias == '\0')
devAlias = NULL;
if (!devAlias && !devid) {
VIR_WARN("missing device in tray change event");
return;
}
if (virJSONValueObjectGetBoolean(data, "tray-open", &trayOpened) < 0) {
VIR_WARN("missing tray-open in tray change event");
return;
}
if (trayOpened)
reason = VIR_DOMAIN_EVENT_TRAY_CHANGE_OPEN;
else
reason = VIR_DOMAIN_EVENT_TRAY_CHANGE_CLOSE;
qemuMonitorEmitTrayChange(mon, devAlias, devid, reason);
}
Add domain events for graphics network clients This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarios typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initialize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *a lot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor
2010-03-19 13:27:45 +00:00
static void
qemuMonitorJSONHandlePMWakeup(qemuMonitor *mon,
virJSONValue *data G_GNUC_UNUSED)
{
qemuMonitorEmitPMWakeup(mon);
}
static void
qemuMonitorJSONHandlePMSuspend(qemuMonitor *mon,
virJSONValue *data G_GNUC_UNUSED)
{
qemuMonitorEmitPMSuspend(mon);
}
static void
qemuMonitorJSONHandleBlockJobCompleted(qemuMonitor *mon,
virJSONValue *data)
{
qemuMonitorJSONHandleBlockJobImpl(mon, data,
VIR_DOMAIN_BLOCK_JOB_COMPLETED);
}
static void
qemuMonitorJSONHandleBlockJobCanceled(qemuMonitor *mon,
virJSONValue *data)
{
qemuMonitorJSONHandleBlockJobImpl(mon, data,
VIR_DOMAIN_BLOCK_JOB_CANCELED);
}
static void
qemuMonitorJSONHandleBlockJobReady(qemuMonitor *mon,
virJSONValue *data)
{
qemuMonitorJSONHandleBlockJobImpl(mon, data,
VIR_DOMAIN_BLOCK_JOB_READY);
}
static void
qemuMonitorJSONHandleBalloonChange(qemuMonitor *mon,
virJSONValue *data)
{
unsigned long long actual = 0;
if (virJSONValueObjectGetNumberUlong(data, "actual", &actual) < 0) {
VIR_WARN("missing actual in balloon change event");
return;
}
actual = VIR_DIV_UP(actual, 1024);
qemuMonitorEmitBalloonChange(mon, actual);
}
static void
qemuMonitorJSONHandlePMSuspendDisk(qemuMonitor *mon,
virJSONValue *data G_GNUC_UNUSED)
{
qemuMonitorEmitPMSuspendDisk(mon);
}
static void
qemuMonitorJSONHandleDeviceDeleted(qemuMonitor *mon, virJSONValue *data)
{
const char *device;
if (!(device = virJSONValueObjectGetString(data, "device"))) {
VIR_DEBUG("missing device in device deleted event");
return;
}
qemuMonitorEmitDeviceDeleted(mon, device);
}
static void
qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitor *mon, virJSONValue *data)
{
const char *name;
if (!(name = virJSONValueObjectGetString(data, "name"))) {
VIR_WARN("missing device in NIC_RX_FILTER_CHANGED event");
return;
}
qemuMonitorEmitNicRxFilterChanged(mon, name);
}
static void
qemuMonitorJSONHandleSerialChange(qemuMonitor *mon,
virJSONValue *data)
{
const char *name;
bool connected;
if (!(name = virJSONValueObjectGetString(data, "id"))) {
VIR_WARN("missing device alias in VSERPORT_CHANGE event");
return;
}
if (virJSONValueObjectGetBoolean(data, "open", &connected) < 0) {
VIR_WARN("missing port state for '%s' in VSERPORT_CHANGE event", name);
return;
}
qemuMonitorEmitSerialChange(mon, name, connected);
}
static void
qemuMonitorJSONHandleSpiceMigrated(qemuMonitor *mon,
virJSONValue *data G_GNUC_UNUSED)
{
qemuMonitorEmitSpiceMigrated(mon);
}
static void
qemuMonitorJSONHandleMemoryDeviceSizeChange(qemuMonitor *mon,
virJSONValue *data)
{
const char *name;
unsigned long long size;
if (!(name = virJSONValueObjectGetString(data, "id"))) {
VIR_WARN("missing device alias in MEMORY_DEVICE_SIZE_CHANGE event");
return;
}
if (virJSONValueObjectGetNumberUlong(data, "size", &size) < 0) {
VIR_WARN("missing new size for '%s' in MEMORY_DEVICE_SIZE_CHANGE event", name);
return;
}
qemuMonitorEmitMemoryDeviceSizeChange(mon, name, size);
}
static void
qemuMonitorJSONHandleMemoryFailure(qemuMonitor *mon,
virJSONValue *data)
{
virJSONValue *flagsjson = virJSONValueObjectGetObject(data, "flags");
const char *str;
int recipient;
int action;
bool ar = false;
bool recursive = false;
qemuMonitorEventMemoryFailure mf = {0};
if (!(str = virJSONValueObjectGetString(data, "recipient"))) {
VIR_WARN("missing recipient in memory failure event");
return;
}
recipient = qemuMonitorMemoryFailureRecipientTypeFromString(str);
if (recipient < 0) {
VIR_WARN("unknown recipient '%s' in memory_failure event", str);
return;
}
if (!(str = virJSONValueObjectGetString(data, "action"))) {
VIR_WARN("missing action in memory failure event");
return;
}
action = qemuMonitorMemoryFailureActionTypeFromString(str);
if (action < 0) {
VIR_WARN("unknown action '%s' in memory_failure event", str);
return;
}
if (flagsjson) {
virJSONValueObjectGetBoolean(flagsjson, "action-required", &ar);
virJSONValueObjectGetBoolean(flagsjson, "recursive", &recursive);
}
mf.recipient = recipient;
mf.action = action;
mf.action_required = ar;
mf.recursive = recursive;
qemuMonitorEmitMemoryFailure(mon, &mf);
}
static void
qemuMonitorJSONHandleMigrationStatus(qemuMonitor *mon,
virJSONValue *data)
{
const char *str;
int status;
if (!(str = virJSONValueObjectGetString(data, "status"))) {
VIR_WARN("missing status in migration event");
return;
}
if ((status = qemuMonitorMigrationStatusTypeFromString(str)) == -1) {
VIR_WARN("unknown status '%s' in migration event", str);
return;
}
qemuMonitorEmitMigrationStatus(mon, status);
}
static void
qemuMonitorJSONHandleMigrationPass(qemuMonitor *mon,
virJSONValue *data)
{
int pass;
if (virJSONValueObjectGetNumberInt(data, "pass", &pass) < 0) {
VIR_WARN("missing dirty-sync-count in migration-pass event");
return;
}
qemuMonitorEmitMigrationPass(mon, pass);
}
static void
qemuMonitorJSONHandleAcpiOstInfo(qemuMonitor *mon, virJSONValue *data)
{
virJSONValue *info;
const char *alias;
const char *slotType;
const char *slot;
unsigned int source;
unsigned int status;
if (!(info = virJSONValueObjectGetObject(data, "info")))
goto error;
/* optional */
alias = virJSONValueObjectGetString(info, "device");
if (!(slotType = virJSONValueObjectGetString(info, "slot-type")))
goto error;
if (!(slot = virJSONValueObjectGetString(info, "slot")))
goto error;
if (virJSONValueObjectGetNumberUint(info, "source", &source) < 0)
goto error;
if (virJSONValueObjectGetNumberUint(info, "status", &status) < 0)
goto error;
qemuMonitorEmitAcpiOstInfo(mon, alias, slotType, slot, source, status);
return;
error:
VIR_WARN("malformed ACPI_DEVICE_OST event");
return;
}
static void
qemuMonitorJSONHandleBlockThreshold(qemuMonitor *mon, virJSONValue *data)
{
const char *nodename;
unsigned long long threshold;
unsigned long long excess;
if (!(nodename = virJSONValueObjectGetString(data, "node-name")))
goto error;
if (virJSONValueObjectGetNumberUlong(data, "write-threshold", &threshold) < 0)
goto error;
if (virJSONValueObjectGetNumberUlong(data, "amount-exceeded", &excess) < 0)
goto error;
qemuMonitorEmitBlockThreshold(mon, nodename, threshold, excess);
return;
error:
VIR_WARN("malformed 'BLOCK_WRITE_THRESHOLD' event");
}
static int
qemuMonitorJSONExtractDumpStats(virJSONValue *result,
qemuMonitorDumpStats *ret)
{
const char *statusstr;
if (!(statusstr = virJSONValueObjectGetString(result, "status"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("incomplete result, failed to get status"));
return -1;
}
ret->status = qemuMonitorDumpStatusTypeFromString(statusstr);
if (ret->status < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("incomplete result, unknown status string '%s'"),
statusstr);
return -1;
}
if (virJSONValueObjectGetNumberUlong(result, "completed", &ret->completed) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("incomplete result, failed to get completed"));
return -1;
}
if (virJSONValueObjectGetNumberUlong(result, "total", &ret->total) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("incomplete result, failed to get total"));
return -1;
}
return 0;
}
static void
qemuMonitorJSONHandleDumpCompleted(qemuMonitor *mon,
virJSONValue *data)
{
virJSONValue *result;
int status;
qemuMonitorDumpStats stats = { 0 };
const char *error = NULL;
if (!(result = virJSONValueObjectGetObject(data, "result"))) {
VIR_WARN("missing result in dump completed event");
return;
}
status = qemuMonitorJSONExtractDumpStats(result, &stats);
error = virJSONValueObjectGetString(data, "error");
qemuMonitorEmitDumpCompleted(mon, status, &stats, error);
}
static void qemuMonitorJSONHandlePRManagerStatusChanged(qemuMonitor *mon,
virJSONValue *data)
{
const char *name;
bool connected;
if (!(name = virJSONValueObjectGetString(data, "id"))) {
VIR_WARN("missing pr-manager alias in PR_MANAGER_STATUS_CHANGED event");
return;
}
if (virJSONValueObjectGetBoolean(data, "connected", &connected) < 0) {
VIR_WARN("missing connected state for %s "
"in PR_MANAGER_STATUS_CHANGED event", name);
return;
}
qemuMonitorEmitPRManagerStatusChanged(mon, name, connected);
}
static void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitor *mon,
virJSONValue *data)
{
const char *netdev;
bool gid_status;
unsigned long long subnet_prefix, interface_id;
if (!(netdev = virJSONValueObjectGetString(data, "netdev"))) {
VIR_WARN("missing netdev in GID_STATUS_CHANGED event");
return;
}
if (virJSONValueObjectGetBoolean(data, "gid-status", &gid_status)) {
VIR_WARN("missing gid-status in GID_STATUS_CHANGED event");
return;
}
if (virJSONValueObjectGetNumberUlong(data, "subnet-prefix",
&subnet_prefix)) {
VIR_WARN("missing subnet-prefix in GID_STATUS_CHANGED event");
return;
}
if (virJSONValueObjectGetNumberUlong(data, "interface-id",
&interface_id)) {
VIR_WARN("missing interface-id in GID_STATUS_CHANGED event");
return;
}
qemuMonitorEmitRdmaGidStatusChanged(mon, netdev, gid_status, subnet_prefix,
interface_id);
}
static void
qemuMonitorJSONHandleGuestCrashloaded(qemuMonitor *mon,
virJSONValue *data)
{
VIR_DEBUG("qemuMonitorJSONHandleGuestCrashloaded event, mon %p, data %p", mon, data);
qemuMonitorEmitGuestCrashloaded(mon);
}
int
qemuMonitorJSONHumanCommand(qemuMonitor *mon,
const char *cmd_str,
char **reply_str)
{
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
virJSONValue *obj;
const char *data;
int ret = -1;
cmd = qemuMonitorJSONMakeCommand("human-monitor-command",
"s:command-line", cmd_str,
NULL);
if (!cmd || qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
_("Human monitor command is not available to run %s"),
cmd_str);
goto cleanup;
}
if (qemuMonitorJSONCheckError(cmd, reply))
goto cleanup;
obj = virJSONValueObjectGet(reply, "return");
data = virJSONValueGetString(obj);
*reply_str = g_strdup(NULLSTR_EMPTY(data));
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONSetCapabilities(qemuMonitor *mon)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("qmp_capabilities",
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONStartCPUs(qemuMonitor *mon)
{
int ret;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("cont", NULL);
virJSONValue *reply = NULL;
size_t i = 0;
int timeout = 3;
if (!cmd)
return -1;
do {
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret != 0)
break;
/* If no error, we're done */
if ((ret = qemuMonitorJSONCheckError(cmd, reply)) == 0)
break;
/* If error class is not MigrationExpected, we're done.
* Otherwise try 'cont' cmd again */
if (!qemuMonitorJSONHasError(reply, "MigrationExpected"))
break;
virJSONValueFree(reply);
reply = NULL;
g_usleep(250000);
} while (++i <= timeout);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONStopCPUs(qemuMonitor *mon)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("stop", NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetStatus(qemuMonitor *mon,
bool *running,
virDomainPausedReason *reason)
{
const char *status;
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
virJSONValue *data;
if (reason)
*reason = VIR_DOMAIN_PAUSED_UNKNOWN;
if (!(cmd = qemuMonitorJSONMakeCommand("query-status", NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
return -1;
data = virJSONValueObjectGetObject(reply, "return");
if (virJSONValueObjectGetBoolean(data, "running", running) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-status reply was missing running state"));
return -1;
}
if ((status = virJSONValueObjectGetString(data, "status"))) {
if (!*running && reason)
*reason = qemuMonitorVMStatusToPausedReason(status);
} else if (!*running) {
VIR_DEBUG("query-status reply was missing status details");
}
return 0;
}
int qemuMonitorJSONSystemPowerdown(qemuMonitor *mon)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("system_powerdown", NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONSetLink(qemuMonitor *mon,
const char *name,
virDomainNetInterfaceLinkState state)
{
int ret = -1;
virJSONValue *reply = NULL;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("set_link",
"s:name", name,
"b:up", state != VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN,
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONSystemReset(qemuMonitor *mon)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("system_reset", NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/**
* qemuMonitorJSONExtractCPUS390Info:
* @jsoncpu: pointer to a single JSON cpu entry
* @cpu: pointer to a single cpu entry
*
* Derive the legacy cpu info 'halted' information
* from the more accurate s390 cpu state. @cpu is
* modified only on success.
*
* Note: the 'uninitialized' s390 cpu state can't be
* mapped to halted yes/no.
*
* A s390 cpu entry could look like this
* { "arch": "s390",
* "cpu-index": 0,
* "qom-path": "/machine/unattached/device[0]",
* "thread_id": 3081,
* "cpu-state": "operating" }
*
*/
static void
qemuMonitorJSONExtractCPUS390Info(virJSONValue *jsoncpu,
struct qemuMonitorQueryCpusEntry *cpu)
{
const char *cpu_state = virJSONValueObjectGetString(jsoncpu, "cpu-state");
if (STREQ_NULLABLE(cpu_state, "operating") ||
STREQ_NULLABLE(cpu_state, "load"))
cpu->halted = false;
else if (STREQ_NULLABLE(cpu_state, "stopped") ||
STREQ_NULLABLE(cpu_state, "check-stop"))
cpu->halted = true;
}
/**
* qemuMonitorJSONExtractCPUInfo:
* @data: JSON response data
* @entries: filled with detected cpu entries on success
* @nentries: number of entries returned
* @fast: true if this is a response from query-cpus-fast
*
* The JSON response @data will have the following format
* in case @fast == false
* [{ "arch": "x86",
* "current": true,
* "CPU": 0,
* "qom_path": "/machine/unattached/device[0]",
* "pc": -2130415978,
* "halted": true,
* "thread_id": 2631237,
* ...},
* {...}
* ]
* and for @fast == true
* [{ "arch": "x86",
* "cpu-index": 0,
* "props": {
* "core-id": 0,
* "thread-id": 0,
* "socket-id": 0
* },
* "qom-path": "/machine/unattached/device[0]",
* "thread-id": 2631237,
* ...},
* {...}
* ]
* or for s390
* [{ "arch": "s390",
* "cpu-index": 0,
* "props": {
* "core-id": 0
* },
* "qom-path": "/machine/unattached/device[0]",
* "thread-id": 1237,
* "cpu-state": "operating",
* ...},
* {...}
* ]
*
* Note that since QEMU 2.13.0 the "arch" output member of the
* "query-cpus-fast" command is replaced by "target".
*/
static int
qemuMonitorJSONExtractCPUInfo(virJSONValue *data,
struct qemuMonitorQueryCpusEntry **entries,
size_t *nentries,
bool fast)
{
const char *arch = NULL;
struct qemuMonitorQueryCpusEntry *cpus = NULL;
int ret = -1;
size_t i;
size_t ncpus;
if ((ncpus = virJSONValueArraySize(data)) == 0)
return -2;
cpus = g_new0(struct qemuMonitorQueryCpusEntry, ncpus);
for (i = 0; i < ncpus; i++) {
virJSONValue *entry = virJSONValueArrayGet(data, i);
int cpuid = -1;
int thread = 0;
bool halted = false;
const char *qom_path;
if (!entry) {
ret = -2;
goto cleanup;
}
/* Some older qemu versions don't report the thread_id so treat this as
* non-fatal, simply returning no data.
* The return data of query-cpus-fast has different field names
*/
if (fast) {
if (!(arch = virJSONValueObjectGetString(entry, "target")))
arch = virJSONValueObjectGetString(entry, "arch");
ignore_value(virJSONValueObjectGetNumberInt(entry, "cpu-index", &cpuid));
ignore_value(virJSONValueObjectGetNumberInt(entry, "thread-id", &thread));
qom_path = virJSONValueObjectGetString(entry, "qom-path");
} else {
arch = virJSONValueObjectGetString(entry, "arch");
ignore_value(virJSONValueObjectGetNumberInt(entry, "CPU", &cpuid));
ignore_value(virJSONValueObjectGetNumberInt(entry, "thread_id", &thread));
ignore_value(virJSONValueObjectGetBoolean(entry, "halted", &halted));
qom_path = virJSONValueObjectGetString(entry, "qom_path");
}
cpus[i].qemu_id = cpuid;
cpus[i].tid = thread;
cpus[i].halted = halted;
cpus[i].qom_path = g_strdup(qom_path);
/* process optional architecture-specific data */
if (STREQ_NULLABLE(arch, "s390") || STREQ_NULLABLE(arch, "s390x"))
qemuMonitorJSONExtractCPUS390Info(entry, cpus + i);
}
*entries = g_steal_pointer(&cpus);
*nentries = ncpus;
ret = 0;
cleanup:
qemuMonitorQueryCpusFree(cpus, ncpus);
return ret;
}
/**
* qemuMonitorJSONQueryCPUs:
*
* @mon: monitor object
* @entries: filled with detected entries on success
* @nentries: number of entries returned
* @force: force exit on error
* @fast: use query-cpus-fast
*
* Queries qemu for cpu-related information. Failure to execute the command or
* extract results does not produce an error as libvirt can continue without
* this information, unless the caller has specified @force == true.
*
2017-04-22 19:06:20 +00:00
* Returns 0 on success, -1 on a fatal error (oom ...) and -2 if the
* query failed gracefully.
*/
int
qemuMonitorJSONQueryCPUs(qemuMonitor *mon,
struct qemuMonitorQueryCpusEntry **entries,
size_t *nentries,
bool force,
bool fast)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *data;
if (fast)
cmd = qemuMonitorJSONMakeCommand("query-cpus-fast", NULL);
else
cmd = qemuMonitorJSONMakeCommand("query-cpus", NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (force && qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
if (!(data = virJSONValueObjectGetArray(reply, "return"))) {
ret = -2;
goto cleanup;
}
ret = qemuMonitorJSONExtractCPUInfo(data, entries, nentries, fast);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/**
* Loads correct video memory size values from QEMU and update the video
* definition.
*
* Return 0 on success, -1 on failure and set proper error message.
*/
int
qemuMonitorJSONUpdateVideoMemorySize(qemuMonitor *mon,
virDomainVideoDef *video,
char *path)
{
qemuMonitorJSONObjectProperty prop = {
QEMU_MONITOR_OBJECT_PROPERTY_ULONG,
{0}
};
switch (video->type) {
case VIR_DOMAIN_VIDEO_TYPE_VGA:
if (qemuMonitorJSONGetObjectProperty(mon, path, "vgamem_mb", &prop) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("QOM Object '%s' has no property 'vgamem_mb'"),
path);
return -1;
}
video->vram = prop.val.ul * 1024;
break;
case VIR_DOMAIN_VIDEO_TYPE_QXL:
if (qemuMonitorJSONGetObjectProperty(mon, path, "vram_size", &prop) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("QOM Object '%s' has no property 'vram_size'"),
path);
return -1;
}
video->vram = prop.val.ul / 1024;
if (qemuMonitorJSONGetObjectProperty(mon, path, "ram_size", &prop) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("QOM Object '%s' has no property 'ram_size'"),
path);
return -1;
}
video->ram = prop.val.ul / 1024;
if (qemuMonitorJSONGetObjectProperty(mon, path, "vgamem_mb", &prop) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("QOM Object '%s' has no property 'vgamem_mb'"),
path);
return -1;
}
video->vgamem = prop.val.ul * 1024;
break;
case VIR_DOMAIN_VIDEO_TYPE_VMVGA:
if (qemuMonitorJSONGetObjectProperty(mon, path, "vgamem_mb", &prop) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("QOM Object '%s' has no property 'vgamem_mb'"),
path);
return -1;
}
video->vram = prop.val.ul * 1024;
break;
case VIR_DOMAIN_VIDEO_TYPE_CIRRUS:
case VIR_DOMAIN_VIDEO_TYPE_XEN:
case VIR_DOMAIN_VIDEO_TYPE_VBOX:
case VIR_DOMAIN_VIDEO_TYPE_LAST:
break;
}
return 0;
}
/**
* Loads correct video vram64 size value from QEMU and update the video
* definition.
*
* Return 0 on success, -1 on failure and set proper error message.
*/
int
qemuMonitorJSONUpdateVideoVram64Size(qemuMonitor *mon,
virDomainVideoDef *video,
char *path)
{
qemuMonitorJSONObjectProperty prop = {
QEMU_MONITOR_OBJECT_PROPERTY_ULONG,
{0}
};
switch (video->type) {
case VIR_DOMAIN_VIDEO_TYPE_QXL:
if (video->vram64 != 0) {
if (qemuMonitorJSONGetObjectProperty(mon, path,
"vram64_size_mb", &prop) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("QOM Object '%s' has no property 'vram64_size_mb'"),
path);
return -1;
}
video->vram64 = prop.val.ul * 1024;
}
break;
case VIR_DOMAIN_VIDEO_TYPE_VGA:
case VIR_DOMAIN_VIDEO_TYPE_VMVGA:
case VIR_DOMAIN_VIDEO_TYPE_CIRRUS:
case VIR_DOMAIN_VIDEO_TYPE_XEN:
case VIR_DOMAIN_VIDEO_TYPE_VBOX:
case VIR_DOMAIN_VIDEO_TYPE_LAST:
break;
}
return 0;
}
int
qemuMonitorJSONGetBalloonInfo(qemuMonitor *mon,
unsigned long long *currmem)
{
int ret = -1;
virJSONValue *data;
unsigned long long mem;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("query-balloon",
NULL);
virJSONValue *reply = NULL;
*currmem = 0;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
/* See if balloon soft-failed */
if (qemuMonitorJSONHasError(reply, "DeviceNotActive") ||
qemuMonitorJSONHasError(reply, "KVMMissingCap")) {
ret = 0;
goto cleanup;
}
/* See if any other fatal error occurred */
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
goto cleanup;
data = virJSONValueObjectGetObject(reply, "return");
if (virJSONValueObjectGetNumberUlong(data, "actual", &mem) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("info balloon reply was missing balloon data"));
goto cleanup;
}
*currmem = (mem/1024);
ret = 1;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/* Process the balloon driver statistics. The request and data returned
* will be as follows (although the 'child[#]' entry will differ based on
* where it's run).
*
* { "execute": "qom-get","arguments": \
* { "path": "/machine/i440fx/pci.0/child[7]","property": "guest-stats"} }
*
* {"return": {"stats": \
* {"stat-swap-out": 0,
* "stat-free-memory": 686350336,
* "stat-minor-faults": 697283,
* "stat-major-faults": 951,
* "stat-total-memory": 1019924480,
* "stat-swap-in": 0},
* "last-update": 1371221540}}
*
* A value in "stats" can be -1 indicating it's never been collected/stored.
* The 'last-update' value could be used in the future in order to determine
* rates and/or whether data has been collected since a previous cycle.
* It's currently unused.
*/
#define GET_BALLOON_STATS(OBJECT, FIELD, TAG, DIVISOR) \
if (virJSONValueObjectHasKey(OBJECT, FIELD) && \
(got < nr_stats)) { \
if (virJSONValueObjectGetNumberUlong(OBJECT, FIELD, &mem) < 0) { \
VIR_DEBUG("Failed to get '%s' value", FIELD); \
} else { \
/* Not being collected? No point in providing bad data */ \
if (mem != -1UL) { \
stats[got].tag = TAG; \
stats[got].val = mem / DIVISOR; \
got++; \
} \
} \
}
int qemuMonitorJSONGetMemoryStats(qemuMonitor *mon,
char *balloonpath,
virDomainMemoryStatPtr stats,
unsigned int nr_stats)
{
int ret = -1;
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
virJSONValue *data;
virJSONValue *statsdata;
unsigned long long mem;
int got = 0;
ret = qemuMonitorJSONGetBalloonInfo(mon, &mem);
if (ret == 1 && (got < nr_stats)) {
stats[got].tag = VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON;
stats[got].val = mem;
got++;
}
if (!balloonpath)
goto cleanup;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-get",
"s:path", balloonpath,
"s:property", "guest-stats",
NULL)))
goto cleanup;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
if ((data = virJSONValueObjectGetObject(reply, "error"))) {
const char *klass = virJSONValueObjectGetString(data, "class");
const char *desc = virJSONValueObjectGetString(data, "desc");
if (STREQ_NULLABLE(klass, "GenericError") &&
STREQ_NULLABLE(desc, "guest hasn't updated any stats yet")) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("the guest hasn't updated any stats yet"));
goto cleanup;
}
}
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
goto cleanup;
data = virJSONValueObjectGetObject(reply, "return");
if (!(statsdata = virJSONValueObjectGet(data, "stats"))) {
VIR_DEBUG("data does not include 'stats'");
goto cleanup;
}
GET_BALLOON_STATS(statsdata, "stat-swap-in",
VIR_DOMAIN_MEMORY_STAT_SWAP_IN, 1024);
GET_BALLOON_STATS(statsdata, "stat-swap-out",
VIR_DOMAIN_MEMORY_STAT_SWAP_OUT, 1024);
GET_BALLOON_STATS(statsdata, "stat-major-faults",
VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT, 1);
GET_BALLOON_STATS(statsdata, "stat-minor-faults",
VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT, 1);
GET_BALLOON_STATS(statsdata, "stat-free-memory",
VIR_DOMAIN_MEMORY_STAT_UNUSED, 1024);
GET_BALLOON_STATS(statsdata, "stat-total-memory",
VIR_DOMAIN_MEMORY_STAT_AVAILABLE, 1024);
GET_BALLOON_STATS(statsdata, "stat-available-memory",
VIR_DOMAIN_MEMORY_STAT_USABLE, 1024);
GET_BALLOON_STATS(data, "last-update",
VIR_DOMAIN_MEMORY_STAT_LAST_UPDATE, 1);
GET_BALLOON_STATS(statsdata, "stat-disk-caches",
VIR_DOMAIN_MEMORY_STAT_DISK_CACHES, 1024);
GET_BALLOON_STATS(statsdata, "stat-htlb-pgalloc",
VIR_DOMAIN_MEMORY_STAT_HUGETLB_PGALLOC, 1);
GET_BALLOON_STATS(statsdata, "stat-htlb-pgfail",
VIR_DOMAIN_MEMORY_STAT_HUGETLB_PGFAIL, 1);
ret = got;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
#undef GET_BALLOON_STATS
/*
* Using the provided balloonpath, determine if we need to set the
* collection interval property to enable statistics gathering.
*/
int
qemuMonitorJSONSetMemoryStatsPeriod(qemuMonitor *mon,
char *balloonpath,
int period)
{
qemuMonitorJSONObjectProperty prop;
/* Set to the value in memballoon (could enable or disable) */
memset(&prop, 0, sizeof(qemuMonitorJSONObjectProperty));
prop.type = QEMU_MONITOR_OBJECT_PROPERTY_INT;
prop.val.iv = period;
if (qemuMonitorJSONSetObjectProperty(mon, balloonpath,
"guest-stats-polling-interval",
&prop) < 0) {
return -1;
}
return 0;
}
int
qemuMonitorJSONSetDBusVMStateIdList(qemuMonitor *mon,
const char *vmstatepath,
const char *idstr)
{
qemuMonitorJSONObjectProperty prop = {
.type = QEMU_MONITOR_OBJECT_PROPERTY_STRING,
.val.str = (char *) idstr,
};
return qemuMonitorJSONSetObjectProperty(mon, vmstatepath, "id-list", &prop);
}
/* qemuMonitorJSONQueryBlock:
* @mon: Monitor pointer
*
* This helper will attempt to make a "query-block" call and check for
* errors before returning with the reply.
*
* Returns: NULL on error, reply on success
*/
static virJSONValue *
qemuMonitorJSONQueryBlock(qemuMonitor *mon)
{
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *devices = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-block", NULL)))
return NULL;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0 ||
qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
goto cleanup;
devices = virJSONValueObjectStealArray(reply, "return");
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return devices;
}
static virJSONValue *
qemuMonitorJSONGetBlockDev(virJSONValue *devices,
size_t idx)
{
virJSONValue *dev = virJSONValueArrayGet(devices, idx);
if (!dev || virJSONValueGetType(dev) != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-block device entry was not in expected format"));
return NULL;
}
return dev;
}
static const char *
qemuMonitorJSONGetBlockDevDevice(virJSONValue *dev)
{
const char *thisdev;
if (!(thisdev = virJSONValueObjectGetString(dev, "device"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-block device entry was not in expected format"));
return NULL;
}
return thisdev;
}
static int
qemuMonitorJSONBlockInfoAdd(GHashTable *table,
struct qemuDomainDiskInfo *info,
const char *entryname)
{
struct qemuDomainDiskInfo *tmp = NULL;
int ret = -1;
tmp = g_new0(struct qemuDomainDiskInfo, 1);
*tmp = *info;
tmp->nodename = NULL;
if (info->nodename)
tmp->nodename = g_strdup(info->nodename);
if (virHashAddEntry(table, entryname, tmp) < 0)
goto cleanup;
tmp = NULL;
ret = 0;
cleanup:
if (tmp)
VIR_FREE(tmp->nodename);
VIR_FREE(tmp);
return ret;
}
int qemuMonitorJSONGetBlockInfo(qemuMonitor *mon,
GHashTable *table)
{
int ret = -1;
size_t i;
virJSONValue *devices;
if (!(devices = qemuMonitorJSONQueryBlock(mon)))
return -1;
for (i = 0; i < virJSONValueArraySize(devices); i++) {
virJSONValue *dev;
virJSONValue *image;
struct qemuDomainDiskInfo info = { false };
const char *thisdev;
2012-01-19 16:58:58 +00:00
const char *status;
const char *qdev;
if (!(dev = qemuMonitorJSONGetBlockDev(devices, i)))
goto cleanup;
if (!(thisdev = qemuMonitorJSONGetBlockDevDevice(dev)))
goto cleanup;
thisdev = qemuAliasDiskDriveSkipPrefix(thisdev);
qdev = virJSONValueObjectGetString(dev, "qdev");
if (*thisdev == '\0')
thisdev = NULL;
if (!qdev && !thisdev) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-block device entry was not in expected format"));
goto cleanup;
}
if (virJSONValueObjectGetBoolean(dev, "removable", &info.removable) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s value"),
"removable");
goto cleanup;
}
/* 'tray_open' is present only if the device has a tray */
if (virJSONValueObjectGetBoolean(dev, "tray_open", &info.tray_open) == 0)
info.tray = true;
/* presence of 'inserted' notifies that a medium is in the device */
if ((image = virJSONValueObjectGetObject(dev, "inserted"))) {
info.nodename = (char *) virJSONValueObjectGetString(image, "node-name");
} else {
info.empty = true;
}
2012-01-19 16:58:58 +00:00
/* Missing io-status indicates no error */
if ((status = virJSONValueObjectGetString(dev, "io-status"))) {
info.io_status = qemuMonitorBlockIOStatusToError(status);
if (info.io_status < 0)
2012-01-19 16:58:58 +00:00
goto cleanup;
}
if (thisdev &&
qemuMonitorJSONBlockInfoAdd(table, &info, thisdev) < 0)
goto cleanup;
if (qdev && STRNEQ_NULLABLE(thisdev, qdev) &&
qemuMonitorJSONBlockInfoAdd(table, &info, qdev) < 0)
goto cleanup;
}
ret = 0;
cleanup:
virJSONValueFree(devices);
return ret;
}
static qemuBlockStats *
qemuMonitorJSONBlockStatsCollectData(virJSONValue *dev,
int *nstats)
{
g_autofree qemuBlockStats *bstats = NULL;
virJSONValue *parent;
virJSONValue *parentstats;
virJSONValue *stats;
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
if ((stats = virJSONValueObjectGetObject(dev, "stats")) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats stats entry was not "
"in expected format"));
return NULL;
}
bstats = g_new0(qemuBlockStats, 1);
#define QEMU_MONITOR_BLOCK_STAT_GET(NAME, VAR, MANDATORY) \
if (MANDATORY || virJSONValueObjectHasKey(stats, NAME)) { \
(*nstats)++; \
if (virJSONValueObjectGetNumberUlong(stats, NAME, &VAR) < 0) { \
virReportError(VIR_ERR_INTERNAL_ERROR, \
_("cannot read %s statistic"), NAME); \
return NULL; \
} \
}
QEMU_MONITOR_BLOCK_STAT_GET("rd_bytes", bstats->rd_bytes, true);
QEMU_MONITOR_BLOCK_STAT_GET("wr_bytes", bstats->wr_bytes, true);
QEMU_MONITOR_BLOCK_STAT_GET("rd_operations", bstats->rd_req, true);
QEMU_MONITOR_BLOCK_STAT_GET("wr_operations", bstats->wr_req, true);
QEMU_MONITOR_BLOCK_STAT_GET("rd_total_time_ns", bstats->rd_total_times, false);
QEMU_MONITOR_BLOCK_STAT_GET("wr_total_time_ns", bstats->wr_total_times, false);
QEMU_MONITOR_BLOCK_STAT_GET("flush_operations", bstats->flush_req, false);
QEMU_MONITOR_BLOCK_STAT_GET("flush_total_time_ns", bstats->flush_total_times, false);
#undef QEMU_MONITOR_BLOCK_STAT_GET
if ((parent = virJSONValueObjectGetObject(dev, "parent")) &&
(parentstats = virJSONValueObjectGetObject(parent, "stats"))) {
if (virJSONValueObjectGetNumberUlong(parentstats, "wr_highest_offset",
&bstats->wr_highest_offset) == 0)
bstats->wr_highest_offset_valid = true;
}
return g_steal_pointer(&bstats);
}
static int
qemuMonitorJSONAddOneBlockStatsInfo(qemuBlockStats *bstats,
const char *name,
GHashTable *stats)
{
qemuBlockStats *copy = NULL;
copy = g_new0(qemuBlockStats, 1);
if (bstats)
*copy = *bstats;
if (virHashAddEntry(stats, name, copy) < 0) {
VIR_FREE(copy);
return -1;
}
return 0;
}
static int
qemuMonitorJSONGetOneBlockStatsInfo(virJSONValue *dev,
const char *dev_name,
int depth,
GHashTable *hash)
{
g_autofree qemuBlockStats *bstats = NULL;
int nstats = 0;
const char *qdevname = NULL;
const char *nodename = NULL;
g_autofree char *devicename = NULL;
virJSONValue *backing;
if (dev_name &&
!(devicename = qemuDomainStorageAlias(dev_name, depth)))
return -1;
qdevname = virJSONValueObjectGetString(dev, "qdev");
nodename = virJSONValueObjectGetString(dev, "node-name");
if (!devicename && !qdevname && !nodename) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats device entry was not in expected format"));
return -1;
}
if (!(bstats = qemuMonitorJSONBlockStatsCollectData(dev, &nstats)))
return -1;
if (devicename &&
qemuMonitorJSONAddOneBlockStatsInfo(bstats, devicename, hash) < 0)
return -1;
if (qdevname && STRNEQ_NULLABLE(qdevname, devicename) &&
qemuMonitorJSONAddOneBlockStatsInfo(bstats, qdevname, hash) < 0)
return -1;
if (nodename &&
qemuMonitorJSONAddOneBlockStatsInfo(bstats, nodename, hash) < 0)
return -1;
if ((backing = virJSONValueObjectGetObject(dev, "backing")) &&
qemuMonitorJSONGetOneBlockStatsInfo(backing, dev_name, depth + 1, hash) < 0)
return -1;
return nstats;
}
virJSONValue *
qemuMonitorJSONQueryBlockstats(qemuMonitor *mon)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-blockstats", NULL)))
return NULL;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return NULL;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
return NULL;
return virJSONValueObjectStealArray(reply, "return");
}
int
qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitor *mon,
GHashTable *hash)
{
int nstats = 0;
int rc;
size_t i;
g_autoptr(virJSONValue) devices = NULL;
if (!(devices = qemuMonitorJSONQueryBlockstats(mon)))
return -1;
for (i = 0; i < virJSONValueArraySize(devices); i++) {
virJSONValue *dev = virJSONValueArrayGet(devices, i);
const char *dev_name;
if (!dev || virJSONValueGetType(dev) != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats device entry was not "
"in expected format"));
return -1;
}
if (!(dev_name = virJSONValueObjectGetString(dev, "device"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats device entry was not "
"in expected format"));
return -1;
}
if (*dev_name == '\0')
dev_name = NULL;
rc = qemuMonitorJSONGetOneBlockStatsInfo(dev, dev_name, 0, hash);
if (rc < 0)
return -1;
if (rc > nstats)
nstats = rc;
}
return nstats;
}
static int
qemuMonitorJSONBlockStatsUpdateCapacityData(virJSONValue *image,
const char *name,
GHashTable *stats,
qemuBlockStats **entry)
{
qemuBlockStats *bstats;
if (!(bstats = virHashLookup(stats, name))) {
bstats = g_new0(qemuBlockStats, 1);
if (virHashAddEntry(stats, name, bstats) < 0) {
VIR_FREE(bstats);
return -1;
}
}
if (entry)
*entry = bstats;
/* failures can be ignored after this point */
if (virJSONValueObjectGetNumberUlong(image, "virtual-size",
&bstats->capacity) < 0)
return 0;
/* if actual-size is missing, image is not thin provisioned */
if (virJSONValueObjectGetNumberUlong(image, "actual-size",
&bstats->physical) < 0)
bstats->physical = bstats->capacity;
return 0;
}
static int
qemuMonitorJSONBlockStatsUpdateCapacityOne(virJSONValue *image,
const char *dev_name,
int depth,
GHashTable *stats)
{
g_autofree char *entry_name = qemuDomainStorageAlias(dev_name, depth);
virJSONValue *backing;
if (qemuMonitorJSONBlockStatsUpdateCapacityData(image, entry_name,
stats, NULL) < 0)
return -1;
if ((backing = virJSONValueObjectGetObject(image, "backing-image")) &&
qemuMonitorJSONBlockStatsUpdateCapacityOne(backing,
dev_name,
depth + 1,
stats) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONBlockStatsUpdateCapacity(qemuMonitor *mon,
GHashTable *stats)
{
size_t i;
g_autoptr(virJSONValue) devices = NULL;
if (!(devices = qemuMonitorJSONQueryBlock(mon)))
return -1;
for (i = 0; i < virJSONValueArraySize(devices); i++) {
virJSONValue *dev;
virJSONValue *inserted;
virJSONValue *image;
const char *dev_name;
if (!(dev = qemuMonitorJSONGetBlockDev(devices, i)))
return -1;
if (!(dev_name = qemuMonitorJSONGetBlockDevDevice(dev)))
return -1;
/* drive may be empty */
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
if (!(inserted = virJSONValueObjectGetObject(dev, "inserted")) ||
!(image = virJSONValueObjectGetObject(inserted, "image")))
continue;
if (qemuMonitorJSONBlockStatsUpdateCapacityOne(image, dev_name, 0, stats) < 0)
return -1;
}
return 0;
}
static int
qemuMonitorJSONBlockStatsUpdateCapacityBlockdevWorker(size_t pos G_GNUC_UNUSED,
virJSONValue *val,
void *opaque)
{
GHashTable *stats = opaque;
virJSONValue *image;
const char *nodename;
qemuBlockStats *entry;
if (!(nodename = virJSONValueObjectGetString(val, "node-name")) ||
!(image = virJSONValueObjectGetObject(val, "image"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-named-block-nodes entry was not in expected format"));
return -1;
}
if (qemuMonitorJSONBlockStatsUpdateCapacityData(image, nodename, stats, &entry) < 0)
return -1;
if (entry)
ignore_value(virJSONValueObjectGetNumberUlong(val, "write_threshold",
&entry->write_threshold));
return 1; /* we don't want to steal the value from the JSON array */
}
int
qemuMonitorJSONBlockStatsUpdateCapacityBlockdev(qemuMonitor *mon,
GHashTable *stats)
{
virJSONValue *nodes;
int ret = -1;
if (!(nodes = qemuMonitorJSONQueryNamedBlockNodes(mon, false)))
return -1;
if (virJSONValueArrayForeachSteal(nodes,
qemuMonitorJSONBlockStatsUpdateCapacityBlockdevWorker,
stats) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(nodes);
return ret;
}
static void
qemuMonitorJSONBlockNamedNodeDataBitmapFree(qemuBlockNamedNodeDataBitmap *bitmap)
{
if (!bitmap)
return;
g_free(bitmap->name);
g_free(bitmap);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuBlockNamedNodeDataBitmap,
qemuMonitorJSONBlockNamedNodeDataBitmapFree);
static void
qemuMonitorJSONBlockNamedNodeDataFree(qemuBlockNamedNodeData *data)
{
size_t i;
if (!data)
return;
for (i = 0; i < data->nbitmaps; i++)
qemuMonitorJSONBlockNamedNodeDataBitmapFree(data->bitmaps[i]);
g_free(data->bitmaps);
g_free(data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuBlockNamedNodeData, qemuMonitorJSONBlockNamedNodeDataFree);
static qemuBlockNamedNodeDataBitmap *
qemuMonitorJSONBlockGetNamedNodeDataBitmapOne(virJSONValue *val)
{
g_autoptr(qemuBlockNamedNodeDataBitmap) bitmap = NULL;
const char *name;
bitmap = g_new0(qemuBlockNamedNodeDataBitmap, 1);
if (!(name = virJSONValueObjectGetString(val, "name")))
return NULL;
bitmap->name = g_strdup(name);
ignore_value(virJSONValueObjectGetBoolean(val, "recording", &bitmap->recording));
ignore_value(virJSONValueObjectGetBoolean(val, "persistent", &bitmap->persistent));
ignore_value(virJSONValueObjectGetBoolean(val, "busy", &bitmap->busy));
ignore_value(virJSONValueObjectGetBoolean(val, "inconsistent", &bitmap->inconsistent));
ignore_value(virJSONValueObjectGetNumberUlong(val, "granularity", &bitmap->granularity));
ignore_value(virJSONValueObjectGetNumberUlong(val, "count", &bitmap->dirtybytes));
return g_steal_pointer(&bitmap);
}
static void
qemuMonitorJSONBlockGetNamedNodeDataBitmaps(virJSONValue *bitmaps,
qemuBlockNamedNodeData *data)
{
size_t nbitmaps = virJSONValueArraySize(bitmaps);
size_t i;
data->bitmaps = g_new0(qemuBlockNamedNodeDataBitmap *, nbitmaps);
for (i = 0; i < nbitmaps; i++) {
virJSONValue *bitmap = virJSONValueArrayGet(bitmaps, i);
qemuBlockNamedNodeDataBitmap *tmp;
if (!bitmap)
continue;
if (!(tmp = qemuMonitorJSONBlockGetNamedNodeDataBitmapOne(bitmap)))
continue;
data->bitmaps[data->nbitmaps++] = tmp;
}
}
static int
qemuMonitorJSONBlockGetNamedNodeDataWorker(size_t pos G_GNUC_UNUSED,
virJSONValue *val,
void *opaque)
{
GHashTable *nodes = opaque;
virJSONValue *img;
virJSONValue *bitmaps;
virJSONValue *format_specific;
const char *nodename;
g_autoptr(qemuBlockNamedNodeData) ent = NULL;
ent = g_new0(qemuBlockNamedNodeData, 1);
if (!(nodename = virJSONValueObjectGetString(val, "node-name")) ||
!(img = virJSONValueObjectGetObject(val, "image")))
goto broken;
if (virJSONValueObjectGetNumberUlong(img, "virtual-size", &ent->capacity) < 0)
goto broken;
/* if actual-size is missing, image is not thin provisioned */
if (virJSONValueObjectGetNumberUlong(img, "actual-size", &ent->physical) < 0)
ent->physical = ent->capacity;
/* try looking up the cluster size */
ignore_value(virJSONValueObjectGetNumberUlong(img, "cluster-size", &ent->clusterSize));
if ((bitmaps = virJSONValueObjectGetArray(val, "dirty-bitmaps")))
qemuMonitorJSONBlockGetNamedNodeDataBitmaps(bitmaps, ent);
/* query qcow2 format specific props */
if ((format_specific = virJSONValueObjectGetObject(img, "format-specific")) &&
STREQ_NULLABLE(virJSONValueObjectGetString(format_specific, "type"), "qcow2")) {
virJSONValue *qcow2props = virJSONValueObjectGetObject(format_specific, "data");
if (qcow2props &&
STREQ_NULLABLE(virJSONValueObjectGetString(qcow2props, "compat"), "0.10"))
ent->qcow2v2 = true;
}
if (virHashAddEntry(nodes, nodename, ent) < 0)
return -1;
ent = NULL;
return 1; /* we don't want to steal the value from the JSON array */
broken:
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-named-block-nodes entry was not in expected format"));
return -1;
}
GHashTable *
qemuMonitorJSONBlockGetNamedNodeDataJSON(virJSONValue *nodes)
{
g_autoptr(GHashTable) ret = NULL;
ret = virHashNew((virHashDataFree) qemuMonitorJSONBlockNamedNodeDataFree);
if (virJSONValueArrayForeachSteal(nodes,
qemuMonitorJSONBlockGetNamedNodeDataWorker,
ret) < 0)
return NULL;
return g_steal_pointer(&ret);
}
GHashTable *
qemuMonitorJSONBlockGetNamedNodeData(qemuMonitor *mon,
bool supports_flat)
{
g_autoptr(virJSONValue) nodes = NULL;
if (!(nodes = qemuMonitorJSONQueryNamedBlockNodes(mon, supports_flat)))
return NULL;
return qemuMonitorJSONBlockGetNamedNodeDataJSON(nodes);
}
int qemuMonitorJSONBlockResize(qemuMonitor *mon,
const char *device,
const char *nodename,
unsigned long long size)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
cmd = qemuMonitorJSONMakeCommand("block_resize",
"S:device", device,
"S:node-name", nodename,
"U:size", size,
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONSetPassword(qemuMonitor *mon,
const char *protocol,
const char *password,
const char *action_if_connected)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("set_password",
"s:protocol", protocol,
"s:password", password,
"s:connected", action_if_connected,
NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONExpirePassword(qemuMonitor *mon,
const char *protocol,
const char *expire_time)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("expire_password",
"s:protocol", protocol,
"s:time", expire_time,
NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONSetBalloon(qemuMonitor *mon,
unsigned long long newmem)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("balloon",
"U:value", newmem * 1024,
NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
/* See if balloon soft-failed */
if (qemuMonitorJSONHasError(reply, "DeviceNotActive") ||
qemuMonitorJSONHasError(reply, "KVMMissingCap")) {
ret = 0;
goto cleanup;
}
/* See if any other fatal error occurred */
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
/* Real success */
ret = 1;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONSetCPU(qemuMonitor *mon,
int cpu, bool online)
{
int ret = -1;
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
if (online) {
cmd = qemuMonitorJSONMakeCommand("cpu-add",
"i:id", cpu,
NULL);
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("vCPU unplug is not supported by this QEMU"));
goto cleanup;
}
if (!cmd)
goto cleanup;
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
ret = qemuMonitorJSONCheckError(cmd, reply);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/**
* Run QMP command to eject a media from ejectable device.
*
* Returns:
* -1 on error
* 0 on success
*/
int qemuMonitorJSONEjectMedia(qemuMonitor *mon,
const char *dev_name,
bool force)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("eject",
"s:device", dev_name,
"b:force", force ? 1 : 0,
NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONChangeMedia(qemuMonitor *mon,
const char *dev_name,
const char *newmedia,
const char *format)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
cmd = qemuMonitorJSONMakeCommand("change",
"s:device", dev_name,
"s:target", newmedia,
"S:arg", format,
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
static int qemuMonitorJSONSaveMemory(qemuMonitor *mon,
const char *cmdtype,
unsigned long long offset,
unsigned long long length,
const char *path)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand(cmdtype,
"U:val", offset,
"U:size", length,
"s:filename", path,
NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONSaveVirtualMemory(qemuMonitor *mon,
unsigned long long offset,
unsigned long long length,
const char *path)
{
return qemuMonitorJSONSaveMemory(mon, "memsave", offset, length, path);
}
int qemuMonitorJSONSavePhysicalMemory(qemuMonitor *mon,
unsigned long long offset,
unsigned long long length,
const char *path)
{
return qemuMonitorJSONSaveMemory(mon, "pmemsave", offset, length, path);
}
int qemuMonitorJSONSetMigrationSpeed(qemuMonitor *mon,
unsigned long bandwidth)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
cmd = qemuMonitorJSONMakeCommand("migrate_set_speed",
"U:value", bandwidth * 1024ULL * 1024ULL,
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONSetMigrationDowntime(qemuMonitor *mon,
unsigned long long downtime)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
cmd = qemuMonitorJSONMakeCommand("migrate_set_downtime",
"d:value", downtime / 1000.0,
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetMigrationCacheSize(qemuMonitor *mon,
unsigned long long *cacheSize)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
*cacheSize = 0;
cmd = qemuMonitorJSONMakeCommand("query-migrate-cache-size", NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_NUMBER) < 0)
goto cleanup;
if (virJSONValueObjectGetNumberUlong(reply, "return", cacheSize) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("invalid cache size in query-migrate-cache-size reply"));
goto cleanup;
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONSetMigrationCacheSize(qemuMonitor *mon,
unsigned long long cacheSize)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
cmd = qemuMonitorJSONMakeCommand("migrate-set-cache-size",
"U:value", cacheSize,
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetMigrationParams(qemuMonitor *mon,
virJSONValue **params)
{
int ret = -1;
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
*params = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-migrate-parameters", NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
goto cleanup;
*params = virJSONValueObjectStealObject(reply, "return");
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONSetMigrationParams(qemuMonitor *mon,
virJSONValue **params)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommandInternal("migrate-set-parameters", params)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
static int
qemuMonitorJSONGetMigrationStatsReply(virJSONValue *reply,
qemuMonitorMigrationStats *stats,
char **error)
{
virJSONValue *ret;
virJSONValue *ram;
virJSONValue *disk;
virJSONValue *comp;
const char *statusstr;
int rc;
double mbps;
const char *tmp;
ret = virJSONValueObjectGetObject(reply, "return");
if (!(statusstr = virJSONValueObjectGetString(ret, "status"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("info migration reply was missing return status"));
return -1;
}
stats->status = qemuMonitorMigrationStatusTypeFromString(statusstr);
if (stats->status < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected migration status in %s"), statusstr);
return -1;
}
ignore_value(virJSONValueObjectGetNumberUlong(ret, "total-time",
&stats->total_time));
if (stats->status == QEMU_MONITOR_MIGRATION_STATUS_COMPLETED) {
rc = virJSONValueObjectGetNumberUlong(ret, "downtime",
&stats->downtime);
} else {
rc = virJSONValueObjectGetNumberUlong(ret, "expected-downtime",
&stats->downtime);
}
if (rc == 0)
stats->downtime_set = true;
if (virJSONValueObjectGetNumberUlong(ret, "setup-time",
&stats->setup_time) == 0)
stats->setup_time_set = true;
ignore_value(virJSONValueObjectGetNumberInt(ret, "cpu-throttle-percentage",
&stats->cpu_throttle_percentage));
switch ((qemuMonitorMigrationStatus) stats->status) {
case QEMU_MONITOR_MIGRATION_STATUS_INACTIVE:
case QEMU_MONITOR_MIGRATION_STATUS_SETUP:
case QEMU_MONITOR_MIGRATION_STATUS_CANCELLED:
case QEMU_MONITOR_MIGRATION_STATUS_WAIT_UNPLUG:
case QEMU_MONITOR_MIGRATION_STATUS_LAST:
break;
case QEMU_MONITOR_MIGRATION_STATUS_ERROR:
if (error) {
tmp = virJSONValueObjectGetString(ret, "error-desc");
if (tmp)
*error = g_strdup(tmp);
}
break;
case QEMU_MONITOR_MIGRATION_STATUS_ACTIVE:
case QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY:
case QEMU_MONITOR_MIGRATION_STATUS_COMPLETED:
case QEMU_MONITOR_MIGRATION_STATUS_CANCELLING:
case QEMU_MONITOR_MIGRATION_STATUS_PRE_SWITCHOVER:
case QEMU_MONITOR_MIGRATION_STATUS_DEVICE:
ram = virJSONValueObjectGetObject(ret, "ram");
if (!ram) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("migration was active, but no RAM info was set"));
return -1;
}
if (virJSONValueObjectGetNumberUlong(ram, "transferred",
&stats->ram_transferred) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("migration was active, but RAM 'transferred' "
"data was missing"));
return -1;
}
if (virJSONValueObjectGetNumberUlong(ram, "remaining",
&stats->ram_remaining) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("migration was active, but RAM 'remaining' "
"data was missing"));
return -1;
}
if (virJSONValueObjectGetNumberUlong(ram, "total",
&stats->ram_total) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("migration was active, but RAM 'total' "
"data was missing"));
return -1;
}
if (virJSONValueObjectGetNumberDouble(ram, "mbps", &mbps) == 0 &&
mbps > 0) {
/* mpbs from QEMU reports Mbits/s (M as in 10^6 not Mi as 2^20) */
stats->ram_bps = mbps * (1000 * 1000 / 8);
}
if (virJSONValueObjectGetNumberUlong(ram, "duplicate",
&stats->ram_duplicate) == 0)
stats->ram_duplicate_set = true;
ignore_value(virJSONValueObjectGetNumberUlong(ram, "normal",
&stats->ram_normal));
ignore_value(virJSONValueObjectGetNumberUlong(ram, "normal-bytes",
&stats->ram_normal_bytes));
ignore_value(virJSONValueObjectGetNumberUlong(ram, "dirty-pages-rate",
&stats->ram_dirty_rate));
ignore_value(virJSONValueObjectGetNumberUlong(ram, "page-size",
&stats->ram_page_size));
ignore_value(virJSONValueObjectGetNumberUlong(ram, "dirty-sync-count",
&stats->ram_iteration));
ignore_value(virJSONValueObjectGetNumberUlong(ram, "postcopy-requests",
&stats->ram_postcopy_reqs));
disk = virJSONValueObjectGetObject(ret, "disk");
if (disk) {
rc = virJSONValueObjectGetNumberUlong(disk, "transferred",
&stats->disk_transferred);
if (rc < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("disk migration was active, but "
"'transferred' data was missing"));
return -1;
}
rc = virJSONValueObjectGetNumberUlong(disk, "remaining",
&stats->disk_remaining);
if (rc < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("disk migration was active, but 'remaining' "
"data was missing"));
return -1;
}
rc = virJSONValueObjectGetNumberUlong(disk, "total",
&stats->disk_total);
if (rc < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("disk migration was active, but 'total' "
"data was missing"));
return -1;
}
if (virJSONValueObjectGetNumberDouble(disk, "mbps", &mbps) == 0 &&
mbps > 0) {
/* mpbs from QEMU reports Mbits/s (M as in 10^6 not Mi as 2^20) */
stats->disk_bps = mbps * (1000 * 1000 / 8);
}
}
comp = virJSONValueObjectGetObject(ret, "xbzrle-cache");
if (comp) {
stats->xbzrle_set = true;
rc = virJSONValueObjectGetNumberUlong(comp, "cache-size",
&stats->xbzrle_cache_size);
if (rc < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("XBZRLE is active, but 'cache-size' data "
"was missing"));
return -1;
}
rc = virJSONValueObjectGetNumberUlong(comp, "bytes",
&stats->xbzrle_bytes);
if (rc < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("XBZRLE is active, but 'bytes' data "
"was missing"));
return -1;
}
rc = virJSONValueObjectGetNumberUlong(comp, "pages",
&stats->xbzrle_pages);
if (rc < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("XBZRLE is active, but 'pages' data "
"was missing"));
return -1;
}
rc = virJSONValueObjectGetNumberUlong(comp, "cache-miss",
&stats->xbzrle_cache_miss);
if (rc < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("XBZRLE is active, but 'cache-miss' data "
"was missing"));
return -1;
}
rc = virJSONValueObjectGetNumberUlong(comp, "overflow",
&stats->xbzrle_overflow);
if (rc < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("XBZRLE is active, but 'overflow' data "
"was missing"));
return -1;
}
}
break;
}
return 0;
}
int qemuMonitorJSONGetMigrationStats(qemuMonitor *mon,
qemuMonitorMigrationStats *stats,
char **error)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("query-migrate",
NULL);
virJSONValue *reply = NULL;
memset(stats, 0, sizeof(*stats));
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
goto cleanup;
if (qemuMonitorJSONGetMigrationStatsReply(reply, stats, error) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONMigrate(qemuMonitor *mon,
unsigned int flags,
const char *uri)
{
int ret = -1;
virJSONValue *cmd =
qemuMonitorJSONMakeCommand("migrate",
"b:detach", flags & QEMU_MONITOR_MIGRATE_BACKGROUND ? 1 : 0,
"b:blk", flags & QEMU_MONITOR_MIGRATE_NON_SHARED_DISK ? 1 : 0,
"b:inc", flags & QEMU_MONITOR_MIGRATE_NON_SHARED_INC ? 1 : 0,
"s:uri", uri,
NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONMigrateCancel(qemuMonitor *mon)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("migrate_cancel", NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/* qemuMonitorJSONQueryDump:
* @mon: Monitor pointer
* @stats: Monitor dump stats
*
* Attempt to make a "query-dump" call, check for errors, and get/return
* the current from the reply
*
* Returns: 0 on success, -1 on failure
*/
int
qemuMonitorJSONQueryDump(qemuMonitor *mon,
qemuMonitorDumpStats *stats)
{
virJSONValue *cmd = qemuMonitorJSONMakeCommand("query-dump", NULL);
virJSONValue *reply = NULL;
virJSONValue *result = NULL;
int ret = -1;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
goto cleanup;
result = virJSONValueObjectGetObject(reply, "return");
ret = qemuMonitorJSONExtractDumpStats(result, stats);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetDumpGuestMemoryCapability(qemuMonitor *mon,
const char *capability)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *caps;
virJSONValue *formats;
size_t i;
if (!(cmd = qemuMonitorJSONMakeCommand("query-dump-guest-memory-capability",
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
goto cleanup;
caps = virJSONValueObjectGetObject(reply, "return");
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
if (!(formats = virJSONValueObjectGetArray(caps, "formats"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing supported dump formats"));
goto cleanup;
}
for (i = 0; i < virJSONValueArraySize(formats); i++) {
virJSONValue *dumpformat = virJSONValueArrayGet(formats, i);
if (!dumpformat || virJSONValueGetType(dumpformat) != VIR_JSON_TYPE_STRING) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing entry in supported dump formats"));
goto cleanup;
}
if (STREQ(virJSONValueGetString(dumpformat), capability)) {
ret = 1;
goto cleanup;
}
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONDump(qemuMonitor *mon,
const char *protocol,
const char *dumpformat,
bool detach)
{
int ret = -1;
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
cmd = qemuMonitorJSONMakeCommand("dump-guest-memory",
"b:paging", false,
"s:protocol", protocol,
"S:format", dumpformat,
"B:detach", detach,
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONGraphicsRelocate(qemuMonitor *mon,
int type,
const char *hostname,
int port,
int tlsPort,
const char *tlsSubject)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("client_migrate_info",
"s:protocol",
(type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE ? "spice" : "vnc"),
"s:hostname", hostname,
"i:port", port,
"i:tls-port", tlsPort,
"S:cert-subject", tlsSubject,
NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
static int
qemuAddfdInfoParse(virJSONValue *msg,
qemuMonitorAddFdInfo *fdinfo)
{
virJSONValue *returnObj;
if (!(returnObj = virJSONValueObjectGetObject(msg, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid return data in add-fd response"));
return -1;
}
if (virJSONValueObjectGetNumberInt(returnObj, "fd", &fdinfo->fd) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid fd in add-fd response"));
return -1;
}
if (virJSONValueObjectGetNumberInt(returnObj, "fdset-id", &fdinfo->fdset) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid fdset-id in add-fd response"));
return -1;
}
return 0;
}
/* if fdset is negative, qemu will create a new fdset and add the fd to that */
int qemuMonitorJSONAddFileHandleToSet(qemuMonitor *mon,
int fd,
int fdset,
const char *opaque,
qemuMonitorAddFdInfo *fdinfo)
{
g_autoptr(virJSONValue) args = NULL;
g_autoptr(virJSONValue) reply = NULL;
g_autoptr(virJSONValue) cmd = NULL;
if (virJSONValueObjectCreate(&args, "S:opaque", opaque, NULL) < 0)
return -1;
if (fdset >= 0 &&
virJSONValueObjectAdd(args, "j:fdset-id", fdset, NULL) < 0) {
return -1;
}
if (!(cmd = qemuMonitorJSONMakeCommandInternal("add-fd", &args)))
return -1;
if (qemuMonitorJSONCommandWithFd(mon, cmd, fd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
if (qemuAddfdInfoParse(reply, fdinfo) < 0)
return -1;
return 0;
}
static int
qemuMonitorJSONQueryFdsetsParse(virJSONValue *msg,
qemuMonitorFdsets **fdsets)
{
virJSONValue *returnArray;
virJSONValue *entry;
size_t i;
g_autoptr(qemuMonitorFdsets) sets = g_new0(qemuMonitorFdsets, 1);
int ninfo;
returnArray = virJSONValueObjectGetArray(msg, "return");
ninfo = virJSONValueArraySize(returnArray);
if (ninfo > 0)
sets->fdsets = g_new0(qemuMonitorFdsetInfo, ninfo);
sets->nfdsets = ninfo;
for (i = 0; i < ninfo; i++) {
size_t j;
const char *tmp;
virJSONValue *fdarray;
qemuMonitorFdsetInfo *fdsetinfo = &sets->fdsets[i];
if (!(entry = virJSONValueArrayGet(returnArray, i))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-fdsets return data missing fdset array element"));
return -1;
}
if (virJSONValueObjectGetNumberInt(entry, "fdset-id", &fdsetinfo->id) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-fdsets reply was missing 'fdset-id'"));
return -1;
}
fdarray = virJSONValueObjectGetArray(entry, "fds");
fdsetinfo->nfds = virJSONValueArraySize(fdarray);
if (fdsetinfo->nfds > 0)
fdsetinfo->fds = g_new0(qemuMonitorFdsetFdInfo, fdsetinfo->nfds);
for (j = 0; j < fdsetinfo->nfds; j++) {
qemuMonitorFdsetFdInfo *fdinfo = &fdsetinfo->fds[j];
virJSONValue *fdentry;
if (!(fdentry = virJSONValueArrayGet(fdarray, j))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-fdsets return data missing fd array element"));
return -1;
}
if (virJSONValueObjectGetNumberInt(fdentry, "fd", &fdinfo->fd) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-fdsets return data missing 'fd'"));
return -1;
}
/* opaque is optional and may be missing */
tmp = virJSONValueObjectGetString(fdentry, "opaque");
if (tmp)
fdinfo->opaque = g_strdup(tmp);
}
}
*fdsets = g_steal_pointer(&sets);
return 0;
}
int qemuMonitorJSONQueryFdsets(qemuMonitor *mon,
qemuMonitorFdsets **fdsets)
{
g_autoptr(virJSONValue) reply = NULL;
g_autoptr(virJSONValue) cmd = qemuMonitorJSONMakeCommand("query-fdsets",
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
if (qemuMonitorJSONQueryFdsetsParse(reply, fdsets) < 0)
return -1;
return 0;
}
int qemuMonitorJSONRemoveFdset(qemuMonitor *mon,
int fdset)
{
g_autoptr(virJSONValue) reply = NULL;
g_autoptr(virJSONValue) cmd = qemuMonitorJSONMakeCommand("remove-fd",
"i:fdset-id", fdset,
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
int qemuMonitorJSONSendFileHandle(qemuMonitor *mon,
const char *fdname,
int fd)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("getfd",
"s:fdname", fdname,
NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommandWithFd(mon, cmd, fd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONCloseFileHandle(qemuMonitor *mon,
const char *fdname)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("closefd",
"s:fdname", fdname,
NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONAddNetdev(qemuMonitor *mon,
virJSONValue **props)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommandInternal("netdev_add", props)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONRemoveNetdev(qemuMonitor *mon,
const char *alias)
{
g_autoptr(virJSONValue) cmd = qemuMonitorJSONMakeCommand("netdev_del",
"s:id", alias,
NULL);
g_autoptr(virJSONValue) reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
static int
qemuMonitorJSONQueryRxFilterParse(virJSONValue *msg,
virNetDevRxFilter **filter)
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
{
int ret = -1;
const char *tmp;
virJSONValue *returnArray;
virJSONValue *entry;
virJSONValue *table;
virJSONValue *element;
size_t nTable;
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
size_t i;
g_autoptr(virNetDevRxFilter) fil = virNetDevRxFilterNew();
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
if (!fil)
goto cleanup;
returnArray = virJSONValueObjectGetArray(msg, "return");
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
if (!(entry = virJSONValueArrayGet(returnArray, 0))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query -rx-filter return data missing array element"));
goto cleanup;
}
if (!(tmp = virJSONValueObjectGetString(entry, "name"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid name "
"in query-rx-filter response"));
goto cleanup;
}
fil->name = g_strdup(tmp);
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
if ((!(tmp = virJSONValueObjectGetString(entry, "main-mac"))) ||
virMacAddrParse(tmp, &fil->mac) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'main-mac' "
"in query-rx-filter response"));
goto cleanup;
}
if (virJSONValueObjectGetBoolean(entry, "promiscuous",
&fil->promiscuous) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'promiscuous' "
"in query-rx-filter response"));
goto cleanup;
}
if (virJSONValueObjectGetBoolean(entry, "broadcast-allowed",
&fil->broadcastAllowed) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'broadcast-allowed' "
"in query-rx-filter response"));
goto cleanup;
}
if ((!(tmp = virJSONValueObjectGetString(entry, "unicast"))) ||
((fil->unicast.mode
= virNetDevRxFilterModeTypeFromString(tmp)) < 0)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'unicast' "
"in query-rx-filter response"));
goto cleanup;
}
if (virJSONValueObjectGetBoolean(entry, "unicast-overflow",
&fil->unicast.overflow) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'unicast-overflow' "
"in query-rx-filter response"));
goto cleanup;
}
if ((!(table = virJSONValueObjectGet(entry, "unicast-table"))) ||
(!virJSONValueIsArray(table))) {
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'unicast-table' array "
"in query-rx-filter response"));
goto cleanup;
}
nTable = virJSONValueArraySize(table);
fil->unicast.table = g_new0(virMacAddr, nTable);
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
for (i = 0; i < nTable; i++) {
if (!(element = virJSONValueArrayGet(table, i)) ||
!(tmp = virJSONValueGetString(element))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Missing or invalid element %zu of 'unicast' "
"list in query-rx-filter response"), i);
goto cleanup;
}
if (virMacAddrParse(tmp, &fil->unicast.table[i]) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("invalid mac address '%s' in 'unicast-table' "
"array in query-rx-filter response"), tmp);
goto cleanup;
}
}
fil->unicast.nTable = nTable;
if ((!(tmp = virJSONValueObjectGetString(entry, "multicast"))) ||
((fil->multicast.mode
= virNetDevRxFilterModeTypeFromString(tmp)) < 0)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'multicast' "
"in query-rx-filter response"));
goto cleanup;
}
if (virJSONValueObjectGetBoolean(entry, "multicast-overflow",
&fil->multicast.overflow) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'multicast-overflow' "
"in query-rx-filter response"));
goto cleanup;
}
if ((!(table = virJSONValueObjectGet(entry, "multicast-table"))) ||
(!virJSONValueIsArray(table))) {
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'multicast-table' array "
"in query-rx-filter response"));
goto cleanup;
}
nTable = virJSONValueArraySize(table);
fil->multicast.table = g_new0(virMacAddr, nTable);
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
for (i = 0; i < nTable; i++) {
if (!(element = virJSONValueArrayGet(table, i)) ||
!(tmp = virJSONValueGetString(element))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Missing or invalid element %zu of 'multicast' "
"list in query-rx-filter response"), i);
goto cleanup;
}
if (virMacAddrParse(tmp, &fil->multicast.table[i]) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("invalid mac address '%s' in 'multicast-table' "
"array in query-rx-filter response"), tmp);
goto cleanup;
}
}
fil->multicast.nTable = nTable;
if ((!(tmp = virJSONValueObjectGetString(entry, "vlan"))) ||
((fil->vlan.mode
= virNetDevRxFilterModeTypeFromString(tmp)) < 0)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'vlan' "
"in query-rx-filter response"));
goto cleanup;
}
if ((!(table = virJSONValueObjectGet(entry, "vlan-table"))) ||
(!virJSONValueIsArray(table))) {
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'vlan-table' array "
"in query-rx-filter response"));
goto cleanup;
}
nTable = virJSONValueArraySize(table);
fil->vlan.table = g_new0(unsigned int, nTable);
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
for (i = 0; i < nTable; i++) {
if (!(element = virJSONValueArrayGet(table, i)) ||
virJSONValueGetNumberUint(element, &fil->vlan.table[i]) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Missing or invalid element %zu of 'vlan-table' "
"array in query-rx-filter response"), i);
goto cleanup;
}
}
fil->vlan.nTable = nTable;
*filter = g_steal_pointer(&fil);
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
ret = 0;
cleanup:
return ret;
}
int
qemuMonitorJSONQueryRxFilter(qemuMonitor *mon, const char *alias,
virNetDevRxFilter **filter)
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("query-rx-filter",
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
"s:name", alias,
NULL);
virJSONValue *reply = NULL;
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
if (!cmd)
goto cleanup;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
goto cleanup;
qemu: qemuMonitorQueryRxFilter - retrieve guest netdev rx-filter This function can be called at any time to get the current status of a guest's network device rx-filter. In particular it is useful to call after libvirt recieves a NIC_RX_FILTER_CHANGED event - this event only tells you that something has changed in the rx-filter, the details are retrieved with the query-rx-filter monitor command (only available in the json monitor). The command sent to the qemu monitor looks like this: {"execute":"query-rx-filter", "arguments": {"name":"net2"} }' and the results will look something like this: { "return": [ { "promiscuous": false, "name": "net2", "main-mac": "52:54:00:98:2d:e3", "unicast": "normal", "vlan": "normal", "vlan-table": [ 42, 0 ], "unicast-table": [ ], "multicast": "normal", "multicast-overflow": false, "unicast-overflow": false, "multicast-table": [ "33:33:ff:98:2d:e3", "01:80:c2:00:00:21", "01:00:5e:00:00:fb", "33:33:ff:98:2d:e2", "01:00:5e:00:00:01", "33:33:00:00:00:01" ], "broadcast-allowed": false } ], "id": "libvirt-14" } This is all parsed from JSON into a virNetDevRxFilter object for easier consumption. (unicast-table is usually empty, but is also an array of mac addresses similar to multicast-table). (NB: LIBNL_CFLAGS was added to tests/Makefile.am because virnetdev.h now includes util/virnetlink.h, which includes netlink/msg.h when appropriate. Without LIBNL_CFLAGS, gcc can't find that file (if libnl/netlink isn't available, LIBNL_CFLAGS will be empty and virnetlink.h won't try to include netlink/msg.h anyway).)
2014-09-22 16:19:41 +00:00
if (qemuMonitorJSONQueryRxFilterParse(reply, filter) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/*
* Example return data
*
* {"return": [
* {"filename": "stdio", "label": "monitor"},
* {"filename": "pty:/dev/pts/6", "label": "serial0", "frontend-open": true},
* {"filename": "pty:/dev/pts/7", "label": "parallel0"}
* ]}
*
*/
static int
qemuMonitorJSONExtractChardevInfo(virJSONValue *reply,
GHashTable *info)
{
virJSONValue *data;
int ret = -1;
size_t i;
qemuMonitorChardevInfo *entry = NULL;
data = virJSONValueObjectGetArray(reply, "return");
for (i = 0; i < virJSONValueArraySize(data); i++) {
virJSONValue *chardev = virJSONValueArrayGet(data, i);
const char *type;
const char *alias;
bool connected;
if (!chardev) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("character device information was missing array element"));
goto cleanup;
}
if (!(alias = virJSONValueObjectGetString(chardev, "label"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("character device information was missing label"));
goto cleanup;
}
if (!(type = virJSONValueObjectGetString(chardev, "filename"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("character device information was missing filename"));
goto cleanup;
}
entry = g_new0(qemuMonitorChardevInfo, 1);
if (STRPREFIX(type, "pty:"))
entry->ptyPath = g_strdup(type + strlen("pty:"));
if (virJSONValueObjectGetBoolean(chardev, "frontend-open", &connected) == 0) {
if (connected)
entry->state = VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED;
else
entry->state = VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED;
}
if (virHashAddEntry(info, alias, entry) < 0) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("failed to add chardev '%s' info"), alias);
goto cleanup;
}
entry = NULL;
}
ret = 0;
cleanup:
if (entry) {
VIR_FREE(entry->ptyPath);
VIR_FREE(entry);
}
return ret;
}
int
qemuMonitorJSONGetChardevInfo(qemuMonitor *mon,
GHashTable *info)
{
int ret = -1;
virJSONValue *cmd = qemuMonitorJSONMakeCommand("query-chardev",
NULL);
virJSONValue *reply = NULL;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
goto cleanup;
ret = qemuMonitorJSONExtractChardevInfo(reply, info);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONDelDevice(qemuMonitor *mon,
const char *devalias)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
cmd = qemuMonitorJSONMakeCommand("device_del",
"s:id", devalias,
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONHasError(reply, "DeviceNotFound")) {
ret = -2;
goto cleanup;
}
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONAddDeviceProps(qemuMonitor *mon,
virJSONValue **props)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("device_add", NULL)))
return -1;
if (virJSONValueObjectAppend(cmd, "arguments", props) < 0)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONAddObject(qemuMonitor *mon,
virJSONValue **props)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommandInternal("object-add", props)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONDelObject(qemuMonitor *mon,
const char *objalias,
bool report_error)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("object-del", "s:id", objalias, NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckErrorFull(cmd, reply, report_error) < 0)
return -1;
return 0;
}
blockjob: add qemu capabilities related to block jobs 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, and has been using patches similar to these upstream patches for several months now.] 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 error policy, setting granularity, or using a persistent bitmap, may later require a new libvirt API virDomainBlockCopy. I will wait to add that API until we know more about what qemu 1.3 will finally provide. This patch caters only to the upstream qemu 1.3 interface, although I have proven that the changes for RHEL 6.3 can be isolated to just qemu_monitor_json.c, and the rest of this series will gracefully handle either interface once the JSON differences are papered over in a downstream patch. For consistency with other block job commands, libvirt must handle the bandwidth argument as MiB/sec from the user, even though qemu exposes the speed argument as bytes/sec; then again, qemu rounds up to cluster size internally, so using MiB hides the worst effects of that rounding if you pass small numbers. [1]https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg04123.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.
2012-09-28 23:29:53 +00:00
/* speed is in bytes/sec */
int
qemuMonitorJSONDriveMirror(qemuMonitor *mon,
blockjob: add qemu capabilities related to block jobs 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, and has been using patches similar to these upstream patches for several months now.] 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 error policy, setting granularity, or using a persistent bitmap, may later require a new libvirt API virDomainBlockCopy. I will wait to add that API until we know more about what qemu 1.3 will finally provide. This patch caters only to the upstream qemu 1.3 interface, although I have proven that the changes for RHEL 6.3 can be isolated to just qemu_monitor_json.c, and the rest of this series will gracefully handle either interface once the JSON differences are papered over in a downstream patch. For consistency with other block job commands, libvirt must handle the bandwidth argument as MiB/sec from the user, even though qemu exposes the speed argument as bytes/sec; then again, qemu rounds up to cluster size internally, so using MiB hides the worst effects of that rounding if you pass small numbers. [1]https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg04123.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.
2012-09-28 23:29:53 +00:00
const char *device, const char *file,
const char *format, unsigned long long speed,
blockcopy: add qemu implementation of new tunables Upstream qemu 1.4 added some drive-mirror tunables not present when it was first introduced in 1.3. Management apps may want to set these in some cases (for example, without tuning granularity down to sector size, a copy may end up occupying more bytes than the original because an entire cluster is copied even when only a sector within the cluster is dirty, although tuning it down results in more CPU time to do the copy). I haven't personally needed to use the parameters, but since they exist, and since the new API supports virTypedParams, we might as well expose them. Since the tuning parameters aren't often used, and omitted from the QMP command when unspecified, I think it is safe to rely on qemu 1.3 to issue an error about them being unsupported, rather than trying to create a new capability bit in libvirt. Meanwhile, all versions of qemu from 1.4 to 2.1 have a bug where a bad granularity (such as non-power-of-2) gives a poor message: error: internal error: unable to execute QEMU command 'drive-mirror': Invalid parameter 'drive-virtio-disk0' because of abuse of QERR_INVALID_PARAMETER (which is supposed to name the parameter that was given a bad value, rather than the value passed to some other parameter). I don't see that a capability check will help, so we'll just live with it (and it has since been improved in upstream qemu). * src/qemu/qemu_monitor.h (qemuMonitorDriveMirror): Add parameters. * src/qemu/qemu_monitor.c (qemuMonitorDriveMirror): Likewise. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDriveMirror): Likewise. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDriveMirror): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCopyCommon): Likewise. (qemuDomainBlockRebase, qemuDomainBlockCopy): Adjust callers. * src/qemu/qemu_migration.c (qemuMigrationDriveMirror): Likewise. * tests/qemumonitorjsontest.c (qemuMonitorJSONDriveMirror): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-09-08 20:53:12 +00:00
unsigned int granularity,
unsigned long long buf_size,
bool shallow,
bool reuse)
blockjob: add qemu capabilities related to block jobs 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, and has been using patches similar to these upstream patches for several months now.] 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 error policy, setting granularity, or using a persistent bitmap, may later require a new libvirt API virDomainBlockCopy. I will wait to add that API until we know more about what qemu 1.3 will finally provide. This patch caters only to the upstream qemu 1.3 interface, although I have proven that the changes for RHEL 6.3 can be isolated to just qemu_monitor_json.c, and the rest of this series will gracefully handle either interface once the JSON differences are papered over in a downstream patch. For consistency with other block job commands, libvirt must handle the bandwidth argument as MiB/sec from the user, even though qemu exposes the speed argument as bytes/sec; then again, qemu rounds up to cluster size internally, so using MiB hides the worst effects of that rounding if you pass small numbers. [1]https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg04123.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.
2012-09-28 23:29:53 +00:00
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
blockjob: add qemu capabilities related to block jobs 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, and has been using patches similar to these upstream patches for several months now.] 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 error policy, setting granularity, or using a persistent bitmap, may later require a new libvirt API virDomainBlockCopy. I will wait to add that API until we know more about what qemu 1.3 will finally provide. This patch caters only to the upstream qemu 1.3 interface, although I have proven that the changes for RHEL 6.3 can be isolated to just qemu_monitor_json.c, and the rest of this series will gracefully handle either interface once the JSON differences are papered over in a downstream patch. For consistency with other block job commands, libvirt must handle the bandwidth argument as MiB/sec from the user, even though qemu exposes the speed argument as bytes/sec; then again, qemu rounds up to cluster size internally, so using MiB hides the worst effects of that rounding if you pass small numbers. [1]https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg04123.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.
2012-09-28 23:29:53 +00:00
cmd = qemuMonitorJSONMakeCommand("drive-mirror",
"s:device", device,
"s:target", file,
"Y:speed", speed,
blockcopy: add qemu implementation of new tunables Upstream qemu 1.4 added some drive-mirror tunables not present when it was first introduced in 1.3. Management apps may want to set these in some cases (for example, without tuning granularity down to sector size, a copy may end up occupying more bytes than the original because an entire cluster is copied even when only a sector within the cluster is dirty, although tuning it down results in more CPU time to do the copy). I haven't personally needed to use the parameters, but since they exist, and since the new API supports virTypedParams, we might as well expose them. Since the tuning parameters aren't often used, and omitted from the QMP command when unspecified, I think it is safe to rely on qemu 1.3 to issue an error about them being unsupported, rather than trying to create a new capability bit in libvirt. Meanwhile, all versions of qemu from 1.4 to 2.1 have a bug where a bad granularity (such as non-power-of-2) gives a poor message: error: internal error: unable to execute QEMU command 'drive-mirror': Invalid parameter 'drive-virtio-disk0' because of abuse of QERR_INVALID_PARAMETER (which is supposed to name the parameter that was given a bad value, rather than the value passed to some other parameter). I don't see that a capability check will help, so we'll just live with it (and it has since been improved in upstream qemu). * src/qemu/qemu_monitor.h (qemuMonitorDriveMirror): Add parameters. * src/qemu/qemu_monitor.c (qemuMonitorDriveMirror): Likewise. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDriveMirror): Likewise. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDriveMirror): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCopyCommon): Likewise. (qemuDomainBlockRebase, qemuDomainBlockCopy): Adjust callers. * src/qemu/qemu_migration.c (qemuMigrationDriveMirror): Likewise. * tests/qemumonitorjsontest.c (qemuMonitorJSONDriveMirror): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-09-08 20:53:12 +00:00
"z:granularity", granularity,
"P:buf-size", buf_size,
blockjob: add qemu capabilities related to block jobs 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, and has been using patches similar to these upstream patches for several months now.] 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 error policy, setting granularity, or using a persistent bitmap, may later require a new libvirt API virDomainBlockCopy. I will wait to add that API until we know more about what qemu 1.3 will finally provide. This patch caters only to the upstream qemu 1.3 interface, although I have proven that the changes for RHEL 6.3 can be isolated to just qemu_monitor_json.c, and the rest of this series will gracefully handle either interface once the JSON differences are papered over in a downstream patch. For consistency with other block job commands, libvirt must handle the bandwidth argument as MiB/sec from the user, even though qemu exposes the speed argument as bytes/sec; then again, qemu rounds up to cluster size internally, so using MiB hides the worst effects of that rounding if you pass small numbers. [1]https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg04123.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.
2012-09-28 23:29:53 +00:00
"s:sync", shallow ? "top" : "full",
"s:mode", reuse ? "existing" : "absolute-paths",
"S:format", format,
blockjob: add qemu capabilities related to block jobs 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, and has been using patches similar to these upstream patches for several months now.] 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 error policy, setting granularity, or using a persistent bitmap, may later require a new libvirt API virDomainBlockCopy. I will wait to add that API until we know more about what qemu 1.3 will finally provide. This patch caters only to the upstream qemu 1.3 interface, although I have proven that the changes for RHEL 6.3 can be isolated to just qemu_monitor_json.c, and the rest of this series will gracefully handle either interface once the JSON differences are papered over in a downstream patch. For consistency with other block job commands, libvirt must handle the bandwidth argument as MiB/sec from the user, even though qemu exposes the speed argument as bytes/sec; then again, qemu rounds up to cluster size internally, so using MiB hides the worst effects of that rounding if you pass small numbers. [1]https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg04123.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.
2012-09-28 23:29:53 +00:00
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
blockjob: add qemu capabilities related to block jobs 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, and has been using patches similar to these upstream patches for several months now.] 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 error policy, setting granularity, or using a persistent bitmap, may later require a new libvirt API virDomainBlockCopy. I will wait to add that API until we know more about what qemu 1.3 will finally provide. This patch caters only to the upstream qemu 1.3 interface, although I have proven that the changes for RHEL 6.3 can be isolated to just qemu_monitor_json.c, and the rest of this series will gracefully handle either interface once the JSON differences are papered over in a downstream patch. For consistency with other block job commands, libvirt must handle the bandwidth argument as MiB/sec from the user, even though qemu exposes the speed argument as bytes/sec; then again, qemu rounds up to cluster size internally, so using MiB hides the worst effects of that rounding if you pass small numbers. [1]https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg04123.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.
2012-09-28 23:29:53 +00:00
return qemuMonitorJSONCheckError(cmd, reply);
blockjob: add qemu capabilities related to block jobs 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, and has been using patches similar to these upstream patches for several months now.] 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 error policy, setting granularity, or using a persistent bitmap, may later require a new libvirt API virDomainBlockCopy. I will wait to add that API until we know more about what qemu 1.3 will finally provide. This patch caters only to the upstream qemu 1.3 interface, although I have proven that the changes for RHEL 6.3 can be isolated to just qemu_monitor_json.c, and the rest of this series will gracefully handle either interface once the JSON differences are papered over in a downstream patch. For consistency with other block job commands, libvirt must handle the bandwidth argument as MiB/sec from the user, even though qemu exposes the speed argument as bytes/sec; then again, qemu rounds up to cluster size internally, so using MiB hides the worst effects of that rounding if you pass small numbers. [1]https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg04123.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.
2012-09-28 23:29:53 +00:00
}
int
qemuMonitorJSONBlockdevMirror(qemuMonitor *mon,
const char *jobname,
bool persistjob,
const char *device,
const char *target,
unsigned long long speed,
unsigned int granularity,
unsigned long long buf_size,
bool shallow)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
virTristateBool autofinalize = VIR_TRISTATE_BOOL_ABSENT;
virTristateBool autodismiss = VIR_TRISTATE_BOOL_ABSENT;
if (persistjob) {
autofinalize = VIR_TRISTATE_BOOL_YES;
autodismiss = VIR_TRISTATE_BOOL_NO;
}
cmd = qemuMonitorJSONMakeCommand("blockdev-mirror",
"S:job-id", jobname,
"s:device", device,
"s:target", target,
"Y:speed", speed,
"z:granularity", granularity,
"P:buf-size", buf_size,
"s:sync", shallow ? "top" : "full",
"T:auto-finalize", autofinalize,
"T:auto-dismiss", autodismiss,
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
return qemuMonitorJSONCheckError(cmd, reply);
}
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.
2012-03-17 04:17:28 +00:00
int
qemuMonitorJSONTransaction(qemuMonitor *mon, virJSONValue **actions)
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.
2012-03-17 04:17:28 +00:00
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
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.
2012-03-17 04:17:28 +00:00
cmd = qemuMonitorJSONMakeCommand("transaction",
"a:actions", actions,
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.
2012-03-17 04:17:28 +00:00
NULL);
if (!cmd)
goto cleanup;
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.
2012-03-17 04:17:28 +00:00
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
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.
2012-03-17 04:17:28 +00:00
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
blockjob: allow omitted arguments to QMP block-commit We are about to turn on support for active block commit. Although qemu 2.0 was the first version to mostly support it, that version mis-handles 0-length files, and doesn't have anything available for easy probing. But qemu 2.1 fixed bugs, and made life simpler by letting the 'top' argument be optional. Unless someone begs for active commit with qemu 2.0, for now we are just going to enable it only by probing for qemu 2.1 behavior (anyone backporting active commit can also backport the optional argument behavior). This requires qemu.git commit 7676e2c597000eff3a7233b40cca768b358f9bc9. Although all our actual uses of block-commit supply arguments for both base and top, we can omit both arguments and use a bogus device string to trigger an interesting behavior in qemu. All QMP commands first do argument validation, failing with GenericError if a mandatory argument is missing. Once that passes, the code in the specific command gets to do further checking, and the qemu developers made sure that if device is the only supplied argument, then the block-commit code will look up the device first, with a failure of DeviceNotFound, before attempting any further argument validation (most other validations fail with GenericError). Thus, the category of error class can reliably be used to decipher whether the top argument was optional, which in turn implies a working active commit. Since we expect our bogus device string to trigger an error either way, the code is written to return a distinct return value without spamming the logs. * src/qemu/qemu_monitor.h (qemuMonitorSupportsActiveCommit): New prototype. * src/qemu/qemu_monitor.c (qemuMonitorSupportsActiveCommit): Implement it. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONBlockCommit): Allow NULL for top and base, for probing purposes. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONBlockCommit): Likewise, implementing the probe. * tests/qemumonitorjsontest.c (mymain): Enable... (testQemuMonitorJSONqemuMonitorSupportsActiveCommit): ...a new test. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-06-17 03:42:49 +00:00
/* speed is in bytes/sec. Returns 0 on success, -1 with error message
* emitted on failure. */
int
qemuMonitorJSONBlockCommit(qemuMonitor *mon,
const char *device,
const char *jobname,
bool persistjob,
const char *top,
const char *topNode,
const char *base,
const char *baseNode,
const char *backingName,
unsigned long long speed)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virTristateBool autofinalize = VIR_TRISTATE_BOOL_ABSENT;
virTristateBool autodismiss = VIR_TRISTATE_BOOL_ABSENT;
if (persistjob) {
autofinalize = VIR_TRISTATE_BOOL_YES;
autodismiss = VIR_TRISTATE_BOOL_NO;
}
cmd = qemuMonitorJSONMakeCommand("block-commit",
"s:device", device,
"S:job-id", jobname,
"Y:speed", speed,
blockjob: allow omitted arguments to QMP block-commit We are about to turn on support for active block commit. Although qemu 2.0 was the first version to mostly support it, that version mis-handles 0-length files, and doesn't have anything available for easy probing. But qemu 2.1 fixed bugs, and made life simpler by letting the 'top' argument be optional. Unless someone begs for active commit with qemu 2.0, for now we are just going to enable it only by probing for qemu 2.1 behavior (anyone backporting active commit can also backport the optional argument behavior). This requires qemu.git commit 7676e2c597000eff3a7233b40cca768b358f9bc9. Although all our actual uses of block-commit supply arguments for both base and top, we can omit both arguments and use a bogus device string to trigger an interesting behavior in qemu. All QMP commands first do argument validation, failing with GenericError if a mandatory argument is missing. Once that passes, the code in the specific command gets to do further checking, and the qemu developers made sure that if device is the only supplied argument, then the block-commit code will look up the device first, with a failure of DeviceNotFound, before attempting any further argument validation (most other validations fail with GenericError). Thus, the category of error class can reliably be used to decipher whether the top argument was optional, which in turn implies a working active commit. Since we expect our bogus device string to trigger an error either way, the code is written to return a distinct return value without spamming the logs. * src/qemu/qemu_monitor.h (qemuMonitorSupportsActiveCommit): New prototype. * src/qemu/qemu_monitor.c (qemuMonitorSupportsActiveCommit): Implement it. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONBlockCommit): Allow NULL for top and base, for probing purposes. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONBlockCommit): Likewise, implementing the probe. * tests/qemumonitorjsontest.c (mymain): Enable... (testQemuMonitorJSONqemuMonitorSupportsActiveCommit): ...a new test. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-06-17 03:42:49 +00:00
"S:top", top,
"S:top-node", topNode,
blockjob: allow omitted arguments to QMP block-commit We are about to turn on support for active block commit. Although qemu 2.0 was the first version to mostly support it, that version mis-handles 0-length files, and doesn't have anything available for easy probing. But qemu 2.1 fixed bugs, and made life simpler by letting the 'top' argument be optional. Unless someone begs for active commit with qemu 2.0, for now we are just going to enable it only by probing for qemu 2.1 behavior (anyone backporting active commit can also backport the optional argument behavior). This requires qemu.git commit 7676e2c597000eff3a7233b40cca768b358f9bc9. Although all our actual uses of block-commit supply arguments for both base and top, we can omit both arguments and use a bogus device string to trigger an interesting behavior in qemu. All QMP commands first do argument validation, failing with GenericError if a mandatory argument is missing. Once that passes, the code in the specific command gets to do further checking, and the qemu developers made sure that if device is the only supplied argument, then the block-commit code will look up the device first, with a failure of DeviceNotFound, before attempting any further argument validation (most other validations fail with GenericError). Thus, the category of error class can reliably be used to decipher whether the top argument was optional, which in turn implies a working active commit. Since we expect our bogus device string to trigger an error either way, the code is written to return a distinct return value without spamming the logs. * src/qemu/qemu_monitor.h (qemuMonitorSupportsActiveCommit): New prototype. * src/qemu/qemu_monitor.c (qemuMonitorSupportsActiveCommit): Implement it. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONBlockCommit): Allow NULL for top and base, for probing purposes. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONBlockCommit): Likewise, implementing the probe. * tests/qemumonitorjsontest.c (mymain): Enable... (testQemuMonitorJSONqemuMonitorSupportsActiveCommit): ...a new test. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-06-17 03:42:49 +00:00
"S:base", base,
"S:base-node", baseNode,
"S:backing-file", backingName,
"T:auto-finalize", autofinalize,
"T:auto-dismiss", autodismiss,
NULL);
blockjob: add qemu capabilities related to block jobs 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, and has been using patches similar to these upstream patches for several months now.] 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 error policy, setting granularity, or using a persistent bitmap, may later require a new libvirt API virDomainBlockCopy. I will wait to add that API until we know more about what qemu 1.3 will finally provide. This patch caters only to the upstream qemu 1.3 interface, although I have proven that the changes for RHEL 6.3 can be isolated to just qemu_monitor_json.c, and the rest of this series will gracefully handle either interface once the JSON differences are papered over in a downstream patch. For consistency with other block job commands, libvirt must handle the bandwidth argument as MiB/sec from the user, even though qemu exposes the speed argument as bytes/sec; then again, qemu rounds up to cluster size internally, so using MiB hides the worst effects of that rounding if you pass small numbers. [1]https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg04123.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.
2012-09-28 23:29:53 +00:00
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
blockjob: add qemu capabilities related to block jobs 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, and has been using patches similar to these upstream patches for several months now.] 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 error policy, setting granularity, or using a persistent bitmap, may later require a new libvirt API virDomainBlockCopy. I will wait to add that API until we know more about what qemu 1.3 will finally provide. This patch caters only to the upstream qemu 1.3 interface, although I have proven that the changes for RHEL 6.3 can be isolated to just qemu_monitor_json.c, and the rest of this series will gracefully handle either interface once the JSON differences are papered over in a downstream patch. For consistency with other block job commands, libvirt must handle the bandwidth argument as MiB/sec from the user, even though qemu exposes the speed argument as bytes/sec; then again, qemu rounds up to cluster size internally, so using MiB hides the worst effects of that rounding if you pass small numbers. [1]https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg04123.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.
2012-09-28 23:29:53 +00:00
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
blockjob: add qemu capabilities related to block jobs 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, and has been using patches similar to these upstream patches for several months now.] 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 error policy, setting granularity, or using a persistent bitmap, may later require a new libvirt API virDomainBlockCopy. I will wait to add that API until we know more about what qemu 1.3 will finally provide. This patch caters only to the upstream qemu 1.3 interface, although I have proven that the changes for RHEL 6.3 can be isolated to just qemu_monitor_json.c, and the rest of this series will gracefully handle either interface once the JSON differences are papered over in a downstream patch. For consistency with other block job commands, libvirt must handle the bandwidth argument as MiB/sec from the user, even though qemu exposes the speed argument as bytes/sec; then again, qemu rounds up to cluster size internally, so using MiB hides the worst effects of that rounding if you pass small numbers. [1]https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg04123.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.
2012-09-28 23:29:53 +00:00
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
static char *
qemuMonitorJSONDiskNameLookupOne(virJSONValue *image,
virStorageSource *top,
virStorageSource *target)
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
{
virJSONValue *backing;
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
char *ret;
/* The caller will report a generic message if we return NULL
* without an error; but in some cases we can improve by reporting
* a more specific message. */
if (!top || !image)
return NULL;
if (top != target) {
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
backing = virJSONValueObjectGetObject(image, "backing-image");
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
return qemuMonitorJSONDiskNameLookupOne(backing, top->backingStore,
target);
}
ret = g_strdup(virJSONValueObjectGetString(image, "filename"));
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
/* Sanity check - the name qemu gave us should resolve to the same
file tracked by our target description. */
if (virStorageSourceIsLocalStorage(target) &&
STRNEQ(ret, target->path) &&
!virFileLinkPointsTo(ret, target->path)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("qemu block name '%s' doesn't match expected '%s'"),
ret, target->path);
VIR_FREE(ret);
}
return ret;
}
char *
qemuMonitorJSONDiskNameLookup(qemuMonitor *mon,
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
const char *device,
virStorageSource *top,
virStorageSource *target)
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
{
char *ret = NULL;
virJSONValue *devices;
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
size_t i;
if (!(devices = qemuMonitorJSONQueryBlock(mon)))
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
return NULL;
for (i = 0; i < virJSONValueArraySize(devices); i++) {
virJSONValue *dev;
virJSONValue *inserted;
virJSONValue *image;
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
const char *thisdev;
if (!(dev = qemuMonitorJSONGetBlockDev(devices, i)))
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
goto cleanup;
if (!(thisdev = qemuMonitorJSONGetBlockDevDevice(dev)))
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
goto cleanup;
if (STREQ(thisdev, device)) {
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
if ((inserted = virJSONValueObjectGetObject(dev, "inserted")) &&
(image = virJSONValueObjectGetObject(inserted, "image"))) {
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
ret = qemuMonitorJSONDiskNameLookupOne(image, top, target);
}
break;
}
}
/* Guarantee an error when returning NULL, but don't override a
* more specific error if one was already generated. */
if (!ret && virGetLastErrorCode() == VIR_ERR_OK)
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unable to find backing name for device %s"),
device);
cleanup:
virJSONValueFree(devices);
qemu: read backing chain names from qemu https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that after a series of disk snapshots into existing destination images, followed by active commits of the top image, it is possible for qemu 2.2 and earlier to end up tracking a different name for the image than what it would have had when opening the chain afresh. That is, when starting with the chain 'a <- b <- c', the name associated with 'b' is how it was spelled in the metadata of 'c', but when starting with 'a', taking two snapshots into 'a <- b <- c', then committing 'c' back into 'b', the name associated with 'b' is now the name used when taking the first snapshot. Sadly, older qemu doesn't know how to treat different spellings of the same filename as identical files (it uses strcmp() instead of checking for the same inode), which means libvirt's attempt to commit an image using solely the names learned from qcow2 metadata fails with a cryptic: error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found even though the file exists. Trying to teach libvirt the rules on which name qemu will expect is not worth the effort (besides, we'd have to remember it across libvirtd restarts, and track whether a file was opened via metadata or via snapshot creation for a given qemu process); it is easier to just always directly ask qemu what string it expects to see in the first place. As a safety valve, we validate that any name returned by qemu still maps to the same local file as we have tracked it, so that a compromised qemu cannot accidentally cause us to act on an incorrect file. * src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONDiskNameLookupOne): Likewise. * src/qemu/qemu_driver.c (qemuDomainBlockCommit) (qemuDomainBlockJobImpl): Use it. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-03-11 20:37:04 +00:00
return ret;
}
int qemuMonitorJSONArbitraryCommand(qemuMonitor *mon,
const char *cmd_str,
char **reply_str)
{
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
int ret = -1;
if (!(cmd = virJSONValueFromString(cmd_str)))
goto cleanup;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (!(*reply_str = virJSONValueToString(reply, false)))
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONInjectNMI(qemuMonitor *mon)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
cmd = qemuMonitorJSONMakeCommand("inject-nmi", NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONSendKey(qemuMonitor *mon,
unsigned int holdtime,
unsigned int *keycodes,
unsigned int nkeycodes)
{
int ret = -1;
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
virJSONValue *keys = NULL;
virJSONValue *key = NULL;
size_t i;
/* create the key data array */
keys = virJSONValueNewArray();
for (i = 0; i < nkeycodes; i++) {
if (keycodes[i] > 0xffff) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("keycode %zu is invalid: 0x%X"), i, keycodes[i]);
goto cleanup;
}
/* create single key object */
key = virJSONValueNewObject();
/* Union KeyValue has two types, use the generic one */
if (virJSONValueObjectAppendString(key, "type", "number") < 0)
goto cleanup;
/* with the keycode */
if (virJSONValueObjectAppendNumberInt(key, "data", keycodes[i]) < 0)
goto cleanup;
if (virJSONValueArrayAppend(keys, &key) < 0)
goto cleanup;
}
cmd = qemuMonitorJSONMakeCommand("send-key",
"a:keys", &keys,
"p:hold-time", holdtime,
NULL);
if (!cmd)
goto cleanup;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
virJSONValueFree(keys);
virJSONValueFree(key);
return ret;
}
int qemuMonitorJSONScreendump(qemuMonitor *mon,
const char *device,
unsigned int head,
const char *file)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
cmd = qemuMonitorJSONMakeCommand("screendump",
"s:filename", file,
"S:device", device,
"p:head", head,
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
static int
qemuMonitorJSONParseBlockJobInfo(GHashTable *blockJobs,
virJSONValue *entry,
bool rawjobname)
{
qemuMonitorBlockJobInfo *info = NULL;
const char *device;
const char *type;
if (!(device = virJSONValueObjectGetString(entry, "device"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("entry was missing 'device'"));
return -1;
}
if (!rawjobname)
device = qemuAliasDiskDriveSkipPrefix(device);
info = g_new0(qemuMonitorBlockJobInfo, 1);
if (virHashAddEntry(blockJobs, device, info) < 0) {
VIR_FREE(info);
return -1;
}
if (!(type = virJSONValueObjectGetString(entry, "type"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("entry was missing 'type'"));
return -1;
}
if (STREQ(type, "stream"))
info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL;
else if (STREQ(type, "commit"))
info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT;
else if (STREQ(type, "mirror"))
info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY;
else if (STREQ(type, "backup"))
info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_BACKUP;
else
info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
if (virJSONValueObjectGetNumberUlong(entry, "speed", &info->bandwidth) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("entry was missing 'speed'"));
return -1;
}
if (virJSONValueObjectGetNumberUlong(entry, "offset", &info->cur) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("entry was missing 'offset'"));
return -1;
}
if (virJSONValueObjectGetNumberUlong(entry, "len", &info->end) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("entry was missing 'len'"));
return -1;
}
if (virJSONValueObjectGetBoolean(entry, "ready", &info->ready) == 0)
info->ready_present = true;
return 0;
}
GHashTable *
qemuMonitorJSONGetAllBlockJobInfo(qemuMonitor *mon,
bool rawjobname)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
virJSONValue *data;
size_t nr_results;
size_t i;
g_autoptr(GHashTable) blockJobs = virHashNew(g_free);
cmd = qemuMonitorJSONMakeCommand("query-block-jobs", NULL);
if (!cmd)
return NULL;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return NULL;
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
if ((data = virJSONValueObjectGetArray(reply, "return")) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("reply was missing return data"));
return NULL;
}
nr_results = virJSONValueArraySize(data);
for (i = 0; i < nr_results; i++) {
virJSONValue *entry = virJSONValueArrayGet(data, i);
if (!entry) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing array element"));
return NULL;
}
if (qemuMonitorJSONParseBlockJobInfo(blockJobs, entry, rawjobname) < 0)
return NULL;
}
return g_steal_pointer(&blockJobs);
}
static int
qemuMonitorJSONBlockJobError(virJSONValue *cmd,
virJSONValue *reply,
const char *jobname)
{
virJSONValue *error;
if ((error = virJSONValueObjectGet(reply, "error")) &&
(qemuMonitorJSONErrorIsClass(error, "DeviceNotActive"))) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("No active block job '%s'"), jobname);
return -1;
}
return qemuMonitorJSONCheckError(cmd, reply);
}
blockjob: fix block-stream bandwidth race With RHEL 6.2, virDomainBlockPull(dom, dev, bandwidth, 0) has a race with non-zero bandwidth: there is a window between the block_stream and block_job_set_speed monitor commands where an unlimited amount of data was let through, defeating the point of a throttle. This race was first identified in commit a9d3495e, and libvirt was able to reduce the size of the window for that race. In the meantime, the qemu developers decided to fix things properly; per this message: https://lists.gnu.org/archive/html/qemu-devel/2012-04/msg03793.html the fix will be in qemu 1.1, and changes block-job-set-speed to use a different parameter name, as well as adding a new optional parameter to block-stream, which eliminates the race altogether. Since our documentation already mentioned that we can refuse a non-zero bandwidth for some hypervisors, I think the best solution is to do just that for RHEL 6.2 qemu, so that the race is obvious to the user (anyone using stock RHEL 6.2 binaries won't have this patch, and anyone building their own libvirt with this patch for RHEL can also rebuild qemu to get the modern semantics, so it is no real loss in behavior). Meanwhile the code must be fixed to honor actual qemu 1.1 naming. Rename the parameter to 'modern', since the naming difference now covers more than just 'async' block-job-cancel. And while at it, fix an unchecked integer overflow. * src/qemu/qemu_monitor.h (enum BLOCK_JOB_CMD): Drop unused value, rename enum to match conventions. * src/qemu/qemu_monitor.c (qemuMonitorBlockJob): Reflect enum rename. * src/qemu_qemu_monitor_json.h (qemuMonitorJSONBlockJob): Likewise. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONBlockJob): Likewise, and support difference between RHEL 6.2 and qemu 1.1 block pull. * src/qemu/qemu_driver.c (qemuDomainBlockJobImpl): Reject bandwidth during pull with too-old qemu. * src/libvirt.c (virDomainBlockPull, virDomainBlockRebase): Document this.
2012-04-25 22:49:44 +00:00
/* speed is in bytes/sec */
int
qemuMonitorJSONBlockStream(qemuMonitor *mon,
const char *device,
const char *jobname,
bool persistjob,
const char *base,
const char *baseNode,
const char *backingName,
unsigned long long speed)
{
int ret = -1;
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
virTristateBool autofinalize = VIR_TRISTATE_BOOL_ABSENT;
virTristateBool autodismiss = VIR_TRISTATE_BOOL_ABSENT;
if (persistjob) {
autofinalize = VIR_TRISTATE_BOOL_YES;
autodismiss = VIR_TRISTATE_BOOL_NO;
}
if (!(cmd = qemuMonitorJSONMakeCommand("block-stream",
"s:device", device,
"S:job-id", jobname,
"Y:speed", speed,
"S:base", base,
"S:base-node", baseNode,
"S:backing-file", backingName,
"T:auto-finalize", autofinalize,
"T:auto-dismiss", autodismiss,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONBlockJobCancel(qemuMonitor *mon,
const char *jobname,
bool force)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("block-job-cancel",
"s:device", jobname,
"B:force", force,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONBlockJobSetSpeed(qemuMonitor *mon,
const char *jobname,
unsigned long long speed)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("block-job-set-speed",
"s:device", jobname,
"J:speed", speed,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONDrivePivot(qemuMonitor *mon,
const char *jobname)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
cmd = qemuMonitorJSONMakeCommand("block-job-complete",
"s:device", jobname,
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONJobDismiss(qemuMonitor *mon,
const char *jobname)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("job-dismiss",
"s:id", jobname,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONJobComplete(qemuMonitor *mon,
const char *jobname)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("job-complete",
"s:id", jobname,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0)
return -1;
return 0;
}
int qemuMonitorJSONOpenGraphics(qemuMonitor *mon,
const char *protocol,
const char *fdname,
bool skipauth)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
cmd = qemuMonitorJSONMakeCommand("add_client",
"s:protocol", protocol,
"s:fdname", fdname,
"b:skipauth", skipauth,
NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
#define GET_THROTTLE_STATS_OPTIONAL(FIELD, STORE) \
if (virJSONValueObjectGetNumberUlong(inserted, \
FIELD, \
&reply->STORE) < 0) { \
reply->STORE = 0; \
}
#define GET_THROTTLE_STATS(FIELD, STORE) \
if (virJSONValueObjectGetNumberUlong(inserted, \
FIELD, \
&reply->STORE) < 0) { \
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, \
_("block_io_throttle field '%s' missing " \
"in qemu's output"), \
#STORE); \
return -1; \
}
static int
qemuMonitorJSONBlockIoThrottleInfo(virJSONValue *io_throttle,
const char *drivealias,
const char *qdevid,
virDomainBlockIoTuneInfo *reply)
{
size_t i;
bool found = false;
for (i = 0; i < virJSONValueArraySize(io_throttle); i++) {
virJSONValue *temp_dev = virJSONValueArrayGet(io_throttle, i);
virJSONValue *inserted;
const char *current_drive;
const char *current_qdev;
if (!temp_dev || virJSONValueGetType(temp_dev) != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("block_io_throttle device entry "
"was not in expected format"));
return -1;
}
current_qdev = virJSONValueObjectGetString(temp_dev, "qdev");
current_drive = virJSONValueObjectGetString(temp_dev, "device");
if (!current_drive && !current_qdev) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("block_io_throttle device entry "
"was not in expected format"));
return -1;
}
if ((drivealias && current_drive && STRNEQ(current_drive, drivealias)) ||
(qdevid && current_qdev && STRNEQ(current_qdev, qdevid)))
continue;
found = true;
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
if (!(inserted = virJSONValueObjectGetObject(temp_dev, "inserted"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("block_io_throttle inserted entry "
"was not in expected format"));
return -1;
}
GET_THROTTLE_STATS("bps", total_bytes_sec);
GET_THROTTLE_STATS("bps_rd", read_bytes_sec);
GET_THROTTLE_STATS("bps_wr", write_bytes_sec);
GET_THROTTLE_STATS("iops", total_iops_sec);
GET_THROTTLE_STATS("iops_rd", read_iops_sec);
GET_THROTTLE_STATS("iops_wr", write_iops_sec);
GET_THROTTLE_STATS_OPTIONAL("bps_max", total_bytes_sec_max);
GET_THROTTLE_STATS_OPTIONAL("bps_rd_max", read_bytes_sec_max);
GET_THROTTLE_STATS_OPTIONAL("bps_wr_max", write_bytes_sec_max);
GET_THROTTLE_STATS_OPTIONAL("iops_max", total_iops_sec_max);
GET_THROTTLE_STATS_OPTIONAL("iops_rd_max", read_iops_sec_max);
GET_THROTTLE_STATS_OPTIONAL("iops_wr_max", write_iops_sec_max);
GET_THROTTLE_STATS_OPTIONAL("iops_size", size_iops_sec);
reply->group_name = g_strdup(virJSONValueObjectGetString(inserted, "group"));
GET_THROTTLE_STATS_OPTIONAL("bps_max_length", total_bytes_sec_max_length);
GET_THROTTLE_STATS_OPTIONAL("bps_rd_max_length", read_bytes_sec_max_length);
GET_THROTTLE_STATS_OPTIONAL("bps_wr_max_length", write_bytes_sec_max_length);
GET_THROTTLE_STATS_OPTIONAL("iops_max_length", total_iops_sec_max_length);
GET_THROTTLE_STATS_OPTIONAL("iops_rd_max_length", read_iops_sec_max_length);
GET_THROTTLE_STATS_OPTIONAL("iops_wr_max_length", write_iops_sec_max_length);
break;
}
if (!found) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot find throttling info for device '%s'"),
drivealias ? drivealias : qdevid);
return -1;
}
return 0;
}
#undef GET_THROTTLE_STATS
#undef GET_THROTTLE_STATS_OPTIONAL
int qemuMonitorJSONSetBlockIoThrottle(qemuMonitor *mon,
const char *drivealias,
const char *qomid,
virDomainBlockIoTuneInfo *info)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) result = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("block_set_io_throttle",
"S:device", drivealias,
"S:id", qomid,
"U:bps", info->total_bytes_sec,
"U:bps_rd", info->read_bytes_sec,
"U:bps_wr", info->write_bytes_sec,
"U:iops", info->total_iops_sec,
"U:iops_rd", info->read_iops_sec,
"U:iops_wr", info->write_iops_sec,
"U:bps_max", info->total_bytes_sec_max,
"U:bps_rd_max", info->read_bytes_sec_max,
"U:bps_wr_max", info->write_bytes_sec_max,
"U:iops_max", info->total_iops_sec_max,
"U:iops_rd_max", info->read_iops_sec_max,
"U:iops_wr_max", info->write_iops_sec_max,
"U:iops_size", info->size_iops_sec,
"S:group", info->group_name,
"P:bps_max_length", info->total_bytes_sec_max_length,
"P:bps_rd_max_length", info->read_bytes_sec_max_length,
"P:bps_wr_max_length", info->write_bytes_sec_max_length,
"P:iops_max_length", info->total_iops_sec_max_length,
"P:iops_rd_max_length", info->read_iops_sec_max_length,
"P:iops_wr_max_length", info->write_iops_sec_max_length,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &result) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, result) < 0)
return -1;
return 0;
}
int qemuMonitorJSONGetBlockIoThrottle(qemuMonitor *mon,
const char *drivealias,
const char *qdevid,
virDomainBlockIoTuneInfo *reply)
{
int ret = -1;
virJSONValue *devices = NULL;
if (!(devices = qemuMonitorJSONQueryBlock(mon)))
return -1;
ret = qemuMonitorJSONBlockIoThrottleInfo(devices, drivealias, qdevid, reply);
virJSONValueFree(devices);
return ret;
}
int qemuMonitorJSONSystemWakeup(qemuMonitor *mon)
{
int ret = -1;
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
cmd = qemuMonitorJSONMakeCommand("system_wakeup", NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONGetVersion(qemuMonitor *mon,
int *major,
int *minor,
int *micro,
char **package)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *data;
virJSONValue *qemu;
*major = *minor = *micro = 0;
if (package)
*package = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-version", NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
goto cleanup;
data = virJSONValueObjectGetObject(reply, "return");
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
if (!(qemu = virJSONValueObjectGetObject(data, "qemu"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-version reply was missing 'qemu' data"));
goto cleanup;
}
if (virJSONValueObjectGetNumberInt(qemu, "major", major) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-version reply was missing 'major' version"));
goto cleanup;
}
if (virJSONValueObjectGetNumberInt(qemu, "minor", minor) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-version reply was missing 'minor' version"));
goto cleanup;
}
if (virJSONValueObjectGetNumberInt(qemu, "micro", micro) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-version reply was missing 'micro' version"));
goto cleanup;
}
if (package) {
const char *tmp;
if (!(tmp = virJSONValueObjectGetString(data, "package"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-version reply was missing 'package' version"));
goto cleanup;
}
*package = g_strdup(tmp);
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONGetMachines(qemuMonitor *mon,
qemuMonitorMachineInfo ***machines)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *data;
qemuMonitorMachineInfo **infolist = NULL;
size_t n = 0;
size_t i;
*machines = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-machines", NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
goto cleanup;
data = virJSONValueObjectGetArray(reply, "return");
n = virJSONValueArraySize(data);
/* null-terminated list */
infolist = g_new0(qemuMonitorMachineInfo *, n + 1);
for (i = 0; i < n; i++) {
virJSONValue *child = virJSONValueArrayGet(data, i);
const char *tmp;
qemuMonitorMachineInfo *info;
info = g_new0(qemuMonitorMachineInfo, 1);
infolist[i] = info;
if (!(tmp = virJSONValueObjectGetString(child, "name"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-machines reply data was missing 'name'"));
goto cleanup;
}
info->name = g_strdup(tmp);
if (virJSONValueObjectHasKey(child, "is-default") &&
virJSONValueObjectGetBoolean(child, "is-default", &info->isDefault) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-machines reply has malformed 'is-default' data"));
goto cleanup;
}
if (virJSONValueObjectHasKey(child, "alias")) {
if (!(tmp = virJSONValueObjectGetString(child, "alias"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-machines reply has malformed 'alias' data"));
goto cleanup;
}
info->alias = g_strdup(tmp);
}
if (virJSONValueObjectHasKey(child, "cpu-max") &&
virJSONValueObjectGetNumberUint(child, "cpu-max", &info->maxCpus) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-machines reply has malformed 'cpu-max' data"));
goto cleanup;
}
ignore_value(virJSONValueObjectGetBoolean(child, "hotpluggable-cpus",
&info->hotplugCpus));
if (virJSONValueObjectHasKey(child, "default-cpu-type")) {
if (!(tmp = virJSONValueObjectGetString(child, "default-cpu-type"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-machines reply has malformed "
"'default-cpu-type' data"));
goto cleanup;
}
info->defaultCPU = g_strdup(tmp);
}
if (virJSONValueObjectHasKey(child, "numa-mem-supported")) {
if (virJSONValueObjectGetBoolean(child, "numa-mem-supported", &info->numaMemSupported) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qemu-machines reply has malformed "
"'numa-mem-supported' data"));
goto cleanup;
}
} else {
info->numaMemSupported = true;
}
if (virJSONValueObjectHasKey(child, "default-ram-id")) {
if (!(tmp = virJSONValueObjectGetString(child, "default-ram-id"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-machines reply has malformed "
"'default-ram-id' data"));
goto cleanup;
}
info->defaultRAMid = g_strdup(tmp);
}
if (virJSONValueObjectHasKey(child, "deprecated") &&
virJSONValueObjectGetBoolean(child, "deprecated", &info->deprecated) < 0)
goto cleanup;
}
ret = n;
*machines = g_steal_pointer(&infolist);
cleanup:
if (infolist) {
for (i = 0; i < n; i++)
qemuMonitorMachineInfoFree(infolist[i]);
VIR_FREE(infolist);
}
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetCPUDefinitions(qemuMonitor *mon,
qemuMonitorCPUDefs **cpuDefs)
{
g_autoptr(qemuMonitorCPUDefs) defs = NULL;
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
virJSONValue *data;
size_t ncpus;
size_t i;
*cpuDefs = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-definitions", NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
/* Urgh, some QEMU architectures have the query-cpu-definitions
* command, but return 'GenericError' with string "Not supported",
* instead of simply omitting the command entirely :-(
*/
if (qemuMonitorJSONHasError(reply, "GenericError"))
return 0;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
return -1;
data = virJSONValueObjectGetArray(reply, "return");
ncpus = virJSONValueArraySize(data);
if (!(defs = qemuMonitorCPUDefsNew(ncpus)))
return -1;
for (i = 0; i < defs->ncpus; i++) {
virJSONValue *child = virJSONValueArrayGet(data, i);
const char *tmp;
qemuMonitorCPUDefInfo *cpu = defs->cpus + i;
if (!(tmp = virJSONValueObjectGetString(child, "name"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-cpu-definitions reply data was missing 'name'"));
return -1;
}
cpu->name = g_strdup(tmp);
if ((tmp = virJSONValueObjectGetString(child, "typename")) && *tmp)
cpu->type = g_strdup(tmp);
if (virJSONValueObjectHasKey(child, "unavailable-features")) {
if (!(cpu->blockers = virJSONValueObjectGetStringArray(child,
"unavailable-features")))
return -1;
if (g_strv_length(cpu->blockers) == 0) {
cpu->usable = VIR_DOMCAPS_CPU_USABLE_YES;
g_clear_pointer(&cpu->blockers, g_strfreev);
continue;
}
cpu->usable = VIR_DOMCAPS_CPU_USABLE_NO;
}
if (virJSONValueObjectHasKey(child, "deprecated") &&
virJSONValueObjectGetBoolean(child, "deprecated", &cpu->deprecated) < 0)
return -1;
}
*cpuDefs = g_steal_pointer(&defs);
return 0;
}
VIR_ENUM_IMPL(qemuMonitorCPUProperty,
QEMU_MONITOR_CPU_PROPERTY_LAST,
"boolean", "string", "number",
);
static int
qemuMonitorJSONParseCPUModelProperty(const char *key,
virJSONValue *value,
void *opaque)
{
qemuMonitorCPUModelInfo *machine_model = opaque;
qemuMonitorCPUProperty *prop;
prop = machine_model->props + machine_model->nprops;
switch ((virJSONType)virJSONValueGetType(value)) {
case VIR_JSON_TYPE_STRING:
prop->value.string = g_strdup(virJSONValueGetString(value));
prop->type = QEMU_MONITOR_CPU_PROPERTY_STRING;
break;
case VIR_JSON_TYPE_NUMBER:
/* Ignore numbers which cannot be parsed as unsigned long long */
if (virJSONValueGetNumberLong(value, &prop->value.number) < 0)
return 0;
prop->type = QEMU_MONITOR_CPU_PROPERTY_NUMBER;
break;
case VIR_JSON_TYPE_BOOLEAN:
virJSONValueGetBoolean(value, &prop->value.boolean);
prop->type = QEMU_MONITOR_CPU_PROPERTY_BOOLEAN;
break;
case VIR_JSON_TYPE_OBJECT:
case VIR_JSON_TYPE_ARRAY:
case VIR_JSON_TYPE_NULL:
return 0;
}
machine_model->nprops++;
prop->name = g_strdup(key);
return 0;
}
static virJSONValue *
qemuMonitorJSONMakeCPUModel(virCPUDef *cpu,
bool migratable)
{
virJSONValue *model = virJSONValueNewObject();
virJSONValue *props = NULL;
size_t i;
if (virJSONValueObjectAppendString(model, "name", cpu->model) < 0)
goto error;
if (cpu->nfeatures || !migratable) {
props = virJSONValueNewObject();
for (i = 0; i < cpu->nfeatures; i++) {
char *name = cpu->features[i].name;
bool enabled = false;
/* policy may be reported as -1 if the CPU def is a host model */
if (cpu->features[i].policy == VIR_CPU_FEATURE_REQUIRE ||
cpu->features[i].policy == VIR_CPU_FEATURE_FORCE ||
cpu->features[i].policy == -1)
enabled = true;
if (virJSONValueObjectAppendBoolean(props, name, enabled) < 0)
goto error;
}
if (!migratable &&
virJSONValueObjectAppendBoolean(props, "migratable", false) < 0) {
goto error;
}
if (virJSONValueObjectAppend(model, "props", &props) < 0)
goto error;
}
return model;
error:
virJSONValueFree(model);
virJSONValueFree(props);
return NULL;
}
static int
qemuMonitorJSONParseCPUModelData(virJSONValue *data,
const char *cmd_name,
bool fail_no_props,
virJSONValue **cpu_model,
virJSONValue **cpu_props,
const char **cpu_name)
{
if (!(*cpu_model = virJSONValueObjectGetObject(data, "model"))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s reply data was missing 'model'"), cmd_name);
return -1;
}
if (!(*cpu_name = virJSONValueObjectGetString(*cpu_model, "name"))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s reply data was missing 'name'"), cmd_name);
return -1;
}
if (!(*cpu_props = virJSONValueObjectGetObject(*cpu_model, "props")) &&
fail_no_props) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s reply data was missing 'props'"), cmd_name);
return -1;
}
return 0;
}
static int
qemuMonitorJSONParseCPUModel(const char *cpu_name,
virJSONValue *cpu_props,
qemuMonitorCPUModelInfo **model_info)
{
qemuMonitorCPUModelInfo *machine_model = NULL;
int ret = -1;
machine_model = g_new0(qemuMonitorCPUModelInfo, 1);
machine_model->name = g_strdup(cpu_name);
if (cpu_props) {
size_t nprops = virJSONValueObjectKeysNumber(cpu_props);
machine_model->props = g_new0(qemuMonitorCPUProperty, nprops);
if (virJSONValueObjectForeachKeyValue(cpu_props,
qemuMonitorJSONParseCPUModelProperty,
machine_model) < 0)
goto cleanup;
}
*model_info = g_steal_pointer(&machine_model);
ret = 0;
cleanup:
qemuMonitorCPUModelInfoFree(machine_model);
return ret;
}
int
qemuMonitorJSONGetCPUModelExpansion(qemuMonitor *mon,
qemuMonitorCPUModelExpansionType type,
virCPUDef *cpu,
bool migratable,
bool fail_no_props,
qemuMonitorCPUModelInfo **model_info)
{
g_autoptr(virJSONValue) model = NULL;
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
virJSONValue *data;
virJSONValue *cpu_model;
virJSONValue *cpu_props = NULL;
const char *cpu_name = "";
const char *typeStr = "";
*model_info = NULL;
if (!(model = qemuMonitorJSONMakeCPUModel(cpu, migratable)))
return -1;
retry:
switch (type) {
case QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC:
case QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC_FULL:
typeStr = "static";
break;
case QEMU_MONITOR_CPU_MODEL_EXPANSION_FULL:
typeStr = "full";
break;
}
if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-model-expansion",
"s:type", typeStr,
"a:model", &model,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
/* Even though query-cpu-model-expansion is advertised by query-commands it
* may just return GenericError if it is not implemented for the requested
* guest architecture or it is not supported in the host environment.
*/
if (qemuMonitorJSONHasError(reply, "GenericError"))
return 0;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
return -1;
data = virJSONValueObjectGetObject(reply, "return");
if (qemuMonitorJSONParseCPUModelData(data, "query-cpu-model-expansion",
fail_no_props, &cpu_model, &cpu_props,
&cpu_name) < 0)
return -1;
/* QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC_FULL requests "full" expansion
* on the result of the initial "static" expansion.
*/
if (type == QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC_FULL) {
if (!(model = virJSONValueCopy(cpu_model)))
return -1;
virJSONValueFree(cmd);
virJSONValueFree(reply);
type = QEMU_MONITOR_CPU_MODEL_EXPANSION_FULL;
goto retry;
}
return qemuMonitorJSONParseCPUModel(cpu_name, cpu_props, model_info);
}
int
qemuMonitorJSONGetCPUModelBaseline(qemuMonitor *mon,
virCPUDef *cpu_a,
virCPUDef *cpu_b,
qemuMonitorCPUModelInfo **baseline)
{
g_autoptr(virJSONValue) model_a = NULL;
g_autoptr(virJSONValue) model_b = NULL;
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
virJSONValue *data;
virJSONValue *cpu_model;
virJSONValue *cpu_props = NULL;
const char *cpu_name = "";
if (!(model_a = qemuMonitorJSONMakeCPUModel(cpu_a, true)) ||
!(model_b = qemuMonitorJSONMakeCPUModel(cpu_b, true)))
return -1;
if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-model-baseline",
"a:modela", &model_a,
"a:modelb", &model_b,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
return -1;
data = virJSONValueObjectGetObject(reply, "return");
if (qemuMonitorJSONParseCPUModelData(data, "query-cpu-model-baseline",
false, &cpu_model, &cpu_props,
&cpu_name) < 0)
return -1;
return qemuMonitorJSONParseCPUModel(cpu_name, cpu_props, baseline);
}
int
qemuMonitorJSONGetCPUModelComparison(qemuMonitor *mon,
virCPUDef *cpu_a,
virCPUDef *cpu_b,
char **result)
{
g_autoptr(virJSONValue) model_a = NULL;
g_autoptr(virJSONValue) model_b = NULL;
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
const char *data_result;
virJSONValue *data;
if (!(model_a = qemuMonitorJSONMakeCPUModel(cpu_a, true)) ||
!(model_b = qemuMonitorJSONMakeCPUModel(cpu_b, true)))
return -1;
if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-model-comparison",
"a:modela", &model_a,
"a:modelb", &model_b,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
data = virJSONValueObjectGetObject(reply, "return");
if (!(data_result = virJSONValueObjectGetString(data, "result"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-cpu-model-comparison reply data was missing "
"'result'"));
return -1;
}
*result = g_strdup(data_result);
return 0;
}
int qemuMonitorJSONGetCommands(qemuMonitor *mon,
char ***commands)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
virJSONValue *data;
g_auto(GStrv) commandlist = NULL;
size_t n = 0;
size_t i;
*commands = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-commands", NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
return -1;
data = virJSONValueObjectGetArray(reply, "return");
n = virJSONValueArraySize(data);
/* null-terminated list */
commandlist = g_new0(char *, n + 1);
for (i = 0; i < n; i++) {
virJSONValue *child = virJSONValueArrayGet(data, i);
const char *tmp;
if (!(tmp = virJSONValueObjectGetString(child, "name"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-commands reply data was missing 'name'"));
return -1;
}
commandlist[i] = g_strdup(tmp);
}
*commands = g_steal_pointer(&commandlist);
return n;
}
static int
qemuMonitorJSONGetCommandLineOptionsWorker(size_t pos G_GNUC_UNUSED,
virJSONValue *item,
void *opaque)
{
const char *name = virJSONValueObjectGetString(item, "option");
g_autoptr(virJSONValue) parameters = NULL;
GHashTable *options = opaque;
if (!name ||
virJSONValueObjectRemoveKey(item, "parameters", &parameters) <= 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("reply data was missing 'option' name or parameters"));
return -1;
}
g_hash_table_insert(options, g_strdup(name), parameters);
parameters = NULL;
qemu_monitor_json: Don't leak "option" in qemuMonitorJSONGetCommandLineOptions() In recent commit of bf8bd93df0 (and friends) we switched the way we process queried command line arguments: from string lists to virJSONValue stored in a hash table. To achieve this qemuMonitorJSONGetCommandLineOptions() helper was introduced which executes the "query-command-line-options" monitor command and then calls virJSONValueArrayForeachSteal() to process the output. The array process function is also given qemuMonitorJSONGetCommandLineOptionsWorker() as the callback which is called over each item of the returned array. This callback then steals "parameters" attribute of each array iteam storing it in the hash table, but it leaves behind "option" attribute (because it's g_strdup()-ed). After all of this, the callback returns 0 which is a signal to the array processing function that the callback took ownership of the array item. But this is not true. While it removed "parameters" it did not take the rest ("option" for instance). And therefore, it leads to a memory leak: 5,347 (1,656 direct, 3,691 indirect) bytes in 69 blocks are definitely lost in loss record 2,752 of 2,794 at 0x483BEC5: calloc (vg_replace_malloc.c:760) by 0x4E25A10: g_malloc0 (in /usr/lib64/libglib-2.0.so.0.6400.5) by 0x4943317: virJSONValueNewObject (virjson.c:569) by 0x4945692: virJSONParserHandleStartMap (virjson.c:1768) by 0x5825A86: yajl_do_parse (in /usr/lib64/libyajl.so.2.1.0) by 0x4945BFA: virJSONValueFromString (virjson.c:1896) by 0xAF5C115: qemuMonitorJSONIOProcessLine (qemu_monitor_json.c:224) by 0xAF5C45E: qemuMonitorJSONIOProcess (qemu_monitor_json.c:279) by 0xAF4BB6C: qemuMonitorIOProcess (qemu_monitor.c:342) by 0xAF4C444: qemuMonitorIO (qemu_monitor.c:574) by 0x4FEF846: socket_source_dispatch (in /usr/lib64/libgio-2.0.so.0.6400.5) by 0x4E1F727: g_main_context_dispatch (in /usr/lib64/libglib-2.0.so.0.6400.5) The callback must return 1 so that the array item is properly freed. Fixes: ebeff6cd57d07c89d42e191ed0085a9dd89835c5 Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Peter Krempa <pkrempa@redhat.com>
2020-12-07 08:32:27 +00:00
return 1;
}
GHashTable *
qemuMonitorJSONGetCommandLineOptions(qemuMonitor *mon)
{
g_autoptr(GHashTable) ret = virHashNew(virJSONValueHashFree);
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-command-line-options", NULL)))
return NULL;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return NULL;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
return NULL;
if (virJSONValueArrayForeachSteal(virJSONValueObjectGetArray(reply, "return"),
qemuMonitorJSONGetCommandLineOptionsWorker,
ret) < 0)
return NULL;
return g_steal_pointer(&ret);
}
int qemuMonitorJSONGetKVMState(qemuMonitor *mon,
bool *enabled,
bool *present)
{
int ret = -1;
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
virJSONValue *data = NULL;
/* Safe defaults */
*enabled = *present = false;
if (!(cmd = qemuMonitorJSONMakeCommand("query-kvm", NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
goto cleanup;
data = virJSONValueObjectGetObject(reply, "return");
if (virJSONValueObjectGetBoolean(data, "enabled", enabled) < 0 ||
virJSONValueObjectGetBoolean(data, "present", present) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-kvm replied unexpected data"));
goto cleanup;
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetObjectTypes(qemuMonitor *mon,
char ***types)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
virJSONValue *data;
g_auto(GStrv) typelist = NULL;
size_t n = 0;
size_t i;
*types = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-list-types", NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
return -1;
data = virJSONValueObjectGetArray(reply, "return");
n = virJSONValueArraySize(data);
/* null-terminated list */
typelist = g_new0(char *, n + 1);
for (i = 0; i < n; i++) {
virJSONValue *child = virJSONValueArrayGet(data, i);
const char *tmp;
if (!(tmp = virJSONValueObjectGetString(child, "name"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qom-list-types reply data was missing 'name'"));
return -1;
}
typelist[i] = g_strdup(tmp);
}
*types = g_steal_pointer(&typelist);
return n;
}
int qemuMonitorJSONGetObjectListPaths(qemuMonitor *mon,
const char *path,
qemuMonitorJSONListPath ***paths)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *data;
qemuMonitorJSONListPath **pathlist = NULL;
size_t n = 0;
size_t i;
*paths = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-list",
"s:path", path,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
goto cleanup;
data = virJSONValueObjectGetArray(reply, "return");
n = virJSONValueArraySize(data);
/* null-terminated list */
pathlist = g_new0(qemuMonitorJSONListPath *, n + 1);
for (i = 0; i < n; i++) {
virJSONValue *child = virJSONValueArrayGet(data, i);
const char *tmp;
qemuMonitorJSONListPath *info;
info = g_new0(qemuMonitorJSONListPath, 1);
pathlist[i] = info;
if (!(tmp = virJSONValueObjectGetString(child, "name"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qom-list reply data was missing 'name'"));
goto cleanup;
}
info->name = g_strdup(tmp);
if (virJSONValueObjectHasKey(child, "type")) {
if (!(tmp = virJSONValueObjectGetString(child, "type"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qom-list reply has malformed 'type' data"));
goto cleanup;
}
info->type = g_strdup(tmp);
}
}
ret = n;
*paths = g_steal_pointer(&pathlist);
cleanup:
if (pathlist) {
for (i = 0; i < n; i++)
qemuMonitorJSONListPathFree(pathlist[i]);
VIR_FREE(pathlist);
}
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
void qemuMonitorJSONListPathFree(qemuMonitorJSONListPath *paths)
{
if (!paths)
return;
g_free(paths->name);
g_free(paths->type);
g_free(paths);
}
int qemuMonitorJSONGetObjectProperty(qemuMonitor *mon,
const char *path,
const char *property,
qemuMonitorJSONObjectProperty *prop)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *data;
const char *tmp;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-get",
"s:path", path,
"s:property", property,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
data = virJSONValueObjectGet(reply, "return");
switch ((qemuMonitorJSONObjectPropertyType) prop->type) {
/* Simple cases of boolean, int, long, uint, ulong, double, and string
* will receive return value as part of {"return": xxx} statement
*/
case QEMU_MONITOR_OBJECT_PROPERTY_BOOLEAN:
ret = virJSONValueGetBoolean(data, &prop->val.b);
break;
case QEMU_MONITOR_OBJECT_PROPERTY_INT:
ret = virJSONValueGetNumberInt(data, &prop->val.iv);
break;
case QEMU_MONITOR_OBJECT_PROPERTY_LONG:
ret = virJSONValueGetNumberLong(data, &prop->val.l);
break;
case QEMU_MONITOR_OBJECT_PROPERTY_UINT:
ret = virJSONValueGetNumberUint(data, &prop->val.ui);
break;
case QEMU_MONITOR_OBJECT_PROPERTY_ULONG:
ret = virJSONValueGetNumberUlong(data, &prop->val.ul);
break;
case QEMU_MONITOR_OBJECT_PROPERTY_DOUBLE:
ret = virJSONValueGetNumberDouble(data, &prop->val.d);
break;
case QEMU_MONITOR_OBJECT_PROPERTY_STRING:
tmp = virJSONValueGetString(data);
if (tmp)
prop->val.str = g_strdup(tmp);
if (tmp)
ret = 0;
break;
case QEMU_MONITOR_OBJECT_PROPERTY_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("qom-get invalid object property type %d"),
prop->type);
goto cleanup;
break;
}
if (ret == -1) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qom-get reply was missing return data"));
goto cleanup;
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
static int
qemuMonitorJSONGetStringListProperty(qemuMonitor *mon,
const char *path,
const char *property,
char ***strList)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
*strList = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-get",
"s:path", path,
"s:property", property,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
return -1;
if (!(*strList = virJSONValueObjectGetStringArray(reply, "return")))
return -1;
return 0;
}
#define MAKE_SET_CMD(STRING, VALUE) \
cmd = qemuMonitorJSONMakeCommand("qom-set", \
"s:path", path, \
"s:property", property, \
STRING, VALUE, \
NULL)
int qemuMonitorJSONSetObjectProperty(qemuMonitor *mon,
const char *path,
const char *property,
qemuMonitorJSONObjectProperty *prop)
{
int ret = -1;
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
switch ((qemuMonitorJSONObjectPropertyType) prop->type) {
/* Simple cases of boolean, int, long, uint, ulong, double, and string
* will receive return value as part of {"return": xxx} statement
*/
case QEMU_MONITOR_OBJECT_PROPERTY_BOOLEAN:
MAKE_SET_CMD("b:value", prop->val.b);
break;
case QEMU_MONITOR_OBJECT_PROPERTY_INT:
MAKE_SET_CMD("i:value", prop->val.iv);
break;
case QEMU_MONITOR_OBJECT_PROPERTY_LONG:
MAKE_SET_CMD("I:value", prop->val.l);
break;
case QEMU_MONITOR_OBJECT_PROPERTY_UINT:
MAKE_SET_CMD("u:value", prop->val.ui);
break;
case QEMU_MONITOR_OBJECT_PROPERTY_ULONG:
MAKE_SET_CMD("U:value", prop->val.ul);
break;
case QEMU_MONITOR_OBJECT_PROPERTY_DOUBLE:
MAKE_SET_CMD("d:value", prop->val.d);
break;
case QEMU_MONITOR_OBJECT_PROPERTY_STRING:
MAKE_SET_CMD("s:value", prop->val.str);
break;
case QEMU_MONITOR_OBJECT_PROPERTY_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("qom-set invalid object property type %d"),
prop->type);
goto cleanup;
}
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
#undef MAKE_SET_CMD
static int
qemuMonitorJSONParsePropsList(virJSONValue *cmd,
virJSONValue *reply,
const char *type,
char ***props)
{
virJSONValue *data;
g_auto(GStrv) proplist = NULL;
size_t n = 0;
size_t count = 0;
size_t i;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
return -1;
data = virJSONValueObjectGetArray(reply, "return");
n = virJSONValueArraySize(data);
/* null-terminated list */
proplist = g_new0(char *, n + 1);
for (i = 0; i < n; i++) {
virJSONValue *child = virJSONValueArrayGet(data, i);
const char *tmp;
if (type &&
STRNEQ_NULLABLE(virJSONValueObjectGetString(child, "type"), type))
continue;
if (!(tmp = virJSONValueObjectGetString(child, "name"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("reply data was missing 'name'"));
return -1;
}
proplist[count++] = g_strdup(tmp);
}
*props = g_steal_pointer(&proplist);
return count;
}
static int
qemuMonitorJSONGetDevicePropsWorker(size_t pos G_GNUC_UNUSED,
virJSONValue *item,
void *opaque)
{
const char *name = virJSONValueObjectGetString(item, "name");
GHashTable *devices = opaque;
if (!name) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("reply data was missing 'name'"));
return -1;
}
if (virHashAddEntry(devices, name, item) < 0)
return -1;
return 0;
}
GHashTable *
qemuMonitorJSONGetDeviceProps(qemuMonitor *mon,
const char *device)
{
g_autoptr(GHashTable) props = virHashNew(virJSONValueHashFree);
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("device-list-properties",
"s:typename", device,
NULL)))
return NULL;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return NULL;
/* return empty hash */
if (qemuMonitorJSONHasError(reply, "DeviceNotFound"))
return g_steal_pointer(&props);
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
return NULL;
if (virJSONValueArrayForeachSteal(virJSONValueObjectGetArray(reply, "return"),
qemuMonitorJSONGetDevicePropsWorker,
props) < 0)
return NULL;
return g_steal_pointer(&props);
}
int
qemuMonitorJSONGetObjectProps(qemuMonitor *mon,
const char *object,
char ***props)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
*props = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-list-properties",
"s:typename", object,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONHasError(reply, "DeviceNotFound"))
return 0;
return qemuMonitorJSONParsePropsList(cmd, reply, NULL, props);
}
char *
qemuMonitorJSONGetTargetArch(qemuMonitor *mon)
{
char *ret = NULL;
const char *arch;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *data;
if (!(cmd = qemuMonitorJSONMakeCommand("query-target", NULL)))
return NULL;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
goto cleanup;
data = virJSONValueObjectGetObject(reply, "return");
if (!(arch = virJSONValueObjectGetString(data, "arch"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-target reply was missing arch data"));
goto cleanup;
}
ret = g_strdup(arch);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetMigrationCapabilities(qemuMonitor *mon,
2014-09-11 12:11:54 +00:00
char ***capabilities)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
virJSONValue *caps;
g_auto(GStrv) list = NULL;
size_t i;
size_t n;
2014-09-11 12:11:54 +00:00
*capabilities = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-migrate-capabilities",
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
return -1;
caps = virJSONValueObjectGetArray(reply, "return");
n = virJSONValueArraySize(caps);
list = g_new0(char *, n + 1);
2014-09-11 12:11:54 +00:00
for (i = 0; i < n; i++) {
virJSONValue *cap = virJSONValueArrayGet(caps, i);
const char *name;
if (!cap || virJSONValueGetType(cap) != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing entry in migration capabilities list"));
return -1;
}
if (!(name = virJSONValueObjectGetString(cap, "capability"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing migration capability name"));
return -1;
}
list[i] = g_strdup(name);
}
*capabilities = g_steal_pointer(&list);
return n;
}
int
qemuMonitorJSONSetMigrationCapabilities(qemuMonitor *mon,
virJSONValue **caps)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("migrate-set-capabilities",
"a:capabilities", caps,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
/**
* qemuMonitorJSONGetGICCapabilities:
* @mon: QEMU JSON monitor
* @capabilities: where to store the GIC capabilities
*
* Use @mon to obtain information about the GIC capabilities for the
* corresponding QEMU binary, and store them in @capabilities.
*
* If the QEMU binary has no GIC capabilities, or if GIC capabilities could
* not be determined due to the lack of 'query-gic-capabilities' QMP command,
* a NULL pointer will be returned instead of an empty array.
*
* Returns: the number of GIC capabilities obtained from the monitor,
* <0 on failure
*/
int
qemuMonitorJSONGetGICCapabilities(qemuMonitor *mon,
virGICCapability **capabilities)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *caps;
virGICCapability *list = NULL;
size_t i;
size_t n;
*capabilities = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-gic-capabilities",
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
/* If the 'query-gic-capabilities' QMP command was not available
* we simply successfully return zero capabilities.
* This is the case for QEMU <2.6 and all non-ARM architectures */
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
ret = 0;
goto cleanup;
}
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
goto cleanup;
caps = virJSONValueObjectGetArray(reply, "return");
n = virJSONValueArraySize(caps);
/* If the returned array was empty we have to return successfully */
if (n == 0) {
ret = 0;
goto cleanup;
}
list = g_new0(virGICCapability, n);
for (i = 0; i < n; i++) {
virJSONValue *cap = virJSONValueArrayGet(caps, i);
int version;
bool kernel;
bool emulated;
if (!cap || virJSONValueGetType(cap) != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing entry in GIC capabilities list"));
goto cleanup;
}
if (virJSONValueObjectGetNumberInt(cap, "version", &version) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing GIC version"));
goto cleanup;
}
if (virJSONValueObjectGetBoolean(cap, "kernel", &kernel) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing in-kernel GIC information"));
goto cleanup;
}
if (virJSONValueObjectGetBoolean(cap, "emulated", &emulated) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing emulated GIC information"));
goto cleanup;
}
list[i].version = version;
if (kernel)
list[i].implementation |= VIR_GIC_IMPLEMENTATION_KERNEL;
if (emulated)
list[i].implementation |= VIR_GIC_IMPLEMENTATION_EMULATED;
}
ret = n;
*capabilities = g_steal_pointer(&list);
cleanup:
VIR_FREE(list);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/**
* qemuMonitorJSONGetSEVCapabilities:
* @mon: qemu monitor object
* @capabilities: pointer to pointer to a SEV capability structure to be filled
*
* This function queries and fills in AMD's SEV platform-specific data.
* Note that from QEMU's POV both -object sev-guest and query-sev-capabilities
* can be present even if SEV is not available, which basically leaves us with
* checking for JSON "GenericError" in order to differentiate between
* compiled-in support and actual SEV support on the platform.
*
* Returns -1 on error, 0 if SEV is not supported, and 1 if SEV is supported on
* the platform.
*/
int
qemuMonitorJSONGetSEVCapabilities(qemuMonitor *mon,
virSEVCapability **capabilities)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *caps;
const char *pdh = NULL;
const char *cert_chain = NULL;
unsigned int cbitpos;
unsigned int reduced_phys_bits;
g_autoptr(virSEVCapability) capability = NULL;
*capabilities = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-sev-capabilities",
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
/* QEMU has only compiled-in support of SEV */
if (qemuMonitorJSONHasError(reply, "GenericError")) {
ret = 0;
goto cleanup;
}
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
caps = virJSONValueObjectGetObject(reply, "return");
if (virJSONValueObjectGetNumberUint(caps, "cbitpos", &cbitpos) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-sev-capabilities reply was missing"
" 'cbitpos' field"));
goto cleanup;
}
if (virJSONValueObjectGetNumberUint(caps, "reduced-phys-bits",
&reduced_phys_bits) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-sev-capabilities reply was missing"
" 'reduced-phys-bits' field"));
goto cleanup;
}
if (!(pdh = virJSONValueObjectGetString(caps, "pdh"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-sev-capabilities reply was missing"
" 'pdh' field"));
goto cleanup;
}
if (!(cert_chain = virJSONValueObjectGetString(caps, "cert-chain"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-sev-capabilities reply was missing"
" 'cert-chain' field"));
goto cleanup;
}
capability = g_new0(virSEVCapability, 1);
capability->pdh = g_strdup(pdh);
capability->cert_chain = g_strdup(cert_chain);
capability->cbitpos = cbitpos;
capability->reduced_phys_bits = reduced_phys_bits;
*capabilities = g_steal_pointer(&capability);
ret = 1;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
static virJSONValue *
qemuMonitorJSONBuildInetSocketAddress(const char *host,
const char *port)
{
virJSONValue *addr = NULL;
virJSONValue *data = NULL;
if (virJSONValueObjectCreate(&data, "s:host", host,
"s:port", port, NULL) < 0)
return NULL;
if (virJSONValueObjectCreate(&addr, "s:type", "inet",
"a:data", &data, NULL) < 0) {
virJSONValueFree(data);
return NULL;
}
return addr;
}
static virJSONValue *
qemuMonitorJSONBuildUnixSocketAddress(const char *path)
{
virJSONValue *addr = NULL;
virJSONValue *data = NULL;
if (virJSONValueObjectCreate(&data, "s:path", path, NULL) < 0)
return NULL;
if (virJSONValueObjectCreate(&addr, "s:type", "unix",
"a:data", &data, NULL) < 0) {
virJSONValueFree(data);
return NULL;
}
return addr;
}
int
qemuMonitorJSONNBDServerStart(qemuMonitor *mon,
const virStorageNetHostDef *server,
const char *tls_alias)
{
int ret = -1;
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
virJSONValue *addr = NULL;
char *port_str = NULL;
switch ((virStorageNetHostTransport)server->transport) {
case VIR_STORAGE_NET_HOST_TRANS_TCP:
port_str = g_strdup_printf("%u", server->port);
addr = qemuMonitorJSONBuildInetSocketAddress(server->name, port_str);
break;
case VIR_STORAGE_NET_HOST_TRANS_UNIX:
addr = qemuMonitorJSONBuildUnixSocketAddress(server->socket);
break;
case VIR_STORAGE_NET_HOST_TRANS_RDMA:
case VIR_STORAGE_NET_HOST_TRANS_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("invalid server address"));
goto cleanup;
}
if (!addr)
goto cleanup;
if (!(cmd = qemuMonitorJSONMakeCommand("nbd-server-start",
"a:addr", &addr,
"S:tls-creds", tls_alias,
NULL)))
goto cleanup;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(port_str);
virJSONValueFree(reply);
virJSONValueFree(cmd);
virJSONValueFree(addr);
return ret;
}
int
qemuMonitorJSONNBDServerAdd(qemuMonitor *mon,
const char *deviceID,
const char *export,
bool writable,
const char *bitmap)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
/* Note: bitmap must be NULL if QEMU_CAPS_NBD_BITMAP is lacking */
if (!(cmd = qemuMonitorJSONMakeCommand("nbd-server-add",
"s:device", deviceID,
"S:name", export,
"b:writable", writable,
"S:bitmap", bitmap,
NULL)))
return ret;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONNBDServerStop(qemuMonitor *mon)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("nbd-server-stop",
NULL)))
return ret;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONBlockExportAdd(qemuMonitor *mon,
virJSONValue **props)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommandInternal("block-export-add", props)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
static int
qemuMonitorJSONGetStringArray(qemuMonitor *mon,
const char *qmpCmd,
char ***array)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
*array = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand(qmpCmd, NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
return 0;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
return -1;
if (!(*array = virJSONValueObjectGetStringArray(reply, "return")))
return -1;
return 0;
}
int qemuMonitorJSONGetTPMModels(qemuMonitor *mon,
char ***tpmmodels)
{
return qemuMonitorJSONGetStringArray(mon, "query-tpm-models", tpmmodels);
}
int qemuMonitorJSONGetTPMTypes(qemuMonitor *mon,
char ***tpmtypes)
{
return qemuMonitorJSONGetStringArray(mon, "query-tpm-types", tpmtypes);
}
static int
qemuMonitorJSONBuildChrChardevReconnect(virJSONValue *object,
const virDomainChrSourceReconnectDef *def)
{
if (def->enabled != VIR_TRISTATE_BOOL_YES)
return 0;
return virJSONValueObjectAppendNumberUint(object, "reconnect", def->timeout);
}
static virJSONValue *
qemuMonitorJSONAttachCharDevCommand(const char *chrID,
const virDomainChrSourceDef *chr)
{
virJSONValue *ret = NULL;
virJSONValue *backend = virJSONValueNewObject();
virJSONValue *data = virJSONValueNewObject();
virJSONValue *addr = NULL;
const char *backend_type = NULL;
const char *host;
const char *port;
char *tlsalias = NULL;
bool telnet;
switch ((virDomainChrType)chr->type) {
case VIR_DOMAIN_CHR_TYPE_NULL:
backend_type = "null";
break;
case VIR_DOMAIN_CHR_TYPE_VC:
backend_type = "vc";
break;
case VIR_DOMAIN_CHR_TYPE_PTY:
backend_type = "pty";
break;
case VIR_DOMAIN_CHR_TYPE_FILE:
backend_type = "file";
if (virJSONValueObjectAppendString(data, "out", chr->data.file.path) < 0)
goto cleanup;
if (virJSONValueObjectAdd(data,
"T:append", chr->data.file.append,
NULL) < 0)
goto cleanup;
break;
case VIR_DOMAIN_CHR_TYPE_DEV:
backend_type = STRPREFIX(chrID, "parallel") ? "parallel" : "serial";
if (virJSONValueObjectAppendString(data, "device",
chr->data.file.path) < 0)
goto cleanup;
break;
case VIR_DOMAIN_CHR_TYPE_TCP:
backend_type = "socket";
addr = qemuMonitorJSONBuildInetSocketAddress(chr->data.tcp.host,
chr->data.tcp.service);
if (!addr ||
virJSONValueObjectAppend(data, "addr", &addr) < 0)
goto cleanup;
telnet = chr->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET;
if (chr->data.tcp.listen &&
virJSONValueObjectAppendBoolean(data, "wait", false) < 0)
goto cleanup;
if (virJSONValueObjectAppendBoolean(data, "telnet", telnet) < 0 ||
virJSONValueObjectAppendBoolean(data, "server", chr->data.tcp.listen) < 0)
goto cleanup;
if (chr->data.tcp.tlscreds) {
if (!(tlsalias = qemuAliasTLSObjFromSrcAlias(chrID)))
goto cleanup;
if (virJSONValueObjectAppendString(data, "tls-creds", tlsalias) < 0)
goto cleanup;
}
if (qemuMonitorJSONBuildChrChardevReconnect(data, &chr->data.tcp.reconnect) < 0)
goto cleanup;
break;
case VIR_DOMAIN_CHR_TYPE_UDP:
backend_type = "udp";
host = chr->data.udp.connectHost;
if (!host)
host = "";
addr = qemuMonitorJSONBuildInetSocketAddress(host,
chr->data.udp.connectService);
if (!addr ||
virJSONValueObjectAppend(data, "remote", &addr) < 0)
goto cleanup;
host = chr->data.udp.bindHost;
port = chr->data.udp.bindService;
if (host || port) {
if (!host)
host = "";
if (!port)
port = "";
addr = qemuMonitorJSONBuildInetSocketAddress(host, port);
if (!addr ||
virJSONValueObjectAppend(data, "local", &addr) < 0)
goto cleanup;
}
break;
case VIR_DOMAIN_CHR_TYPE_UNIX:
backend_type = "socket";
addr = qemuMonitorJSONBuildUnixSocketAddress(chr->data.nix.path);
if (!addr ||
virJSONValueObjectAppend(data, "addr", &addr) < 0)
goto cleanup;
if (chr->data.nix.listen &&
virJSONValueObjectAppendBoolean(data, "wait", false) < 0)
goto cleanup;
if (virJSONValueObjectAppendBoolean(data, "server", chr->data.nix.listen) < 0)
goto cleanup;
if (qemuMonitorJSONBuildChrChardevReconnect(data, &chr->data.nix.reconnect) < 0)
goto cleanup;
break;
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
backend_type = "spicevmc";
if (virJSONValueObjectAppendString(data, "type",
virDomainChrSpicevmcTypeToString(chr->data.spicevmc)) < 0)
goto cleanup;
break;
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
case VIR_DOMAIN_CHR_TYPE_PIPE:
case VIR_DOMAIN_CHR_TYPE_STDIO:
case VIR_DOMAIN_CHR_TYPE_NMDM:
case VIR_DOMAIN_CHR_TYPE_LAST:
if (virDomainChrTypeToString(chr->type)) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("Hotplug unsupported for char device type '%s'"),
virDomainChrTypeToString(chr->type));
} else {
virReportError(VIR_ERR_OPERATION_FAILED,
_("Hotplug unsupported for char device type '%d'"),
chr->type);
}
goto cleanup;
}
if (chr->logfile &&
virJSONValueObjectAdd(data,
"s:logfile", chr->logfile,
"T:logappend", chr->logappend,
NULL) < 0)
goto cleanup;
if (virJSONValueObjectAppendString(backend, "type", backend_type) < 0 ||
virJSONValueObjectAppend(backend, "data", &data) < 0)
goto cleanup;
if (!(ret = qemuMonitorJSONMakeCommand("chardev-add",
"s:id", chrID,
"a:backend", &backend,
NULL)))
goto cleanup;
cleanup:
VIR_FREE(tlsalias);
virJSONValueFree(addr);
virJSONValueFree(data);
virJSONValueFree(backend);
return ret;
}
int
qemuMonitorJSONAttachCharDev(qemuMonitor *mon,
const char *chrID,
virDomainChrSourceDef *chr)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
if (!(cmd = qemuMonitorJSONAttachCharDevCommand(chrID, chr)))
return ret;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) {
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
goto cleanup;
} else {
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
}
if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) {
virJSONValue *data = virJSONValueObjectGetObject(reply, "return");
const char *path;
if (!(path = virJSONValueObjectGetString(data, "pty"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("chardev-add reply was missing pty path"));
goto cleanup;
}
chr->data.file.path = g_strdup(path);
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONDetachCharDev(qemuMonitor *mon,
const char *chrID)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("chardev-remove",
"s:id", chrID,
NULL)))
return ret;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetDeviceAliases(qemuMonitor *mon,
char ***aliases)
{
qemuMonitorJSONListPath **paths = NULL;
char **alias;
int ret = -1;
size_t i;
int n;
*aliases = NULL;
n = qemuMonitorJSONGetObjectListPaths(mon, "/machine/peripheral", &paths);
if (n < 0)
return -1;
*aliases = g_new0(char *, n + 1);
alias = *aliases;
for (i = 0; i < n; i++) {
if (STRPREFIX(paths[i]->type, "child<")) {
*alias = g_steal_pointer(&paths[i]->name);
alias++;
}
}
ret = 0;
for (i = 0; i < n; i++)
qemuMonitorJSONListPathFree(paths[i]);
VIR_FREE(paths);
return ret;
}
static int
qemuMonitorJSONParseCPUx86FeatureWord(virJSONValue *data,
virCPUx86CPUID *cpuid)
{
const char *reg;
unsigned long long eax_in;
unsigned long long ecx_in = 0;
unsigned long long features;
memset(cpuid, 0, sizeof(*cpuid));
if (!(reg = virJSONValueObjectGetString(data, "cpuid-register"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing cpuid-register in CPU data"));
return -1;
}
if (virJSONValueObjectGetNumberUlong(data, "cpuid-input-eax", &eax_in) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing or invalid cpuid-input-eax in CPU data"));
return -1;
}
ignore_value(virJSONValueObjectGetNumberUlong(data, "cpuid-input-ecx",
&ecx_in));
if (virJSONValueObjectGetNumberUlong(data, "features", &features) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing or invalid features in CPU data"));
return -1;
}
cpuid->eax_in = eax_in;
cpuid->ecx_in = ecx_in;
if (STREQ(reg, "EAX")) {
cpuid->eax = features;
} else if (STREQ(reg, "EBX")) {
cpuid->ebx = features;
} else if (STREQ(reg, "ECX")) {
cpuid->ecx = features;
} else if (STREQ(reg, "EDX")) {
cpuid->edx = features;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown CPU register '%s'"), reg);
return -1;
}
return 0;
}
static virCPUData *
qemuMonitorJSONParseCPUx86Features(virJSONValue *data)
{
virCPUData *cpudata = NULL;
virCPUx86DataItem item = { 0 };
size_t i;
if (!(cpudata = virCPUDataNew(VIR_ARCH_X86_64)))
goto error;
item.type = VIR_CPU_X86_DATA_CPUID;
for (i = 0; i < virJSONValueArraySize(data); i++) {
if (qemuMonitorJSONParseCPUx86FeatureWord(virJSONValueArrayGet(data, i),
&item.data.cpuid) < 0 ||
virCPUx86DataAdd(cpudata, &item) < 0)
goto error;
}
return cpudata;
error:
virCPUDataFree(cpudata);
return NULL;
}
int
qemuMonitorJSONGetCPUx86Data(qemuMonitor *mon,
const char *property,
virCPUData **cpudata)
{
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
virJSONValue *data;
int ret = -1;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-get",
"s:path", QOM_CPU_PATH,
"s:property", property,
NULL)))
goto cleanup;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
goto cleanup;
qemu: simplify json parsing Rather than grabbing an arbitrary JSON value and then checking if it has the right type, we might as well request the correct type to begin with. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONIOProcessEvent) (qemuMonitorJSONCommandWithFd, qemuMonitorJSONHandleGraphics) (qemuMonitorJSONGetStatus, qemuMonitorJSONExtractCPUInfo) (qemuMonitorJSONGetVirtType, qemuMonitorJSONGetBalloonInfo) (qemuMonitorJSONGetMemoryStats) (qemuMonitorJSONDevGetBlockExtent) (qemuMonitorJSONGetOneBlockStatsInfo) (qemuMonitorJSONGetAllBlockStatsInfo) (qemuMonitorJSONBlockStatsUpdateCapacityOne) (qemuMonitorJSONBlockStatsUpdateCapacity) (qemuMonitorJSONGetBlockExtent) (qemuMonitorJSONGetMigrationStatusReply) (qemuMonitorJSONGetDumpGuestMemoryCapability) (qemuMonitorJSONAddFd, qemuMonitorJSONQueryRxFilterParse) (qemuMonitorJSONExtractChardevInfo) (qemuMonitorJSONDiskNameLookupOne) (qemuMonitorJSONDiskNameLookup) (qemuMonitorJSONGetAllBlockJobInfo) (qemuMonitorJSONBlockIoThrottleInfo, qemuMonitorJSONGetVersion) (qemuMonitorJSONGetMachines, qemuMonitorJSONGetCPUDefinitions) (qemuMonitorJSONGetCommands, qemuMonitorJSONGetEvents) (qemuMonitorJSONGetKVMState, qemuMonitorJSONGetObjectTypes) (qemuMonitorJSONGetObjectListPaths) (qemuMonitorJSONGetObjectProps, qemuMonitorJSONGetTargetArch) (qemuMonitorJSONGetMigrationCapabilities) (qemuMonitorJSONGetStringArray, qemuMonitorJSONAttachCharDev) (qemuMonitorJSONGetCPUx86Data, qemuMonitorJSONGetIOThreads) (qemuMonitorJSONGetMemoryDeviceInfo): Use shorter idioms. Signed-off-by: Eric Blake <eblake@redhat.com>
2015-06-20 19:40:21 +00:00
data = virJSONValueObjectGetArray(reply, "return");
if (!(*cpudata = qemuMonitorJSONParseCPUx86Features(data)))
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/*
* Returns -1 on error, 0 if QEMU does not support reporting CPUID features
* of a guest CPU, and 1 if the feature is supported.
*/
static int
qemuMonitorJSONCheckCPUx86(qemuMonitor *mon)
{
virJSONValue *cmd = NULL;
virJSONValue *reply = NULL;
virJSONValue *data;
size_t i;
size_t n;
int ret = -1;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-list",
"s:path", QOM_CPU_PATH,
NULL)))
goto cleanup;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if ((data = virJSONValueObjectGet(reply, "error"))) {
const char *klass = virJSONValueObjectGetString(data, "class");
if (STREQ_NULLABLE(klass, "DeviceNotFound") ||
STREQ_NULLABLE(klass, "CommandNotFound")) {
ret = 0;
goto cleanup;
}
}
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
goto cleanup;
data = virJSONValueObjectGetArray(reply, "return");
n = virJSONValueArraySize(data);
for (i = 0; i < n; i++) {
virJSONValue *element = virJSONValueArrayGet(data, i);
if (STREQ_NULLABLE(virJSONValueObjectGetString(element, "name"),
"feature-words"))
break;
}
if (i == n)
ret = 0;
else
ret = 1;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/**
* qemuMonitorJSONGetGuestCPUx86:
* @mon: Pointer to the monitor
* @data: returns the cpu data of the guest
* @disabled: returns the CPU data for features which were disabled by QEMU
*
* Retrieve the definition of the guest CPU from a running qemu instance.
*
* Returns 0 on success, -2 if guest doesn't support this feature,
* -1 on other errors.
*/
int
qemuMonitorJSONGetGuestCPUx86(qemuMonitor *mon,
virCPUData **data,
virCPUData **disabled)
{
virCPUData *cpuEnabled = NULL;
virCPUData *cpuDisabled = NULL;
int rc;
if ((rc = qemuMonitorJSONCheckCPUx86(mon)) < 0)
return -1;
else if (!rc)
return -2;
if (qemuMonitorJSONGetCPUx86Data(mon, "feature-words",
&cpuEnabled) < 0)
goto error;
if (disabled &&
qemuMonitorJSONGetCPUx86Data(mon, "filtered-features",
&cpuDisabled) < 0)
goto error;
*data = cpuEnabled;
if (disabled)
*disabled = cpuDisabled;
return 0;
error:
virCPUDataFree(cpuEnabled);
virCPUDataFree(cpuDisabled);
return -1;
}
static int
qemuMonitorJSONGetCPUProperties(qemuMonitor *mon,
char ***props)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
*props = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-list",
"s:path", QOM_CPU_PATH,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONHasError(reply, "DeviceNotFound"))
return 0;
return qemuMonitorJSONParsePropsList(cmd, reply, "bool", props);
}
static int
qemuMonitorJSONGetCPUData(qemuMonitor *mon,
qemuMonitorCPUFeatureTranslationCallback translate,
void *opaque,
virCPUData *data)
{
qemuMonitorJSONObjectProperty prop = { .type = QEMU_MONITOR_OBJECT_PROPERTY_BOOLEAN };
g_auto(GStrv) props = NULL;
char **p;
if (qemuMonitorJSONGetCPUProperties(mon, &props) < 0)
return -1;
for (p = props; p && *p; p++) {
const char *name = *p;
if (qemuMonitorJSONGetObjectProperty(mon, QOM_CPU_PATH, name, &prop) < 0)
return -1;
if (!prop.val.b)
continue;
if (translate)
name = translate(name, opaque);
if (virCPUDataAddFeature(data, name) < 0)
return -1;
}
return 0;
}
static int
qemuMonitorJSONGetCPUDataDisabled(qemuMonitor *mon,
qemuMonitorCPUFeatureTranslationCallback translate,
void *opaque,
virCPUData *data)
{
g_auto(GStrv) props = NULL;
char **p;
if (qemuMonitorJSONGetStringListProperty(mon, QOM_CPU_PATH,
"unavailable-features", &props) < 0)
return -1;
for (p = props; p && *p; p++) {
const char *name = *p;
if (translate)
name = translate(name, opaque);
if (virCPUDataAddFeature(data, name) < 0)
return -1;
}
return 0;
}
/**
* qemuMonitorJSONGetGuestCPU:
* @mon: Pointer to the monitor
* @arch: CPU architecture
* @translate: callback for translating CPU feature names from QEMU to libvirt
* @opaque: data for @translate callback
* @enabled: returns the CPU data for all enabled features
* @disabled: returns the CPU data for features which we asked for
* (either explicitly or via a named CPU model) but QEMU disabled them
*
* Retrieve the definition of the guest CPU from a running QEMU instance.
*
* Returns 0 on success, -1 on error.
*/
int
qemuMonitorJSONGetGuestCPU(qemuMonitor *mon,
virArch arch,
qemuMonitorCPUFeatureTranslationCallback translate,
void *opaque,
virCPUData **enabled,
virCPUData **disabled)
{
virCPUData *cpuEnabled = NULL;
virCPUData *cpuDisabled = NULL;
int ret = -1;
if (!(cpuEnabled = virCPUDataNew(arch)) ||
!(cpuDisabled = virCPUDataNew(arch)))
goto cleanup;
if (qemuMonitorJSONGetCPUData(mon, translate, opaque, cpuEnabled) < 0)
goto cleanup;
if (disabled &&
qemuMonitorJSONGetCPUDataDisabled(mon, translate, opaque, cpuDisabled) < 0)
goto cleanup;
*enabled = g_steal_pointer(&cpuEnabled);
if (disabled)
*disabled = g_steal_pointer(&cpuDisabled);
ret = 0;
cleanup:
virCPUDataFree(cpuEnabled);
virCPUDataFree(cpuDisabled);
return ret;
}
int
qemuMonitorJSONRTCResetReinjection(qemuMonitor *mon)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("rtc-reset-reinjection",
NULL)))
return ret;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/**
* Query and parse returned array of data such as:
*
* {u'return': [{u'id': u'iothread1', u'thread-id': 30992}, \
* {u'id': u'iothread2', u'thread-id': 30993}]}
*/
int
qemuMonitorJSONGetIOThreads(qemuMonitor *mon,
qemuMonitorIOThreadInfo ***iothreads,
int *niothreads)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *data;
qemuMonitorIOThreadInfo **infolist = NULL;
size_t n = 0;
size_t i;
*iothreads = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-iothreads", NULL)))
return ret;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
goto cleanup;
data = virJSONValueObjectGetArray(reply, "return");
n = virJSONValueArraySize(data);
/* null-terminated list */
infolist = g_new0(qemuMonitorIOThreadInfo *, n + 1);
for (i = 0; i < n; i++) {
virJSONValue *child = virJSONValueArrayGet(data, i);
const char *tmp;
qemuMonitorIOThreadInfo *info;
if (!(tmp = virJSONValueObjectGetString(child, "id"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-iothreads reply data was missing 'id'"));
goto cleanup;
}
if (!STRPREFIX(tmp, "iothread"))
continue;
info = g_new0(qemuMonitorIOThreadInfo, 1);
infolist[i] = info;
if (virStrToLong_ui(tmp + strlen("iothread"),
NULL, 10, &info->iothread_id) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to find iothread id for '%s'"),
tmp);
goto cleanup;
}
if (virJSONValueObjectGetNumberInt(child, "thread-id",
&info->thread_id) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-iothreads reply has malformed "
"'thread-id' data"));
goto cleanup;
}
/* Fetch poll values (since QEMU 2.9 ) if available. QEMU
* stores these values as int64_t's; however, the qapi type
* is an int. The qapi/misc.json also mis-describes the grow
* and shrink values as pure add/remove values. The source
* util/aio-posix.c function aio_poll uses them as a factor
* or divisor in it's calculation. We will fetch and store
* them as defined in our structures. */
if (virJSONValueObjectGetNumberUlong(child, "poll-max-ns",
&info->poll_max_ns) == 0 &&
virJSONValueObjectGetNumberUint(child, "poll-grow",
&info->poll_grow) == 0 &&
virJSONValueObjectGetNumberUint(child, "poll-shrink",
&info->poll_shrink) == 0)
info->poll_valid = true;
}
*niothreads = n;
*iothreads = g_steal_pointer(&infolist);
ret = 0;
cleanup:
if (infolist) {
for (i = 0; i < n; i++)
VIR_FREE(infolist[i]);
VIR_FREE(infolist);
}
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONSetIOThread(qemuMonitor *mon,
qemuMonitorIOThreadInfo *iothreadInfo)
{
int ret = -1;
char *path = NULL;
qemuMonitorJSONObjectProperty prop;
path = g_strdup_printf("/objects/iothread%u", iothreadInfo->iothread_id);
#define VIR_IOTHREAD_SET_PROP(propName, propVal) \
if (iothreadInfo->set_##propVal) { \
memset(&prop, 0, sizeof(qemuMonitorJSONObjectProperty)); \
prop.type = QEMU_MONITOR_OBJECT_PROPERTY_INT; \
prop.val.iv = iothreadInfo->propVal; \
if (qemuMonitorJSONSetObjectProperty(mon, path, propName, &prop) < 0) \
goto cleanup; \
}
VIR_IOTHREAD_SET_PROP("poll-max-ns", poll_max_ns);
VIR_IOTHREAD_SET_PROP("poll-grow", poll_grow);
VIR_IOTHREAD_SET_PROP("poll-shrink", poll_shrink);
#undef VIR_IOTHREAD_SET_PROP
ret = 0;
cleanup:
VIR_FREE(path);
return ret;
}
int
qemuMonitorJSONGetMemoryDeviceInfo(qemuMonitor *mon,
GHashTable *info)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *data = NULL;
size_t i;
if (!(cmd = qemuMonitorJSONMakeCommand("query-memory-devices", NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
goto cleanup;
data = virJSONValueObjectGetArray(reply, "return");
for (i = 0; i < virJSONValueArraySize(data); i++) {
virJSONValue *elem = virJSONValueArrayGet(data, i);
g_autofree qemuMonitorMemoryDeviceInfo *meminfo = NULL;
virJSONValue *dimminfo;
const char *devalias;
const char *type;
if (!(type = virJSONValueObjectGetString(elem, "type"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-memory-devices reply data doesn't contain "
"enum type discriminator"));
goto cleanup;
}
if (!(dimminfo = virJSONValueObjectGetObject(elem, "data"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-memory-devices reply data doesn't "
"contain enum data"));
goto cleanup;
}
/* While 'id' attribute is marked as optional in QEMU's QAPI
* specification, Libvirt always sets it. Thus we can fail if not
* present. */
if (!(devalias = virJSONValueObjectGetString(dimminfo, "id"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("dimm memory info data is missing 'id'"));
goto cleanup;
}
meminfo = g_new0(qemuMonitorMemoryDeviceInfo, 1);
/* dimm memory devices */
if (STREQ(type, "dimm")) {
if (virJSONValueObjectGetNumberUlong(dimminfo, "addr",
&meminfo->address) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed/missing addr in dimm memory info"));
goto cleanup;
}
if (virJSONValueObjectGetNumberUint(dimminfo, "slot",
&meminfo->slot) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed/missing slot in dimm memory info"));
goto cleanup;
}
if (virJSONValueObjectGetBoolean(dimminfo, "hotplugged",
&meminfo->hotplugged) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed/missing hotplugged in dimm memory info"));
goto cleanup;
}
if (virJSONValueObjectGetBoolean(dimminfo, "hotpluggable",
&meminfo->hotpluggable) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed/missing hotpluggable in dimm memory info"));
goto cleanup;
}
} else if (STREQ(type, "virtio-mem")) {
if (virJSONValueObjectGetNumberUlong(dimminfo, "size",
&meminfo->size) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed/missing size in virtio memory info"));
goto cleanup;
}
} else {
/* type not handled yet */
continue;
}
if (virHashAddEntry(info, devalias, meminfo) < 0)
goto cleanup;
meminfo = NULL;
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/**
* Search for a QOM object link by alias and name.
*
* For @alias and @name, this function tries to find QOM object named @name
* with id @alias in /machine/peripheral.
*
* Returns:
* 0 - Found
* -1 - Error - bail out
* -2 - Not found
*/
static int
qemuMonitorJSONFindObjectPathByAlias(qemuMonitor *mon,
const char *name,
const char *alias,
char **path)
{
qemuMonitorJSONListPath **paths = NULL;
char *child = NULL;
int npaths;
int ret = -1;
size_t i;
npaths = qemuMonitorJSONGetObjectListPaths(mon, "/machine/peripheral", &paths);
if (npaths < 0)
return -1;
child = g_strdup_printf("child<%s>", name);
for (i = 0; i < npaths; i++) {
if (STREQ(paths[i]->name, alias) && STREQ(paths[i]->type, child)) {
*path = g_strdup_printf("/machine/peripheral/%s", alias);
ret = 0;
goto cleanup;
}
}
ret = -2;
cleanup:
for (i = 0; i < npaths; i++)
qemuMonitorJSONListPathFree(paths[i]);
VIR_FREE(paths);
VIR_FREE(child);
return ret;
}
/**
* Recursively search for a QOM object link only by name.
*
* For @name, this function finds the first QOM object
* named @name, recursively going through all the "child<>"
* entries, starting from @curpath.
*
* Returns:
* 0 - Found
* -1 - Error - bail out
* -2 - Not found
*/
static int
qemuMonitorJSONFindObjectPathByName(qemuMonitor *mon,
const char *curpath,
const char *name,
char **path)
{
ssize_t i, npaths = 0;
int ret = -2;
char *nextpath = NULL;
qemuMonitorJSONListPath **paths = NULL;
VIR_DEBUG("Searching for '%s' Object Path starting at '%s'", name, curpath);
npaths = qemuMonitorJSONGetObjectListPaths(mon, curpath, &paths);
if (npaths < 0)
goto cleanup;
for (i = 0; i < npaths && ret == -2; i++) {
if (STREQ_NULLABLE(paths[i]->type, name)) {
VIR_DEBUG("Path to '%s' is '%s/%s'", name, curpath, paths[i]->name);
ret = 0;
*path = g_strdup_printf("%s/%s", curpath, paths[i]->name);
goto cleanup;
}
/* Type entries that begin with "child<" are a branch that can be
* traversed looking for more entries
*/
if (paths[i]->type && STRPREFIX(paths[i]->type, "child<")) {
nextpath = g_strdup_printf("%s/%s", curpath, paths[i]->name);
ret = qemuMonitorJSONFindObjectPathByName(mon, nextpath, name, path);
VIR_FREE(nextpath);
}
}
cleanup:
for (i = 0; i < npaths; i++)
qemuMonitorJSONListPathFree(paths[i]);
VIR_FREE(paths);
VIR_FREE(nextpath);
return ret;
}
/**
* Recursively search for a QOM object link.
*
* For @name and @alias, this function finds the first QOM object.
* The search is done at first by @alias and @name and if nothing was found
* it continues recursively only with @name.
*
* Returns:
* 0 - Found
* -1 - Error
* -2 - Not found
*/
int
qemuMonitorJSONFindLinkPath(qemuMonitor *mon,
const char *name,
const char *alias,
char **path)
{
char *linkname = NULL;
int ret = -1;
if (alias) {
ret = qemuMonitorJSONFindObjectPathByAlias(mon, name, alias, path);
if (ret == -1 || ret == 0)
return ret;
}
linkname = g_strdup_printf("link<%s>", name);
ret = qemuMonitorJSONFindObjectPathByName(mon, "/", linkname, path);
VIR_FREE(linkname);
return ret;
}
int
qemuMonitorJSONMigrateIncoming(qemuMonitor *mon,
const char *uri)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("migrate-incoming",
"s:uri", uri,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
return qemuMonitorJSONCheckError(cmd, reply);
}
int
qemuMonitorJSONMigrateStartPostCopy(qemuMonitor *mon)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("migrate-start-postcopy", NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
ret = qemuMonitorJSONCheckError(cmd, reply);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONMigrateContinue(qemuMonitor *mon,
qemuMonitorMigrationStatus status)
{
const char *statusStr = qemuMonitorMigrationStatusTypeToString(status);
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("migrate-continue",
"s:state", statusStr,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
ret = qemuMonitorJSONCheckError(cmd, reply);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetRTCTime(qemuMonitor *mon,
struct tm *tm)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *data;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-get",
"s:path", "/machine",
"s:property", "rtc-time",
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
goto cleanup;
data = virJSONValueObjectGet(reply, "return");
if (virJSONValueObjectGetNumberInt(data, "tm_year", &tm->tm_year) < 0 ||
virJSONValueObjectGetNumberInt(data, "tm_mon", &tm->tm_mon) < 0 ||
virJSONValueObjectGetNumberInt(data, "tm_mday", &tm->tm_mday) < 0 ||
virJSONValueObjectGetNumberInt(data, "tm_hour", &tm->tm_hour) < 0 ||
virJSONValueObjectGetNumberInt(data, "tm_min", &tm->tm_min) < 0 ||
virJSONValueObjectGetNumberInt(data, "tm_sec", &tm->tm_sec) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qemu returned malformed time"));
goto cleanup;
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
void
qemuMonitorQueryHotpluggableCpusFree(struct qemuMonitorQueryHotpluggableCpusEntry *entries,
size_t nentries)
{
struct qemuMonitorQueryHotpluggableCpusEntry *entry;
size_t i;
if (!entries)
return;
for (i = 0; i < nentries; i++) {
entry = entries + i;
g_free(entry->type);
g_free(entry->qom_path);
g_free(entry->alias);
virJSONValueFree(entry->props);
}
g_free(entries);
}
/**
* [{
* "props": {
* "core-id": 0,
* "thread-id": 0,
* "socket-id": 0
* },
* "vcpus-count": 1,
* "qom-path": "/machine/unattached/device[0]",
* "type": "qemu64-x86_64-cpu"
* },
* {...}
* ]
*/
static int
qemuMonitorJSONProcessHotpluggableCpusReply(virJSONValue *vcpu,
struct qemuMonitorQueryHotpluggableCpusEntry *entry)
{
virJSONValue *props;
const char *tmp;
if (!(tmp = virJSONValueObjectGetString(vcpu, "type"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-hotpluggable-cpus didn't return device type"));
return -1;
}
entry->type = g_strdup(tmp);
if (virJSONValueObjectGetNumberUint(vcpu, "vcpus-count", &entry->vcpus) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-hotpluggable-cpus didn't return vcpus-count"));
return -1;
}
if (!(props = virJSONValueObjectGetObject(vcpu, "props"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-hotpluggable-cpus didn't return device props"));
return -1;
}
if (!(entry->props = virJSONValueCopy(props)))
return -1;
entry->node_id = -1;
entry->socket_id = -1;
entry->die_id = -1;
entry->core_id = -1;
entry->thread_id = -1;
ignore_value(virJSONValueObjectGetNumberInt(props, "node-id", &entry->node_id));
ignore_value(virJSONValueObjectGetNumberInt(props, "socket-id", &entry->socket_id));
ignore_value(virJSONValueObjectGetNumberInt(props, "die-id", &entry->die_id));
ignore_value(virJSONValueObjectGetNumberInt(props, "core-id", &entry->core_id));
ignore_value(virJSONValueObjectGetNumberInt(props, "thread-id", &entry->thread_id));
if (entry->node_id == -1 && entry->socket_id == -1 &&
entry->core_id == -1 && entry->thread_id == -1) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-hotpluggable-cpus entry doesn't report "
"topology information"));
return -1;
}
/* qom path is not present unless the vCPU is online */
if ((tmp = virJSONValueObjectGetString(vcpu, "qom-path"))) {
entry->qom_path = g_strdup(tmp);
/* alias is the part after last slash having a "vcpu" prefix */
if ((tmp = strrchr(tmp, '/')) && STRPREFIX(tmp + 1, "vcpu"))
entry->alias = g_strdup(tmp + 1);
}
return 0;
}
static int
qemuMonitorQueryHotpluggableCpusEntrySort(const void *p1,
const void *p2)
{
const struct qemuMonitorQueryHotpluggableCpusEntry *a = p1;
const struct qemuMonitorQueryHotpluggableCpusEntry *b = p2;
if (a->socket_id != b->socket_id)
return a->socket_id - b->socket_id;
if (a->die_id != b->die_id)
return a->die_id - b->die_id;
if (a->core_id != b->core_id)
return a->core_id - b->core_id;
return a->thread_id - b->thread_id;
}
int
qemuMonitorJSONGetHotpluggableCPUs(qemuMonitor *mon,
struct qemuMonitorQueryHotpluggableCpusEntry **entries,
size_t *nentries)
{
struct qemuMonitorQueryHotpluggableCpusEntry *info = NULL;
size_t ninfo = 0;
int ret = -1;
size_t i;
virJSONValue *data;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *vcpu;
if (!(cmd = qemuMonitorJSONMakeCommand("query-hotpluggable-cpus", NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
goto cleanup;
data = virJSONValueObjectGet(reply, "return");
ninfo = virJSONValueArraySize(data);
info = g_new0(struct qemuMonitorQueryHotpluggableCpusEntry, ninfo);
for (i = 0; i < ninfo; i++) {
vcpu = virJSONValueArrayGet(data, i);
if (qemuMonitorJSONProcessHotpluggableCpusReply(vcpu, info + i) < 0)
goto cleanup;
}
qsort(info, ninfo, sizeof(*info), qemuMonitorQueryHotpluggableCpusEntrySort);
*entries = g_steal_pointer(&info);
*nentries = ninfo;
ret = 0;
cleanup:
qemuMonitorQueryHotpluggableCpusFree(info, ninfo);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
virJSONValue *
qemuMonitorJSONQueryQMPSchema(qemuMonitor *mon)
{
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *ret = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-qmp-schema", NULL)))
return NULL;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
goto cleanup;
ret = virJSONValueObjectStealArray(reply, "return");
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONSetBlockThreshold(qemuMonitor *mon,
const char *nodename,
unsigned long long threshold)
{
virJSONValue *cmd;
virJSONValue *reply = NULL;
int ret = -1;
if (!(cmd = qemuMonitorJSONMakeCommand("block-set-write-threshold",
"s:node-name", nodename,
"U:write-threshold", threshold,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
virJSONValue *
qemuMonitorJSONQueryNamedBlockNodes(qemuMonitor *mon,
bool flat)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-named-block-nodes",
"B:flat", flat,
NULL)))
return NULL;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return NULL;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
return NULL;
return virJSONValueObjectStealArray(reply, "return");
}
int
qemuMonitorJSONSetWatchdogAction(qemuMonitor *mon,
const char *action)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("watchdog-set-action",
"s:action", action,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONBlockdevCreate(qemuMonitor *mon,
const char *jobname,
virJSONValue *props)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
cmd = qemuMonitorJSONMakeCommand("blockdev-create",
"s:job-id", jobname,
"a:options", &props,
NULL);
virJSONValueFree(props);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONBlockdevAdd(qemuMonitor *mon,
virJSONValue **props)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommandInternal("blockdev-add", props)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONBlockdevReopen(qemuMonitor *mon,
virJSONValue **props)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommandInternal("blockdev-reopen", props)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONBlockdevDel(qemuMonitor *mon,
const char *nodename)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("blockdev-del",
"s:node-name", nodename,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONBlockdevTrayOpen(qemuMonitor *mon,
const char *id,
bool force)
{
virJSONValue *cmd;
virJSONValue *reply = NULL;
int ret = -1;
if (!(cmd = qemuMonitorJSONMakeCommand("blockdev-open-tray",
"s:id", id,
"b:force", force, NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONBlockdevTrayClose(qemuMonitor *mon,
const char *id)
{
virJSONValue *cmd;
virJSONValue *reply = NULL;
int ret = -1;
if (!(cmd = qemuMonitorJSONMakeCommand("blockdev-close-tray",
"s:id", id, NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONBlockdevMediumRemove(qemuMonitor *mon,
const char *id)
{
virJSONValue *cmd;
virJSONValue *reply = NULL;
int ret = -1;
if (!(cmd = qemuMonitorJSONMakeCommand("blockdev-remove-medium",
"s:id", id, NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONBlockdevMediumInsert(qemuMonitor *mon,
const char *id,
const char *nodename)
{
virJSONValue *cmd;
virJSONValue *reply = NULL;
int ret = -1;
if (!(cmd = qemuMonitorJSONMakeCommand("blockdev-insert-medium",
"s:id", id,
"s:node-name", nodename,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/**
* The function is used to retrieve the measurement of a SEV guest.
* The measurement is signature of the memory contents that was encrypted
* through the SEV launch flow.
*
* A example JSON output:
*
* { "execute" : "query-sev-launch-measure" }
* { "return" : { "data" : "4l8LXeNlSPUDlXPJG5966/8%YZ" } }
*/
char *
qemuMonitorJSONGetSEVMeasurement(qemuMonitor *mon)
{
const char *tmp;
char *measurement = NULL;
virJSONValue *cmd;
virJSONValue *reply = NULL;
virJSONValue *data;
if (!(cmd = qemuMonitorJSONMakeCommand("query-sev-launch-measure", NULL)))
return NULL;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
goto cleanup;
data = virJSONValueObjectGetObject(reply, "return");
if (!(tmp = virJSONValueObjectGetString(data, "data")))
goto cleanup;
measurement = g_strdup(tmp);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return measurement;
}
/*
* Example return data
*
* "return": [
* { "connected": true, "id": "pr-helper0" }
* ]
*/
static int
qemuMonitorJSONExtractPRManagerInfo(virJSONValue *reply,
GHashTable *info)
{
qemuMonitorPRManagerInfo *entry = NULL;
virJSONValue *data;
int ret = -1;
size_t i;
data = virJSONValueObjectGetArray(reply, "return");
for (i = 0; i < virJSONValueArraySize(data); i++) {
virJSONValue *prManager = virJSONValueArrayGet(data, i);
const char *alias;
if (!(alias = virJSONValueObjectGetString(prManager, "id")))
goto malformed;
entry = g_new0(qemuMonitorPRManagerInfo, 1);
if (virJSONValueObjectGetBoolean(prManager,
"connected",
&entry->connected) < 0) {
goto malformed;
}
if (virHashAddEntry(info, alias, entry) < 0)
goto cleanup;
entry = NULL;
}
ret = 0;
cleanup:
VIR_FREE(entry);
return ret;
malformed:
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed prManager reply"));
goto cleanup;
}
int
qemuMonitorJSONGetPRManagerInfo(qemuMonitor *mon,
GHashTable *info)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-pr-managers",
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
goto cleanup;
ret = qemuMonitorJSONExtractPRManagerInfo(reply, info);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
static int
qemuMonitorJSONExtractCurrentMachineInfo(virJSONValue *reply,
qemuMonitorCurrentMachineInfo *info)
{
virJSONValue *data;
data = virJSONValueObjectGetObject(reply, "return");
if (!data)
goto malformed;
if (virJSONValueObjectGetBoolean(data, "wakeup-suspend-support",
&info->wakeupSuspendSupport) < 0)
goto malformed;
return 0;
malformed:
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed qemu-current-machine reply"));
return -1;
}
int
qemuMonitorJSONGetCurrentMachineInfo(qemuMonitor *mon,
qemuMonitorCurrentMachineInfo *info)
{
int ret = -1;
virJSONValue *cmd;
virJSONValue *reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-current-machine",
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0)
goto cleanup;
ret = qemuMonitorJSONExtractCurrentMachineInfo(reply, info);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONTransactionBitmapAdd(virJSONValue *actions,
const char *node,
const char *name,
bool persistent,
bool disabled,
unsigned long long granularity)
{
return qemuMonitorJSONTransactionAdd(actions,
"block-dirty-bitmap-add",
"s:node", node,
"s:name", name,
"b:persistent", persistent,
"b:disabled", disabled,
"P:granularity", granularity,
NULL);
}
int
qemuMonitorJSONTransactionBitmapRemove(virJSONValue *actions,
const char *node,
const char *name)
{
return qemuMonitorJSONTransactionAdd(actions,
"block-dirty-bitmap-remove",
"s:node", node,
"s:name", name,
NULL);
}
int
qemuMonitorJSONBitmapRemove(qemuMonitor *mon,
const char *node,
const char *name)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("block-dirty-bitmap-remove",
"s:node", node,
"s:name", name,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONTransactionBitmapEnable(virJSONValue *actions,
const char *node,
const char *name)
{
return qemuMonitorJSONTransactionAdd(actions,
"block-dirty-bitmap-enable",
"s:node", node,
"s:name", name,
NULL);
}
int
qemuMonitorJSONTransactionBitmapDisable(virJSONValue *actions,
const char *node,
const char *name)
{
return qemuMonitorJSONTransactionAdd(actions,
"block-dirty-bitmap-disable",
"s:node", node,
"s:name", name,
NULL);
}
int
qemuMonitorJSONTransactionBitmapMerge(virJSONValue *actions,
const char *node,
const char *target,
virJSONValue **sources)
{
return qemuMonitorJSONTransactionAdd(actions,
"block-dirty-bitmap-merge",
"s:node", node,
"s:target", target,
"a:bitmaps", sources,
NULL);
}
int
qemuMonitorJSONTransactionBitmapMergeSourceAddBitmap(virJSONValue *sources,
const char *sourcenode,
const char *sourcebitmap)
{
g_autoptr(virJSONValue) sourceobj = NULL;
if (virJSONValueObjectCreate(&sourceobj,
"s:node", sourcenode,
"s:name", sourcebitmap,
NULL) < 0)
return -1;
if (virJSONValueArrayAppend(sources, &sourceobj) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONTransactionSnapshotLegacy(virJSONValue *actions,
const char *device,
const char *path,
const char *format,
bool existing)
{
const char *mode = NULL;
if (existing)
mode = "existing";
return qemuMonitorJSONTransactionAdd(actions,
"blockdev-snapshot-sync",
"s:device", device,
"s:snapshot-file", path,
"s:format", format,
"S:mode", mode,
NULL);
}
int
qemuMonitorJSONTransactionSnapshotBlockdev(virJSONValue *actions,
const char *node,
const char *overlay)
{
return qemuMonitorJSONTransactionAdd(actions,
"blockdev-snapshot",
"s:node", node,
"s:overlay", overlay,
NULL);
}
VIR_ENUM_DECL(qemuMonitorTransactionBackupSyncMode);
VIR_ENUM_IMPL(qemuMonitorTransactionBackupSyncMode,
QEMU_MONITOR_TRANSACTION_BACKUP_SYNC_MODE_LAST,
"none",
"incremental",
"full");
int
qemuMonitorJSONTransactionBackup(virJSONValue *actions,
const char *device,
const char *jobname,
const char *target,
const char *bitmap,
qemuMonitorTransactionBackupSyncMode syncmode)
{
const char *syncmodestr = qemuMonitorTransactionBackupSyncModeTypeToString(syncmode);
return qemuMonitorJSONTransactionAdd(actions,
"blockdev-backup",
"s:device", device,
"s:job-id", jobname,
"s:target", target,
"s:sync", syncmodestr,
"S:bitmap", bitmap,
"T:auto-finalize", VIR_TRISTATE_BOOL_YES,
"T:auto-dismiss", VIR_TRISTATE_BOOL_NO,
NULL);
}
static qemuMonitorJobInfo *
qemuMonitorJSONGetJobInfoOne(virJSONValue *data)
{
const char *id = virJSONValueObjectGetString(data, "id");
const char *type = virJSONValueObjectGetString(data, "type");
const char *status = virJSONValueObjectGetString(data, "status");
const char *errmsg = virJSONValueObjectGetString(data, "error");
int tmp;
g_autoptr(qemuMonitorJobInfo) job = NULL;
job = g_new0(qemuMonitorJobInfo, 1);
if ((tmp = qemuMonitorJobTypeFromString(type)) < 0)
tmp = QEMU_MONITOR_JOB_TYPE_UNKNOWN;
job->type = tmp;
if ((tmp = qemuMonitorJobStatusTypeFromString(status)) < 0)
tmp = QEMU_MONITOR_JOB_STATUS_UNKNOWN;
job->status = tmp;
job->id = g_strdup(id);
job->error = g_strdup(errmsg);
/* failure to fetch progress stats is not fatal */
ignore_value(virJSONValueObjectGetNumberUlong(data, "current-progress",
&job->progressCurrent));
ignore_value(virJSONValueObjectGetNumberUlong(data, "total-progress",
&job->progressTotal));
return g_steal_pointer(&job);
}
int
qemuMonitorJSONGetJobInfo(qemuMonitor *mon,
qemuMonitorJobInfo ***jobs,
size_t *njobs)
{
virJSONValue *data;
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
size_t i;
if (!(cmd = qemuMonitorJSONMakeCommand("query-jobs", NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0)
return -1;
data = virJSONValueObjectGetArray(reply, "return");
for (i = 0; i < virJSONValueArraySize(data); i++) {
qemuMonitorJobInfo *job = NULL;
if (!(job = qemuMonitorJSONGetJobInfoOne(virJSONValueArrayGet(data, i))))
return -1;
VIR_APPEND_ELEMENT(*jobs, *njobs, job);
}
return 0;
}
int
qemuMonitorJSONGetCPUMigratable(qemuMonitor *mon,
bool *migratable)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-get",
"s:path", QOM_CPU_PATH,
"s:property", "migratable",
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONHasError(reply, "GenericError"))
return 1;
if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_BOOLEAN) < 0)
return -1;
return virJSONValueGetBoolean(virJSONValueObjectGet(reply, "return"),
migratable);
}
int
qemuMonitorJSONStartDirtyRateCalc(qemuMonitor *mon,
int seconds)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("calc-dirty-rate",
"i:calc-time", seconds,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
VIR_ENUM_DECL(qemuMonitorDirtyRateStatus);
VIR_ENUM_IMPL(qemuMonitorDirtyRateStatus,
VIR_DOMAIN_DIRTYRATE_LAST,
"unstarted",
"measuring",
"measured");
static int
qemuMonitorJSONExtractDirtyRateInfo(virJSONValue *data,
qemuMonitorDirtyRateInfo *info)
{
const char *statusstr;
int status;
if (!(statusstr = virJSONValueObjectGetString(data, "status"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-dirty-rate reply was missing 'status' data"));
return -1;
}
if ((status = qemuMonitorDirtyRateStatusTypeFromString(statusstr)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unknown dirty rate status: %s"), statusstr);
return -1;
}
info->status = status;
/* `query-dirty-rate` replies `dirty-rate` data only if the status of the latest
* calculation is `measured`.
*/
if ((info->status == VIR_DOMAIN_DIRTYRATE_MEASURED) &&
(virJSONValueObjectGetNumberLong(data, "dirty-rate", &info->dirtyRate) < 0)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-dirty-rate reply was missing 'dirty-rate' data"));
return -1;
}
if (virJSONValueObjectGetNumberLong(data, "start-time", &info->startTime) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-dirty-rate reply was missing 'start-time' data"));
return -1;
}
if (virJSONValueObjectGetNumberInt(data, "calc-time", &info->calcTime) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-dirty-rate reply was missing 'calc-time' data"));
return -1;
}
return 0;
}
int
qemuMonitorJSONQueryDirtyRate(qemuMonitor *mon,
qemuMonitorDirtyRateInfo *info)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
virJSONValue *data = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-dirty-rate", NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
if (!(data = virJSONValueObjectGetObject(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-dirty-rate reply was missing 'return' data"));
return -1;
}
return qemuMonitorJSONExtractDirtyRateInfo(data, info);
}
VIR_ENUM_DECL(qemuMonitorActionShutdown);
VIR_ENUM_IMPL(qemuMonitorActionShutdown,
QEMU_MONITOR_ACTION_SHUTDOWN_LAST,
"",
"poweroff",
"pause");
VIR_ENUM_DECL(qemuMonitorActionReboot);
VIR_ENUM_IMPL(qemuMonitorActionReboot,
QEMU_MONITOR_ACTION_REBOOT_LAST,
"",
"reset",
"shutdown");
VIR_ENUM_DECL(qemuMonitorActionWatchdog);
VIR_ENUM_IMPL(qemuMonitorActionWatchdog,
QEMU_MONITOR_ACTION_WATCHDOG_LAST,
"",
"reset",
"shutdown",
"poweroff",
"pause",
"debug",
"none",
"inject-nmi");
VIR_ENUM_DECL(qemuMonitorActionPanic);
VIR_ENUM_IMPL(qemuMonitorActionPanic,
QEMU_MONITOR_ACTION_PANIC_LAST,
"",
"pause",
"shutdown",
"none");
int
qemuMonitorJSONSetAction(qemuMonitor *mon,
qemuMonitorActionShutdown shutdown,
qemuMonitorActionReboot reboot,
qemuMonitorActionWatchdog watchdog,
qemuMonitorActionPanic panic)
{
g_autoptr(virJSONValue) cmd = NULL;
g_autoptr(virJSONValue) reply = NULL;
const char *actionShutdown = NULL;
const char *actionReboot = NULL;
const char *actionWatchdog = NULL;
const char *actionPanic = NULL;
if (shutdown != QEMU_MONITOR_ACTION_SHUTDOWN_KEEP)
actionShutdown = qemuMonitorActionShutdownTypeToString(shutdown);
if (reboot != QEMU_MONITOR_ACTION_REBOOT_KEEP)
actionReboot = qemuMonitorActionRebootTypeToString(reboot);
if (watchdog != QEMU_MONITOR_ACTION_WATCHDOG_KEEP)
actionWatchdog = qemuMonitorActionWatchdogTypeToString(watchdog);
if (panic != QEMU_MONITOR_ACTION_PANIC_KEEP)
actionPanic = qemuMonitorActionPanicTypeToString(panic);
if (!(cmd = qemuMonitorJSONMakeCommand("set-action",
"S:shutdown", actionShutdown,
"S:reboot", actionReboot,
"S:watchdog", actionWatchdog,
"S:panic", actionPanic,
NULL)))
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
return -1;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
return -1;
return 0;
}
int
qemuMonitorJSONChangeMemoryRequestedSize(qemuMonitor *mon,
const char *alias,
unsigned long long requestedsize)
{
g_autofree char *path = g_strdup_printf("/machine/peripheral/%s", alias);
qemuMonitorJSONObjectProperty prop = {
.type = QEMU_MONITOR_OBJECT_PROPERTY_ULONG,
.val.ul = requestedsize * 1024, /* monitor needs bytes */
};
return qemuMonitorJSONSetObjectProperty(mon, path, "requested-size", &prop);
}