2009-11-03 18:59:18 +00:00
|
|
|
/*
|
|
|
|
* qemu_monitor_json.c: interaction with QEMU monitor console
|
|
|
|
*
|
2014-02-10 10:18:16 +00:00
|
|
|
* Copyright (C) 2006-2014 Red Hat, Inc.
|
2009-11-03 18:59:18 +00:00
|
|
|
* 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
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2009-11-03 18:59:18 +00:00
|
|
|
*
|
|
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <poll.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
2011-02-23 11:12:11 +00:00
|
|
|
#include "qemu_monitor_text.h"
|
2009-11-03 18:59:18 +00:00
|
|
|
#include "qemu_monitor_json.h"
|
2010-12-16 15:07:07 +00:00
|
|
|
#include "qemu_command.h"
|
2012-02-13 11:19:24 +00:00
|
|
|
#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"
|
2009-11-03 18:59:18 +00:00
|
|
|
#include "driver.h"
|
|
|
|
#include "datatypes.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-12-12 17:53:50 +00:00
|
|
|
#include "virjson.h"
|
2014-02-27 13:41:11 +00:00
|
|
|
#include "virprobe.h"
|
2013-04-12 20:55:45 +00:00
|
|
|
#include "virstring.h"
|
2013-07-22 11:07:23 +00:00
|
|
|
#include "cpu/cpu_x86.h"
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2012-04-02 17:24:29 +00:00
|
|
|
#ifdef WITH_DTRACE_PROBES
|
|
|
|
# include "libvirt_qemu_probes.h"
|
|
|
|
#endif
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_QEMU
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("qemu.qemu_monitor_json");
|
|
|
|
|
2013-07-22 11:07:23 +00:00
|
|
|
#define QOM_CPU_PATH "/machine/unattached/device[0]"
|
2009-11-03 18:59:18 +00:00
|
|
|
|
|
|
|
#define LINE_ENDING "\r\n"
|
|
|
|
|
2009-11-26 13:06:24 +00:00
|
|
|
static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data);
|
|
|
|
static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data);
|
|
|
|
static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data);
|
|
|
|
static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data);
|
2013-01-07 21:25:01 +00:00
|
|
|
static void qemuMonitorJSONHandleResume(qemuMonitorPtr mon, virJSONValuePtr data);
|
2010-03-18 18:28:15 +00:00
|
|
|
static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr data);
|
Add support for an explicit watchdog event
This introduces a new event type
VIR_DOMAIN_EVENT_ID_WATCHDOG
This event includes the action that is about to be taken
as a result of the watchdog triggering
typedef enum {
VIR_DOMAIN_EVENT_WATCHDOG_NONE = 0,
VIR_DOMAIN_EVENT_WATCHDOG_PAUSE,
VIR_DOMAIN_EVENT_WATCHDOG_RESET,
VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF,
VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN,
VIR_DOMAIN_EVENT_WATCHDOG_DEBUG,
} virDomainEventWatchdogAction;
Thus there is a new callback definition for this event type
typedef void (*virConnectDomainEventWatchdogCallback)(virConnectPtr conn,
virDomainPtr dom,
int action,
void *opaque);
* daemon/remote.c: Dispatch watchdog events to client
* examples/domain-events/events-c/event-test.c: Watch for
watchdog events
* include/libvirt/libvirt.h.in: Define new watchdg event ID
and callback signature
* src/conf/domain_event.c, src/conf/domain_event.h,
src/libvirt_private.syms: Extend API to handle watchdog events
* src/qemu/qemu_driver.c: Connect to the QEMU monitor event
for watchdogs and emit a libvirt watchdog event
* src/remote/remote_driver.c: Receive and dispatch watchdog
events to application
* src/remote/remote_protocol.x: Wire protocol definition for
watchdog events
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c: Watch for WATCHDOG event
from QEMU monitor
2010-03-18 19:07:48 +00:00
|
|
|
static void qemuMonitorJSONHandleWatchdog(qemuMonitorPtr mon, virJSONValuePtr 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
|
|
|
static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr 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
|
|
|
static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, virJSONValuePtr data);
|
|
|
|
static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data);
|
|
|
|
static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data);
|
2012-03-14 05:41:35 +00:00
|
|
|
static void qemuMonitorJSONHandleSPICEConnect(qemuMonitorPtr mon, virJSONValuePtr data);
|
|
|
|
static void qemuMonitorJSONHandleSPICEInitialize(qemuMonitorPtr mon, virJSONValuePtr data);
|
|
|
|
static void qemuMonitorJSONHandleSPICEDisconnect(qemuMonitorPtr mon, virJSONValuePtr data);
|
2012-03-23 13:44:50 +00:00
|
|
|
static void qemuMonitorJSONHandleTrayChange(qemuMonitorPtr mon, virJSONValuePtr data);
|
2012-03-23 14:43:14 +00:00
|
|
|
static void qemuMonitorJSONHandlePMWakeup(qemuMonitorPtr mon, virJSONValuePtr data);
|
2012-03-23 14:50:36 +00:00
|
|
|
static void qemuMonitorJSONHandlePMSuspend(qemuMonitorPtr mon, virJSONValuePtr data);
|
2012-04-11 22:17:36 +00:00
|
|
|
static void qemuMonitorJSONHandleBlockJobCompleted(qemuMonitorPtr mon, virJSONValuePtr data);
|
|
|
|
static void qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon, virJSONValuePtr data);
|
2012-10-12 20:06:10 +00:00
|
|
|
static void qemuMonitorJSONHandleBlockJobReady(qemuMonitorPtr mon, virJSONValuePtr data);
|
2012-07-12 15:45:57 +00:00
|
|
|
static void qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, virJSONValuePtr data);
|
2012-10-12 19:13:39 +00:00
|
|
|
static void qemuMonitorJSONHandlePMSuspendDisk(qemuMonitorPtr mon, virJSONValuePtr data);
|
2013-06-07 10:23:34 +00:00
|
|
|
static void qemuMonitorJSONHandleGuestPanic(qemuMonitorPtr mon, virJSONValuePtr data);
|
2013-07-11 15:07:26 +00:00
|
|
|
static void qemuMonitorJSONHandleDeviceDeleted(qemuMonitorPtr mon, virJSONValuePtr data);
|
2014-09-17 17:07:50 +00:00
|
|
|
static void qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitorPtr mon, virJSONValuePtr data);
|
2009-11-26 13:06:24 +00:00
|
|
|
|
2012-04-11 14:58:09 +00:00
|
|
|
typedef struct {
|
2009-11-26 13:06:24 +00:00
|
|
|
const char *type;
|
|
|
|
void (*handler)(qemuMonitorPtr mon, virJSONValuePtr data);
|
2012-04-11 14:58:09 +00:00
|
|
|
} qemuEventHandler;
|
|
|
|
|
|
|
|
static qemuEventHandler eventHandlers[] = {
|
2012-07-12 15:45:57 +00:00
|
|
|
{ "BALLOON_CHANGE", qemuMonitorJSONHandleBalloonChange, },
|
2010-02-16 12:07:49 +00:00
|
|
|
{ "BLOCK_IO_ERROR", qemuMonitorJSONHandleIOError, },
|
2012-04-11 22:17:36 +00:00
|
|
|
{ "BLOCK_JOB_CANCELLED", qemuMonitorJSONHandleBlockJobCanceled, },
|
|
|
|
{ "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJobCompleted, },
|
2012-10-12 20:06:10 +00:00
|
|
|
{ "BLOCK_JOB_READY", qemuMonitorJSONHandleBlockJobReady, },
|
2013-07-11 15:07:26 +00:00
|
|
|
{ "DEVICE_DELETED", qemuMonitorJSONHandleDeviceDeleted, },
|
2012-04-11 14:58:09 +00:00
|
|
|
{ "DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange, },
|
2013-06-07 10:23:34 +00:00
|
|
|
{ "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, },
|
2014-09-17 17:07:50 +00:00
|
|
|
{ "NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged, },
|
2012-04-11 14:58:09 +00:00
|
|
|
{ "POWERDOWN", qemuMonitorJSONHandlePowerdown, },
|
|
|
|
{ "RESET", qemuMonitorJSONHandleReset, },
|
2013-01-07 21:25:01 +00:00
|
|
|
{ "RESUME", qemuMonitorJSONHandleResume, },
|
2012-04-11 14:58:09 +00:00
|
|
|
{ "RTC_CHANGE", qemuMonitorJSONHandleRTCChange, },
|
|
|
|
{ "SHUTDOWN", qemuMonitorJSONHandleShutdown, },
|
2012-03-14 05:41:35 +00:00
|
|
|
{ "SPICE_CONNECTED", qemuMonitorJSONHandleSPICEConnect, },
|
|
|
|
{ "SPICE_DISCONNECTED", qemuMonitorJSONHandleSPICEDisconnect, },
|
2012-04-11 14:58:09 +00:00
|
|
|
{ "SPICE_INITIALIZED", qemuMonitorJSONHandleSPICEInitialize, },
|
|
|
|
{ "STOP", qemuMonitorJSONHandleStop, },
|
2012-03-23 14:50:36 +00:00
|
|
|
{ "SUSPEND", qemuMonitorJSONHandlePMSuspend, },
|
2012-10-12 19:13:39 +00:00
|
|
|
{ "SUSPEND_DISK", qemuMonitorJSONHandlePMSuspendDisk, },
|
2012-04-11 14:58:09 +00:00
|
|
|
{ "VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect, },
|
|
|
|
{ "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, },
|
|
|
|
{ "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, },
|
|
|
|
{ "WAKEUP", qemuMonitorJSONHandlePMWakeup, },
|
|
|
|
{ "WATCHDOG", qemuMonitorJSONHandleWatchdog, },
|
|
|
|
/* We use bsearch, so keep this list sorted. */
|
2009-11-26 13:06:24 +00:00
|
|
|
};
|
|
|
|
|
2012-04-11 14:58:09 +00:00
|
|
|
static int
|
|
|
|
qemuMonitorEventCompare(const void *key, const void *elt)
|
|
|
|
{
|
|
|
|
const char *type = key;
|
|
|
|
const qemuEventHandler *handler = elt;
|
|
|
|
return strcmp(type, handler->type);
|
|
|
|
}
|
2009-11-26 13:06:24 +00:00
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon,
|
|
|
|
virJSONValuePtr obj)
|
|
|
|
{
|
2010-01-22 13:22:53 +00:00
|
|
|
const char *type;
|
2012-04-11 14:58:09 +00:00
|
|
|
qemuEventHandler *handler;
|
2014-01-30 00:14:44 +00:00
|
|
|
virJSONValuePtr data;
|
|
|
|
char *details = NULL;
|
|
|
|
virJSONValuePtr timestamp;
|
|
|
|
long long seconds = -1;
|
|
|
|
unsigned int micros = 0;
|
|
|
|
|
2009-11-26 13:06:24 +00:00
|
|
|
VIR_DEBUG("mon=%p obj=%p", mon, obj);
|
|
|
|
|
|
|
|
type = virJSONValueObjectGetString(obj, "event");
|
|
|
|
if (!type) {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_WARN("missing event type in message");
|
2009-11-26 13:06:24 +00:00
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-01-30 00:14:44 +00:00
|
|
|
/* 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"))) {
|
|
|
|
virJSONValuePtr elt;
|
|
|
|
|
|
|
|
if ((elt = virJSONValueObjectGet(timestamp, "seconds")))
|
|
|
|
ignore_value(virJSONValueGetNumberLong(elt, &seconds));
|
|
|
|
if ((elt = virJSONValueObjectGet(timestamp, "microseconds")))
|
|
|
|
ignore_value(virJSONValueGetNumberUint(elt, µs));
|
|
|
|
}
|
|
|
|
qemuMonitorEmitEvent(mon, type, seconds, micros, details);
|
|
|
|
VIR_FREE(details);
|
|
|
|
|
2012-04-11 14:58:09 +00:00
|
|
|
handler = bsearch(type, eventHandlers, ARRAY_CARDINALITY(eventHandlers),
|
|
|
|
sizeof(eventHandlers[0]), qemuMonitorEventCompare);
|
|
|
|
if (handler) {
|
|
|
|
VIR_DEBUG("handle %s handler=%p data=%p", type,
|
|
|
|
handler->handler, data);
|
|
|
|
(handler->handler)(mon, data);
|
2009-11-26 13:06:24 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
static int
|
2009-11-26 13:06:24 +00:00
|
|
|
qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon,
|
2009-11-03 18:59:18 +00:00
|
|
|
const char *line,
|
|
|
|
qemuMonitorMessagePtr msg)
|
|
|
|
{
|
|
|
|
virJSONValuePtr obj = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
VIR_DEBUG("Line [%s]", line);
|
|
|
|
|
2011-05-29 12:51:08 +00:00
|
|
|
if (!(obj = virJSONValueFromString(line)))
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (obj->type != VIR_JSON_TYPE_OBJECT) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Parsed JSON reply '%s' isn't an object"), line);
|
2011-05-29 12:51:08 +00:00
|
|
|
goto cleanup;
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectHasKey(obj, "QMP") == 1) {
|
|
|
|
ret = 0;
|
2011-05-29 12:51:08 +00:00
|
|
|
} else if (virJSONValueObjectHasKey(obj, "event") == 1) {
|
2011-10-24 14:31:37 +00:00
|
|
|
PROBE(QEMU_MONITOR_RECV_EVENT,
|
|
|
|
"mon=%p event=%s", mon, line);
|
2009-11-26 13:06:24 +00:00
|
|
|
ret = qemuMonitorJSONIOProcessEvent(mon, obj);
|
2011-05-29 12:51:08 +00:00
|
|
|
} else if (virJSONValueObjectHasKey(obj, "error") == 1 ||
|
|
|
|
virJSONValueObjectHasKey(obj, "return") == 1) {
|
2011-10-24 14:31:37 +00:00
|
|
|
PROBE(QEMU_MONITOR_RECV_REPLY,
|
|
|
|
"mon=%p reply=%s", mon, line);
|
2011-05-29 12:51:08 +00:00
|
|
|
if (msg) {
|
|
|
|
msg->rxObject = obj;
|
|
|
|
msg->finished = 1;
|
2011-06-30 14:03:31 +00:00
|
|
|
obj = NULL;
|
2011-05-29 12:51:08 +00:00
|
|
|
ret = 0;
|
|
|
|
} else {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unexpected JSON reply '%s'"), line);
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
} else {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unknown JSON reply '%s'"), line);
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2011-06-30 14:03:31 +00:00
|
|
|
virJSONValueFree(obj);
|
2009-11-03 18:59:18 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorJSONIOProcess(qemuMonitorPtr mon,
|
|
|
|
const char *data,
|
|
|
|
size_t len,
|
|
|
|
qemuMonitorMessagePtr 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);
|
2013-05-20 09:23:13 +00:00
|
|
|
char *line;
|
|
|
|
if (VIR_STRNDUP(line, data + used, got) < 0)
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_DEBUG("Total used %d bytes out of %zd available in buffer", used, len);
|
|
|
|
return used;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONCommandWithFd(qemuMonitorPtr mon,
|
|
|
|
virJSONValuePtr cmd,
|
|
|
|
int scm_fd,
|
|
|
|
virJSONValuePtr *reply)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
qemuMonitorMessage msg;
|
|
|
|
char *cmdstr = NULL;
|
2011-05-29 12:51:08 +00:00
|
|
|
char *id = NULL;
|
|
|
|
virJSONValuePtr exe;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
|
|
|
*reply = NULL;
|
|
|
|
|
2012-03-29 09:52:04 +00:00
|
|
|
memset(&msg, 0, sizeof(msg));
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2011-05-29 12:51:08 +00:00
|
|
|
exe = virJSONValueObjectGet(cmd, "execute");
|
|
|
|
if (exe) {
|
|
|
|
if (!(id = qemuMonitorNextCommandID(mon)))
|
|
|
|
goto cleanup;
|
|
|
|
if (virJSONValueObjectAppendString(cmd, "id", id) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to append command 'id' string"));
|
2011-05-29 12:51:08 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-22 16:41:38 +00:00
|
|
|
if (!(cmdstr = virJSONValueToString(cmd, false)))
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
2013-07-04 10:14:12 +00:00
|
|
|
if (virAsprintf(&msg.txBuffer, "%s\r\n", cmdstr) < 0)
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
msg.txLength = strlen(msg.txBuffer);
|
|
|
|
msg.txFD = scm_fd;
|
|
|
|
|
|
|
|
VIR_DEBUG("Send command '%s' for write with FD %d", cmdstr, scm_fd);
|
|
|
|
|
|
|
|
ret = qemuMonitorSend(mon, &msg);
|
|
|
|
|
2011-05-29 12:51:08 +00:00
|
|
|
VIR_DEBUG("Receive command reply ret=%d rxObject=%p",
|
|
|
|
ret, msg.rxObject);
|
2009-11-03 18:59:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
if (ret == 0) {
|
2011-05-29 12:51:08 +00:00
|
|
|
if (!msg.rxObject) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Missing monitor reply object"));
|
2011-05-29 12:51:08 +00:00
|
|
|
ret = -1;
|
|
|
|
} else {
|
|
|
|
*reply = msg.rxObject;
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2011-05-29 12:51:08 +00:00
|
|
|
VIR_FREE(id);
|
2009-11-03 18:59:18 +00:00
|
|
|
VIR_FREE(cmdstr);
|
|
|
|
VIR_FREE(msg.txBuffer);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONCommand(qemuMonitorPtr mon,
|
|
|
|
virJSONValuePtr cmd,
|
2014-03-18 08:15:21 +00:00
|
|
|
virJSONValuePtr *reply)
|
|
|
|
{
|
2009-11-03 18:59:18 +00:00
|
|
|
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
|
|
|
|
*/
|
2010-02-12 16:45:11 +00:00
|
|
|
static const char *
|
|
|
|
qemuMonitorJSONStringifyError(virJSONValuePtr error)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
2010-01-22 13:22:53 +00:00
|
|
|
const char *klass = virJSONValueObjectGetString(error, "class");
|
2010-02-12 16:45:11 +00:00
|
|
|
const char *detail = NULL;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2010-02-12 16:45:11 +00:00
|
|
|
/* 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(virJSONValuePtr cmd)
|
|
|
|
{
|
|
|
|
const char *name = virJSONValueObjectGetString(cmd, "execute");
|
|
|
|
if (name)
|
|
|
|
return name;
|
|
|
|
else
|
|
|
|
return "<unknown>";
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONCheckError(virJSONValuePtr cmd,
|
|
|
|
virJSONValuePtr reply)
|
|
|
|
{
|
|
|
|
if (virJSONValueObjectHasKey(reply, "error")) {
|
|
|
|
virJSONValuePtr error = virJSONValueObjectGet(reply, "error");
|
2012-08-09 10:46:29 +00:00
|
|
|
char *cmdstr = virJSONValueToString(cmd, false);
|
|
|
|
char *replystr = virJSONValueToString(reply, false);
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2010-02-12 16:45:11 +00:00
|
|
|
/* Log the full JSON formatted command & error */
|
|
|
|
VIR_DEBUG("unable to execute QEMU command %s: %s",
|
2013-02-22 16:41:38 +00:00
|
|
|
NULLSTR(cmdstr), NULLSTR(replystr));
|
2010-02-12 16:45:11 +00:00
|
|
|
|
|
|
|
/* Only send the user the command name + friendly error */
|
|
|
|
if (!error)
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unable to execute QEMU command '%s'"),
|
|
|
|
qemuMonitorJSONCommandName(cmd));
|
2010-02-12 16:45:11 +00:00
|
|
|
else
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unable to execute QEMU command '%s': %s"),
|
|
|
|
qemuMonitorJSONCommandName(cmd),
|
|
|
|
qemuMonitorJSONStringifyError(error));
|
2010-02-12 16:45:11 +00:00
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
VIR_FREE(cmdstr);
|
|
|
|
VIR_FREE(replystr);
|
|
|
|
return -1;
|
|
|
|
} else if (!virJSONValueObjectHasKey(reply, "return")) {
|
2012-08-09 10:46:29 +00:00
|
|
|
char *cmdstr = virJSONValueToString(cmd, false);
|
|
|
|
char *replystr = virJSONValueToString(reply, false);
|
2009-11-03 18:59:18 +00:00
|
|
|
|
|
|
|
VIR_DEBUG("Neither 'return' nor 'error' is set in the JSON reply %s: %s",
|
2013-02-22 16:41:38 +00:00
|
|
|
NULLSTR(cmdstr), NULLSTR(replystr));
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unable to execute QEMU command '%s'"),
|
|
|
|
qemuMonitorJSONCommandName(cmd));
|
2009-11-03 18:59:18 +00:00
|
|
|
VIR_FREE(cmdstr);
|
|
|
|
VIR_FREE(replystr);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONHasError(virJSONValuePtr reply,
|
|
|
|
const char *klass)
|
|
|
|
{
|
|
|
|
virJSONValuePtr error;
|
2010-01-22 13:22:53 +00:00
|
|
|
const char *thisklass;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
|
|
|
if (!virJSONValueObjectHasKey(reply, "error"))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error = virJSONValueObjectGet(reply, "error");
|
|
|
|
if (!error)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!virJSONValueObjectHasKey(error, "class"))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
thisklass = virJSONValueObjectGetString(error, "class");
|
|
|
|
|
|
|
|
if (!thisklass)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return STREQ(klass, thisklass);
|
|
|
|
}
|
|
|
|
|
2012-03-17 04:17:28 +00:00
|
|
|
/* Top-level commands and nested transaction list elements share a
|
|
|
|
* common structure for everything except the dictionary names. */
|
2009-11-03 18:59:18 +00:00
|
|
|
static virJSONValuePtr ATTRIBUTE_SENTINEL
|
2012-03-17 04:17:28 +00:00
|
|
|
qemuMonitorJSONMakeCommandRaw(bool wrap, const char *cmdname, ...)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
|
|
|
virJSONValuePtr obj;
|
|
|
|
virJSONValuePtr jargs = NULL;
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, cmdname);
|
|
|
|
|
|
|
|
if (!(obj = virJSONValueNewObject()))
|
2013-07-04 10:14:12 +00:00
|
|
|
goto error;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2012-03-17 04:17:28 +00:00
|
|
|
if (virJSONValueObjectAppendString(obj, wrap ? "type" : "execute",
|
|
|
|
cmdname) < 0)
|
2013-07-04 10:14:12 +00:00
|
|
|
goto error;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2014-09-22 14:35:02 +00:00
|
|
|
if (virJSONValueObjectCreateVArgs(&jargs, args) < 0)
|
|
|
|
goto error;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
|
|
|
if (jargs &&
|
2012-03-17 04:17:28 +00:00
|
|
|
virJSONValueObjectAppend(obj, wrap ? "data" : "arguments", jargs) < 0)
|
2013-07-04 10:14:12 +00:00
|
|
|
goto error;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
error:
|
2009-11-03 18:59:18 +00:00
|
|
|
virJSONValueFree(obj);
|
|
|
|
virJSONValueFree(jargs);
|
|
|
|
va_end(args);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-03-17 04:17:28 +00:00
|
|
|
#define qemuMonitorJSONMakeCommand(cmdname, ...) \
|
|
|
|
qemuMonitorJSONMakeCommandRaw(false, cmdname, __VA_ARGS__)
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2010-04-15 13:52:03 +00:00
|
|
|
static void
|
|
|
|
qemuFreeKeywords(int nkeywords, char **keywords, char **values)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < nkeywords; i++) {
|
2010-04-15 13:52:03 +00:00
|
|
|
VIR_FREE(keywords[i]);
|
|
|
|
VIR_FREE(values[i]);
|
|
|
|
}
|
|
|
|
VIR_FREE(keywords);
|
|
|
|
VIR_FREE(values);
|
|
|
|
}
|
|
|
|
|
|
|
|
static virJSONValuePtr
|
|
|
|
qemuMonitorJSONKeywordStringToJSON(const char *str, const char *firstkeyword)
|
|
|
|
{
|
|
|
|
virJSONValuePtr ret = NULL;
|
|
|
|
char **keywords = NULL;
|
|
|
|
char **values = NULL;
|
|
|
|
int nkeywords = 0;
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2010-04-15 13:52:03 +00:00
|
|
|
|
|
|
|
if (!(ret = virJSONValueNewObject()))
|
2014-09-04 20:12:44 +00:00
|
|
|
return NULL;
|
2010-04-15 13:52:03 +00:00
|
|
|
|
2013-09-23 13:16:09 +00:00
|
|
|
if (qemuParseKeywords(str, &keywords, &values, &nkeywords, 1) < 0)
|
2010-04-15 13:52:03 +00:00
|
|
|
goto error;
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < nkeywords; i++) {
|
2010-04-15 13:52:03 +00:00
|
|
|
if (values[i] == NULL) {
|
|
|
|
if (i != 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected empty keyword in %s"), str);
|
2010-04-15 13:52:03 +00:00
|
|
|
goto error;
|
|
|
|
} else {
|
|
|
|
/* This 3rd arg isn't a typo - the way the parser works is
|
|
|
|
* that the value ended up in the keyword field */
|
|
|
|
if (virJSONValueObjectAppendString(ret, firstkeyword, keywords[i]) < 0)
|
2013-07-04 10:14:12 +00:00
|
|
|
goto error;
|
2010-04-15 13:52:03 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (virJSONValueObjectAppendString(ret, keywords[i], values[i]) < 0)
|
2013-07-04 10:14:12 +00:00
|
|
|
goto error;
|
2010-04-15 13:52:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
qemuFreeKeywords(nkeywords, keywords, values);
|
|
|
|
return ret;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
error:
|
2010-04-15 13:52:03 +00:00
|
|
|
qemuFreeKeywords(nkeywords, keywords, values);
|
|
|
|
virJSONValueFree(ret);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-26 13:06:24 +00:00
|
|
|
static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
qemuMonitorEmitShutdown(mon);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
qemuMonitorEmitReset(mon);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
qemuMonitorEmitPowerdown(mon);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
qemuMonitorEmitStop(mon);
|
|
|
|
}
|
|
|
|
|
2013-01-07 21:25:01 +00:00
|
|
|
static void qemuMonitorJSONHandleResume(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
qemuMonitorEmitResume(mon);
|
|
|
|
}
|
|
|
|
|
2013-06-07 10:23:34 +00:00
|
|
|
static void qemuMonitorJSONHandleGuestPanic(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
qemuMonitorEmitGuestPanic(mon);
|
|
|
|
}
|
|
|
|
|
2010-03-18 18:28:15 +00:00
|
|
|
static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
long long offset = 0;
|
|
|
|
if (virJSONValueObjectGetNumberLong(data, "offset", &offset) < 0) {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_WARN("missing offset in RTC change event");
|
2010-03-18 18:28:15 +00:00
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
qemuMonitorEmitRTCChange(mon, offset);
|
|
|
|
}
|
|
|
|
|
Add support for an explicit watchdog event
This introduces a new event type
VIR_DOMAIN_EVENT_ID_WATCHDOG
This event includes the action that is about to be taken
as a result of the watchdog triggering
typedef enum {
VIR_DOMAIN_EVENT_WATCHDOG_NONE = 0,
VIR_DOMAIN_EVENT_WATCHDOG_PAUSE,
VIR_DOMAIN_EVENT_WATCHDOG_RESET,
VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF,
VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN,
VIR_DOMAIN_EVENT_WATCHDOG_DEBUG,
} virDomainEventWatchdogAction;
Thus there is a new callback definition for this event type
typedef void (*virConnectDomainEventWatchdogCallback)(virConnectPtr conn,
virDomainPtr dom,
int action,
void *opaque);
* daemon/remote.c: Dispatch watchdog events to client
* examples/domain-events/events-c/event-test.c: Watch for
watchdog events
* include/libvirt/libvirt.h.in: Define new watchdg event ID
and callback signature
* src/conf/domain_event.c, src/conf/domain_event.h,
src/libvirt_private.syms: Extend API to handle watchdog events
* src/qemu/qemu_driver.c: Connect to the QEMU monitor event
for watchdogs and emit a libvirt watchdog event
* src/remote/remote_driver.c: Receive and dispatch watchdog
events to application
* src/remote/remote_protocol.x: Wire protocol definition for
watchdog events
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c: Watch for WATCHDOG event
from QEMU monitor
2010-03-18 19:07:48 +00:00
|
|
|
VIR_ENUM_DECL(qemuMonitorWatchdogAction)
|
2012-01-20 21:00:58 +00:00
|
|
|
VIR_ENUM_IMPL(qemuMonitorWatchdogAction, VIR_DOMAIN_EVENT_WATCHDOG_LAST,
|
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
|
|
|
"none", "pause", "reset", "poweroff", "shutdown", "debug");
|
Add support for an explicit watchdog event
This introduces a new event type
VIR_DOMAIN_EVENT_ID_WATCHDOG
This event includes the action that is about to be taken
as a result of the watchdog triggering
typedef enum {
VIR_DOMAIN_EVENT_WATCHDOG_NONE = 0,
VIR_DOMAIN_EVENT_WATCHDOG_PAUSE,
VIR_DOMAIN_EVENT_WATCHDOG_RESET,
VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF,
VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN,
VIR_DOMAIN_EVENT_WATCHDOG_DEBUG,
} virDomainEventWatchdogAction;
Thus there is a new callback definition for this event type
typedef void (*virConnectDomainEventWatchdogCallback)(virConnectPtr conn,
virDomainPtr dom,
int action,
void *opaque);
* daemon/remote.c: Dispatch watchdog events to client
* examples/domain-events/events-c/event-test.c: Watch for
watchdog events
* include/libvirt/libvirt.h.in: Define new watchdg event ID
and callback signature
* src/conf/domain_event.c, src/conf/domain_event.h,
src/libvirt_private.syms: Extend API to handle watchdog events
* src/qemu/qemu_driver.c: Connect to the QEMU monitor event
for watchdogs and emit a libvirt watchdog event
* src/remote/remote_driver.c: Receive and dispatch watchdog
events to application
* src/remote/remote_protocol.x: Wire protocol definition for
watchdog events
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c: Watch for WATCHDOG event
from QEMU monitor
2010-03-18 19:07:48 +00:00
|
|
|
|
|
|
|
static void qemuMonitorJSONHandleWatchdog(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
const char *action;
|
|
|
|
int actionID;
|
|
|
|
if (!(action = virJSONValueObjectGetString(data, "action"))) {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_WARN("missing action in watchdog event");
|
Add support for an explicit watchdog event
This introduces a new event type
VIR_DOMAIN_EVENT_ID_WATCHDOG
This event includes the action that is about to be taken
as a result of the watchdog triggering
typedef enum {
VIR_DOMAIN_EVENT_WATCHDOG_NONE = 0,
VIR_DOMAIN_EVENT_WATCHDOG_PAUSE,
VIR_DOMAIN_EVENT_WATCHDOG_RESET,
VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF,
VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN,
VIR_DOMAIN_EVENT_WATCHDOG_DEBUG,
} virDomainEventWatchdogAction;
Thus there is a new callback definition for this event type
typedef void (*virConnectDomainEventWatchdogCallback)(virConnectPtr conn,
virDomainPtr dom,
int action,
void *opaque);
* daemon/remote.c: Dispatch watchdog events to client
* examples/domain-events/events-c/event-test.c: Watch for
watchdog events
* include/libvirt/libvirt.h.in: Define new watchdg event ID
and callback signature
* src/conf/domain_event.c, src/conf/domain_event.h,
src/libvirt_private.syms: Extend API to handle watchdog events
* src/qemu/qemu_driver.c: Connect to the QEMU monitor event
for watchdogs and emit a libvirt watchdog event
* src/remote/remote_driver.c: Receive and dispatch watchdog
events to application
* src/remote/remote_protocol.x: Wire protocol definition for
watchdog events
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c: Watch for WATCHDOG event
from QEMU monitor
2010-03-18 19:07:48 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
2010-04-08 15:01:00 +00:00
|
|
|
qemuMonitorEmitWatchdog(mon, actionID);
|
Add support for an explicit watchdog event
This introduces a new event type
VIR_DOMAIN_EVENT_ID_WATCHDOG
This event includes the action that is about to be taken
as a result of the watchdog triggering
typedef enum {
VIR_DOMAIN_EVENT_WATCHDOG_NONE = 0,
VIR_DOMAIN_EVENT_WATCHDOG_PAUSE,
VIR_DOMAIN_EVENT_WATCHDOG_RESET,
VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF,
VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN,
VIR_DOMAIN_EVENT_WATCHDOG_DEBUG,
} virDomainEventWatchdogAction;
Thus there is a new callback definition for this event type
typedef void (*virConnectDomainEventWatchdogCallback)(virConnectPtr conn,
virDomainPtr dom,
int action,
void *opaque);
* daemon/remote.c: Dispatch watchdog events to client
* examples/domain-events/events-c/event-test.c: Watch for
watchdog events
* include/libvirt/libvirt.h.in: Define new watchdg event ID
and callback signature
* src/conf/domain_event.c, src/conf/domain_event.h,
src/libvirt_private.syms: Extend API to handle watchdog events
* src/qemu/qemu_driver.c: Connect to the QEMU monitor event
for watchdogs and emit a libvirt watchdog event
* src/remote/remote_driver.c: Receive and dispatch watchdog
events to application
* src/remote/remote_protocol.x: Wire protocol definition for
watchdog events
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c: Watch for WATCHDOG event
from QEMU monitor
2010-03-18 19:07:48 +00:00
|
|
|
}
|
|
|
|
|
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(qemuMonitorIOErrorAction)
|
2012-01-20 21:00:58 +00:00
|
|
|
VIR_ENUM_IMPL(qemuMonitorIOErrorAction, VIR_DOMAIN_EVENT_IO_ERROR_LAST,
|
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
|
|
|
"ignore", "stop", "report");
|
|
|
|
|
|
|
|
|
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(qemuMonitorPtr mon, virJSONValuePtr 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 *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 imporatant to get as much info as possible out
|
|
|
|
to the application */
|
|
|
|
|
|
|
|
if ((action = virJSONValueObjectGetString(data, "action")) == NULL) {
|
2011-05-09 09:24:09 +00:00
|
|
|
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) {
|
2011-05-09 09:24:09 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
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 another explicit IO error event
This introduces a new event type
VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON
This event is the same as the previous VIR_DOMAIN_ID_IO_ERROR
event, but also includes a string describing the cause of
the event.
Thus there is a new callback definition for this event type
typedef void (*virConnectDomainEventIOErrorReasonCallback)(virConnectPtr conn,
virDomainPtr dom,
const char *srcPath,
const char *devAlias,
int action,
const char *reason,
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
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
Add support for another explicit IO error event
This introduces a new event type
VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON
This event is the same as the previous VIR_DOMAIN_ID_IO_ERROR
event, but also includes a string describing the cause of
the event.
Thus there is a new callback definition for this event type
typedef void (*virConnectDomainEventIOErrorReasonCallback)(virConnectPtr conn,
virDomainPtr dom,
const char *srcPath,
const char *devAlias,
int action,
const char *reason,
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
|
|
|
qemuMonitorEmitIOError(mon, device, 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
|
|
|
}
|
|
|
|
|
2009-11-26 13:06:24 +00:00
|
|
|
|
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
|
|
|
VIR_ENUM_DECL(qemuMonitorGraphicsAddressFamily)
|
2012-01-20 21:00:58 +00:00
|
|
|
VIR_ENUM_IMPL(qemuMonitorGraphicsAddressFamily,
|
|
|
|
VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_LAST,
|
2011-10-21 08:14:02 +00:00
|
|
|
"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
|
|
|
|
2012-03-14 05:41:35 +00:00
|
|
|
static void qemuMonitorJSONHandleGraphics(qemuMonitorPtr mon, virJSONValuePtr 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;
|
|
|
|
virJSONValuePtr client;
|
|
|
|
virJSONValuePtr server;
|
|
|
|
|
|
|
|
if (!(client = virJSONValueObjectGet(data, "client"))) {
|
2011-05-09 09:24:09 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
if (!(server = virJSONValueObjectGet(data, "server"))) {
|
2011-05-09 09:24:09 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
authScheme = virJSONValueObjectGetString(server, "auth");
|
2012-03-14 05:41:35 +00:00
|
|
|
if (!authScheme) {
|
2012-09-04 14:52:47 +00:00
|
|
|
/* not all events are required to contain auth scheme */
|
|
|
|
VIR_DEBUG("missing auth scheme in graphics event");
|
|
|
|
authScheme = "";
|
2012-03-14 05:41:35 +00:00
|
|
|
}
|
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
|
|
|
|
|
|
|
localFamily = virJSONValueObjectGetString(server, "family");
|
2012-03-14 05:41:35 +00:00
|
|
|
if (!localFamily) {
|
|
|
|
VIR_WARN("missing local address family in graphics 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
|
|
|
localNode = virJSONValueObjectGetString(server, "host");
|
2012-03-14 05:41:35 +00:00
|
|
|
if (!localNode) {
|
|
|
|
VIR_WARN("missing local hostname in graphics 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
|
|
|
localService = virJSONValueObjectGetString(server, "service");
|
2012-03-14 05:41:35 +00:00
|
|
|
if (!localService)
|
|
|
|
localService = ""; /* Spice has multiple ports, so this isn't provided */
|
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
|
|
|
|
|
|
|
remoteFamily = virJSONValueObjectGetString(client, "family");
|
2012-03-14 05:41:35 +00:00
|
|
|
if (!remoteFamily) {
|
|
|
|
VIR_WARN("missing remote address family in graphics 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
|
|
|
remoteNode = virJSONValueObjectGetString(client, "host");
|
2012-03-14 05:41:35 +00:00
|
|
|
if (!remoteNode) {
|
|
|
|
VIR_WARN("missing remote hostname in graphics 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
|
|
|
remoteService = virJSONValueObjectGetString(client, "service");
|
2012-03-14 05:41:35 +00:00
|
|
|
if (!remoteService)
|
|
|
|
remoteService = ""; /* Spice has multiple ports, so this isn't provided */
|
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(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
2012-03-14 05:41:35 +00:00
|
|
|
qemuMonitorJSONHandleGraphics(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(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
2012-03-14 05:41:35 +00:00
|
|
|
qemuMonitorJSONHandleGraphics(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(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
2012-03-14 05:41:35 +00:00
|
|
|
qemuMonitorJSONHandleGraphics(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void qemuMonitorJSONHandleSPICEConnect(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
qemuMonitorJSONHandleGraphics(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_CONNECT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void qemuMonitorJSONHandleSPICEInitialize(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
qemuMonitorJSONHandleGraphics(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void qemuMonitorJSONHandleSPICEDisconnect(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
qemuMonitorJSONHandleGraphics(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
|
|
|
}
|
|
|
|
|
2012-04-11 22:17:36 +00:00
|
|
|
static void
|
|
|
|
qemuMonitorJSONHandleBlockJobImpl(qemuMonitorPtr mon,
|
|
|
|
virJSONValuePtr data,
|
|
|
|
int event)
|
2011-07-22 05:57:42 +00:00
|
|
|
{
|
|
|
|
const char *device;
|
|
|
|
const char *type_str;
|
|
|
|
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;
|
2012-10-03 21:13:21 +00:00
|
|
|
else if (STREQ(type_str, "commit"))
|
|
|
|
type = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT;
|
2012-10-12 20:06:10 +00:00
|
|
|
else if (STREQ(type_str, "mirror"))
|
|
|
|
type = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY;
|
2011-07-22 05:57:42 +00:00
|
|
|
|
2012-04-11 22:17:36 +00:00
|
|
|
switch ((virConnectDomainEventBlockJobStatus) event) {
|
|
|
|
case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
|
|
|
|
/* Make sure the whole device has been processed */
|
|
|
|
if (offset != len)
|
|
|
|
event = VIR_DOMAIN_BLOCK_JOB_FAILED;
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_BLOCK_JOB_CANCELED:
|
2012-10-12 20:06:10 +00:00
|
|
|
case VIR_DOMAIN_BLOCK_JOB_READY:
|
2012-04-11 22:17:36 +00:00
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_BLOCK_JOB_FAILED:
|
|
|
|
case VIR_DOMAIN_BLOCK_JOB_LAST:
|
2012-10-12 20:06:10 +00:00
|
|
|
VIR_DEBUG("should not get here");
|
|
|
|
break;
|
2012-04-11 22:17:36 +00:00
|
|
|
}
|
2011-07-22 05:57:42 +00:00
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
out:
|
2012-04-11 22:17:36 +00:00
|
|
|
qemuMonitorEmitBlockJob(mon, device, type, event);
|
2011-07-22 05:57:42 +00:00
|
|
|
}
|
|
|
|
|
2012-03-23 13:44:50 +00:00
|
|
|
static void
|
|
|
|
qemuMonitorJSONHandleTrayChange(qemuMonitorPtr mon,
|
|
|
|
virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
const char *devAlias = NULL;
|
|
|
|
bool trayOpened;
|
|
|
|
int reason;
|
|
|
|
|
|
|
|
if ((devAlias = virJSONValueObjectGetString(data, "device")) == NULL) {
|
|
|
|
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, 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
|
|
|
|
2012-03-23 14:43:14 +00:00
|
|
|
static void
|
|
|
|
qemuMonitorJSONHandlePMWakeup(qemuMonitorPtr mon,
|
|
|
|
virJSONValuePtr data ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
qemuMonitorEmitPMWakeup(mon);
|
|
|
|
}
|
|
|
|
|
2012-03-23 14:50:36 +00:00
|
|
|
static void
|
|
|
|
qemuMonitorJSONHandlePMSuspend(qemuMonitorPtr mon,
|
|
|
|
virJSONValuePtr data ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
qemuMonitorEmitPMSuspend(mon);
|
|
|
|
}
|
|
|
|
|
2012-04-11 22:17:36 +00:00
|
|
|
static void
|
|
|
|
qemuMonitorJSONHandleBlockJobCompleted(qemuMonitorPtr mon,
|
|
|
|
virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
qemuMonitorJSONHandleBlockJobImpl(mon, data,
|
|
|
|
VIR_DOMAIN_BLOCK_JOB_COMPLETED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon,
|
|
|
|
virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
qemuMonitorJSONHandleBlockJobImpl(mon, data,
|
|
|
|
VIR_DOMAIN_BLOCK_JOB_CANCELED);
|
|
|
|
}
|
|
|
|
|
2012-10-12 20:06:10 +00:00
|
|
|
static void
|
|
|
|
qemuMonitorJSONHandleBlockJobReady(qemuMonitorPtr mon,
|
|
|
|
virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
qemuMonitorJSONHandleBlockJobImpl(mon, data,
|
|
|
|
VIR_DOMAIN_BLOCK_JOB_READY);
|
|
|
|
}
|
|
|
|
|
2012-07-12 15:45:57 +00:00
|
|
|
static void
|
|
|
|
qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon,
|
|
|
|
virJSONValuePtr 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);
|
|
|
|
}
|
|
|
|
|
2012-10-12 19:13:39 +00:00
|
|
|
static void
|
|
|
|
qemuMonitorJSONHandlePMSuspendDisk(qemuMonitorPtr mon,
|
|
|
|
virJSONValuePtr data ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
qemuMonitorEmitPMSuspendDisk(mon);
|
|
|
|
}
|
|
|
|
|
2013-07-11 15:07:26 +00:00
|
|
|
static void
|
|
|
|
qemuMonitorJSONHandleDeviceDeleted(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
const char *device;
|
|
|
|
|
|
|
|
if (!(device = virJSONValueObjectGetString(data, "device"))) {
|
|
|
|
VIR_WARN("missing device in device deleted event");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
qemuMonitorEmitDeviceDeleted(mon, device);
|
|
|
|
}
|
|
|
|
|
2014-09-17 17:07:50 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
if (!(name = virJSONValueObjectGetString(data, "name"))) {
|
|
|
|
VIR_WARN("missing device in NIC_RX_FILTER_CHANGED event");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
qemuMonitorEmitNicRxFilterChanged(mon, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-09 20:24:04 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon,
|
|
|
|
const char *cmd_str,
|
|
|
|
int scm_fd,
|
|
|
|
char **reply_str)
|
|
|
|
{
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr obj;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("human-monitor-command",
|
|
|
|
"s:command-line", cmd_str,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (!cmd || qemuMonitorJSONCommandWithFd(mon, cmd, scm_fd, &reply) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-09-06 15:28:53 +00:00
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
|
|
|
|
_("Human monitor command is not available to run %s"),
|
|
|
|
cmd_str);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2011-03-09 20:24:04 +00:00
|
|
|
if (qemuMonitorJSONCheckError(cmd, reply))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(obj = virJSONValueObjectGet(reply, "return"))) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("human monitor command was missing return data"));
|
2011-03-09 20:24:04 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reply_str) {
|
|
|
|
const char *data;
|
|
|
|
|
2013-05-20 09:23:13 +00:00
|
|
|
data = virJSONValueGetString(obj);
|
|
|
|
if (VIR_STRDUP(*reply_str, data ? data : "") < 0)
|
2011-03-09 20:24:04 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2011-03-09 20:24:04 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-12 13:45:20 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("qmp_capabilities", NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONStartCPUs(qemuMonitorPtr mon,
|
|
|
|
virConnectPtr conn ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("cont", NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i = 0;
|
|
|
|
int timeout = 3;
|
2009-11-03 18:59:18 +00:00
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
2011-01-13 19:52:23 +00:00
|
|
|
do {
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2011-01-13 19:52:23 +00:00
|
|
|
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;
|
|
|
|
usleep(250000);
|
|
|
|
} while (++i <= timeout);
|
2009-11-03 18:59:18 +00:00
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
qemuMonitorJSONStopCPUs(qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("stop", NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-05 11:50:25 +00:00
|
|
|
int
|
2011-09-27 09:42:04 +00:00
|
|
|
qemuMonitorJSONGetStatus(qemuMonitorPtr mon,
|
|
|
|
bool *running,
|
|
|
|
virDomainPausedReason *reason)
|
2011-05-05 11:50:25 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2011-09-27 09:42:04 +00:00
|
|
|
const char *status;
|
2011-05-05 11:50:25 +00:00
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
|
2011-09-27 09:42:04 +00:00
|
|
|
if (reason)
|
|
|
|
*reason = VIR_DOMAIN_PAUSED_UNKNOWN;
|
|
|
|
|
2011-05-05 11:50:25 +00:00
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-status", NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-status reply was missing return data"));
|
2011-05-05 11:50:25 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetBoolean(data, "running", running) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-status reply was missing running state"));
|
2011-05-05 11:50:25 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2011-09-27 09:42:04 +00:00
|
|
|
if ((status = virJSONValueObjectGetString(data, "status"))) {
|
|
|
|
if (!*running && reason)
|
|
|
|
*reason = qemuMonitorVMStatusToPausedReason(status);
|
|
|
|
} else if (!*running) {
|
|
|
|
VIR_DEBUG("query-status reply was missing status details");
|
|
|
|
}
|
|
|
|
|
2011-05-05 11:50:25 +00:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2011-05-05 11:50:25 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("system_powerdown", NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-09-06 08:18:57 +00:00
|
|
|
int qemuMonitorJSONSetLink(qemuMonitorPtr mon,
|
|
|
|
const char *name,
|
2014-06-01 00:22:30 +00:00
|
|
|
virDomainNetInterfaceLinkState state)
|
2011-09-06 08:18:57 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("set_link",
|
|
|
|
"s:name", name,
|
|
|
|
"b:up", state != VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2011-06-15 16:49:58 +00:00
|
|
|
int qemuMonitorJSONSystemReset(qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("system_reset", NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-22 14:52:05 +00:00
|
|
|
/*
|
|
|
|
* [ { "CPU": 0, "current": true, "halted": false, "pc": 3227107138 },
|
|
|
|
* { "CPU": 1, "current": false, "halted": true, "pc": 7108165 } ]
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONExtractCPUInfo(virJSONValuePtr reply,
|
|
|
|
int **pids)
|
|
|
|
{
|
|
|
|
virJSONValuePtr data;
|
|
|
|
int ret = -1;
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2010-01-22 14:52:05 +00:00
|
|
|
int *threads = NULL;
|
|
|
|
int ncpus;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("cpu reply was missing return data"));
|
2010-01-22 14:52:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->type != VIR_JSON_TYPE_ARRAY) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("cpu information was not an array"));
|
2010-01-22 14:52:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ncpus = virJSONValueArraySize(data)) <= 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("cpu information was empty"));
|
2010-01-22 14:52:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-03-07 08:33:31 +00:00
|
|
|
if (VIR_ALLOC_N(threads, ncpus) < 0)
|
2010-01-22 14:52:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < ncpus; i++) {
|
2010-01-22 14:52:05 +00:00
|
|
|
virJSONValuePtr entry = virJSONValueArrayGet(data, i);
|
|
|
|
int thread;
|
|
|
|
if (!entry) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2014-03-04 09:32:47 +00:00
|
|
|
_("cpu information was missing an array element"));
|
2010-01-22 14:52:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberInt(entry, "thread_id", &thread) < 0) {
|
2014-03-04 09:32:47 +00:00
|
|
|
/* Some older qemu versions don't report the thread_id,
|
|
|
|
* so treat this as non-fatal, simply returning no data */
|
2010-02-15 14:41:50 +00:00
|
|
|
ret = 0;
|
2010-01-22 14:52:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
threads[i] = thread;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pids = threads;
|
|
|
|
threads = NULL;
|
2010-03-10 18:22:02 +00:00
|
|
|
ret = ncpus;
|
2010-01-22 14:52:05 +00:00
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2010-01-22 14:52:05 +00:00
|
|
|
VIR_FREE(threads);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon,
|
|
|
|
int **pids)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-cpus",
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
*pids = NULL;
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
2010-01-22 14:52:05 +00:00
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONExtractCPUInfo(reply, pids);
|
2009-11-03 18:59:18 +00:00
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-17 14:31:45 +00:00
|
|
|
int qemuMonitorJSONGetVirtType(qemuMonitorPtr mon,
|
|
|
|
int *virtType)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-kvm",
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
*virtType = VIR_DOMAIN_VIRT_QEMU;
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
virJSONValuePtr data;
|
|
|
|
bool val = false;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info kvm reply was missing return data"));
|
2011-06-17 14:31:45 +00:00
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetBoolean(data, "enabled", &val) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2013-10-02 11:58:40 +00:00
|
|
|
_("info kvm reply missing 'enabled' field"));
|
2011-06-17 14:31:45 +00:00
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (val)
|
|
|
|
*virtType = VIR_DOMAIN_VIRT_KVM;
|
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2011-06-17 14:31:45 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
/*
|
|
|
|
* Returns: 0 if balloon not supported, +1 if balloon query worked
|
|
|
|
* or -1 on failure
|
|
|
|
*/
|
|
|
|
int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon,
|
2012-03-02 20:27:39 +00:00
|
|
|
unsigned long long *currmem)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-balloon",
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
*currmem = 0;
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
/* See if balloon soft-failed */
|
|
|
|
if (qemuMonitorJSONHasError(reply, "DeviceNotActive") ||
|
|
|
|
qemuMonitorJSONHasError(reply, "KVMMissingCap"))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* See if any other fatal error occurred */
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
/* Success */
|
|
|
|
if (ret == 0) {
|
2010-01-22 13:22:53 +00:00
|
|
|
virJSONValuePtr data;
|
2009-11-03 18:59:18 +00:00
|
|
|
unsigned long long mem;
|
|
|
|
|
2010-01-22 13:22:53 +00:00
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info balloon reply was missing return data"));
|
2010-01-22 13:22:53 +00:00
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2010-02-16 16:31:19 +00:00
|
|
|
if (virJSONValueObjectGetNumberUlong(data, "actual", &mem) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info balloon reply was missing balloon data"));
|
2009-11-03 18:59:18 +00:00
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2010-02-16 16:31:19 +00:00
|
|
|
*currmem = (mem/1024);
|
2009-11-03 18:59:18 +00:00
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2009-11-03 18:59:18 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-11 23:18:48 +00:00
|
|
|
/* 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(FIELD, TAG, DIVISOR) \
|
|
|
|
if (virJSONValueObjectHasKey(statsdata, FIELD) && \
|
|
|
|
(got < nr_stats)) { \
|
|
|
|
if (virJSONValueObjectGetNumberUlong(statsdata, 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++; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-12 11:31:15 +00:00
|
|
|
int qemuMonitorJSONGetMemoryStats(qemuMonitorPtr mon,
|
2013-07-11 23:18:48 +00:00
|
|
|
char *balloonpath,
|
2010-04-12 11:31:15 +00:00
|
|
|
virDomainMemoryStatPtr stats,
|
|
|
|
unsigned int nr_stats)
|
|
|
|
{
|
|
|
|
int ret;
|
2013-07-11 23:18:48 +00:00
|
|
|
virJSONValuePtr cmd = NULL;
|
2010-04-12 11:31:15 +00:00
|
|
|
virJSONValuePtr reply = NULL;
|
2013-07-11 23:18:48 +00:00
|
|
|
virJSONValuePtr data;
|
|
|
|
virJSONValuePtr statsdata;
|
|
|
|
unsigned long long mem;
|
|
|
|
int got = 0;
|
2010-04-12 11:31:15 +00:00
|
|
|
|
2013-07-11 23:18:48 +00:00
|
|
|
ret = qemuMonitorJSONGetBalloonInfo(mon, &mem);
|
|
|
|
if (ret == 1 && (got < nr_stats)) {
|
|
|
|
stats[got].tag = VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON;
|
|
|
|
stats[got].val = mem;
|
|
|
|
got++;
|
|
|
|
}
|
2010-04-12 11:31:15 +00:00
|
|
|
|
2013-07-11 23:18:48 +00:00
|
|
|
if (!balloonpath)
|
|
|
|
goto cleanup;
|
2010-04-12 11:31:15 +00:00
|
|
|
|
2013-07-11 23:18:48 +00:00
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("qom-get",
|
|
|
|
"s:path", balloonpath,
|
|
|
|
"s:property", "guest-stats",
|
|
|
|
NULL)))
|
|
|
|
goto cleanup;
|
2010-04-12 11:31:15 +00:00
|
|
|
|
2014-05-14 07:35:18 +00:00
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
2010-04-12 11:31:15 +00:00
|
|
|
|
2014-05-14 07:35:18 +00:00
|
|
|
if ((data = virJSONValueObjectGet(reply, "error"))) {
|
|
|
|
const char *klass = virJSONValueObjectGetString(data, "class");
|
|
|
|
const char *desc = virJSONValueObjectGetString(data, "desc");
|
2010-04-12 11:31:15 +00:00
|
|
|
|
2014-05-14 07:35:18 +00:00
|
|
|
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 ((ret = qemuMonitorJSONCheckError(cmd, reply)) < 0)
|
2013-07-11 23:18:48 +00:00
|
|
|
goto cleanup;
|
2010-04-12 11:31:15 +00:00
|
|
|
|
2013-07-11 23:18:48 +00:00
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("qom-get reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
qemu: Parse current balloon value returned by query_balloon
Qemu once supported following memory stats which will returned by
"query_balloon":
stat_put(dict, "actual", actual);
stat_put(dict, "mem_swapped_in", dev->stats[VIRTIO_BALLOON_S_SWAP_IN]);
stat_put(dict, "mem_swapped_out", dev->stats[VIRTIO_BALLOON_S_SWAP_OUT]);
stat_put(dict, "major_page_faults", dev->stats[VIRTIO_BALLOON_S_MAJFLT]);
stat_put(dict, "minor_page_faults", dev->stats[VIRTIO_BALLOON_S_MINFLT]);
stat_put(dict, "free_mem", dev->stats[VIRTIO_BALLOON_S_MEMFREE]);
stat_put(dict, "total_mem", dev->stats[VIRTIO_BALLOON_S_MEMTOT]);
But it later disabled all the stats except "actual" by commit
07b0403dfc2b2ac179ae5b48105096cc2d03375a.
libvirt doesn't parse "actual", so user will always see a empty result
with "virsh dommemstat $domain". Even qemu haven't disabled the stats,
we should support parsing "actual".
2011-06-14 03:21:35 +00:00
|
|
|
|
2013-07-11 23:18:48 +00:00
|
|
|
if (!(statsdata = virJSONValueObjectGet(data, "stats"))) {
|
|
|
|
VIR_DEBUG("data does not include 'stats'");
|
|
|
|
goto cleanup;
|
2010-04-12 11:31:15 +00:00
|
|
|
}
|
|
|
|
|
2013-07-11 23:18:48 +00:00
|
|
|
GET_BALLOON_STATS("stat-swap-in",
|
|
|
|
VIR_DOMAIN_MEMORY_STAT_SWAP_IN, 1024);
|
|
|
|
GET_BALLOON_STATS("stat-swap-out",
|
|
|
|
VIR_DOMAIN_MEMORY_STAT_SWAP_OUT, 1024);
|
|
|
|
GET_BALLOON_STATS("stat-major-faults",
|
|
|
|
VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT, 1);
|
|
|
|
GET_BALLOON_STATS("stat-minor-faults",
|
|
|
|
VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT, 1);
|
|
|
|
GET_BALLOON_STATS("stat-free-memory",
|
|
|
|
VIR_DOMAIN_MEMORY_STAT_UNUSED, 1024);
|
|
|
|
GET_BALLOON_STATS("stat-total-memory",
|
|
|
|
VIR_DOMAIN_MEMORY_STAT_AVAILABLE, 1024);
|
|
|
|
|
2010-04-12 11:31:15 +00:00
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2010-04-12 11:31:15 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
2013-07-11 23:18:48 +00:00
|
|
|
|
|
|
|
if (got > 0)
|
|
|
|
ret = got;
|
|
|
|
|
2010-04-12 11:31:15 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2013-07-11 23:18:48 +00:00
|
|
|
#undef GET_BALLOON_STATS
|
2010-04-12 11:31:15 +00:00
|
|
|
|
|
|
|
|
2013-06-27 15:00:31 +00:00
|
|
|
/*
|
|
|
|
* Using the provided balloonpath, determine if we need to set the
|
|
|
|
* collection interval property to enable statistics gathering.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
qemuMonitorJSONSetMemoryStatsPeriod(qemuMonitorPtr 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-13 13:49:50 +00:00
|
|
|
int qemuMonitorJSONGetBlockInfo(qemuMonitorPtr mon,
|
2012-01-18 21:01:30 +00:00
|
|
|
virHashTablePtr table)
|
2011-09-13 13:49:50 +00:00
|
|
|
{
|
2012-01-18 21:01:30 +00:00
|
|
|
int ret;
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2011-09-13 13:49:50 +00:00
|
|
|
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-block",
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr devices;
|
|
|
|
|
2011-10-13 00:10:24 +00:00
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
2011-09-13 13:49:50 +00:00
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
devices = virJSONValueObjectGet(reply, "return");
|
|
|
|
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2011-09-13 13:49:50 +00:00
|
|
|
_("block info reply was missing device list"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < virJSONValueArraySize(devices); i++) {
|
|
|
|
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
|
2012-01-18 21:01:30 +00:00
|
|
|
struct qemuDomainDiskInfo *info;
|
2011-09-13 13:49:50 +00:00
|
|
|
const char *thisdev;
|
2012-01-19 16:58:58 +00:00
|
|
|
const char *status;
|
2011-09-13 13:49:50 +00:00
|
|
|
|
|
|
|
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("block info device entry was not in expected format"));
|
2011-09-13 13:49:50 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("block info device entry was not in expected format"));
|
2011-09-13 13:49:50 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STRPREFIX(thisdev, QEMU_DRIVE_HOST_PREFIX))
|
|
|
|
thisdev += strlen(QEMU_DRIVE_HOST_PREFIX);
|
|
|
|
|
2013-07-04 10:14:12 +00:00
|
|
|
if (VIR_ALLOC(info) < 0)
|
2012-01-18 21:01:30 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virHashAddEntry(table, thisdev, info) < 0) {
|
|
|
|
VIR_FREE(info);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2011-09-13 13:49:50 +00:00
|
|
|
|
|
|
|
if (virJSONValueObjectGetBoolean(dev, "removable", &info->removable) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s value"),
|
|
|
|
"removable");
|
2011-09-13 13:49:50 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetBoolean(dev, "locked", &info->locked) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s value"),
|
|
|
|
"locked");
|
2011-09-13 13:49:50 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-01-25 11:56:48 +00:00
|
|
|
/* Don't check for success here, because 'tray_open' is presented iff
|
2011-09-13 13:49:50 +00:00
|
|
|
* medium is ejected.
|
|
|
|
*/
|
2013-01-25 11:56:48 +00:00
|
|
|
ignore_value(virJSONValueObjectGetBoolean(dev, "tray_open",
|
2011-10-08 02:56:00 +00:00
|
|
|
&info->tray_open));
|
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)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2011-09-13 13:49:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2011-09-13 13:49:50 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
|
2011-09-16 12:05:58 +00:00
|
|
|
const char *dev_name,
|
2009-11-03 18:59:18 +00:00
|
|
|
long long *rd_req,
|
|
|
|
long long *rd_bytes,
|
2011-09-05 08:22:17 +00:00
|
|
|
long long *rd_total_times,
|
2009-11-03 18:59:18 +00:00
|
|
|
long long *wr_req,
|
|
|
|
long long *wr_bytes,
|
2011-09-05 08:22:17 +00:00
|
|
|
long long *wr_total_times,
|
|
|
|
long long *flush_req,
|
|
|
|
long long *flush_total_times,
|
2009-11-03 18:59:18 +00:00
|
|
|
long long *errs)
|
|
|
|
{
|
2014-09-25 08:12:15 +00:00
|
|
|
qemuBlockStats *stats;
|
|
|
|
virHashTablePtr blockstats = NULL;
|
2014-09-15 08:48:09 +00:00
|
|
|
int ret = -1;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2011-09-05 08:22:17 +00:00
|
|
|
*rd_req = *rd_bytes = -1;
|
|
|
|
*wr_req = *wr_bytes = *errs = -1;
|
|
|
|
|
|
|
|
if (rd_total_times)
|
|
|
|
*rd_total_times = -1;
|
|
|
|
if (wr_total_times)
|
|
|
|
*wr_total_times = -1;
|
|
|
|
if (flush_req)
|
|
|
|
*flush_req = -1;
|
|
|
|
if (flush_total_times)
|
|
|
|
*flush_total_times = -1;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2014-09-25 08:12:15 +00:00
|
|
|
if (qemuMonitorJSONGetAllBlockStatsInfo(mon, &blockstats) < 0)
|
2014-09-15 08:48:09 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2014-09-25 08:12:15 +00:00
|
|
|
if (!(stats = virHashLookup(blockstats, dev_name))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot find statistics for device '%s'"), dev_name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
*rd_req = stats->rd_req;
|
|
|
|
*rd_bytes = stats->rd_bytes;
|
|
|
|
*wr_req = stats->wr_req;
|
|
|
|
*wr_bytes = stats->wr_bytes;
|
2014-09-15 08:48:09 +00:00
|
|
|
*errs = -1; /* QEMU does not have this */
|
|
|
|
|
|
|
|
if (rd_total_times)
|
2014-09-25 08:12:15 +00:00
|
|
|
*rd_total_times = stats->rd_total_times;
|
2014-09-15 08:48:09 +00:00
|
|
|
if (wr_total_times)
|
2014-09-25 08:12:15 +00:00
|
|
|
*wr_total_times = stats->wr_total_times;
|
2014-09-15 08:48:09 +00:00
|
|
|
if (flush_req)
|
2014-09-25 08:12:15 +00:00
|
|
|
*flush_req = stats->flush_req;
|
2014-09-15 08:48:09 +00:00
|
|
|
if (flush_total_times)
|
2014-09-25 08:12:15 +00:00
|
|
|
*flush_total_times = stats->flush_total_times;
|
2014-09-15 08:48:09 +00:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2014-09-25 08:12:15 +00:00
|
|
|
virHashFree(blockstats);
|
2014-09-15 08:48:09 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-09-15 15:42:52 +00:00
|
|
|
typedef enum {
|
|
|
|
QEMU_MONITOR_BLOCK_EXTENT_ERROR_OK,
|
|
|
|
QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOPARENT,
|
|
|
|
QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOSTATS,
|
|
|
|
QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOOFFSET,
|
|
|
|
} qemuMonitorBlockExtentError;
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONDevGetBlockExtent(virJSONValuePtr dev,
|
|
|
|
unsigned long long *extent)
|
|
|
|
{
|
|
|
|
virJSONValuePtr stats;
|
|
|
|
virJSONValuePtr parent;
|
|
|
|
|
|
|
|
if ((parent = virJSONValueObjectGet(dev, "parent")) == NULL ||
|
|
|
|
parent->type != VIR_JSON_TYPE_OBJECT) {
|
|
|
|
return QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOPARENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((stats = virJSONValueObjectGet(parent, "stats")) == NULL ||
|
|
|
|
stats->type != VIR_JSON_TYPE_OBJECT) {
|
|
|
|
return QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOSTATS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUlong(stats, "wr_highest_offset",
|
|
|
|
extent) < 0) {
|
|
|
|
return QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOOFFSET;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QEMU_MONITOR_BLOCK_EXTENT_ERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-09-15 08:48:09 +00:00
|
|
|
int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
|
2014-09-25 08:12:15 +00:00
|
|
|
virHashTablePtr *ret_stats)
|
2014-09-15 08:48:09 +00:00
|
|
|
{
|
2014-09-25 08:12:15 +00:00
|
|
|
int ret = -1;
|
|
|
|
int rc;
|
2014-09-15 08:48:09 +00:00
|
|
|
size_t i;
|
2014-09-25 08:12:15 +00:00
|
|
|
virJSONValuePtr cmd;
|
2014-09-15 08:48:09 +00:00
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr devices;
|
2014-09-25 08:12:15 +00:00
|
|
|
qemuBlockStatsPtr bstats = NULL;
|
|
|
|
virHashTablePtr hash = NULL;
|
2014-09-15 08:48:09 +00:00
|
|
|
|
2014-09-25 08:12:15 +00:00
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-blockstats", NULL)))
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
|
2014-09-25 08:12:15 +00:00
|
|
|
if (!(hash = virHashCreate(10, virHashValueFree)))
|
|
|
|
goto cleanup;
|
2014-09-15 08:48:09 +00:00
|
|
|
|
2014-09-25 08:12:15 +00:00
|
|
|
if ((rc = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2014-09-25 08:12:15 +00:00
|
|
|
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
|
2010-07-12 13:07:02 +00:00
|
|
|
goto cleanup;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
|
|
|
devices = virJSONValueObjectGet(reply, "return");
|
|
|
|
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats reply was missing device list"));
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-09-25 08:12:15 +00:00
|
|
|
for (i = 0; i < virJSONValueArraySize(devices); i++) {
|
2009-11-03 18:59:18 +00:00
|
|
|
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
|
|
|
|
virJSONValuePtr stats;
|
2014-09-30 09:41:43 +00:00
|
|
|
const char *dev_name;
|
2014-09-25 08:12:15 +00:00
|
|
|
|
|
|
|
if (VIR_ALLOC(bstats) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2014-09-15 08:48:09 +00:00
|
|
|
_("blockstats device entry was not "
|
|
|
|
"in expected format"));
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-09-30 09:41:43 +00:00
|
|
|
if (!(dev_name = virJSONValueObjectGetString(dev, "device"))) {
|
2014-09-25 08:12:15 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats device entry was not "
|
|
|
|
"in expected format"));
|
|
|
|
goto cleanup;
|
2014-09-15 08:48:09 +00:00
|
|
|
}
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2014-09-30 09:41:43 +00:00
|
|
|
if (STRPREFIX(dev_name, QEMU_DRIVE_HOST_PREFIX))
|
|
|
|
dev_name += strlen(QEMU_DRIVE_HOST_PREFIX);
|
2014-09-25 08:12:15 +00:00
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL ||
|
|
|
|
stats->type != VIR_JSON_TYPE_OBJECT) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2014-09-15 08:48:09 +00:00
|
|
|
_("blockstats stats entry was not "
|
|
|
|
"in expected format"));
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-09-15 08:48:09 +00:00
|
|
|
if (virJSONValueObjectGetNumberLong(stats, "rd_bytes",
|
|
|
|
&bstats->rd_bytes) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"rd_bytes");
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2014-09-15 08:48:09 +00:00
|
|
|
if (virJSONValueObjectGetNumberLong(stats, "rd_operations",
|
|
|
|
&bstats->rd_req) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
2010-02-09 18:15:41 +00:00
|
|
|
"rd_operations");
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2014-09-15 08:48:09 +00:00
|
|
|
if (virJSONValueObjectHasKey(stats, "rd_total_time_ns") &&
|
2012-06-21 07:37:13 +00:00
|
|
|
(virJSONValueObjectGetNumberLong(stats, "rd_total_time_ns",
|
2014-09-15 08:48:09 +00:00
|
|
|
&bstats->rd_total_times) < 0)) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"rd_total_time_ns");
|
2011-09-05 08:22:17 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2014-09-15 08:48:09 +00:00
|
|
|
if (virJSONValueObjectGetNumberLong(stats, "wr_bytes",
|
|
|
|
&bstats->wr_bytes) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"wr_bytes");
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2014-09-15 08:48:09 +00:00
|
|
|
if (virJSONValueObjectGetNumberLong(stats, "wr_operations",
|
|
|
|
&bstats->wr_req) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"wr_operations");
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2014-09-15 08:48:09 +00:00
|
|
|
if (virJSONValueObjectHasKey(stats, "wr_total_time_ns") &&
|
2012-06-21 07:37:13 +00:00
|
|
|
(virJSONValueObjectGetNumberLong(stats, "wr_total_time_ns",
|
2014-09-15 08:48:09 +00:00
|
|
|
&bstats->wr_total_times) < 0)) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"wr_total_time_ns");
|
2011-09-05 08:22:17 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2014-09-15 08:48:09 +00:00
|
|
|
if (virJSONValueObjectHasKey(stats, "flush_operations") &&
|
2011-09-05 08:22:17 +00:00
|
|
|
(virJSONValueObjectGetNumberLong(stats, "flush_operations",
|
2014-09-15 08:48:09 +00:00
|
|
|
&bstats->flush_req) < 0)) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"flush_operations");
|
2011-09-05 08:22:17 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2014-09-15 08:48:09 +00:00
|
|
|
if (virJSONValueObjectHasKey(stats, "flush_total_time_ns") &&
|
2012-06-21 07:37:13 +00:00
|
|
|
(virJSONValueObjectGetNumberLong(stats, "flush_total_time_ns",
|
2014-09-15 08:48:09 +00:00
|
|
|
&bstats->flush_total_times) < 0)) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"flush_total_time_ns");
|
2011-09-05 08:22:17 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2014-09-15 08:48:09 +00:00
|
|
|
|
2014-09-15 15:42:52 +00:00
|
|
|
/* it's ok to not have this information here. Just skip silently. */
|
|
|
|
qemuMonitorJSONDevGetBlockExtent(dev, &bstats->wr_highest_offset);
|
|
|
|
|
2014-09-30 09:41:43 +00:00
|
|
|
if (virHashAddEntry(hash, dev_name, bstats) < 0)
|
2014-09-25 08:12:15 +00:00
|
|
|
goto cleanup;
|
|
|
|
bstats = NULL;
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
2014-09-15 08:48:09 +00:00
|
|
|
|
2014-09-25 08:12:15 +00:00
|
|
|
*ret_stats = hash;
|
|
|
|
hash = NULL;
|
|
|
|
ret = 0;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2014-09-25 08:12:15 +00:00
|
|
|
VIR_FREE(bstats);
|
|
|
|
virHashFree(hash);
|
2009-11-03 18:59:18 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-09-25 10:03:26 +00:00
|
|
|
int qemuMonitorJSONBlockStatsUpdateCapacity(qemuMonitorPtr mon,
|
|
|
|
virHashTablePtr stats)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
int rc;
|
|
|
|
size_t i;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr devices;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-block", NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((rc = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
devices = virJSONValueObjectGet(reply, "return");
|
|
|
|
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-block reply was missing device list"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < virJSONValueArraySize(devices); i++) {
|
|
|
|
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
|
|
|
|
virJSONValuePtr inserted;
|
|
|
|
virJSONValuePtr image;
|
|
|
|
qemuBlockStatsPtr bstats;
|
2014-10-01 12:39:23 +00:00
|
|
|
const char *dev_name;
|
2014-09-25 10:03:26 +00:00
|
|
|
|
|
|
|
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-block device entry was not "
|
|
|
|
"in expected format"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-10-01 12:39:23 +00:00
|
|
|
if (!(dev_name = virJSONValueObjectGetString(dev, "device"))) {
|
2014-09-25 10:03:26 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-block device entry was not "
|
|
|
|
"in expected format"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-10-01 12:39:23 +00:00
|
|
|
if (STRPREFIX(dev_name, QEMU_DRIVE_HOST_PREFIX))
|
|
|
|
dev_name += strlen(QEMU_DRIVE_HOST_PREFIX);
|
2014-09-25 10:03:26 +00:00
|
|
|
|
|
|
|
/* ignore missing info */
|
2014-10-01 12:39:23 +00:00
|
|
|
if (!(bstats = virHashLookup(stats, dev_name)))
|
2014-09-25 10:03:26 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* drive may be empty */
|
|
|
|
if (!(inserted = virJSONValueObjectGet(dev, "inserted")) ||
|
|
|
|
!(image = virJSONValueObjectGet(inserted, "image")))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUlong(image, "virtual-size",
|
|
|
|
&bstats->capacity) < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* if actual-size is missing, image is not thin provisioned */
|
|
|
|
if (virJSONValueObjectGetNumberUlong(image, "actual-size",
|
|
|
|
&bstats->physical) < 0)
|
|
|
|
bstats->physical = bstats->capacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-05 08:22:17 +00:00
|
|
|
int qemuMonitorJSONGetBlockStatsParamsNumber(qemuMonitorPtr mon,
|
|
|
|
int *nparams)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
int ret, num = 0;
|
|
|
|
size_t i;
|
2011-09-05 08:22:17 +00:00
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-blockstats",
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr devices = NULL;
|
|
|
|
virJSONValuePtr dev = NULL;
|
|
|
|
virJSONValuePtr stats = NULL;
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
devices = virJSONValueObjectGet(reply, "return");
|
|
|
|
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats reply was missing device list"));
|
2011-09-05 08:22:17 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev = virJSONValueArrayGet(devices, 0);
|
|
|
|
|
|
|
|
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats device entry was not in expected format"));
|
2011-09-05 08:22:17 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL ||
|
|
|
|
stats->type != VIR_JSON_TYPE_OBJECT) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats stats entry was not in expected format"));
|
2011-09-05 08:22:17 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < stats->data.object.npairs; i++) {
|
2011-09-05 08:22:17 +00:00
|
|
|
const char *key = stats->data.object.pairs[i].key;
|
|
|
|
|
|
|
|
if (STREQ(key, "rd_bytes") ||
|
|
|
|
STREQ(key, "rd_operations") ||
|
2012-06-21 07:37:13 +00:00
|
|
|
STREQ(key, "rd_total_time_ns") ||
|
2011-09-05 08:22:17 +00:00
|
|
|
STREQ(key, "wr_bytes") ||
|
|
|
|
STREQ(key, "wr_operations") ||
|
2012-06-21 07:37:13 +00:00
|
|
|
STREQ(key, "wr_total_time_ns") ||
|
2011-09-05 08:22:17 +00:00
|
|
|
STREQ(key, "flush_operations") ||
|
2012-06-21 07:37:13 +00:00
|
|
|
STREQ(key, "flush_total_time_ns")) {
|
2011-09-05 08:22:17 +00:00
|
|
|
num++;
|
|
|
|
} else {
|
|
|
|
/* wr_highest_offset is parsed by qemuMonitorJSONGetBlockExtent. */
|
|
|
|
if (STRNEQ(key, "wr_highest_offset"))
|
|
|
|
VIR_DEBUG("Missed block stat: %s", key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*nparams = num;
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2011-09-05 08:22:17 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-09-15 15:42:52 +00:00
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONReportBlockExtentError(qemuMonitorBlockExtentError error)
|
|
|
|
{
|
|
|
|
switch (error) {
|
|
|
|
case QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOPARENT:
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats parent entry was not in "
|
|
|
|
"expected format"));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOSTATS:
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats stats entry was not in "
|
|
|
|
"expected format"));
|
2014-10-01 12:33:09 +00:00
|
|
|
break;
|
2014-09-15 15:42:52 +00:00
|
|
|
|
|
|
|
case QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOOFFSET:
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"wr_highest_offset");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case QEMU_MONITOR_BLOCK_EXTENT_ERROR_OK:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-05-14 13:10:01 +00:00
|
|
|
int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
|
2011-09-16 12:05:58 +00:00
|
|
|
const char *dev_name,
|
2010-05-14 13:10:01 +00:00
|
|
|
unsigned long long *extent)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2013-05-24 10:14:02 +00:00
|
|
|
bool found = false;
|
2010-05-14 13:10:01 +00:00
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-blockstats",
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr devices;
|
|
|
|
|
|
|
|
*extent = 0;
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
2010-07-12 13:07:02 +00:00
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
2010-05-14 13:10:01 +00:00
|
|
|
|
2010-07-12 13:07:02 +00:00
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
if (ret < 0)
|
2010-05-14 13:10:01 +00:00
|
|
|
goto cleanup;
|
2010-07-12 13:07:02 +00:00
|
|
|
ret = -1;
|
2010-05-14 13:10:01 +00:00
|
|
|
|
|
|
|
devices = virJSONValueObjectGet(reply, "return");
|
|
|
|
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats reply was missing device list"));
|
2010-05-14 13:10:01 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < virJSONValueArraySize(devices); i++) {
|
2010-05-14 13:10:01 +00:00
|
|
|
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
|
|
|
|
const char *thisdev;
|
2014-09-15 15:42:52 +00:00
|
|
|
int err;
|
2010-05-14 13:10:01 +00:00
|
|
|
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats device entry was not in expected format"));
|
2010-05-14 13:10:01 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats device entry was not in expected format"));
|
2010-05-14 13:10:01 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* New QEMU has separate names for host & guest side of the disk
|
|
|
|
* and libvirt gives the host side a 'drive-' prefix. The passed
|
2011-09-16 12:05:58 +00:00
|
|
|
* in dev_name is the guest side though
|
2010-05-14 13:10:01 +00:00
|
|
|
*/
|
|
|
|
if (STRPREFIX(thisdev, QEMU_DRIVE_HOST_PREFIX))
|
|
|
|
thisdev += strlen(QEMU_DRIVE_HOST_PREFIX);
|
|
|
|
|
2011-09-16 12:05:58 +00:00
|
|
|
if (STRNEQ(thisdev, dev_name))
|
2010-05-14 13:10:01 +00:00
|
|
|
continue;
|
|
|
|
|
2013-05-24 10:14:02 +00:00
|
|
|
found = true;
|
2014-09-15 15:42:52 +00:00
|
|
|
if ((err = qemuMonitorJSONDevGetBlockExtent(dev, extent)) !=
|
|
|
|
QEMU_MONITOR_BLOCK_EXTENT_ERROR_OK) {
|
|
|
|
qemuMonitorJSONReportBlockExtentError(err);
|
2010-05-14 13:10:01 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot find statistics for device '%s'"), dev_name);
|
2010-05-14 13:10:01 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2010-05-14 13:10:01 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-03 14:43:22 +00:00
|
|
|
/* Return 0 on success, -1 on failure, or -2 if not supported. Size
|
|
|
|
* is in bytes. */
|
2011-11-29 07:34:53 +00:00
|
|
|
int qemuMonitorJSONBlockResize(qemuMonitorPtr mon,
|
|
|
|
const char *device,
|
|
|
|
unsigned long long size)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("block_resize",
|
|
|
|
"s:device", device,
|
2012-03-03 14:43:22 +00:00
|
|
|
"U:size", size,
|
2011-11-29 07:34:53 +00:00
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
|
|
|
|
ret = -2;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2011-11-29 07:34:53 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2010-05-14 13:10:01 +00:00
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,
|
|
|
|
const char *password)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("change",
|
|
|
|
"s:device", "vnc",
|
|
|
|
"s:target", "password",
|
|
|
|
"s:arg", password,
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-01-10 11:12:32 +00:00
|
|
|
/* Returns -1 on error, -2 if not supported */
|
|
|
|
int qemuMonitorJSONSetPassword(qemuMonitorPtr mon,
|
|
|
|
const char *protocol,
|
|
|
|
const char *password,
|
|
|
|
const char *action_if_connected)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("set_password",
|
|
|
|
"s:protocol", protocol,
|
|
|
|
"s:password", password,
|
|
|
|
"s:connected", action_if_connected,
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
|
|
|
|
ret = -2;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2011-01-10 11:12:32 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-01-18 18:37:45 +00:00
|
|
|
/* Returns -1 on error, -2 if not supported */
|
2011-01-10 11:12:32 +00:00
|
|
|
int qemuMonitorJSONExpirePassword(qemuMonitorPtr mon,
|
|
|
|
const char *protocol,
|
|
|
|
const char *expire_time)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("expire_password",
|
|
|
|
"s:protocol", protocol,
|
|
|
|
"s:time", expire_time,
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
2011-01-18 18:37:45 +00:00
|
|
|
if (ret == 0) {
|
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
|
|
|
|
ret = -2;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2011-01-10 11:12:32 +00:00
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
2011-01-18 18:37:45 +00:00
|
|
|
}
|
2011-01-10 11:12:32 +00:00
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2011-01-10 11:12:32 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
/*
|
|
|
|
* Returns: 0 if balloon not supported, +1 if balloon adjust worked
|
|
|
|
* or -1 on failure
|
|
|
|
*/
|
|
|
|
int qemuMonitorJSONSetBalloon(qemuMonitorPtr mon,
|
|
|
|
unsigned long newmem)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("balloon",
|
2010-02-16 16:31:19 +00:00
|
|
|
"U:value", ((unsigned long long)newmem)*1024,
|
2009-11-03 18:59:18 +00:00
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
/* See if balloon soft-failed */
|
|
|
|
if (qemuMonitorJSONHasError(reply, "DeviceNotActive") ||
|
|
|
|
qemuMonitorJSONHasError(reply, "KVMMissingCap"))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* See if any other fatal error occurred */
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
/* Real success */
|
|
|
|
if (ret == 0)
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2009-11-03 18:59:18 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-08 16:37:17 +00:00
|
|
|
/*
|
|
|
|
* Returns: 0 if CPU hotplug not supported, +1 if CPU hotplug worked
|
|
|
|
* or -1 on failure
|
|
|
|
*/
|
|
|
|
int qemuMonitorJSONSetCPU(qemuMonitorPtr mon,
|
2013-05-27 13:35:35 +00:00
|
|
|
int cpu, bool online)
|
2010-02-08 16:37:17 +00:00
|
|
|
{
|
2013-05-27 14:08:30 +00:00
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
if (online) {
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("cpu-add",
|
|
|
|
"i:id", cpu,
|
|
|
|
NULL);
|
|
|
|
} else {
|
|
|
|
/* offlining is not yet implemented in qmp */
|
|
|
|
goto fallback;
|
|
|
|
}
|
|
|
|
if (!cmd)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
|
|
|
|
goto fallback;
|
|
|
|
else
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
/* this function has non-standard return values, so adapt it */
|
|
|
|
if (ret == 0)
|
|
|
|
ret = 1;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-05-27 14:08:30 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
fallback:
|
qemu: don't attempt undefined QMP commands
https://bugzilla.redhat.com/show_bug.cgi?id=872292
Libvirt should not attempt to call a QMP command that has not been
documented in qemu.git - if future qemu introduces a command by the
same name but with subtly different semantics, then libvirt will be
broken when trying to use that command.
We also had some code that could never be reached - some of our
commands have an alternate for new vs. old qemu HMP commands; but
if we are new enough to support QMP, we only need a fallback to
the new HMP counterpart, and don't need to try for a QMP counterpart
for the old HMP version.
See also this attempt to convert the three snapshot commands to QMP:
https://lists.gnu.org/archive/html/qemu-devel/2012-07/msg01597.html
although it looks like that will still not happen before qemu 1.3.
That thread eventually decided that qemu would use the name
'save-vm' rather than 'savevm', which mitigates the fact that
libvirt's attempt to use a QMP 'savevm' would be broken, but we
might not be as lucky on the other commands.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONSetCPU)
(qemuMonitorJSONAddDrive, qemuMonitorJSONDriveDel)
(qemuMonitorJSONCreateSnapshot, qemuMonitorJSONLoadSnapshot)
(qemuMonitorJSONDeleteSnapshot): Use only HMP fallback for now.
(qemuMonitorJSONAddHostNetwork, qemuMonitorJSONRemoveHostNetwork)
(qemuMonitorJSONAttachDrive, qemuMonitorJSONGetGuestDriveAddress):
Delete; QMP implies QEMU_CAPS_DEVICE, which prefers AddNetdev,
RemoveNetdev, and AddDrive anyways (qemu_hotplug.c has all callers).
* src/qemu/qemu_monitor.c (qemuMonitorAddHostNetwork)
(qemuMonitorRemoveHostNetwork, qemuMonitorAttachDrive): Reflect
deleted commands.
* src/qemu/qemu_monitor_json.h (qemuMonitorJSONAddHostNetwork)
(qemuMonitorJSONRemoveHostNetwork, qemuMonitorJSONAttachDrive):
Likewise.
2012-11-30 00:35:23 +00:00
|
|
|
VIR_DEBUG("no QMP support for cpu_set, trying HMP");
|
2013-05-27 14:08:30 +00:00
|
|
|
ret = qemuMonitorTextSetCPU(mon, cpu, online);
|
|
|
|
goto cleanup;
|
2010-02-08 16:37:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon,
|
2011-09-16 12:05:58 +00:00
|
|
|
const char *dev_name,
|
2010-11-08 17:52:48 +00:00
|
|
|
bool force)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("eject",
|
2011-09-16 12:05:58 +00:00
|
|
|
"s:device", dev_name,
|
2010-11-08 17:52:48 +00:00
|
|
|
"b:force", force ? 1 : 0,
|
2009-11-03 18:59:18 +00:00
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONChangeMedia(qemuMonitorPtr mon,
|
2011-09-16 12:05:58 +00:00
|
|
|
const char *dev_name,
|
2009-11-03 18:59:18 +00:00
|
|
|
const char *newmedia,
|
|
|
|
const char *format)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
2014-05-13 15:28:45 +00:00
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("change",
|
|
|
|
"s:device", dev_name,
|
|
|
|
"s:target", newmedia,
|
|
|
|
"S:arg", format,
|
|
|
|
NULL);
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int qemuMonitorJSONSaveMemory(qemuMonitorPtr mon,
|
|
|
|
const char *cmdtype,
|
|
|
|
unsigned long long offset,
|
|
|
|
size_t length,
|
|
|
|
const char *path)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand(cmdtype,
|
|
|
|
"U:val", offset,
|
|
|
|
"u:size", length,
|
|
|
|
"s:filename", path,
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONSaveVirtualMemory(qemuMonitorPtr mon,
|
|
|
|
unsigned long long offset,
|
|
|
|
size_t length,
|
|
|
|
const char *path)
|
|
|
|
{
|
|
|
|
return qemuMonitorJSONSaveMemory(mon, "memsave", offset, length, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorJSONSavePhysicalMemory(qemuMonitorPtr mon,
|
|
|
|
unsigned long long offset,
|
|
|
|
size_t length,
|
|
|
|
const char *path)
|
|
|
|
{
|
|
|
|
return qemuMonitorJSONSaveMemory(mon, "pmemsave", offset, length, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONSetMigrationSpeed(qemuMonitorPtr mon,
|
|
|
|
unsigned long bandwidth)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("migrate_set_speed",
|
2010-11-25 08:38:32 +00:00
|
|
|
"U:value", bandwidth * 1024ULL * 1024ULL,
|
2009-11-03 18:59:18 +00:00
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-17 15:53:14 +00:00
|
|
|
int qemuMonitorJSONSetMigrationDowntime(qemuMonitorPtr mon,
|
|
|
|
unsigned long long downtime)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
2010-08-19 13:59:25 +00:00
|
|
|
|
2010-03-17 15:53:14 +00:00
|
|
|
cmd = qemuMonitorJSONMakeCommand("migrate_set_downtime",
|
2010-08-19 13:59:25 +00:00
|
|
|
"d:value", downtime / 1000.0,
|
2010-03-17 15:53:14 +00:00
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-02-18 20:54:58 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONGetMigrationCacheSize(qemuMonitorPtr mon,
|
|
|
|
unsigned long long *cacheSize)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
*cacheSize = 0;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("query-migrate-cache-size", NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = virJSONValueObjectGetNumberUlong(reply, "return", cacheSize);
|
|
|
|
if (ret < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-migrate-cache-size reply was missing "
|
|
|
|
"'return' data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-02-18 20:54:58 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
qemuMonitorJSONSetMigrationCacheSize(qemuMonitorPtr mon,
|
|
|
|
unsigned long long cacheSize)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("migrate-set-cache-size",
|
|
|
|
"U:value", cacheSize,
|
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
static int
|
|
|
|
qemuMonitorJSONGetMigrationStatusReply(virJSONValuePtr reply,
|
2013-02-08 08:58:03 +00:00
|
|
|
qemuMonitorMigrationStatusPtr status)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
|
|
|
virJSONValuePtr ret;
|
2010-01-22 13:22:53 +00:00
|
|
|
const char *statusstr;
|
2013-02-08 08:58:03 +00:00
|
|
|
int rc;
|
2014-01-13 06:28:10 +00:00
|
|
|
double mbps;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
|
|
|
if (!(ret = virJSONValueObjectGet(reply, "return"))) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info migration reply was missing return data"));
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(statusstr = virJSONValueObjectGetString(ret, "status"))) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info migration reply was missing return status"));
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-02-08 08:58:03 +00:00
|
|
|
status->status = qemuMonitorMigrationStatusTypeFromString(statusstr);
|
|
|
|
if (status->status < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected migration status in %s"), statusstr);
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-09-09 08:17:46 +00:00
|
|
|
ignore_value(virJSONValueObjectGetNumberUlong(ret, "total-time",
|
|
|
|
&status->total_time));
|
2013-02-08 08:58:03 +00:00
|
|
|
if (status->status == QEMU_MONITOR_MIGRATION_STATUS_COMPLETED) {
|
|
|
|
rc = virJSONValueObjectGetNumberUlong(ret, "downtime",
|
|
|
|
&status->downtime);
|
|
|
|
} else {
|
|
|
|
rc = virJSONValueObjectGetNumberUlong(ret, "expected-downtime",
|
|
|
|
&status->downtime);
|
|
|
|
}
|
|
|
|
if (rc == 0)
|
|
|
|
status->downtime_set = true;
|
|
|
|
|
2014-01-13 06:28:10 +00:00
|
|
|
if (virJSONValueObjectGetNumberUlong(ret, "setup-time",
|
|
|
|
&status->setup_time) == 0)
|
|
|
|
status->setup_time_set = true;
|
|
|
|
|
2014-08-28 11:52:58 +00:00
|
|
|
if (status->status == QEMU_MONITOR_MIGRATION_STATUS_ACTIVE ||
|
|
|
|
status->status == QEMU_MONITOR_MIGRATION_STATUS_COMPLETED) {
|
2009-11-03 18:59:18 +00:00
|
|
|
virJSONValuePtr ram = virJSONValueObjectGet(ret, "ram");
|
|
|
|
if (!ram) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("migration was active, but no RAM info was set"));
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-08-17 06:30:02 +00:00
|
|
|
if (virJSONValueObjectGetNumberUlong(ram, "transferred",
|
2013-02-08 08:58:03 +00:00
|
|
|
&status->ram_transferred) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("migration was active, but RAM 'transferred' "
|
|
|
|
"data was missing"));
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2013-02-08 08:58:03 +00:00
|
|
|
if (virJSONValueObjectGetNumberUlong(ram, "remaining",
|
|
|
|
&status->ram_remaining) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("migration was active, but RAM 'remaining' "
|
|
|
|
"data was missing"));
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2013-02-08 08:58:03 +00:00
|
|
|
if (virJSONValueObjectGetNumberUlong(ram, "total",
|
|
|
|
&status->ram_total) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("migration was active, but RAM 'total' "
|
|
|
|
"data was missing"));
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2011-08-17 06:30:02 +00:00
|
|
|
|
2014-01-13 06:28:10 +00:00
|
|
|
if (virJSONValueObjectGetNumberDouble(ram, "mbps", &mbps) == 0 &&
|
|
|
|
mbps > 0) {
|
|
|
|
/* mpbs from QEMU reports Mbits/s (M as in 10^6 not Mi as 2^20) */
|
|
|
|
status->ram_bps = mbps * (1000 * 1000 / 8);
|
|
|
|
}
|
|
|
|
|
2013-02-08 08:58:03 +00:00
|
|
|
if (virJSONValueObjectGetNumberUlong(ram, "duplicate",
|
|
|
|
&status->ram_duplicate) == 0)
|
|
|
|
status->ram_duplicate_set = true;
|
2014-09-09 08:17:46 +00:00
|
|
|
ignore_value(virJSONValueObjectGetNumberUlong(ram, "normal",
|
|
|
|
&status->ram_normal));
|
|
|
|
ignore_value(virJSONValueObjectGetNumberUlong(ram, "normal-bytes",
|
|
|
|
&status->ram_normal_bytes));
|
2013-02-08 08:58:03 +00:00
|
|
|
|
2011-08-17 06:30:02 +00:00
|
|
|
virJSONValuePtr disk = virJSONValueObjectGet(ret, "disk");
|
2013-02-08 08:58:03 +00:00
|
|
|
if (disk) {
|
|
|
|
rc = virJSONValueObjectGetNumberUlong(disk, "transferred",
|
|
|
|
&status->disk_transferred);
|
|
|
|
if (rc < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("disk migration was active, but "
|
|
|
|
"'transferred' data was missing"));
|
|
|
|
return -1;
|
|
|
|
}
|
2011-08-17 06:30:02 +00:00
|
|
|
|
2013-02-08 08:58:03 +00:00
|
|
|
rc = virJSONValueObjectGetNumberUlong(disk, "remaining",
|
|
|
|
&status->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",
|
|
|
|
&status->disk_total);
|
|
|
|
if (rc < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("disk migration was active, but 'total' "
|
|
|
|
"data was missing"));
|
|
|
|
return -1;
|
|
|
|
}
|
2014-01-13 06:28:10 +00:00
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberDouble(disk, "mbps", &mbps) == 0 &&
|
|
|
|
mbps > 0) {
|
|
|
|
/* mpbs from QEMU reports Mbits/s (M as in 10^6 not Mi as 2^20) */
|
|
|
|
status->disk_bps = mbps * (1000 * 1000 / 8);
|
|
|
|
}
|
2011-08-17 06:30:02 +00:00
|
|
|
}
|
2013-02-08 08:58:03 +00:00
|
|
|
|
|
|
|
virJSONValuePtr comp = virJSONValueObjectGet(ret, "xbzrle-cache");
|
|
|
|
if (comp) {
|
|
|
|
status->xbzrle_set = true;
|
|
|
|
rc = virJSONValueObjectGetNumberUlong(comp, "cache-size",
|
|
|
|
&status->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",
|
|
|
|
&status->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",
|
|
|
|
&status->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",
|
|
|
|
&status->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",
|
|
|
|
&status->xbzrle_overflow);
|
|
|
|
if (rc < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("XBZRLE is active, but 'overflow' data "
|
|
|
|
"was missing"));
|
|
|
|
return -1;
|
|
|
|
}
|
2011-08-17 06:30:02 +00:00
|
|
|
}
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon,
|
2013-02-08 08:58:03 +00:00
|
|
|
qemuMonitorMigrationStatusPtr status)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2010-03-03 17:07:50 +00:00
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-migrate",
|
2009-11-03 18:59:18 +00:00
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
2013-02-08 08:58:03 +00:00
|
|
|
memset(status, 0, sizeof(*status));
|
2009-11-03 18:59:18 +00:00
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret == 0 &&
|
2013-02-08 08:58:03 +00:00
|
|
|
qemuMonitorJSONGetMigrationStatusReply(reply, status) < 0)
|
2009-11-03 18:59:18 +00:00
|
|
|
ret = -1;
|
|
|
|
|
2013-02-08 08:58:03 +00:00
|
|
|
if (ret < 0)
|
|
|
|
memset(status, 0, sizeof(*status));
|
2009-11-03 18:59:18 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-09-20 09:15:31 +00:00
|
|
|
static int
|
|
|
|
qemuMonitorJSONSpiceGetMigrationStatusReply(virJSONValuePtr reply,
|
|
|
|
bool *spice_migrated)
|
|
|
|
{
|
|
|
|
virJSONValuePtr ret;
|
|
|
|
|
|
|
|
if (!(ret = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-spice reply was missing return data"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-10-18 07:38:41 +00:00
|
|
|
if (virJSONValueObjectGetBoolean(ret, "migrated", spice_migrated) < 0) {
|
2012-09-20 09:15:31 +00:00
|
|
|
/* Deliberately don't report error here as we are
|
|
|
|
* probably dealing with older qemu which doesn't
|
|
|
|
* report this yet. Pretend spice is migrated. */
|
|
|
|
*spice_migrated = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONGetSpiceMigrationStatus(qemuMonitorPtr mon,
|
|
|
|
bool *spice_migrated)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-spice",
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONSpiceGetMigrationStatusReply(reply,
|
|
|
|
spice_migrated);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-04 18:51:48 +00:00
|
|
|
int qemuMonitorJSONMigrate(qemuMonitorPtr mon,
|
|
|
|
unsigned int flags,
|
|
|
|
const char *uri)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2010-06-24 18:58:36 +00:00
|
|
|
virJSONValuePtr cmd =
|
|
|
|
qemuMonitorJSONMakeCommand("migrate",
|
2010-09-09 21:05:03 +00:00
|
|
|
"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,
|
2010-06-24 18:58:36 +00:00
|
|
|
"s:uri", uri,
|
|
|
|
NULL);
|
2009-11-03 18:59:18 +00:00
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorJSONMigrateCancel(qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("migrate_cancel", NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-03-23 03:51:13 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONGetDumpGuestMemoryCapability(qemuMonitorPtr mon,
|
|
|
|
const char *capability)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr caps;
|
|
|
|
virJSONValuePtr formats;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-dump-guest-memory-capability",
|
|
|
|
NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
|
|
|
|
goto cleanup;
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
caps = virJSONValueObjectGet(reply, "return");
|
|
|
|
if (!caps || caps->type != VIR_JSON_TYPE_OBJECT) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing dump guest memory capabilities"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
formats = virJSONValueObjectGet(caps, "formats");
|
|
|
|
if (!formats || formats->type != VIR_JSON_TYPE_ARRAY) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing supported dump formats"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < virJSONValueArraySize(formats); i++) {
|
|
|
|
virJSONValuePtr dumpformat = virJSONValueArrayGet(formats, i);
|
|
|
|
|
|
|
|
if (!dumpformat || dumpformat->type != 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;
|
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2014-03-23 03:51:13 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-09-17 19:05:29 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONDump(qemuMonitorPtr mon,
|
2014-03-23 03:51:14 +00:00
|
|
|
const char *protocol,
|
|
|
|
const char *dumpformat)
|
2012-06-12 03:04:51 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
2014-03-23 03:51:14 +00:00
|
|
|
if (dumpformat) {
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("dump-guest-memory",
|
|
|
|
"b:paging", false,
|
|
|
|
"s:protocol", protocol,
|
|
|
|
"s:format", dumpformat,
|
|
|
|
NULL);
|
|
|
|
} else {
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("dump-guest-memory",
|
|
|
|
"b:paging", false,
|
|
|
|
"s:protocol", protocol,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
2012-06-12 03:04:51 +00:00
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2009-11-03 18:59:18 +00:00
|
|
|
|
2011-02-17 13:39:36 +00:00
|
|
|
int qemuMonitorJSONGraphicsRelocate(qemuMonitorPtr mon,
|
|
|
|
int type,
|
|
|
|
const char *hostname,
|
|
|
|
int port,
|
|
|
|
int tlsPort,
|
|
|
|
const char *tlsSubject)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr 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,
|
2014-05-13 15:28:45 +00:00
|
|
|
"S:cert-subject", tlsSubject,
|
2011-02-17 13:39:36 +00:00
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-14 14:23:38 +00:00
|
|
|
int qemuMonitorJSONAddUSBDisk(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
const char *path ATTRIBUTE_UNUSED)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("usb_add not supported in JSON mode"));
|
2010-04-14 14:23:38 +00:00
|
|
|
return -1;
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-14 14:23:38 +00:00
|
|
|
int qemuMonitorJSONAddUSBDeviceExact(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
int bus ATTRIBUTE_UNUSED,
|
|
|
|
int dev ATTRIBUTE_UNUSED)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("usb_add not supported in JSON mode"));
|
2010-04-14 14:23:38 +00:00
|
|
|
return -1;
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-14 14:23:38 +00:00
|
|
|
int qemuMonitorJSONAddUSBDeviceMatch(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
int vendor ATTRIBUTE_UNUSED,
|
|
|
|
int product ATTRIBUTE_UNUSED)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("usb_add not supported in JSON mode"));
|
2010-04-14 14:23:38 +00:00
|
|
|
return -1;
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-14 14:23:38 +00:00
|
|
|
int qemuMonitorJSONAddPCIHostDevice(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
2012-08-16 15:41:06 +00:00
|
|
|
virDevicePCIAddress *hostAddr ATTRIBUTE_UNUSED,
|
|
|
|
virDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_add not supported in JSON mode"));
|
2010-04-14 14:23:38 +00:00
|
|
|
return -1;
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-14 14:23:38 +00:00
|
|
|
int qemuMonitorJSONAddPCIDisk(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
const char *path ATTRIBUTE_UNUSED,
|
|
|
|
const char *bus ATTRIBUTE_UNUSED,
|
2012-08-16 15:41:06 +00:00
|
|
|
virDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
|
2009-12-07 19:07:39 +00:00
|
|
|
{
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_add not supported in JSON mode"));
|
2010-04-14 14:23:38 +00:00
|
|
|
return -1;
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-14 14:23:38 +00:00
|
|
|
int qemuMonitorJSONAddPCINetwork(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
const char *nicstr ATTRIBUTE_UNUSED,
|
2012-08-16 15:41:06 +00:00
|
|
|
virDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_add not supported in JSON mode"));
|
2010-04-14 14:23:38 +00:00
|
|
|
return -1;
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-14 14:23:38 +00:00
|
|
|
int qemuMonitorJSONRemovePCIDevice(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
2012-08-16 15:41:06 +00:00
|
|
|
virDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_del not supported in JSON mode"));
|
2010-04-14 14:23:38 +00:00
|
|
|
return -1;
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONSendFileHandle(qemuMonitorPtr mon,
|
|
|
|
const char *fdname,
|
|
|
|
int fd)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("getfd",
|
|
|
|
"s:fdname", fdname,
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommandWithFd(mon, cmd, fd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONCloseFileHandle(qemuMonitorPtr mon,
|
|
|
|
const char *fdname)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("closefd",
|
|
|
|
"s:fdname", fdname,
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-31 00:18:44 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONAddFd(qemuMonitorPtr mon, int fdset, int fd, const char *name)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("add-fd",
|
|
|
|
"i:fdset-id", fdset,
|
2014-05-13 15:28:45 +00:00
|
|
|
"S:opaque", name,
|
|
|
|
NULL);
|
2013-01-31 00:18:44 +00:00
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommandWithFd(mon, cmd, fd, &reply);
|
|
|
|
|
2013-02-05 15:50:00 +00:00
|
|
|
if (ret == 0) {
|
|
|
|
/* qemu 1.2 lacks the functionality we need; but we have to
|
|
|
|
* probe to find that out. Don't log errors in that case. */
|
|
|
|
if (STREQ_NULLABLE(name, "/dev/null") &&
|
|
|
|
qemuMonitorJSONHasError(reply, "GenericError")) {
|
|
|
|
ret = -2;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-01-31 00:18:44 +00:00
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
2013-02-05 15:50:00 +00:00
|
|
|
}
|
2013-01-31 00:18:44 +00:00
|
|
|
if (ret == 0) {
|
|
|
|
virJSONValuePtr data = virJSONValueObjectGet(reply, "return");
|
|
|
|
|
|
|
|
if (!data || data->type != VIR_JSON_TYPE_OBJECT) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing return information"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
data = virJSONValueObjectGet(data, "fd");
|
|
|
|
if (!data || data->type != VIR_JSON_TYPE_NUMBER ||
|
|
|
|
virJSONValueGetNumberInt(data, &ret) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("incomplete return information"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-01-31 00:18:44 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
error:
|
2013-01-31 00:18:44 +00:00
|
|
|
/* Best effort cleanup - kill the entire fdset (even if it has
|
|
|
|
* earlier successful fd registrations), since we don't know which
|
|
|
|
* fd qemu got, and don't want to leave the fd leaked in qemu. */
|
|
|
|
qemuMonitorJSONRemoveFd(mon, fdset, -1);
|
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
qemuMonitorJSONRemoveFd(qemuMonitorPtr mon, int fdset, int fd)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("remove-fd",
|
|
|
|
"i:fdset-id", fdset,
|
|
|
|
fd < 0 ? NULL : "i:fd",
|
|
|
|
fd, NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-15 13:52:03 +00:00
|
|
|
int qemuMonitorJSONAddNetdev(qemuMonitorPtr mon,
|
|
|
|
const char *netdevstr)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr args = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("netdev_add", NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
args = qemuMonitorJSONKeywordStringToJSON(netdevstr, "type");
|
|
|
|
if (!args)
|
|
|
|
goto cleanup;
|
|
|
|
|
2013-07-04 10:14:12 +00:00
|
|
|
if (virJSONValueObjectAppend(cmd, "arguments", args) < 0)
|
2010-04-15 13:52:03 +00:00
|
|
|
goto cleanup;
|
|
|
|
args = NULL; /* obj owns reference to args now */
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2010-04-15 13:52:03 +00:00
|
|
|
virJSONValueFree(args);
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon,
|
|
|
|
const char *alias)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("netdev_del",
|
|
|
|
"s:id", alias,
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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(virJSONValuePtr msg,
|
|
|
|
virNetDevRxFilterPtr *filter)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
const char *tmp;
|
|
|
|
virJSONValuePtr returnArray, entry, table, element;
|
|
|
|
int nTable;
|
|
|
|
size_t i;
|
|
|
|
virNetDevRxFilterPtr fil = virNetDevRxFilterNew();
|
|
|
|
|
|
|
|
if (!fil)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(returnArray = virJSONValueObjectGet(msg, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-rx-filter reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (returnArray->type != VIR_JSON_TYPE_ARRAY) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-rx-filter return data was not an array"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
if (VIR_STRDUP(fil->name, tmp) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
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"))) ||
|
|
|
|
((nTable = virJSONValueArraySize(table)) < 0)) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Missing or invalid 'unicast-table' array "
|
|
|
|
"in query-rx-filter response"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (VIR_ALLOC_N(fil->unicast.table, nTable))
|
|
|
|
goto cleanup;
|
|
|
|
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"))) ||
|
|
|
|
((nTable = virJSONValueArraySize(table)) < 0)) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Missing or invalid 'multicast-table' array "
|
|
|
|
"in query-rx-filter response"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (VIR_ALLOC_N(fil->multicast.table, nTable))
|
|
|
|
goto cleanup;
|
|
|
|
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"))) ||
|
|
|
|
((nTable = virJSONValueArraySize(table)) < 0)) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Missing or invalid 'vlan-table' array "
|
|
|
|
"in query-rx-filter response"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (VIR_ALLOC_N(fil->vlan.table, nTable))
|
|
|
|
goto cleanup;
|
|
|
|
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;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
if (ret < 0) {
|
|
|
|
virNetDevRxFilterFree(fil);
|
|
|
|
fil = NULL;
|
|
|
|
}
|
|
|
|
*filter = fil;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
qemuMonitorJSONQueryRxFilter(qemuMonitorPtr mon, const char *alias,
|
|
|
|
virNetDevRxFilterPtr *filter)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-rx-filter",
|
|
|
|
"s:name", alias,
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONQueryRxFilterParse(reply, filter) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
virNetDevRxFilterFree(*filter);
|
|
|
|
*filter = NULL;
|
|
|
|
}
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-22 13:22:53 +00:00
|
|
|
/*
|
|
|
|
* Example return data
|
|
|
|
*
|
|
|
|
* {"return": [
|
|
|
|
* {"filename": "stdio", "label": "monitor"},
|
|
|
|
* {"filename": "pty:/dev/pts/6", "label": "serial0"},
|
|
|
|
* {"filename": "pty:/dev/pts/7", "label": "parallel0"}
|
|
|
|
* ]}
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static int qemuMonitorJSONExtractPtyPaths(virJSONValuePtr reply,
|
|
|
|
virHashTablePtr paths)
|
|
|
|
{
|
|
|
|
virJSONValuePtr data;
|
|
|
|
int ret = -1;
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2010-01-22 13:22:53 +00:00
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device reply was missing return data"));
|
2010-01-22 13:22:53 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->type != VIR_JSON_TYPE_ARRAY) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device information was not an array"));
|
2010-01-22 13:22:53 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < virJSONValueArraySize(data); i++) {
|
2010-01-22 13:22:53 +00:00
|
|
|
virJSONValuePtr entry = virJSONValueArrayGet(data, i);
|
|
|
|
const char *type;
|
|
|
|
const char *id;
|
|
|
|
if (!entry) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device information was missing array element"));
|
2010-01-22 13:22:53 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(type = virJSONValueObjectGetString(entry, "filename"))) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device information was missing filename"));
|
2010-01-22 13:22:53 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(id = virJSONValueObjectGetString(entry, "label"))) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device information was missing filename"));
|
2010-01-22 13:22:53 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STRPREFIX(type, "pty:")) {
|
2013-05-20 09:23:13 +00:00
|
|
|
char *path;
|
|
|
|
if (VIR_STRDUP(path, type + strlen("pty:")) < 0)
|
2010-01-22 13:22:53 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virHashAddEntry(paths, id, path) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("failed to save chardev path '%s'"), path);
|
2010-01-22 13:22:53 +00:00
|
|
|
VIR_FREE(path);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2010-01-22 13:22:53 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorJSONGetPtyPaths(qemuMonitorPtr mon,
|
|
|
|
virHashTablePtr paths)
|
|
|
|
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-chardev",
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONExtractPtyPaths(reply, paths);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-14 14:23:38 +00:00
|
|
|
int qemuMonitorJSONAttachPCIDiskController(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
const char *bus ATTRIBUTE_UNUSED,
|
2012-08-16 15:41:06 +00:00
|
|
|
virDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
|
2009-12-07 19:28:05 +00:00
|
|
|
{
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_add not supported in JSON mode"));
|
2010-04-14 14:23:38 +00:00
|
|
|
return -1;
|
Properly support SCSI drive hotplug
The current SCSI hotplug support attaches a brand new SCSI controller
for every disk. This is broken because the semantics differ from those
used when starting the VM initially. In the latter case, each SCSI
controller is filled before a new one is added.
If the user specifies an high drive index (sdazz) then at initial
startup, many intermediate SCSI controllers may be added with no
drives.
This patch changes SCSI hotplug so that it exactly matches the
behaviour of initial startup. First the SCSI controller number is
determined for the drive to be hotplugged. If any controller upto
and including that controller number is not yet present, it is
attached. Then finally the drive is attached to the last controller.
NB, this breaks SCSI hotunplug, because there is no 'drive_del'
command in current QEMU. Previous SCSI hotunplug was broken in
any case because it was unplugging the entire controller, not
just the drive in question.
A future QEMU will allow proper SCSI hotunplug of a drive.
This patch is derived from work done by Wolfgang Mauerer on disk
controllers.
* src/qemu/qemu_driver.c: Fix SCSI hotplug to add a drive to
the correct controller, instead of just attaching a new
controller.
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
support for 'drive_add' command
2009-12-09 17:57:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
Detect PCI addresses at QEMU startup
Hotunplug of devices requires that we know their PCI address. Even
hotplug of SCSI drives, required that we know the PCI address of
the SCSI controller to attach the drive to. We can find this out
by running 'info pci' and then correlating the vendor/product IDs
with the devices we booted with.
Although this approach is somewhat fragile, it is the only viable
option with QEMU < 0.12, since there is no way for libvirto set
explicit PCI addresses when creating devices in the first place.
For QEMU > 0.12, this code will not be used.
* src/qemu/qemu_driver.c: Assign all dynamic PCI addresses on
startup of QEMU VM, matching vendor/product IDs
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
API for fetching PCI device address mapping
2009-12-09 21:59:04 +00:00
|
|
|
int qemuMonitorJSONGetAllPCIAddresses(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
qemuMonitorPCIAddress **addrs ATTRIBUTE_UNUSED)
|
|
|
|
{
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-pci not supported in JSON mode"));
|
Detect PCI addresses at QEMU startup
Hotunplug of devices requires that we know their PCI address. Even
hotplug of SCSI drives, required that we know the PCI address of
the SCSI controller to attach the drive to. We can find this out
by running 'info pci' and then correlating the vendor/product IDs
with the devices we booted with.
Although this approach is somewhat fragile, it is the only viable
option with QEMU < 0.12, since there is no way for libvirto set
explicit PCI addresses when creating devices in the first place.
For QEMU > 0.12, this code will not be used.
* src/qemu/qemu_driver.c: Assign all dynamic PCI addresses on
startup of QEMU VM, matching vendor/product IDs
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
API for fetching PCI device address mapping
2009-12-09 21:59:04 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2010-01-26 15:34:46 +00:00
|
|
|
|
|
|
|
|
2010-03-02 08:40:51 +00:00
|
|
|
int qemuMonitorJSONDelDevice(qemuMonitorPtr mon,
|
2010-04-14 14:36:42 +00:00
|
|
|
const char *devalias)
|
2010-03-02 08:40:51 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("device_del",
|
2010-04-15 11:17:29 +00:00
|
|
|
"s:id", devalias,
|
2010-03-02 08:40:51 +00:00
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-26 15:34:46 +00:00
|
|
|
int qemuMonitorJSONAddDevice(qemuMonitorPtr mon,
|
|
|
|
const char *devicestr)
|
|
|
|
{
|
2010-04-14 15:02:37 +00:00
|
|
|
int ret = -1;
|
2010-01-26 15:34:46 +00:00
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
2010-04-14 15:02:37 +00:00
|
|
|
virJSONValuePtr args;
|
2010-01-26 15:34:46 +00:00
|
|
|
|
2010-04-14 15:02:37 +00:00
|
|
|
cmd = qemuMonitorJSONMakeCommand("device_add", NULL);
|
2010-01-26 15:34:46 +00:00
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
2010-04-14 15:02:37 +00:00
|
|
|
args = qemuMonitorJSONKeywordStringToJSON(devicestr, "driver");
|
|
|
|
if (!args)
|
|
|
|
goto cleanup;
|
|
|
|
|
2013-07-04 10:14:12 +00:00
|
|
|
if (virJSONValueObjectAppend(cmd, "arguments", args) < 0)
|
2010-04-14 15:02:37 +00:00
|
|
|
goto cleanup;
|
|
|
|
args = NULL; /* obj owns reference to args now */
|
|
|
|
|
2010-01-26 15:34:46 +00:00
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2010-04-14 15:02:37 +00:00
|
|
|
virJSONValueFree(args);
|
2010-01-26 15:34:46 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-09-23 11:25:25 +00:00
|
|
|
int qemuMonitorJSONAddObject(qemuMonitorPtr mon,
|
|
|
|
const char *type,
|
|
|
|
const char *objalias,
|
|
|
|
virJSONValuePtr props)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("object-add",
|
|
|
|
"s:qom-type", type,
|
|
|
|
"s:id", objalias,
|
|
|
|
"A:props", props,
|
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* @props is part of @cmd now. Avoid double free */
|
|
|
|
props = NULL;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
virJSONValueFree(props);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONDelObject(qemuMonitorPtr mon,
|
|
|
|
const char *objalias)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("object-del",
|
|
|
|
"s:id", objalias,
|
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-26 15:34:46 +00:00
|
|
|
int qemuMonitorJSONAddDrive(qemuMonitorPtr mon,
|
|
|
|
const char *drivestr)
|
|
|
|
{
|
qemu: don't attempt undefined QMP commands
https://bugzilla.redhat.com/show_bug.cgi?id=872292
Libvirt should not attempt to call a QMP command that has not been
documented in qemu.git - if future qemu introduces a command by the
same name but with subtly different semantics, then libvirt will be
broken when trying to use that command.
We also had some code that could never be reached - some of our
commands have an alternate for new vs. old qemu HMP commands; but
if we are new enough to support QMP, we only need a fallback to
the new HMP counterpart, and don't need to try for a QMP counterpart
for the old HMP version.
See also this attempt to convert the three snapshot commands to QMP:
https://lists.gnu.org/archive/html/qemu-devel/2012-07/msg01597.html
although it looks like that will still not happen before qemu 1.3.
That thread eventually decided that qemu would use the name
'save-vm' rather than 'savevm', which mitigates the fact that
libvirt's attempt to use a QMP 'savevm' would be broken, but we
might not be as lucky on the other commands.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONSetCPU)
(qemuMonitorJSONAddDrive, qemuMonitorJSONDriveDel)
(qemuMonitorJSONCreateSnapshot, qemuMonitorJSONLoadSnapshot)
(qemuMonitorJSONDeleteSnapshot): Use only HMP fallback for now.
(qemuMonitorJSONAddHostNetwork, qemuMonitorJSONRemoveHostNetwork)
(qemuMonitorJSONAttachDrive, qemuMonitorJSONGetGuestDriveAddress):
Delete; QMP implies QEMU_CAPS_DEVICE, which prefers AddNetdev,
RemoveNetdev, and AddDrive anyways (qemu_hotplug.c has all callers).
* src/qemu/qemu_monitor.c (qemuMonitorAddHostNetwork)
(qemuMonitorRemoveHostNetwork, qemuMonitorAttachDrive): Reflect
deleted commands.
* src/qemu/qemu_monitor_json.h (qemuMonitorJSONAddHostNetwork)
(qemuMonitorJSONRemoveHostNetwork, qemuMonitorJSONAttachDrive):
Likewise.
2012-11-30 00:35:23 +00:00
|
|
|
/* XXX Update to use QMP, if QMP ever adds support for drive_add */
|
|
|
|
VIR_DEBUG("drive_add command not found, trying HMP");
|
|
|
|
return qemuMonitorTextAddDrive(mon, drivestr);
|
2010-01-26 15:34:46 +00:00
|
|
|
}
|
2010-02-11 14:28:16 +00:00
|
|
|
|
|
|
|
|
2010-12-08 21:30:12 +00:00
|
|
|
int qemuMonitorJSONDriveDel(qemuMonitorPtr mon,
|
|
|
|
const char *drivestr)
|
2010-10-22 14:14:22 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
qemu: don't attempt undefined QMP commands
https://bugzilla.redhat.com/show_bug.cgi?id=872292
Libvirt should not attempt to call a QMP command that has not been
documented in qemu.git - if future qemu introduces a command by the
same name but with subtly different semantics, then libvirt will be
broken when trying to use that command.
We also had some code that could never be reached - some of our
commands have an alternate for new vs. old qemu HMP commands; but
if we are new enough to support QMP, we only need a fallback to
the new HMP counterpart, and don't need to try for a QMP counterpart
for the old HMP version.
See also this attempt to convert the three snapshot commands to QMP:
https://lists.gnu.org/archive/html/qemu-devel/2012-07/msg01597.html
although it looks like that will still not happen before qemu 1.3.
That thread eventually decided that qemu would use the name
'save-vm' rather than 'savevm', which mitigates the fact that
libvirt's attempt to use a QMP 'savevm' would be broken, but we
might not be as lucky on the other commands.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONSetCPU)
(qemuMonitorJSONAddDrive, qemuMonitorJSONDriveDel)
(qemuMonitorJSONCreateSnapshot, qemuMonitorJSONLoadSnapshot)
(qemuMonitorJSONDeleteSnapshot): Use only HMP fallback for now.
(qemuMonitorJSONAddHostNetwork, qemuMonitorJSONRemoveHostNetwork)
(qemuMonitorJSONAttachDrive, qemuMonitorJSONGetGuestDriveAddress):
Delete; QMP implies QEMU_CAPS_DEVICE, which prefers AddNetdev,
RemoveNetdev, and AddDrive anyways (qemu_hotplug.c has all callers).
* src/qemu/qemu_monitor.c (qemuMonitorAddHostNetwork)
(qemuMonitorRemoveHostNetwork, qemuMonitorAttachDrive): Reflect
deleted commands.
* src/qemu/qemu_monitor_json.h (qemuMonitorJSONAddHostNetwork)
(qemuMonitorJSONRemoveHostNetwork, qemuMonitorJSONAttachDrive):
Likewise.
2012-11-30 00:35:23 +00:00
|
|
|
/* XXX Update to use QMP, if QMP ever adds support for drive_del */
|
|
|
|
VIR_DEBUG("drive_del command not found, trying HMP");
|
|
|
|
if ((ret = qemuMonitorTextDriveDel(mon, drivestr)) < 0) {
|
|
|
|
virErrorPtr err = virGetLastError();
|
|
|
|
if (err && err->code == VIR_ERR_OPERATION_UNSUPPORTED) {
|
|
|
|
VIR_ERROR("%s",
|
|
|
|
_("deleting disk is not supported. "
|
|
|
|
"This may leak data if disk is reassigned"));
|
|
|
|
ret = 1;
|
|
|
|
virResetLastError();
|
2010-10-22 14:14:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-02-11 14:28:16 +00:00
|
|
|
int qemuMonitorJSONSetDrivePassphrase(qemuMonitorPtr mon,
|
|
|
|
const char *alias,
|
|
|
|
const char *passphrase)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
char *drive;
|
|
|
|
|
2013-07-04 10:14:12 +00:00
|
|
|
if (virAsprintf(&drive, "%s%s", QEMU_DRIVE_HOST_PREFIX, alias) < 0)
|
2010-02-11 14:28:16 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("block_passwd",
|
|
|
|
"s:device", drive,
|
|
|
|
"s:password", passphrase,
|
|
|
|
NULL);
|
|
|
|
VIR_FREE(drive);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2010-04-02 14:10:37 +00:00
|
|
|
|
|
|
|
int qemuMonitorJSONCreateSnapshot(qemuMonitorPtr mon, const char *name)
|
|
|
|
{
|
qemu: don't attempt undefined QMP commands
https://bugzilla.redhat.com/show_bug.cgi?id=872292
Libvirt should not attempt to call a QMP command that has not been
documented in qemu.git - if future qemu introduces a command by the
same name but with subtly different semantics, then libvirt will be
broken when trying to use that command.
We also had some code that could never be reached - some of our
commands have an alternate for new vs. old qemu HMP commands; but
if we are new enough to support QMP, we only need a fallback to
the new HMP counterpart, and don't need to try for a QMP counterpart
for the old HMP version.
See also this attempt to convert the three snapshot commands to QMP:
https://lists.gnu.org/archive/html/qemu-devel/2012-07/msg01597.html
although it looks like that will still not happen before qemu 1.3.
That thread eventually decided that qemu would use the name
'save-vm' rather than 'savevm', which mitigates the fact that
libvirt's attempt to use a QMP 'savevm' would be broken, but we
might not be as lucky on the other commands.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONSetCPU)
(qemuMonitorJSONAddDrive, qemuMonitorJSONDriveDel)
(qemuMonitorJSONCreateSnapshot, qemuMonitorJSONLoadSnapshot)
(qemuMonitorJSONDeleteSnapshot): Use only HMP fallback for now.
(qemuMonitorJSONAddHostNetwork, qemuMonitorJSONRemoveHostNetwork)
(qemuMonitorJSONAttachDrive, qemuMonitorJSONGetGuestDriveAddress):
Delete; QMP implies QEMU_CAPS_DEVICE, which prefers AddNetdev,
RemoveNetdev, and AddDrive anyways (qemu_hotplug.c has all callers).
* src/qemu/qemu_monitor.c (qemuMonitorAddHostNetwork)
(qemuMonitorRemoveHostNetwork, qemuMonitorAttachDrive): Reflect
deleted commands.
* src/qemu/qemu_monitor_json.h (qemuMonitorJSONAddHostNetwork)
(qemuMonitorJSONRemoveHostNetwork, qemuMonitorJSONAttachDrive):
Likewise.
2012-11-30 00:35:23 +00:00
|
|
|
/* XXX Update to use QMP, if QMP ever adds support for savevm */
|
|
|
|
VIR_DEBUG("savevm command not found, trying HMP");
|
|
|
|
return qemuMonitorTextCreateSnapshot(mon, name);
|
2010-04-02 14:10:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon, const char *name)
|
|
|
|
{
|
qemu: don't attempt undefined QMP commands
https://bugzilla.redhat.com/show_bug.cgi?id=872292
Libvirt should not attempt to call a QMP command that has not been
documented in qemu.git - if future qemu introduces a command by the
same name but with subtly different semantics, then libvirt will be
broken when trying to use that command.
We also had some code that could never be reached - some of our
commands have an alternate for new vs. old qemu HMP commands; but
if we are new enough to support QMP, we only need a fallback to
the new HMP counterpart, and don't need to try for a QMP counterpart
for the old HMP version.
See also this attempt to convert the three snapshot commands to QMP:
https://lists.gnu.org/archive/html/qemu-devel/2012-07/msg01597.html
although it looks like that will still not happen before qemu 1.3.
That thread eventually decided that qemu would use the name
'save-vm' rather than 'savevm', which mitigates the fact that
libvirt's attempt to use a QMP 'savevm' would be broken, but we
might not be as lucky on the other commands.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONSetCPU)
(qemuMonitorJSONAddDrive, qemuMonitorJSONDriveDel)
(qemuMonitorJSONCreateSnapshot, qemuMonitorJSONLoadSnapshot)
(qemuMonitorJSONDeleteSnapshot): Use only HMP fallback for now.
(qemuMonitorJSONAddHostNetwork, qemuMonitorJSONRemoveHostNetwork)
(qemuMonitorJSONAttachDrive, qemuMonitorJSONGetGuestDriveAddress):
Delete; QMP implies QEMU_CAPS_DEVICE, which prefers AddNetdev,
RemoveNetdev, and AddDrive anyways (qemu_hotplug.c has all callers).
* src/qemu/qemu_monitor.c (qemuMonitorAddHostNetwork)
(qemuMonitorRemoveHostNetwork, qemuMonitorAttachDrive): Reflect
deleted commands.
* src/qemu/qemu_monitor_json.h (qemuMonitorJSONAddHostNetwork)
(qemuMonitorJSONRemoveHostNetwork, qemuMonitorJSONAttachDrive):
Likewise.
2012-11-30 00:35:23 +00:00
|
|
|
/* XXX Update to use QMP, if QMP ever adds support for loadvm */
|
|
|
|
VIR_DEBUG("loadvm command not found, trying HMP");
|
|
|
|
return qemuMonitorTextLoadSnapshot(mon, name);
|
2010-04-02 14:10:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name)
|
|
|
|
{
|
qemu: don't attempt undefined QMP commands
https://bugzilla.redhat.com/show_bug.cgi?id=872292
Libvirt should not attempt to call a QMP command that has not been
documented in qemu.git - if future qemu introduces a command by the
same name but with subtly different semantics, then libvirt will be
broken when trying to use that command.
We also had some code that could never be reached - some of our
commands have an alternate for new vs. old qemu HMP commands; but
if we are new enough to support QMP, we only need a fallback to
the new HMP counterpart, and don't need to try for a QMP counterpart
for the old HMP version.
See also this attempt to convert the three snapshot commands to QMP:
https://lists.gnu.org/archive/html/qemu-devel/2012-07/msg01597.html
although it looks like that will still not happen before qemu 1.3.
That thread eventually decided that qemu would use the name
'save-vm' rather than 'savevm', which mitigates the fact that
libvirt's attempt to use a QMP 'savevm' would be broken, but we
might not be as lucky on the other commands.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONSetCPU)
(qemuMonitorJSONAddDrive, qemuMonitorJSONDriveDel)
(qemuMonitorJSONCreateSnapshot, qemuMonitorJSONLoadSnapshot)
(qemuMonitorJSONDeleteSnapshot): Use only HMP fallback for now.
(qemuMonitorJSONAddHostNetwork, qemuMonitorJSONRemoveHostNetwork)
(qemuMonitorJSONAttachDrive, qemuMonitorJSONGetGuestDriveAddress):
Delete; QMP implies QEMU_CAPS_DEVICE, which prefers AddNetdev,
RemoveNetdev, and AddDrive anyways (qemu_hotplug.c has all callers).
* src/qemu/qemu_monitor.c (qemuMonitorAddHostNetwork)
(qemuMonitorRemoveHostNetwork, qemuMonitorAttachDrive): Reflect
deleted commands.
* src/qemu/qemu_monitor_json.h (qemuMonitorJSONAddHostNetwork)
(qemuMonitorJSONRemoveHostNetwork, qemuMonitorJSONAttachDrive):
Likewise.
2012-11-30 00:35:23 +00:00
|
|
|
/* XXX Update to use QMP, if QMP ever adds support for delvm */
|
|
|
|
VIR_DEBUG("delvm command not found, trying HMP");
|
|
|
|
return qemuMonitorTextDeleteSnapshot(mon, name);
|
2010-04-02 14:10:37 +00:00
|
|
|
}
|
2010-04-17 02:12:45 +00:00
|
|
|
|
2011-08-15 23:25:54 +00:00
|
|
|
int
|
2012-03-17 04:17:28 +00:00
|
|
|
qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions,
|
snapshot: improve qemu handling of reused snapshot targets
The oVirt developers have stated that the real reasons they want
to have qemu reuse existing volumes when creating a snapshot are:
1. the management framework is set up so that creation has to be
done from a central node for proper resource tracking, and having
libvirt and/or qemu create things violates the framework, and
2. qemu defaults to creating snapshots with an absolute path to
the backing file, but oVirt wants to manage a backing chain that
uses just relative names, to allow for easier migration of a chain
across storage locations.
When 0.9.10 added VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT (commit
4e9953a4), it only addressed point 1, but libvirt was still using
O_TRUNC which violates point 2. Meanwhile, the new qemu
'transaction' monitor command includes a new optional mode argument
that will force qemu to reuse the metadata of the file it just
opened (with the burden on the caller to have valid metadata there
in the first place). So, this tweaks the meaning of the flag to
cover both points as intended for use by oVirt. It is not strictly
backward-compatible to 0.9.10 behavior, but it can be argued that
the O_TRUNC of 0.9.10 was a bug.
Note that this flag is all-or-nothing, and only selects between
'existing' and the default 'absolute-paths'. A more flexible
approach that would allow per-disk selections, as well as adding
support for the 'no-backing-file' mode, would be possible by
extending the <domainsnapshot> xml to have a per-disk mode, but
until we have a management application expressing a need for that
additional complexity, it is not worth doing.
* src/libvirt.c (virDomainSnapshotCreateXML): Tweak documentation.
* src/qemu/qemu_monitor.h (qemuMonitorDiskSnapshot): Add
parameters.
* src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskSnapshot):
Likewise.
* src/qemu/qemu_monitor.c (qemuMonitorDiskSnapshot): Pass them
through.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskSnapshot): Use
new monitor command arguments.
* src/qemu/qemu_driver.c (qemuDomainSnapshotCreateDiskActive)
(qemuDomainSnapshotCreateSingleDiskActive): Adjust callers.
(qemuDomainSnapshotDiskPrepare): Allow qed, modify rules on reuse.
2012-03-20 21:03:45 +00:00
|
|
|
const char *device, const char *file,
|
|
|
|
const char *format, bool reuse)
|
2011-08-15 23:25:54 +00:00
|
|
|
{
|
2012-04-11 21:40:16 +00:00
|
|
|
int ret = -1;
|
2011-08-15 23:25:54 +00:00
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
2012-03-17 04:17:28 +00:00
|
|
|
cmd = qemuMonitorJSONMakeCommandRaw(actions != NULL,
|
|
|
|
"blockdev-snapshot-sync",
|
|
|
|
"s:device", device,
|
|
|
|
"s:snapshot-file", file,
|
snapshot: improve qemu handling of reused snapshot targets
The oVirt developers have stated that the real reasons they want
to have qemu reuse existing volumes when creating a snapshot are:
1. the management framework is set up so that creation has to be
done from a central node for proper resource tracking, and having
libvirt and/or qemu create things violates the framework, and
2. qemu defaults to creating snapshots with an absolute path to
the backing file, but oVirt wants to manage a backing chain that
uses just relative names, to allow for easier migration of a chain
across storage locations.
When 0.9.10 added VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT (commit
4e9953a4), it only addressed point 1, but libvirt was still using
O_TRUNC which violates point 2. Meanwhile, the new qemu
'transaction' monitor command includes a new optional mode argument
that will force qemu to reuse the metadata of the file it just
opened (with the burden on the caller to have valid metadata there
in the first place). So, this tweaks the meaning of the flag to
cover both points as intended for use by oVirt. It is not strictly
backward-compatible to 0.9.10 behavior, but it can be argued that
the O_TRUNC of 0.9.10 was a bug.
Note that this flag is all-or-nothing, and only selects between
'existing' and the default 'absolute-paths'. A more flexible
approach that would allow per-disk selections, as well as adding
support for the 'no-backing-file' mode, would be possible by
extending the <domainsnapshot> xml to have a per-disk mode, but
until we have a management application expressing a need for that
additional complexity, it is not worth doing.
* src/libvirt.c (virDomainSnapshotCreateXML): Tweak documentation.
* src/qemu/qemu_monitor.h (qemuMonitorDiskSnapshot): Add
parameters.
* src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskSnapshot):
Likewise.
* src/qemu/qemu_monitor.c (qemuMonitorDiskSnapshot): Pass them
through.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskSnapshot): Use
new monitor command arguments.
* src/qemu/qemu_driver.c (qemuDomainSnapshotCreateDiskActive)
(qemuDomainSnapshotCreateSingleDiskActive): Adjust callers.
(qemuDomainSnapshotDiskPrepare): Allow qed, modify rules on reuse.
2012-03-20 21:03:45 +00:00
|
|
|
"s:format", format,
|
2014-05-13 15:28:45 +00:00
|
|
|
"S:mode", reuse ? "existing" : NULL,
|
2012-03-17 04:17:28 +00:00
|
|
|
NULL);
|
2011-08-15 23:25:54 +00:00
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
2012-03-17 04:17:28 +00:00
|
|
|
if (actions) {
|
2013-07-04 10:14:12 +00:00
|
|
|
if (virJSONValueArrayAppend(actions, cmd) == 0) {
|
2012-03-17 04:17:28 +00:00
|
|
|
ret = 0;
|
|
|
|
cmd = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
2012-04-11 21:40:16 +00:00
|
|
|
goto cleanup;
|
2011-08-15 23:25:54 +00:00
|
|
|
|
2012-03-17 04:17:28 +00:00
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
2011-08-15 23:25:54 +00:00
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2012-03-17 04:17:28 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
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(qemuMonitorPtr mon,
|
|
|
|
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,
|
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
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
bool shallow = (flags & VIR_DOMAIN_BLOCK_REBASE_SHALLOW) != 0;
|
|
|
|
bool reuse = (flags & VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT) != 0;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("drive-mirror",
|
|
|
|
"s:device", device,
|
|
|
|
"s:target", file,
|
2014-09-05 11:31:47 +00:00
|
|
|
"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",
|
2014-05-13 15:28:45 +00:00
|
|
|
"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 ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-03-17 04:17:28 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions)
|
|
|
|
{
|
2012-04-05 19:15:03 +00:00
|
|
|
int ret = -1;
|
2012-03-17 04:17:28 +00:00
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
2012-04-05 19:15:03 +00:00
|
|
|
bool protect = actions->protect;
|
2012-03-17 04:17:28 +00:00
|
|
|
|
2012-04-05 19:15:03 +00:00
|
|
|
/* We do NOT want to free actions when recursively freeing cmd. */
|
|
|
|
actions->protect = true;
|
2012-03-17 04:17:28 +00:00
|
|
|
cmd = qemuMonitorJSONMakeCommand("transaction",
|
|
|
|
"a:actions", actions,
|
|
|
|
NULL);
|
2012-04-05 19:15:03 +00:00
|
|
|
if (!cmd)
|
|
|
|
goto cleanup;
|
2012-03-17 04:17:28 +00:00
|
|
|
|
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2011-08-15 23:25:54 +00:00
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2011-08-15 23:25:54 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
2012-04-05 19:15:03 +00:00
|
|
|
actions->protect = protect;
|
2011-08-15 23:25:54 +00:00
|
|
|
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.
|
|
|
|
*
|
|
|
|
* Additionally, can be used to probe if active commit is supported:
|
|
|
|
* pass in a bogus device and NULL top and base. The probe return is
|
|
|
|
* -2 if active commit is detected, -3 if inconclusive; with no error
|
|
|
|
* message issued.
|
|
|
|
*/
|
2012-10-03 21:13:21 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device,
|
|
|
|
const char *top, const char *base,
|
2014-05-13 15:41:33 +00:00
|
|
|
const char *backingName,
|
2012-10-03 21:13:21 +00:00
|
|
|
unsigned long long speed)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("block-commit",
|
|
|
|
"s:device", device,
|
2014-09-05 11:31:47 +00:00
|
|
|
"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:base", base,
|
2014-05-13 15:41:33 +00:00
|
|
|
"S:backing-file", backingName,
|
2012-10-03 21:13:21 +00:00
|
|
|
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 ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
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
|
|
|
if (!top && !base) {
|
|
|
|
/* Normally we always specify top and base; but omitting them
|
|
|
|
* allows for probing whether qemu is new enough to support
|
|
|
|
* live commit. */
|
|
|
|
if (qemuMonitorJSONHasError(reply, "DeviceNotFound")) {
|
|
|
|
VIR_DEBUG("block-commit supports active commit");
|
|
|
|
ret = -2;
|
|
|
|
} else {
|
|
|
|
/* This is a false negative for qemu 2.0; but probably not
|
|
|
|
* worth the additional complexity to worry about it */
|
|
|
|
VIR_DEBUG("block-commit requires 'top' parameter, "
|
|
|
|
"assuming it lacks active commit");
|
|
|
|
ret = -3;
|
|
|
|
}
|
|
|
|
goto 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
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
qemuMonitorJSONDrivePivot(qemuMonitorPtr mon, const char *device,
|
|
|
|
const char *file ATTRIBUTE_UNUSED,
|
|
|
|
const char *format ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("block-job-complete",
|
|
|
|
"s:device", device,
|
|
|
|
NULL);
|
2012-10-03 21:13:21 +00:00
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2012-10-03 21:13:21 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-17 02:12:45 +00:00
|
|
|
int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
|
|
|
|
const char *cmd_str,
|
2011-02-02 15:37:10 +00:00
|
|
|
char **reply_str,
|
|
|
|
bool hmp)
|
2010-04-17 02:12:45 +00:00
|
|
|
{
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
2011-03-09 20:24:04 +00:00
|
|
|
if (hmp) {
|
|
|
|
return qemuMonitorJSONHumanCommandWithFd(mon, cmd_str, -1, reply_str);
|
2011-02-02 15:37:10 +00:00
|
|
|
} else {
|
2011-03-09 20:24:04 +00:00
|
|
|
if (!(cmd = virJSONValueFromString(cmd_str)))
|
|
|
|
goto cleanup;
|
2010-04-17 02:12:45 +00:00
|
|
|
|
2011-03-09 20:24:04 +00:00
|
|
|
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
|
|
|
|
goto cleanup;
|
2010-04-17 02:12:45 +00:00
|
|
|
|
2012-08-09 10:46:29 +00:00
|
|
|
if (!(*reply_str = virJSONValueToString(reply, false)))
|
2011-02-02 15:37:10 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2010-04-17 02:12:45 +00:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2010-04-17 02:12:45 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2011-05-10 08:26:06 +00:00
|
|
|
|
|
|
|
int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("inject-nmi", NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-09-06 15:28:53 +00:00
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_DEBUG("inject-nmi command not found, trying HMP");
|
2011-05-10 08:26:06 +00:00
|
|
|
ret = qemuMonitorTextInjectNMI(mon);
|
|
|
|
} else {
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2011-05-10 08:26:06 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2011-04-01 06:23:58 +00:00
|
|
|
|
2011-07-21 07:55:56 +00:00
|
|
|
int qemuMonitorJSONSendKey(qemuMonitorPtr mon,
|
|
|
|
unsigned int holdtime,
|
|
|
|
unsigned int *keycodes,
|
|
|
|
unsigned int nkeycodes)
|
|
|
|
{
|
2013-04-11 12:33:43 +00:00
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr keys = NULL;
|
|
|
|
virJSONValuePtr key = NULL;
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2013-04-11 12:33:43 +00:00
|
|
|
|
|
|
|
/* create the key data array */
|
|
|
|
if (!(keys = virJSONValueNewArray()))
|
2013-07-04 10:14:12 +00:00
|
|
|
goto cleanup;
|
2013-04-11 12:33:43 +00:00
|
|
|
|
|
|
|
for (i = 0; i < nkeycodes; i++) {
|
|
|
|
if (keycodes[i] > 0xffff) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
_("keycode %zu is invalid: 0x%X"), i, keycodes[i]);
|
2013-04-11 12:33:43 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* create single key object */
|
|
|
|
if (!(key = virJSONValueNewObject()))
|
2013-07-04 10:14:12 +00:00
|
|
|
goto cleanup;
|
2013-04-11 12:33:43 +00:00
|
|
|
|
|
|
|
/* Union KeyValue has two types, use the generic one */
|
|
|
|
if (virJSONValueObjectAppendString(key, "type", "number") < 0)
|
2013-07-04 10:14:12 +00:00
|
|
|
goto cleanup;
|
2013-04-11 12:33:43 +00:00
|
|
|
|
|
|
|
/* with the keycode */
|
|
|
|
if (virJSONValueObjectAppendNumberInt(key, "data", keycodes[i]) < 0)
|
2013-07-04 10:14:12 +00:00
|
|
|
goto cleanup;
|
2013-04-11 12:33:43 +00:00
|
|
|
|
|
|
|
if (virJSONValueArrayAppend(keys, key) < 0)
|
2013-07-04 10:14:12 +00:00
|
|
|
goto cleanup;
|
2013-04-11 12:33:43 +00:00
|
|
|
|
|
|
|
key = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("send-key",
|
|
|
|
"a:keys", keys,
|
2014-06-03 09:19:51 +00:00
|
|
|
"p:hold-time", holdtime,
|
2014-05-13 15:28:45 +00:00
|
|
|
NULL);
|
2013-04-11 12:33:43 +00:00
|
|
|
if (!cmd)
|
|
|
|
goto cleanup;
|
|
|
|
|
2013-10-02 16:18:13 +00:00
|
|
|
/* @keys is part of @cmd now. Avoid double free */
|
|
|
|
keys = NULL;
|
|
|
|
|
2013-04-11 12:33:43 +00:00
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
|
|
|
|
VIR_DEBUG("send-key command not found, trying HMP");
|
|
|
|
ret = qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes);
|
|
|
|
} else {
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
}
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-04-11 12:33:43 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
virJSONValueFree(keys);
|
|
|
|
virJSONValueFree(key);
|
|
|
|
return ret;
|
2011-07-21 07:55:56 +00:00
|
|
|
}
|
|
|
|
|
2011-04-01 06:23:58 +00:00
|
|
|
int qemuMonitorJSONScreendump(qemuMonitorPtr mon,
|
|
|
|
const char *file)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd, reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("screendump",
|
|
|
|
"s:filename", file,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2011-07-22 05:39:37 +00:00
|
|
|
|
2014-08-27 20:04:36 +00:00
|
|
|
/* Returns -1 on error, 0 if not the right device, 1 if info was
|
|
|
|
* populated. However, rather than populate info->bandwidth (which
|
|
|
|
* might overflow on 32-bit machines), bandwidth is tracked optionally
|
|
|
|
* on the side. */
|
2014-08-27 19:29:14 +00:00
|
|
|
static int
|
|
|
|
qemuMonitorJSONGetBlockJobInfoOne(virJSONValuePtr entry,
|
|
|
|
const char *device,
|
2014-08-27 20:04:36 +00:00
|
|
|
virDomainBlockJobInfoPtr info,
|
|
|
|
unsigned long long *bandwidth)
|
2011-07-22 05:39:37 +00:00
|
|
|
{
|
|
|
|
const char *this_dev;
|
|
|
|
const char *type;
|
|
|
|
|
|
|
|
if ((this_dev = virJSONValueObjectGetString(entry, "device")) == NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("entry was missing 'device'"));
|
2011-07-22 05:39:37 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!STREQ(this_dev, device))
|
2014-08-27 19:29:14 +00:00
|
|
|
return 0;
|
2011-07-22 05:39:37 +00:00
|
|
|
|
|
|
|
type = virJSONValueObjectGetString(entry, "type");
|
|
|
|
if (!type) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("entry was missing 'type'"));
|
2011-07-22 05:39:37 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (STREQ(type, "stream"))
|
|
|
|
info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL;
|
2012-10-03 21:13:21 +00:00
|
|
|
else if (STREQ(type, "commit"))
|
|
|
|
info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT;
|
2012-10-12 20:06:10 +00:00
|
|
|
else if (STREQ(type, "mirror"))
|
|
|
|
info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY;
|
2011-07-22 05:39:37 +00:00
|
|
|
else
|
|
|
|
info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
|
|
|
|
|
2014-08-27 20:04:36 +00:00
|
|
|
if (bandwidth &&
|
|
|
|
virJSONValueObjectGetNumberUlong(entry, "speed", bandwidth) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("entry was missing 'speed'"));
|
2011-07-22 05:39:37 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUlong(entry, "offset", &info->cur) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("entry was missing 'offset'"));
|
2011-07-22 05:39:37 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUlong(entry, "len", &info->end) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("entry was missing 'len'"));
|
2011-07-22 05:39:37 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2014-08-27 19:29:14 +00:00
|
|
|
return 1;
|
2011-07-22 05:39:37 +00:00
|
|
|
}
|
|
|
|
|
2014-08-27 19:29:14 +00:00
|
|
|
/**
|
|
|
|
* qemuMonitorJSONBlockJobInfo:
|
|
|
|
* Parse Block Job information, and populate info for the named device.
|
|
|
|
* Return 1 if info available, 0 if device has no block job, and -1 on error.
|
2011-07-22 05:39:37 +00:00
|
|
|
*/
|
2014-08-27 19:29:14 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONBlockJobInfo(qemuMonitorPtr mon,
|
|
|
|
const char *device,
|
2014-08-27 20:04:36 +00:00
|
|
|
virDomainBlockJobInfoPtr info,
|
|
|
|
unsigned long long *bandwidth)
|
2011-07-22 05:39:37 +00:00
|
|
|
{
|
2014-08-27 19:29:14 +00:00
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
2011-07-22 05:39:37 +00:00
|
|
|
virJSONValuePtr data;
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
int nr_results;
|
|
|
|
size_t i;
|
2014-08-27 19:29:14 +00:00
|
|
|
int ret = -1;
|
2011-07-22 05:39:37 +00:00
|
|
|
|
2014-08-27 19:29:14 +00:00
|
|
|
cmd = qemuMonitorJSONMakeCommand("query-block-jobs", NULL);
|
|
|
|
if (!cmd)
|
2011-07-22 05:39:37 +00:00
|
|
|
return -1;
|
2014-08-27 19:29:14 +00:00
|
|
|
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
|
|
|
|
goto cleanup;
|
2011-07-22 05:39:37 +00:00
|
|
|
|
|
|
|
if ((data = virJSONValueObjectGet(reply, "return")) == NULL) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("reply was missing return data"));
|
2014-08-27 19:29:14 +00:00
|
|
|
goto cleanup;
|
2011-07-22 05:39:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (data->type != VIR_JSON_TYPE_ARRAY) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("unrecognized format of block job information"));
|
2014-08-27 19:29:14 +00:00
|
|
|
goto cleanup;
|
2011-07-22 05:39:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((nr_results = virJSONValueArraySize(data)) < 0) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("unable to determine array size"));
|
2014-08-27 19:29:14 +00:00
|
|
|
goto cleanup;
|
2011-07-22 05:39:37 +00:00
|
|
|
}
|
|
|
|
|
2014-08-27 19:29:14 +00:00
|
|
|
for (i = ret = 0; i < nr_results && ret == 0; i++) {
|
2011-07-22 05:39:37 +00:00
|
|
|
virJSONValuePtr entry = virJSONValueArrayGet(data, i);
|
2011-08-02 19:17:04 +00:00
|
|
|
if (!entry) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing array element"));
|
2014-08-27 19:29:14 +00:00
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
2011-08-02 19:17:04 +00:00
|
|
|
}
|
2014-08-27 20:04:36 +00:00
|
|
|
ret = qemuMonitorJSONGetBlockJobInfoOne(entry, device, info,
|
|
|
|
bandwidth);
|
2011-07-22 05:39:37 +00:00
|
|
|
}
|
|
|
|
|
2014-08-27 19:29:14 +00:00
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
2011-07-22 05:39:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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 */
|
2012-02-18 16:20:01 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONBlockJob(qemuMonitorPtr mon,
|
|
|
|
const char *device,
|
|
|
|
const char *base,
|
2014-05-16 15:51:21 +00:00
|
|
|
const char *backingName,
|
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
|
|
|
unsigned long long speed,
|
|
|
|
qemuMonitorBlockJobCmd mode,
|
|
|
|
bool modern)
|
2011-07-22 05:39:37 +00:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
2011-08-24 06:39:42 +00:00
|
|
|
const char *cmd_name = NULL;
|
|
|
|
|
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
|
|
|
if (base && (mode != BLOCK_JOB_PULL || !modern)) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("only modern block pull supports base: %s"), base);
|
2012-02-18 16:20:01 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2014-05-16 15:51:21 +00:00
|
|
|
|
|
|
|
if (backingName && mode != BLOCK_JOB_PULL) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("backing name is supported only for block pull"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (backingName && !base) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("backing name requires a base image"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
if (speed && mode == BLOCK_JOB_PULL && !modern) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("only modern block pull supports speed: %llu"),
|
|
|
|
speed);
|
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
|
|
|
return -1;
|
|
|
|
}
|
2012-02-18 16:20:01 +00:00
|
|
|
|
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
|
|
|
switch (mode) {
|
2012-04-11 21:40:16 +00:00
|
|
|
case BLOCK_JOB_ABORT:
|
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
|
|
|
cmd_name = modern ? "block-job-cancel" : "block_job_cancel";
|
2014-05-13 15:28:45 +00:00
|
|
|
cmd = qemuMonitorJSONMakeCommand(cmd_name,
|
|
|
|
"s:device", device,
|
|
|
|
NULL);
|
2012-04-11 21:40:16 +00:00
|
|
|
break;
|
2014-05-13 15:28:45 +00:00
|
|
|
|
2012-04-11 21:40:16 +00:00
|
|
|
case BLOCK_JOB_SPEED:
|
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
|
|
|
cmd_name = modern ? "block-job-set-speed" : "block_job_set_speed";
|
2014-05-13 15:28:45 +00:00
|
|
|
cmd = qemuMonitorJSONMakeCommand(cmd_name,
|
|
|
|
"s:device", device,
|
2014-09-05 11:31:47 +00:00
|
|
|
modern ? "J:speed" : "J:value", speed,
|
2014-05-13 15:28:45 +00:00
|
|
|
NULL);
|
2012-04-11 21:40:16 +00:00
|
|
|
break;
|
2014-05-13 15:28:45 +00:00
|
|
|
|
2012-04-11 21:40:16 +00:00
|
|
|
case BLOCK_JOB_PULL:
|
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
|
|
|
cmd_name = modern ? "block-stream" : "block_stream";
|
2014-05-13 15:28:45 +00:00
|
|
|
cmd = qemuMonitorJSONMakeCommand(cmd_name,
|
|
|
|
"s:device", device,
|
2014-09-05 11:31:47 +00:00
|
|
|
"Y:speed", speed,
|
2014-05-13 15:28:45 +00:00
|
|
|
"S:base", base,
|
2014-05-16 15:51:21 +00:00
|
|
|
"S:backing-file", backingName,
|
2014-05-13 15:28:45 +00:00
|
|
|
NULL);
|
2012-04-11 21:40:16 +00:00
|
|
|
break;
|
2011-08-24 06:39:42 +00:00
|
|
|
}
|
2011-07-22 05:39:37 +00:00
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0 && virJSONValueObjectHasKey(reply, "error")) {
|
blockjob: allow for fast-finishing job
In my testing, I was able to provoke an odd block pull failure:
$ virsh blockpull dom vda --bandwidth 10000
error: Requested operation is not valid: No active operation on device: drive-virtio-disk0
merely by using gdb to artifically wait to do the block job set speed
until after the pull had already finished. But in reality, that should
be a success, since the pull finished before we had a chance to set
speed. Furthermore, using a double job lock is not only annoying, but
a bug in itself - if you do parallel virDomainBlockRebase, and hit
the race window just right, the first call grabs the VM job to start
a fast block job, then the second call grabs the VM job to start
a long-running job with unspecified speed, then the first call finally
regrabs the VM job and sets the speed, which ends up running the
second job under the speed from the first call. By consolidating
things into a single job, we avoid opening that race, as well as reduce
the time between starting the job and changing the speed, for less
likelihood of the speed change happening after block job completion
in the first place.
* src/qemu/qemu_monitor.h (BLOCK_JOB_CMD): Add new mode.
* src/qemu/qemu_driver.c (qemuDomainBlockRebase): Move secondary
job call...
(qemuDomainBlockJobImpl): ...here, for fewer locks.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONBlockJob): Change
return value on new internal mode.
2012-04-11 22:28:35 +00:00
|
|
|
ret = -1;
|
|
|
|
if (qemuMonitorJSONHasError(reply, "DeviceNotActive")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("No active operation on device: %s"),
|
|
|
|
device);
|
2014-08-20 11:00:30 +00:00
|
|
|
} else if (qemuMonitorJSONHasError(reply, "DeviceInUse")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("Device %s in use"), device);
|
blockjob: allow for fast-finishing job
In my testing, I was able to provoke an odd block pull failure:
$ virsh blockpull dom vda --bandwidth 10000
error: Requested operation is not valid: No active operation on device: drive-virtio-disk0
merely by using gdb to artifically wait to do the block job set speed
until after the pull had already finished. But in reality, that should
be a success, since the pull finished before we had a chance to set
speed. Furthermore, using a double job lock is not only annoying, but
a bug in itself - if you do parallel virDomainBlockRebase, and hit
the race window just right, the first call grabs the VM job to start
a fast block job, then the second call grabs the VM job to start
a long-running job with unspecified speed, then the first call finally
regrabs the VM job and sets the speed, which ends up running the
second job under the speed from the first call. By consolidating
things into a single job, we avoid opening that race, as well as reduce
the time between starting the job and changing the speed, for less
likelihood of the speed change happening after block job completion
in the first place.
* src/qemu/qemu_monitor.h (BLOCK_JOB_CMD): Add new mode.
* src/qemu/qemu_driver.c (qemuDomainBlockRebase): Move secondary
job call...
(qemuDomainBlockJobImpl): ...here, for fewer locks.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONBlockJob): Change
return value on new internal mode.
2012-04-11 22:28:35 +00:00
|
|
|
} else if (qemuMonitorJSONHasError(reply, "NotSupported")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("Operation is not supported for device: %s"),
|
|
|
|
device);
|
blockjob: allow for fast-finishing job
In my testing, I was able to provoke an odd block pull failure:
$ virsh blockpull dom vda --bandwidth 10000
error: Requested operation is not valid: No active operation on device: drive-virtio-disk0
merely by using gdb to artifically wait to do the block job set speed
until after the pull had already finished. But in reality, that should
be a success, since the pull finished before we had a chance to set
speed. Furthermore, using a double job lock is not only annoying, but
a bug in itself - if you do parallel virDomainBlockRebase, and hit
the race window just right, the first call grabs the VM job to start
a fast block job, then the second call grabs the VM job to start
a long-running job with unspecified speed, then the first call finally
regrabs the VM job and sets the speed, which ends up running the
second job under the speed from the first call. By consolidating
things into a single job, we avoid opening that race, as well as reduce
the time between starting the job and changing the speed, for less
likelihood of the speed change happening after block job completion
in the first place.
* src/qemu/qemu_monitor.h (BLOCK_JOB_CMD): Add new mode.
* src/qemu/qemu_driver.c (qemuDomainBlockRebase): Move secondary
job call...
(qemuDomainBlockJobImpl): ...here, for fewer locks.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONBlockJob): Change
return value on new internal mode.
2012-04-11 22:28:35 +00:00
|
|
|
} else if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("Command '%s' is not found"), cmd_name);
|
blockjob: allow for fast-finishing job
In my testing, I was able to provoke an odd block pull failure:
$ virsh blockpull dom vda --bandwidth 10000
error: Requested operation is not valid: No active operation on device: drive-virtio-disk0
merely by using gdb to artifically wait to do the block job set speed
until after the pull had already finished. But in reality, that should
be a success, since the pull finished before we had a chance to set
speed. Furthermore, using a double job lock is not only annoying, but
a bug in itself - if you do parallel virDomainBlockRebase, and hit
the race window just right, the first call grabs the VM job to start
a fast block job, then the second call grabs the VM job to start
a long-running job with unspecified speed, then the first call finally
regrabs the VM job and sets the speed, which ends up running the
second job under the speed from the first call. By consolidating
things into a single job, we avoid opening that race, as well as reduce
the time between starting the job and changing the speed, for less
likelihood of the speed change happening after block job completion
in the first place.
* src/qemu/qemu_monitor.h (BLOCK_JOB_CMD): Add new mode.
* src/qemu/qemu_driver.c (qemuDomainBlockRebase): Move secondary
job call...
(qemuDomainBlockJobImpl): ...here, for fewer locks.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONBlockJob): Change
return value on new internal mode.
2012-04-11 22:28:35 +00:00
|
|
|
} else {
|
2014-03-05 14:08:56 +00:00
|
|
|
virJSONValuePtr error = virJSONValueObjectGet(reply, "error");
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unexpected error: (%s) '%s'"),
|
|
|
|
NULLSTR(virJSONValueObjectGetString(error, "class")),
|
|
|
|
NULLSTR(virJSONValueObjectGetString(error, "desc")));
|
blockjob: allow for fast-finishing job
In my testing, I was able to provoke an odd block pull failure:
$ virsh blockpull dom vda --bandwidth 10000
error: Requested operation is not valid: No active operation on device: drive-virtio-disk0
merely by using gdb to artifically wait to do the block job set speed
until after the pull had already finished. But in reality, that should
be a success, since the pull finished before we had a chance to set
speed. Furthermore, using a double job lock is not only annoying, but
a bug in itself - if you do parallel virDomainBlockRebase, and hit
the race window just right, the first call grabs the VM job to start
a fast block job, then the second call grabs the VM job to start
a long-running job with unspecified speed, then the first call finally
regrabs the VM job and sets the speed, which ends up running the
second job under the speed from the first call. By consolidating
things into a single job, we avoid opening that race, as well as reduce
the time between starting the job and changing the speed, for less
likelihood of the speed change happening after block job completion
in the first place.
* src/qemu/qemu_monitor.h (BLOCK_JOB_CMD): Add new mode.
* src/qemu/qemu_driver.c (qemuDomainBlockRebase): Move secondary
job call...
(qemuDomainBlockJobImpl): ...here, for fewer locks.
* src/qemu/qemu_monitor_json.c (qemuMonitorJSONBlockJob): Change
return value on new internal mode.
2012-04-11 22:28:35 +00:00
|
|
|
}
|
2011-07-22 05:39:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2011-10-21 08:00:13 +00:00
|
|
|
|
|
|
|
int qemuMonitorJSONOpenGraphics(qemuMonitorPtr mon,
|
|
|
|
const char *protocol,
|
|
|
|
const char *fdname,
|
|
|
|
bool skipauth)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd, reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("add_client",
|
|
|
|
"s:protocol", protocol,
|
|
|
|
"s:fdname", fdname,
|
|
|
|
"b:skipauth", skipauth,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2011-11-15 09:02:45 +00:00
|
|
|
|
|
|
|
|
2014-10-29 12:16:03 +00:00
|
|
|
#define GET_THROTTLE_STATS_OPTIONAL(FIELD, STORE) \
|
|
|
|
if (virJSONValueObjectGetNumberUlong(inserted, \
|
|
|
|
FIELD, \
|
|
|
|
&reply->STORE) < 0) { \
|
|
|
|
reply->STORE = 0; \
|
|
|
|
}
|
2012-08-09 07:58:05 +00:00
|
|
|
#define GET_THROTTLE_STATS(FIELD, STORE) \
|
|
|
|
if (virJSONValueObjectGetNumberUlong(inserted, \
|
|
|
|
FIELD, \
|
|
|
|
&reply->STORE) < 0) { \
|
2012-08-09 13:12:12 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, \
|
2012-08-09 07:58:05 +00:00
|
|
|
_("block_io_throttle field '%s' missing " \
|
|
|
|
"in qemu's output"), \
|
|
|
|
#STORE); \
|
2012-08-09 13:12:12 +00:00
|
|
|
goto cleanup; \
|
2012-08-09 07:58:05 +00:00
|
|
|
}
|
2011-11-15 09:02:45 +00:00
|
|
|
static int
|
|
|
|
qemuMonitorJSONBlockIoThrottleInfo(virJSONValuePtr result,
|
|
|
|
const char *device,
|
2014-11-10 15:19:09 +00:00
|
|
|
virDomainBlockIoTuneInfoPtr reply,
|
2014-10-29 12:16:03 +00:00
|
|
|
bool supportMaxOptions)
|
2011-11-15 09:02:45 +00:00
|
|
|
{
|
|
|
|
virJSONValuePtr io_throttle;
|
|
|
|
int ret = -1;
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2012-08-09 07:58:05 +00:00
|
|
|
bool found = false;
|
2011-11-15 09:02:45 +00:00
|
|
|
|
|
|
|
io_throttle = virJSONValueObjectGet(result, "return");
|
|
|
|
|
|
|
|
if (!io_throttle || io_throttle->type != VIR_JSON_TYPE_ARRAY) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_(" block_io_throttle reply was missing device list"));
|
2011-11-15 09:02:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < virJSONValueArraySize(io_throttle); i++) {
|
|
|
|
virJSONValuePtr temp_dev = virJSONValueArrayGet(io_throttle, i);
|
|
|
|
virJSONValuePtr inserted;
|
|
|
|
const char *current_dev;
|
|
|
|
|
|
|
|
if (!temp_dev || temp_dev->type != VIR_JSON_TYPE_OBJECT) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2012-08-09 07:58:05 +00:00
|
|
|
_("block_io_throttle device entry "
|
|
|
|
"was not in expected format"));
|
2011-11-15 09:02:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-08-09 07:58:05 +00:00
|
|
|
if (!(current_dev = virJSONValueObjectGetString(temp_dev, "device"))) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2012-08-09 07:58:05 +00:00
|
|
|
_("block_io_throttle device entry "
|
|
|
|
"was not in expected format"));
|
2011-11-15 09:02:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-11-01 17:20:56 +00:00
|
|
|
if (STRNEQ(current_dev, device))
|
2011-11-15 09:02:45 +00:00
|
|
|
continue;
|
|
|
|
|
2012-08-09 07:58:05 +00:00
|
|
|
found = true;
|
2011-11-15 09:02:45 +00:00
|
|
|
if ((inserted = virJSONValueObjectGet(temp_dev, "inserted")) == NULL ||
|
|
|
|
inserted->type != VIR_JSON_TYPE_OBJECT) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2012-08-09 07:58:05 +00:00
|
|
|
_("block_io_throttle inserted entry "
|
|
|
|
"was not in expected format"));
|
2011-11-15 09:02:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-08-09 07:58:05 +00:00
|
|
|
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);
|
2014-10-29 12:16:03 +00:00
|
|
|
if (supportMaxOptions) {
|
|
|
|
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);
|
|
|
|
}
|
2011-11-15 09:02:45 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot find throttling info for device '%s'"),
|
|
|
|
device);
|
2011-11-15 09:02:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2011-11-15 09:02:45 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2012-08-09 07:58:05 +00:00
|
|
|
#undef GET_THROTTLE_STATS
|
2014-10-29 12:16:03 +00:00
|
|
|
#undef GET_THROTTLE_STATS_OPTIONAL
|
2011-11-15 09:02:45 +00:00
|
|
|
|
|
|
|
int qemuMonitorJSONSetBlockIoThrottle(qemuMonitorPtr mon,
|
|
|
|
const char *device,
|
2014-11-10 15:19:09 +00:00
|
|
|
virDomainBlockIoTuneInfoPtr info,
|
2014-10-29 12:16:03 +00:00
|
|
|
bool supportMaxOptions)
|
2011-11-15 09:02:45 +00:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr result = NULL;
|
|
|
|
|
2014-10-29 12:16:03 +00:00
|
|
|
/* The qemu capability check has already been made in
|
|
|
|
* qemuDomainSetBlockIoTune */
|
|
|
|
if (supportMaxOptions) {
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("block_set_io_throttle",
|
|
|
|
"s:device", device,
|
|
|
|
"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,
|
|
|
|
NULL);
|
|
|
|
} else {
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("block_set_io_throttle",
|
|
|
|
"s:device", device,
|
|
|
|
"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,
|
|
|
|
NULL);
|
|
|
|
}
|
2011-11-15 09:02:45 +00:00
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &result);
|
|
|
|
|
|
|
|
if (ret == 0 && virJSONValueObjectHasKey(result, "error")) {
|
|
|
|
if (qemuMonitorJSONHasError(result, "DeviceNotActive"))
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("No active operation on device: %s"), device);
|
2011-11-15 09:02:45 +00:00
|
|
|
else if (qemuMonitorJSONHasError(result, "NotSupported"))
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("Operation is not supported for device: %s"), device);
|
2011-11-15 09:02:45 +00:00
|
|
|
else
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unexpected error"));
|
2011-11-15 09:02:45 +00:00
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(result);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorJSONGetBlockIoThrottle(qemuMonitorPtr mon,
|
|
|
|
const char *device,
|
2014-11-10 15:19:09 +00:00
|
|
|
virDomainBlockIoTuneInfoPtr reply,
|
|
|
|
bool supportMaxOptions)
|
2011-11-15 09:02:45 +00:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr result = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("query-block", NULL);
|
|
|
|
if (!cmd) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &result);
|
|
|
|
|
|
|
|
if (ret == 0 && virJSONValueObjectHasKey(result, "error")) {
|
|
|
|
if (qemuMonitorJSONHasError(result, "DeviceNotActive"))
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("No active operation on device: %s"), device);
|
2011-11-15 09:02:45 +00:00
|
|
|
else if (qemuMonitorJSONHasError(result, "NotSupported"))
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("Operation is not supported for device: %s"), device);
|
2011-11-15 09:02:45 +00:00
|
|
|
else
|
2012-07-18 15:22:03 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unexpected error"));
|
2011-11-15 09:02:45 +00:00
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0)
|
2014-11-10 15:19:09 +00:00
|
|
|
ret = qemuMonitorJSONBlockIoThrottleInfo(result, device, reply, supportMaxOptions);
|
2011-11-15 09:02:45 +00:00
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(result);
|
|
|
|
return ret;
|
|
|
|
}
|
2012-02-10 12:33:52 +00:00
|
|
|
|
|
|
|
int qemuMonitorJSONSystemWakeup(qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("system_wakeup", NULL);
|
|
|
|
if (!cmd) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2012-08-15 14:04:09 +00:00
|
|
|
|
|
|
|
int qemuMonitorJSONGetVersion(qemuMonitorPtr mon,
|
|
|
|
int *major,
|
|
|
|
int *minor,
|
|
|
|
int *micro,
|
|
|
|
char **package)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
virJSONValuePtr qemu;
|
|
|
|
|
|
|
|
*major = *minor = *micro = 0;
|
|
|
|
if (package)
|
|
|
|
*package = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-version", NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-version reply was missing 'return' data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(qemu = virJSONValueObjectGet(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;
|
|
|
|
}
|
2013-05-20 09:23:13 +00:00
|
|
|
if (VIR_STRDUP(*package, tmp) < 0)
|
2012-08-15 14:04:09 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2012-08-15 14:04:09 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2012-08-15 15:18:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONGetMachines(qemuMonitorPtr mon,
|
|
|
|
qemuMonitorMachineInfoPtr **machines)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
qemuMonitorMachineInfoPtr *infolist = NULL;
|
|
|
|
int n = 0;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
*machines = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-machines", NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-machines reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((n = virJSONValueArraySize(data)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-machines reply data was not an array"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-04-26 17:47:23 +00:00
|
|
|
/* null-terminated list */
|
2013-07-04 10:14:12 +00:00
|
|
|
if (VIR_ALLOC_N(infolist, n + 1) < 0)
|
2012-08-15 15:18:41 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
2012-08-15 15:18:41 +00:00
|
|
|
virJSONValuePtr child = virJSONValueArrayGet(data, i);
|
|
|
|
const char *tmp;
|
|
|
|
qemuMonitorMachineInfoPtr info;
|
|
|
|
|
2013-07-04 10:14:12 +00:00
|
|
|
if (VIR_ALLOC(info) < 0)
|
2012-08-15 15:18:41 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
infolist[i] = info;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(child, "name"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-machines reply data was missing 'name'"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-05-20 09:23:13 +00:00
|
|
|
if (VIR_STRDUP(info->name, tmp) < 0)
|
2012-08-15 15:18:41 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2013-05-20 09:23:13 +00:00
|
|
|
if (VIR_STRDUP(info->alias, tmp) < 0)
|
2012-08-15 15:18:41 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-06-26 15:46:35 +00:00
|
|
|
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;
|
|
|
|
}
|
2012-08-15 15:18:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = n;
|
|
|
|
*machines = infolist;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2012-08-15 15:18:41 +00:00
|
|
|
if (ret < 0 && infolist) {
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < n; i++)
|
2012-08-15 15:18:41 +00:00
|
|
|
qemuMonitorMachineInfoFree(infolist[i]);
|
|
|
|
VIR_FREE(infolist);
|
|
|
|
}
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2012-08-20 14:58:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONGetCPUDefinitions(qemuMonitorPtr mon,
|
|
|
|
char ***cpus)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
char **cpulist = NULL;
|
|
|
|
int n = 0;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
*cpus = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-definitions", NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
2012-09-28 14:55:54 +00:00
|
|
|
if (ret == 0) {
|
|
|
|
/* 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")) {
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
}
|
|
|
|
|
2012-08-20 14:58:20 +00:00
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-cpu-definitions reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((n = virJSONValueArraySize(data)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-cpu-definitions reply data was not an array"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-04-26 17:47:23 +00:00
|
|
|
/* null-terminated list */
|
2013-07-04 10:14:12 +00:00
|
|
|
if (VIR_ALLOC_N(cpulist, n + 1) < 0)
|
2012-08-20 14:58:20 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
2012-08-20 14:58:20 +00:00
|
|
|
virJSONValuePtr child = virJSONValueArrayGet(data, i);
|
|
|
|
const char *tmp;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(child, "name"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-cpu-definitions reply data was missing 'name'"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-05-20 09:23:13 +00:00
|
|
|
if (VIR_STRDUP(cpulist[i], tmp) < 0)
|
2012-08-20 14:58:20 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = n;
|
|
|
|
*cpus = cpulist;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-04-26 17:47:23 +00:00
|
|
|
if (ret < 0)
|
|
|
|
virStringFreeList(cpulist);
|
2012-08-20 14:58:20 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2012-08-22 09:25:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONGetCommands(qemuMonitorPtr mon,
|
|
|
|
char ***commands)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
char **commandlist = NULL;
|
|
|
|
int n = 0;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
*commands = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-commands", NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-commands reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((n = virJSONValueArraySize(data)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-commands reply data was not an array"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-04-26 17:47:23 +00:00
|
|
|
/* null-terminated list */
|
2013-07-04 10:14:12 +00:00
|
|
|
if (VIR_ALLOC_N(commandlist, n + 1) < 0)
|
2012-08-22 09:25:20 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
2012-08-22 09:25:20 +00:00
|
|
|
virJSONValuePtr 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'"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-05-20 09:23:13 +00:00
|
|
|
if (VIR_STRDUP(commandlist[i], tmp) < 0)
|
2012-08-22 09:25:20 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = n;
|
|
|
|
*commands = commandlist;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-04-26 17:47:23 +00:00
|
|
|
if (ret < 0)
|
|
|
|
virStringFreeList(commandlist);
|
2012-08-22 09:25:20 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2012-08-22 09:48:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONGetEvents(qemuMonitorPtr mon,
|
|
|
|
char ***events)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
char **eventlist = NULL;
|
|
|
|
int n = 0;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
*events = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-events", NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
2012-09-27 14:25:16 +00:00
|
|
|
if (ret == 0) {
|
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2012-08-22 09:48:41 +00:00
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
2012-09-27 14:25:16 +00:00
|
|
|
}
|
2012-08-22 09:48:41 +00:00
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-events reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((n = virJSONValueArraySize(data)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-events reply data was not an array"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-04-26 17:47:23 +00:00
|
|
|
/* null-terminated list */
|
2013-07-04 10:14:12 +00:00
|
|
|
if (VIR_ALLOC_N(eventlist, n + 1) < 0)
|
2012-08-22 09:48:41 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
2012-08-22 09:48:41 +00:00
|
|
|
virJSONValuePtr child = virJSONValueArrayGet(data, i);
|
|
|
|
const char *tmp;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(child, "name"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-events reply data was missing 'name'"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-05-20 09:23:13 +00:00
|
|
|
if (VIR_STRDUP(eventlist[i], tmp) < 0)
|
2012-08-22 09:48:41 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = n;
|
|
|
|
*events = eventlist;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-04-26 17:47:23 +00:00
|
|
|
if (ret < 0)
|
|
|
|
virStringFreeList(eventlist);
|
2012-08-22 09:48:41 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2012-08-22 09:48:41 +00:00
|
|
|
|
|
|
|
|
2013-04-26 17:13:45 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONGetCommandLineOptionParameters(qemuMonitorPtr mon,
|
|
|
|
const char *option,
|
2014-06-17 11:41:16 +00:00
|
|
|
char ***params,
|
|
|
|
bool *found)
|
2013-04-26 17:13:45 +00:00
|
|
|
{
|
|
|
|
int ret;
|
2013-05-13 22:48:55 +00:00
|
|
|
virJSONValuePtr cmd = NULL;
|
2013-04-26 17:13:45 +00:00
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data = NULL;
|
|
|
|
virJSONValuePtr array = NULL;
|
|
|
|
char **paramlist = NULL;
|
|
|
|
int n = 0;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
*params = NULL;
|
2014-06-17 11:41:16 +00:00
|
|
|
if (found)
|
|
|
|
*found = false;
|
2013-04-26 17:13:45 +00:00
|
|
|
|
|
|
|
/* query-command-line-options has fixed output for a given qemu
|
|
|
|
* binary; but since callers want to query parameters for one
|
|
|
|
* option at a time, we cache the option list from qemu. */
|
|
|
|
if (!(array = qemuMonitorGetOptions(mon))) {
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-command-line-options",
|
|
|
|
NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
|
|
|
|
goto cleanup;
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virJSONValueObjectRemoveKey(reply, "return", &array) <= 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-command-line-options reply was missing "
|
|
|
|
"return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
qemuMonitorSetOptions(mon, array);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if ((n = virJSONValueArraySize(array)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-command-line-options reply data was not "
|
|
|
|
"an array"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
2013-04-26 17:13:45 +00:00
|
|
|
virJSONValuePtr child = virJSONValueArrayGet(array, i);
|
|
|
|
const char *tmp;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(child, "option"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-command-line-options reply data was "
|
|
|
|
"missing 'option'"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (STREQ(tmp, option)) {
|
|
|
|
data = virJSONValueObjectGet(child, "parameters");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
/* Option not found; return 0 parameters rather than an error. */
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-06-17 11:41:16 +00:00
|
|
|
if (found)
|
|
|
|
*found = true;
|
|
|
|
|
2013-04-26 17:13:45 +00:00
|
|
|
if ((n = virJSONValueArraySize(data)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-command-line-options parameter data was not "
|
|
|
|
"an array"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* null-terminated list */
|
2013-07-04 10:14:12 +00:00
|
|
|
if (VIR_ALLOC_N(paramlist, n + 1) < 0)
|
2013-04-26 17:13:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
2013-04-26 17:13:45 +00:00
|
|
|
virJSONValuePtr child = virJSONValueArrayGet(data, i);
|
|
|
|
const char *tmp;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(child, "name"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-command-line-options parameter data was "
|
|
|
|
"missing 'name'"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_STRDUP(paramlist[i], tmp) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = n;
|
|
|
|
*params = paramlist;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-04-26 17:13:45 +00:00
|
|
|
/* If we failed before getting the JSON array of options, we (try)
|
|
|
|
* to cache an empty array to speed up the next failure. */
|
|
|
|
if (!qemuMonitorGetOptions(mon))
|
|
|
|
qemuMonitorSetOptions(mon, virJSONValueNewArray());
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
virStringFreeList(paramlist);
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Make non-KVM machines work with QMP probing
When there is no 'qemu-kvm' binary and the emulator used for a machine
is, for example, 'qemu-system-x86_64' that, by default, runs without
kvm enabled, libvirt still supplies '-no-kvm' option to this process,
even though it does not recognize such option (making the start of a
domain fail in that case).
This patch fixes building a command-line for QEMU machines without KVM
acceleration and is based on following assumptions:
- QEMU_CAPS_KVM flag means that QEMU is running KVM accelerated
machines by default (without explicitly requesting that using a
command-line option). It is the closest to the truth according to
the code with the only exception being the comment next to the
flag, so it's fixed in this patch as well.
- QEMU_CAPS_ENABLE_KVM flag means that QEMU is, by default, running
without KVM acceleration and in case we need KVM acceleration it
needs to be explicitly instructed to do so. This is partially
true for the past (this option essentially means that QEMU
recognizes the '-enable-kvm' option, even though it's almost the
same).
2012-10-31 07:31:49 +00:00
|
|
|
int qemuMonitorJSONGetKVMState(qemuMonitorPtr mon,
|
|
|
|
bool *enabled,
|
|
|
|
bool *present)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data = NULL;
|
|
|
|
|
|
|
|
/* Safe defaults */
|
|
|
|
*enabled = *present = false;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-kvm", NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-kvm reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
Make non-KVM machines work with QMP probing
When there is no 'qemu-kvm' binary and the emulator used for a machine
is, for example, 'qemu-system-x86_64' that, by default, runs without
kvm enabled, libvirt still supplies '-no-kvm' option to this process,
even though it does not recognize such option (making the start of a
domain fail in that case).
This patch fixes building a command-line for QEMU machines without KVM
acceleration and is based on following assumptions:
- QEMU_CAPS_KVM flag means that QEMU is running KVM accelerated
machines by default (without explicitly requesting that using a
command-line option). It is the closest to the truth according to
the code with the only exception being the comment next to the
flag, so it's fixed in this patch as well.
- QEMU_CAPS_ENABLE_KVM flag means that QEMU is, by default, running
without KVM acceleration and in case we need KVM acceleration it
needs to be explicitly instructed to do so. This is partially
true for the past (this option essentially means that QEMU
recognizes the '-enable-kvm' option, even though it's almost the
same).
2012-10-31 07:31:49 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-22 09:48:41 +00:00
|
|
|
int qemuMonitorJSONGetObjectTypes(qemuMonitorPtr mon,
|
|
|
|
char ***types)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
char **typelist = NULL;
|
|
|
|
int n = 0;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
*types = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("qom-list-types", NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("qom-list-types reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((n = virJSONValueArraySize(data)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("qom-list-types reply data was not an array"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-04-26 17:47:23 +00:00
|
|
|
/* null-terminated list */
|
2013-07-04 10:14:12 +00:00
|
|
|
if (VIR_ALLOC_N(typelist, n + 1) < 0)
|
2012-08-22 09:48:41 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
2012-08-22 09:48:41 +00:00
|
|
|
virJSONValuePtr 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'"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-05-20 09:23:13 +00:00
|
|
|
if (VIR_STRDUP(typelist[i], tmp) < 0)
|
2012-08-22 09:48:41 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = n;
|
|
|
|
*types = typelist;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-04-26 17:47:23 +00:00
|
|
|
if (ret < 0)
|
|
|
|
virStringFreeList(typelist);
|
2012-08-22 09:48:41 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2012-08-22 09:48:41 +00:00
|
|
|
|
|
|
|
|
2013-06-24 17:51:56 +00:00
|
|
|
int qemuMonitorJSONGetObjectListPaths(qemuMonitorPtr mon,
|
|
|
|
const char *path,
|
|
|
|
qemuMonitorJSONListPathPtr **paths)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
qemuMonitorJSONListPathPtr *pathlist = NULL;
|
|
|
|
int n = 0;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
*paths = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("qom-list",
|
|
|
|
"s:path", path,
|
|
|
|
NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("qom-list reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((n = virJSONValueArraySize(data)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("qom-list reply data was not an array"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* null-terminated list */
|
|
|
|
if (VIR_ALLOC_N(pathlist, n + 1) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
virJSONValuePtr child = virJSONValueArrayGet(data, i);
|
|
|
|
const char *tmp;
|
|
|
|
qemuMonitorJSONListPathPtr info;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(info) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
pathlist[i] = info;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(child, "name"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("qom-list reply data was missing 'name'"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_STRDUP(info->name, tmp) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virJSONValueObjectHasKey(child, "type")) {
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(child, "type"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("qom-list reply has malformed 'type' data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (VIR_STRDUP(info->type, tmp) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = n;
|
|
|
|
*paths = pathlist;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-06-24 17:51:56 +00:00
|
|
|
if (ret < 0 && pathlist) {
|
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
qemuMonitorJSONListPathFree(pathlist[i]);
|
|
|
|
VIR_FREE(pathlist);
|
|
|
|
}
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qemuMonitorJSONListPathFree(qemuMonitorJSONListPathPtr paths)
|
|
|
|
{
|
|
|
|
if (!paths)
|
|
|
|
return;
|
|
|
|
VIR_FREE(paths->name);
|
|
|
|
VIR_FREE(paths->type);
|
|
|
|
VIR_FREE(paths);
|
|
|
|
}
|
|
|
|
|
2013-07-03 18:15:07 +00:00
|
|
|
|
|
|
|
int qemuMonitorJSONGetObjectProperty(qemuMonitorPtr mon,
|
|
|
|
const char *path,
|
|
|
|
const char *property,
|
|
|
|
qemuMonitorJSONObjectPropertyPtr prop)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
const char *tmp;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("qom-get",
|
|
|
|
"s:path", path,
|
|
|
|
"s:property", property,
|
|
|
|
NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("qom-get reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 && VIR_STRDUP(prop->val.str, tmp) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
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;
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-07-03 18:15:07 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-03 18:42:40 +00:00
|
|
|
#define MAKE_SET_CMD(STRING, VALUE) \
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("qom-set", \
|
|
|
|
"s:path", path, \
|
|
|
|
"s:property", property, \
|
|
|
|
STRING, VALUE, \
|
|
|
|
NULL)
|
|
|
|
int qemuMonitorJSONSetObjectProperty(qemuMonitorPtr mon,
|
|
|
|
const char *path,
|
|
|
|
const char *property,
|
|
|
|
qemuMonitorJSONObjectPropertyPtr prop)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr 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 ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-07-03 18:42:40 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#undef MAKE_SET_CMD
|
|
|
|
|
|
|
|
|
2012-08-22 09:48:41 +00:00
|
|
|
int qemuMonitorJSONGetObjectProps(qemuMonitorPtr mon,
|
|
|
|
const char *type,
|
|
|
|
char ***props)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
char **proplist = NULL;
|
|
|
|
int n = 0;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
*props = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("device-list-properties",
|
|
|
|
"s:typename", type,
|
|
|
|
NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0 &&
|
|
|
|
qemuMonitorJSONHasError(reply, "DeviceNotFound")) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("device-list-properties reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((n = virJSONValueArraySize(data)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("device-list-properties reply data was not an array"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-04-26 17:47:23 +00:00
|
|
|
/* null-terminated list */
|
2013-07-04 10:14:12 +00:00
|
|
|
if (VIR_ALLOC_N(proplist, n + 1) < 0)
|
2012-08-22 09:48:41 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
2012-08-22 09:48:41 +00:00
|
|
|
virJSONValuePtr child = virJSONValueArrayGet(data, i);
|
|
|
|
const char *tmp;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(child, "name"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("device-list-properties reply data was missing 'name'"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-05-20 09:23:13 +00:00
|
|
|
if (VIR_STRDUP(proplist[i], tmp) < 0)
|
2012-08-22 09:48:41 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = n;
|
|
|
|
*props = proplist;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-04-26 17:47:23 +00:00
|
|
|
if (ret < 0)
|
|
|
|
virStringFreeList(proplist);
|
2012-08-22 09:48:41 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2012-08-22 09:48:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
qemuMonitorJSONGetTargetArch(qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
char *ret = NULL;
|
|
|
|
int rv;
|
|
|
|
const char *arch;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-target", NULL)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
rv = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (rv == 0)
|
|
|
|
rv = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (rv < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-target reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(arch = virJSONValueObjectGetString(data, "arch"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-target reply was missing arch data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-05-20 09:23:13 +00:00
|
|
|
ignore_value(VIR_STRDUP(ret, arch));
|
2012-08-22 09:48:41 +00:00
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2012-08-22 09:48:41 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-01-14 11:45:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
int
|
2014-09-11 12:11:54 +00:00
|
|
|
qemuMonitorJSONGetMigrationCapabilities(qemuMonitorPtr mon,
|
|
|
|
char ***capabilities)
|
2013-01-14 11:45:20 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr caps;
|
2014-09-11 12:11:54 +00:00
|
|
|
char **list = NULL;
|
Convert 'int i' to 'size_t i' in src/qemu files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2014-09-11 12:11:54 +00:00
|
|
|
int n;
|
|
|
|
|
|
|
|
*capabilities = NULL;
|
2013-01-14 11:45:20 +00:00
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-migrate-capabilities",
|
|
|
|
NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
|
|
|
|
goto cleanup;
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
2014-09-11 12:11:54 +00:00
|
|
|
if (!(caps = virJSONValueObjectGet(reply, "return")) ||
|
|
|
|
(n = virJSONValueArraySize(caps)) < 0) {
|
2013-01-14 11:45:20 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing migration capabilities"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-09-11 12:11:54 +00:00
|
|
|
if (VIR_ALLOC_N(list, n + 1) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
2013-01-14 11:45:20 +00:00
|
|
|
virJSONValuePtr cap = virJSONValueArrayGet(caps, i);
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
if (!cap || cap->type != VIR_JSON_TYPE_OBJECT) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing entry in migration capabilities list"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(name = virJSONValueObjectGetString(cap, "capability"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing migration capability name"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-09-11 12:11:54 +00:00
|
|
|
if (VIR_STRDUP(list[i], name) < 1)
|
2013-01-14 11:45:20 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2014-09-11 12:11:54 +00:00
|
|
|
ret = n;
|
|
|
|
*capabilities = list;
|
2013-01-14 11:45:20 +00:00
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2014-09-11 12:11:54 +00:00
|
|
|
if (ret < 0)
|
|
|
|
virStringFreeList(list);
|
2013-01-14 11:45:20 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-09-11 12:11:54 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONGetMigrationCapability(qemuMonitorPtr mon,
|
|
|
|
qemuMonitorMigrationCaps capability)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
char **capsList = NULL;
|
|
|
|
const char *cap = qemuMonitorMigrationCapsTypeToString(capability);
|
|
|
|
|
|
|
|
if (qemuMonitorJSONGetMigrationCapabilities(mon, &capsList) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = virStringArrayHasString(capsList, cap);
|
|
|
|
|
|
|
|
virStringFreeList(capsList);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-14 11:45:20 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONSetMigrationCapability(qemuMonitorPtr mon,
|
|
|
|
qemuMonitorMigrationCaps capability)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr cap = NULL;
|
|
|
|
virJSONValuePtr caps;
|
|
|
|
|
|
|
|
if (!(caps = virJSONValueNewArray()))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(cap = virJSONValueNewObject()))
|
2013-07-04 10:14:12 +00:00
|
|
|
goto cleanup;
|
2013-01-14 11:45:20 +00:00
|
|
|
|
|
|
|
if (virJSONValueObjectAppendString(
|
|
|
|
cap, "capability",
|
|
|
|
qemuMonitorMigrationCapsTypeToString(capability)) < 0)
|
2013-07-04 10:14:12 +00:00
|
|
|
goto cleanup;
|
2013-01-14 11:45:20 +00:00
|
|
|
|
|
|
|
if (virJSONValueObjectAppendBoolean(cap, "state", 1) < 0)
|
2013-07-04 10:14:12 +00:00
|
|
|
goto cleanup;
|
2013-01-14 11:45:20 +00:00
|
|
|
|
|
|
|
if (virJSONValueArrayAppend(caps, cap) < 0)
|
2013-07-04 10:14:12 +00:00
|
|
|
goto cleanup;
|
2013-01-14 11:45:20 +00:00
|
|
|
|
|
|
|
cap = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("migrate-set-capabilities",
|
|
|
|
"a:capabilities", caps,
|
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
caps = NULL;
|
|
|
|
|
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-01-14 11:45:20 +00:00
|
|
|
virJSONValueFree(caps);
|
|
|
|
virJSONValueFree(cap);
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2012-11-22 15:08:52 +00:00
|
|
|
|
2013-03-12 18:18:22 +00:00
|
|
|
static virJSONValuePtr
|
|
|
|
qemuMonitorJSONBuildInetSocketAddress(const char *host,
|
|
|
|
const char *port)
|
|
|
|
{
|
|
|
|
virJSONValuePtr addr = NULL;
|
|
|
|
virJSONValuePtr data = NULL;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueNewObject()) ||
|
|
|
|
!(addr = virJSONValueNewObject()))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* port is really expected as a string here by qemu */
|
|
|
|
if (virJSONValueObjectAppendString(data, "host", host) < 0 ||
|
|
|
|
virJSONValueObjectAppendString(data, "port", port) < 0 ||
|
|
|
|
virJSONValueObjectAppendString(addr, "type", "inet") < 0 ||
|
|
|
|
virJSONValueObjectAppend(addr, "data", data) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
return addr;
|
2014-03-25 06:49:44 +00:00
|
|
|
error:
|
2013-03-12 18:18:22 +00:00
|
|
|
virJSONValueFree(data);
|
|
|
|
virJSONValueFree(addr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-03-12 18:48:04 +00:00
|
|
|
static virJSONValuePtr
|
|
|
|
qemuMonitorJSONBuildUnixSocketAddress(const char *path)
|
|
|
|
{
|
|
|
|
virJSONValuePtr addr = NULL;
|
|
|
|
virJSONValuePtr data = NULL;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueNewObject()) ||
|
|
|
|
!(addr = virJSONValueNewObject()))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (virJSONValueObjectAppendString(data, "path", path) < 0 ||
|
|
|
|
virJSONValueObjectAppendString(addr, "type", "unix") < 0 ||
|
|
|
|
virJSONValueObjectAppend(addr, "data", data) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
return addr;
|
2014-03-25 06:49:44 +00:00
|
|
|
error:
|
2013-03-12 18:48:04 +00:00
|
|
|
virJSONValueFree(data);
|
|
|
|
virJSONValueFree(addr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-11-22 15:08:52 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONNBDServerStart(qemuMonitorPtr mon,
|
|
|
|
const char *host,
|
|
|
|
unsigned int port)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr addr = NULL;
|
|
|
|
char *port_str = NULL;
|
|
|
|
|
2013-03-12 18:18:22 +00:00
|
|
|
if (virAsprintf(&port_str, "%u", port) < 0)
|
|
|
|
return ret;
|
2012-11-22 15:08:52 +00:00
|
|
|
|
2013-03-12 18:18:22 +00:00
|
|
|
if (!(addr = qemuMonitorJSONBuildInetSocketAddress(host, port_str)))
|
|
|
|
return ret;
|
2012-11-22 15:08:52 +00:00
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("nbd-server-start",
|
|
|
|
"a:addr", addr,
|
|
|
|
NULL)))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* From now on, @addr is part of @cmd */
|
|
|
|
addr = NULL;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2012-11-22 15:08:52 +00:00
|
|
|
VIR_FREE(port_str);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(addr);
|
|
|
|
return ret;
|
|
|
|
}
|
2012-11-22 15:17:13 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
qemuMonitorJSONNBDServerAdd(qemuMonitorPtr mon,
|
|
|
|
const char *deviceID,
|
|
|
|
bool writable)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("nbd-server-add",
|
|
|
|
"s:device", deviceID,
|
|
|
|
"b:writable", writable,
|
|
|
|
NULL)))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-01-31 13:47:49 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
qemuMonitorJSONNBDServerStop(qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("nbd-server-stop",
|
|
|
|
NULL)))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-04-12 20:55:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONGetStringArray(qemuMonitorPtr mon, const char *qmpCmd,
|
|
|
|
char ***array)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
char **list = NULL;
|
|
|
|
int n = 0;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
*array = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand(qmpCmd, NULL)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
2013-04-16 11:05:21 +00:00
|
|
|
if (ret == 0) {
|
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
|
|
|
|
goto cleanup;
|
|
|
|
|
2013-04-12 20:55:45 +00:00
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
2013-04-16 11:05:21 +00:00
|
|
|
}
|
2013-04-12 20:55:45 +00:00
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("%s reply was missing return data"),
|
|
|
|
qmpCmd);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((n = virJSONValueArraySize(data)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("%s reply data was not an array"),
|
|
|
|
qmpCmd);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* null-terminated list */
|
2013-07-04 10:14:12 +00:00
|
|
|
if (VIR_ALLOC_N(list, n + 1) < 0)
|
2013-04-12 20:55:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-05-21 07:21:20 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
2013-04-12 20:55:45 +00:00
|
|
|
virJSONValuePtr child = virJSONValueArrayGet(data, i);
|
|
|
|
const char *tmp;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueGetString(child))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("%s array element does not contain data"),
|
|
|
|
qmpCmd);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-05-20 09:23:13 +00:00
|
|
|
if (VIR_STRDUP(list[i], tmp) < 0)
|
2013-04-12 20:55:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = n;
|
|
|
|
*array = list;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-04-12 20:55:45 +00:00
|
|
|
if (ret < 0)
|
|
|
|
virStringFreeList(list);
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorJSONGetTPMModels(qemuMonitorPtr mon,
|
|
|
|
char ***tpmmodels)
|
|
|
|
{
|
|
|
|
return qemuMonitorJSONGetStringArray(mon, "query-tpm-models", tpmmodels);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONGetTPMTypes(qemuMonitorPtr mon,
|
|
|
|
char ***tpmtypes)
|
|
|
|
{
|
|
|
|
return qemuMonitorJSONGetStringArray(mon, "query-tpm-types", tpmtypes);
|
|
|
|
}
|
2013-03-12 18:48:04 +00:00
|
|
|
|
|
|
|
static virJSONValuePtr
|
|
|
|
qemuMonitorJSONAttachCharDevCommand(const char *chrID,
|
2013-10-08 17:07:53 +00:00
|
|
|
const virDomainChrSourceDef *chr)
|
2013-03-12 18:48:04 +00:00
|
|
|
{
|
|
|
|
virJSONValuePtr ret;
|
|
|
|
virJSONValuePtr backend;
|
|
|
|
virJSONValuePtr data = NULL;
|
|
|
|
virJSONValuePtr addr = NULL;
|
|
|
|
const char *backend_type = NULL;
|
|
|
|
bool telnet;
|
|
|
|
|
|
|
|
if (!(backend = virJSONValueNewObject()) ||
|
|
|
|
!(data = virJSONValueNewObject())) {
|
2014-06-27 06:45:14 +00:00
|
|
|
goto error;
|
2013-03-12 18:48:04 +00:00
|
|
|
}
|
|
|
|
|
2014-06-01 00:22:30 +00:00
|
|
|
switch ((virDomainChrType) chr->type) {
|
2013-03-12 18:48:04 +00:00
|
|
|
case VIR_DOMAIN_CHR_TYPE_NULL:
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_VC:
|
|
|
|
backend_type = "null";
|
|
|
|
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)
|
2014-06-27 06:45:14 +00:00
|
|
|
goto error;
|
2013-03-12 18:48:04 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_DEV:
|
|
|
|
backend_type = STRPREFIX(chrID, "parallel") ? "parallel" : "serial";
|
|
|
|
if (virJSONValueObjectAppendString(data, "device",
|
|
|
|
chr->data.file.path) < 0)
|
2014-06-27 06:45:14 +00:00
|
|
|
goto error;
|
2013-03-12 18:48:04 +00:00
|
|
|
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)
|
2014-06-27 06:45:14 +00:00
|
|
|
goto error;
|
2013-03-12 18:48:04 +00:00
|
|
|
addr = NULL;
|
|
|
|
|
|
|
|
telnet = chr->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET;
|
|
|
|
|
|
|
|
if (virJSONValueObjectAppendBoolean(data, "wait", false) < 0 ||
|
|
|
|
virJSONValueObjectAppendBoolean(data, "telnet", telnet) < 0 ||
|
|
|
|
virJSONValueObjectAppendBoolean(data, "server", chr->data.tcp.listen) < 0)
|
2014-06-27 06:45:14 +00:00
|
|
|
goto error;
|
2013-03-12 18:48:04 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_UDP:
|
|
|
|
backend_type = "socket";
|
|
|
|
addr = qemuMonitorJSONBuildInetSocketAddress(chr->data.udp.connectHost,
|
|
|
|
chr->data.udp.connectService);
|
|
|
|
if (!addr ||
|
|
|
|
virJSONValueObjectAppend(data, "addr", addr) < 0)
|
2014-06-27 06:45:14 +00:00
|
|
|
goto error;
|
2013-03-12 18:48:04 +00:00
|
|
|
addr = NULL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_UNIX:
|
|
|
|
backend_type = "socket";
|
|
|
|
addr = qemuMonitorJSONBuildUnixSocketAddress(chr->data.nix.path);
|
|
|
|
|
|
|
|
if (!addr ||
|
|
|
|
virJSONValueObjectAppend(data, "addr", addr) < 0)
|
2014-06-27 06:45:14 +00:00
|
|
|
goto error;
|
2013-03-12 18:48:04 +00:00
|
|
|
addr = NULL;
|
|
|
|
|
|
|
|
if (virJSONValueObjectAppendBoolean(data, "wait", false) < 0 ||
|
|
|
|
virJSONValueObjectAppendBoolean(data, "server", chr->data.nix.listen) < 0)
|
2014-06-27 06:45:14 +00:00
|
|
|
goto error;
|
2013-03-12 18:48:04 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
|
2014-02-10 10:18:16 +00:00
|
|
|
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
|
2013-03-12 18:48:04 +00:00
|
|
|
case VIR_DOMAIN_CHR_TYPE_PIPE:
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_STDIO:
|
2014-03-15 12:30:01 +00:00
|
|
|
case VIR_DOMAIN_CHR_TYPE_NMDM:
|
2013-03-12 18:48:04 +00:00
|
|
|
case VIR_DOMAIN_CHR_TYPE_LAST:
|
2014-11-10 15:52:49 +00:00
|
|
|
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);
|
|
|
|
}
|
2013-03-12 18:48:04 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectAppendString(backend, "type", backend_type) < 0 ||
|
|
|
|
virJSONValueObjectAppend(backend, "data", data) < 0)
|
2014-06-27 06:45:14 +00:00
|
|
|
goto error;
|
2013-03-12 18:48:04 +00:00
|
|
|
data = NULL;
|
|
|
|
|
|
|
|
if (!(ret = qemuMonitorJSONMakeCommand("chardev-add",
|
|
|
|
"s:id", chrID,
|
|
|
|
"a:backend", backend,
|
|
|
|
NULL)))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
error:
|
2013-03-12 18:48:04 +00:00
|
|
|
virJSONValueFree(addr);
|
|
|
|
virJSONValueFree(data);
|
|
|
|
virJSONValueFree(backend);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
qemuMonitorJSONAttachCharDev(qemuMonitorPtr mon,
|
|
|
|
const char *chrID,
|
|
|
|
virDomainChrSourceDefPtr chr)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONAttachCharDevCommand(chrID, chr)))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) {
|
|
|
|
virJSONValuePtr data;
|
|
|
|
const char *path;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("chardev-add reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(path = virJSONValueObjectGetString(data, "pty"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("chardev-add reply was missing pty path"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_STRDUP(chr->data.file.path, path) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-03-12 18:48:04 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-03-12 18:57:48 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
qemuMonitorJSONDetachCharDev(qemuMonitorPtr mon,
|
|
|
|
const char *chrID)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("chardev-remove",
|
|
|
|
"s:id", chrID,
|
|
|
|
NULL)))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-07-19 13:01:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
qemuMonitorJSONGetDeviceAliases(qemuMonitorPtr mon,
|
|
|
|
char ***aliases)
|
|
|
|
{
|
|
|
|
qemuMonitorJSONListPathPtr *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;
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(*aliases, n + 1) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
alias = *aliases;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
if (STRPREFIX(paths[i]->type, "child<")) {
|
|
|
|
*alias = paths[i]->name;
|
|
|
|
paths[i]->name = NULL;
|
|
|
|
alias++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-07-19 13:01:38 +00:00
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
qemuMonitorJSONListPathFree(paths[i]);
|
|
|
|
VIR_FREE(paths);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-07-22 11:07:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONParseCPUx86FeatureWord(virJSONValuePtr data,
|
|
|
|
virCPUx86CPUID *cpuid)
|
|
|
|
{
|
|
|
|
const char *reg;
|
|
|
|
unsigned long long fun;
|
|
|
|
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", &fun) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing or invalid cpuid-input-eax in CPU data"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberUlong(data, "features", &features) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("missing or invalid features in CPU data"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cpuid->function = fun;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-11-11 13:47:08 +00:00
|
|
|
static int
|
2013-07-22 11:07:23 +00:00
|
|
|
qemuMonitorJSONGetCPUx86Data(qemuMonitorPtr mon,
|
2013-11-11 13:47:08 +00:00
|
|
|
const char *property,
|
|
|
|
virCPUDataPtr *cpudata)
|
2013-07-22 11:07:23 +00:00
|
|
|
{
|
2013-11-11 15:34:53 +00:00
|
|
|
virJSONValuePtr cmd = NULL;
|
2013-07-22 11:07:23 +00:00
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
2013-11-11 15:34:53 +00:00
|
|
|
virJSONValuePtr element;
|
2013-07-22 11:07:23 +00:00
|
|
|
virCPUx86Data *x86Data = NULL;
|
|
|
|
virCPUx86CPUID cpuid;
|
|
|
|
size_t i;
|
|
|
|
int n;
|
2013-11-11 13:47:08 +00:00
|
|
|
int ret = -1;
|
2013-07-22 11:07:23 +00:00
|
|
|
|
2013-11-11 15:34:53 +00:00
|
|
|
/* look up if the property exists before asking */
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("qom-list",
|
|
|
|
"s:path", QOM_CPU_PATH,
|
|
|
|
NULL)))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* check if device exists */
|
2013-11-19 14:42:28 +00:00
|
|
|
if ((data = virJSONValueObjectGet(reply, "error"))) {
|
|
|
|
const char *klass = virJSONValueObjectGetString(data, "class");
|
|
|
|
if (STREQ_NULLABLE(klass, "DeviceNotFound") ||
|
|
|
|
STREQ_NULLABLE(klass, "CommandNotFound")) {
|
|
|
|
ret = -2;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-11-11 15:34:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (qemuMonitorJSONCheckError(cmd, reply))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
data = virJSONValueObjectGet(reply, "return");
|
|
|
|
|
|
|
|
if ((n = virJSONValueArraySize(data)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("%s CPU property did not return an array"),
|
|
|
|
property);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
element = virJSONValueArrayGet(data, i);
|
|
|
|
if (STREQ_NULLABLE(virJSONValueObjectGetString(element, "name"),
|
|
|
|
property))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* "property" was not found */
|
|
|
|
if (i == n) {
|
|
|
|
ret = -2;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
|
2013-07-22 11:07:23 +00:00
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("qom-get",
|
|
|
|
"s:path", QOM_CPU_PATH,
|
|
|
|
"s:property", property,
|
|
|
|
NULL)))
|
2013-11-11 15:34:53 +00:00
|
|
|
goto cleanup;
|
2013-07-22 11:07:23 +00:00
|
|
|
|
|
|
|
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONCheckError(cmd, reply))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("qom-get reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((n = virJSONValueArraySize(data)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("%s CPU property did not return an array"),
|
|
|
|
property);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC(x86Data) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
if (qemuMonitorJSONParseCPUx86FeatureWord(virJSONValueArrayGet(data, i),
|
|
|
|
&cpuid) < 0 ||
|
|
|
|
virCPUx86DataAddCPUID(x86Data, &cpuid) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2013-11-11 13:47:08 +00:00
|
|
|
if (!(*cpudata = virCPUx86MakeData(VIR_ARCH_X86_64, &x86Data)))
|
2013-07-22 11:07:23 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2013-11-11 13:47:08 +00:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:49:44 +00:00
|
|
|
cleanup:
|
2013-07-22 11:07:23 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
virCPUx86DataFree(x86Data);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qemuMonitorJSONGetGuestCPU:
|
|
|
|
* @mon: Pointer to the monitor
|
|
|
|
* @arch: arch of the guest
|
2013-11-11 13:47:08 +00:00
|
|
|
* @data: returns the cpu data of the guest
|
2013-07-22 11:07:23 +00:00
|
|
|
*
|
|
|
|
* Retrieve the definition of the guest CPU from a running qemu instance.
|
|
|
|
*
|
2013-11-11 13:47:08 +00:00
|
|
|
* Returns 0 on success, -2 if guest doesn't support this feature,
|
|
|
|
* -1 on other errors.
|
2013-07-22 11:07:23 +00:00
|
|
|
*/
|
2013-11-11 13:47:08 +00:00
|
|
|
int
|
2013-07-22 11:07:23 +00:00
|
|
|
qemuMonitorJSONGetGuestCPU(qemuMonitorPtr mon,
|
2013-11-11 13:47:08 +00:00
|
|
|
virArch arch,
|
|
|
|
virCPUDataPtr *data)
|
2013-07-22 11:07:23 +00:00
|
|
|
{
|
|
|
|
switch (arch) {
|
|
|
|
case VIR_ARCH_X86_64:
|
|
|
|
case VIR_ARCH_I686:
|
2013-11-11 13:47:08 +00:00
|
|
|
return qemuMonitorJSONGetCPUx86Data(mon, "feature-words", data);
|
2013-07-22 11:07:23 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("CPU definition retrieval isn't supported for '%s'"),
|
|
|
|
virArchToString(arch));
|
2013-11-11 13:47:08 +00:00
|
|
|
return -1;
|
2013-07-22 11:07:23 +00:00
|
|
|
}
|
|
|
|
}
|
2014-08-13 12:28:24 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
qemuMonitorJSONRTCResetReinjection(qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("rtc-reset-reinjection",
|
|
|
|
NULL)))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2014-08-29 20:23:11 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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(qemuMonitorPtr mon,
|
|
|
|
qemuMonitorIOThreadsInfoPtr **iothreads)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
qemuMonitorIOThreadsInfoPtr *infolist = NULL;
|
|
|
|
int n = 0;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
*iothreads = NULL;
|
|
|
|
|
|
|
|
if (!(cmd = qemuMonitorJSONMakeCommand("query-iothreads", NULL)))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-iothreads reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((n = virJSONValueArraySize(data)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-iothreads reply data was not an array"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* null-terminated list */
|
|
|
|
if (VIR_ALLOC_N(infolist, n + 1) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
virJSONValuePtr child = virJSONValueArrayGet(data, i);
|
|
|
|
const char *tmp;
|
|
|
|
qemuMonitorIOThreadsInfoPtr info;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(info) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
infolist[i] = info;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(child, "id"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-iothreads reply data was missing 'id'"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_STRDUP(info->name, tmp) < 0)
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = n;
|
|
|
|
*iothreads = infolist;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (ret < 0 && infolist) {
|
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
qemuMonitorIOThreadsInfoFree(infolist[i]);
|
|
|
|
VIR_FREE(infolist);
|
|
|
|
}
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|