2009-11-03 18:59:18 +00:00
|
|
|
/*
|
|
|
|
* qemu_monitor_json.c: interaction with QEMU monitor console
|
|
|
|
*
|
2011-03-04 18:51:48 +00:00
|
|
|
* Copyright (C) 2006-2011 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
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
* 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"
|
2009-11-03 18:59:18 +00:00
|
|
|
#include "memory.h"
|
|
|
|
#include "logging.h"
|
|
|
|
#include "driver.h"
|
|
|
|
#include "datatypes.h"
|
|
|
|
#include "virterror_internal.h"
|
|
|
|
#include "json.h"
|
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_QEMU
|
|
|
|
|
|
|
|
|
|
|
|
#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);
|
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);
|
2011-06-14 14:36:53 +00:00
|
|
|
static void qemuMonitorJSONHandleBlockPull(qemuMonitorPtr mon, virJSONValuePtr data);
|
2009-11-26 13:06:24 +00:00
|
|
|
|
|
|
|
struct {
|
|
|
|
const char *type;
|
|
|
|
void (*handler)(qemuMonitorPtr mon, virJSONValuePtr data);
|
|
|
|
} eventHandlers[] = {
|
|
|
|
{ "SHUTDOWN", qemuMonitorJSONHandleShutdown, },
|
|
|
|
{ "RESET", qemuMonitorJSONHandleReset, },
|
|
|
|
{ "POWERDOWN", qemuMonitorJSONHandlePowerdown, },
|
|
|
|
{ "STOP", qemuMonitorJSONHandleStop, },
|
2010-03-18 18:28:15 +00:00
|
|
|
{ "RTC_CHANGE", qemuMonitorJSONHandleRTCChange, },
|
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
|
|
|
{ "WATCHDOG", qemuMonitorJSONHandleWatchdog, },
|
2010-02-16 12:07:49 +00:00
|
|
|
{ "BLOCK_IO_ERROR", qemuMonitorJSONHandleIOError, },
|
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
|
|
|
{ "VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect, },
|
|
|
|
{ "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, },
|
|
|
|
{ "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, },
|
2011-06-14 14:36:53 +00:00
|
|
|
{ "BLOCK_STREAM_COMPLETED", qemuMonitorJSONHandleBlockPull, },
|
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;
|
2009-11-26 13:06:24 +00:00
|
|
|
int i;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0 ; i < ARRAY_CARDINALITY(eventHandlers) ; i++) {
|
|
|
|
if (STREQ(eventHandlers[i].type, type)) {
|
|
|
|
virJSONValuePtr data = virJSONValueObjectGet(obj, "data");
|
|
|
|
VIR_DEBUG("handle %s handler=%p data=%p", type,
|
|
|
|
eventHandlers[i].handler, data);
|
|
|
|
(eventHandlers[i].handler)(mon, data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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) {
|
2011-05-29 12:51:08 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Parsed JSON reply '%s' isn't an object"), line);
|
|
|
|
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
|
|
|
virJSONValueFree(obj);
|
|
|
|
} else if (virJSONValueObjectHasKey(obj, "event") == 1) {
|
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) {
|
|
|
|
if (msg) {
|
|
|
|
msg->rxObject = obj;
|
|
|
|
msg->finished = 1;
|
|
|
|
ret = 0;
|
|
|
|
} else {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unexpected JSON reply '%s'"), line);
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
} else {
|
2011-05-29 12:51:08 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unknown JSON reply '%s'"), line);
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
2011-05-29 12:51:08 +00:00
|
|
|
if (ret < 0)
|
|
|
|
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);
|
|
|
|
char *line = strndup(data + used, got);
|
|
|
|
if (!line) {
|
2011-05-29 12:51:08 +00:00
|
|
|
virReportOOMError();
|
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;
|
|
|
|
|
|
|
|
memset(&msg, 0, sizeof msg);
|
|
|
|
|
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) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to append command 'id' string"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
if (!(cmdstr = virJSONValueToString(cmd))) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (virAsprintf(&msg.txBuffer, "%s\r\n", cmdstr) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
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) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Missing monitor reply object"));
|
|
|
|
ret = -1;
|
|
|
|
} else {
|
|
|
|
*reply = msg.rxObject;
|
2009-11-03 18:59:18 +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,
|
|
|
|
virJSONValuePtr *reply) {
|
|
|
|
return qemuMonitorJSONCommandWithFd(mon, cmd, -1, reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ignoring OOM in this method, since we're already reporting
|
|
|
|
* a more important error
|
|
|
|
*
|
|
|
|
* XXX see qerror.h for different klasses & fill out useful params
|
|
|
|
*/
|
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");
|
|
|
|
char *cmdstr = virJSONValueToString(cmd);
|
|
|
|
char *replystr = virJSONValueToString(reply);
|
|
|
|
|
2010-02-12 16:45:11 +00:00
|
|
|
/* Log the full JSON formatted command & error */
|
|
|
|
VIR_DEBUG("unable to execute QEMU command %s: %s",
|
|
|
|
cmdstr, replystr);
|
|
|
|
|
|
|
|
/* Only send the user the command name + friendly error */
|
|
|
|
if (!error)
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
2010-02-12 16:45:11 +00:00
|
|
|
_("unable to execute QEMU command '%s'"),
|
|
|
|
qemuMonitorJSONCommandName(cmd));
|
|
|
|
else
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
2010-02-12 16:45:11 +00:00
|
|
|
_("unable to execute QEMU command '%s': %s"),
|
|
|
|
qemuMonitorJSONCommandName(cmd),
|
|
|
|
qemuMonitorJSONStringifyError(error));
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
VIR_FREE(cmdstr);
|
|
|
|
VIR_FREE(replystr);
|
|
|
|
return -1;
|
|
|
|
} else if (!virJSONValueObjectHasKey(reply, "return")) {
|
|
|
|
char *cmdstr = virJSONValueToString(cmd);
|
|
|
|
char *replystr = virJSONValueToString(reply);
|
|
|
|
|
|
|
|
VIR_DEBUG("Neither 'return' nor 'error' is set in the JSON reply %s: %s",
|
|
|
|
cmdstr, replystr);
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
2010-02-12 16:45:11 +00:00
|
|
|
_("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);
|
|
|
|
}
|
|
|
|
|
|
|
|
static virJSONValuePtr ATTRIBUTE_SENTINEL
|
|
|
|
qemuMonitorJSONMakeCommand(const char *cmdname,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
virJSONValuePtr obj;
|
|
|
|
virJSONValuePtr jargs = NULL;
|
|
|
|
va_list args;
|
|
|
|
char *key;
|
|
|
|
|
|
|
|
va_start(args, cmdname);
|
|
|
|
|
|
|
|
if (!(obj = virJSONValueNewObject()))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (virJSONValueObjectAppendString(obj, "execute", cmdname) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
while ((key = va_arg(args, char *)) != NULL) {
|
|
|
|
int ret;
|
|
|
|
char type;
|
|
|
|
|
|
|
|
if (strlen(key) < 3) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("argument key '%s' is too short, missing type prefix"),
|
|
|
|
key);
|
2009-11-03 18:59:18 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Keys look like s:name the first letter is a type code */
|
|
|
|
type = key[0];
|
|
|
|
key += 2;
|
|
|
|
|
|
|
|
if (!jargs &&
|
|
|
|
!(jargs = virJSONValueNewObject()))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
/* This doesn't supports maps/arrays. This hasn't
|
|
|
|
* proved to be a problem..... yet :-) */
|
|
|
|
switch (type) {
|
|
|
|
case 's': {
|
|
|
|
char *val = va_arg(args, char *);
|
|
|
|
ret = virJSONValueObjectAppendString(jargs, key, val);
|
|
|
|
} break;
|
|
|
|
case 'i': {
|
|
|
|
int val = va_arg(args, int);
|
|
|
|
ret = virJSONValueObjectAppendNumberInt(jargs, key, val);
|
|
|
|
} break;
|
|
|
|
case 'u': {
|
|
|
|
unsigned int val = va_arg(args, unsigned int);
|
|
|
|
ret = virJSONValueObjectAppendNumberUint(jargs, key, val);
|
|
|
|
} break;
|
|
|
|
case 'I': {
|
|
|
|
long long val = va_arg(args, long long);
|
|
|
|
ret = virJSONValueObjectAppendNumberLong(jargs, key, val);
|
|
|
|
} break;
|
|
|
|
case 'U': {
|
2011-05-25 16:52:26 +00:00
|
|
|
/* qemu silently truncates numbers larger than LLONG_MAX,
|
|
|
|
* so passing the full range of unsigned 64 bit integers
|
|
|
|
* is not safe here. Pass them as signed 64 bit integers
|
|
|
|
* instead.
|
|
|
|
*/
|
|
|
|
long long val = va_arg(args, long long);
|
|
|
|
ret = virJSONValueObjectAppendNumberLong(jargs, key, val);
|
2009-11-03 18:59:18 +00:00
|
|
|
} break;
|
|
|
|
case 'd': {
|
|
|
|
double val = va_arg(args, double);
|
|
|
|
ret = virJSONValueObjectAppendNumberDouble(jargs, key, val);
|
|
|
|
} break;
|
|
|
|
case 'b': {
|
|
|
|
int val = va_arg(args, int);
|
|
|
|
ret = virJSONValueObjectAppendBoolean(jargs, key, val);
|
|
|
|
} break;
|
|
|
|
case 'n': {
|
|
|
|
ret = virJSONValueObjectAppendNull(jargs, key);
|
|
|
|
} break;
|
|
|
|
default:
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unsupported data type '%c' for arg '%s'"), type, key - 2);
|
2009-11-03 18:59:18 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (ret < 0)
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (jargs &&
|
|
|
|
virJSONValueObjectAppend(obj, "arguments", jargs) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
|
|
|
|
no_memory:
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-11-03 18:59:18 +00:00
|
|
|
error:
|
|
|
|
virJSONValueFree(obj);
|
|
|
|
virJSONValueFree(jargs);
|
|
|
|
va_end(args);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-15 13:52:03 +00:00
|
|
|
static void
|
|
|
|
qemuFreeKeywords(int nkeywords, char **keywords, char **values)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0 ; i < nkeywords ; i++) {
|
|
|
|
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;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!(ret = virJSONValueNewObject()))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
nkeywords = qemuParseKeywords(str, &keywords, &values, 1);
|
|
|
|
|
|
|
|
if (nkeywords < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
for (i = 0 ; i < nkeywords ; i++) {
|
|
|
|
if (values[i] == NULL) {
|
|
|
|
if (i != 0) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected empty keyword in %s"), str);
|
|
|
|
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)
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (virJSONValueObjectAppendString(ret, keywords[i], values[i]) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
qemuFreeKeywords(nkeywords, keywords, values);
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
error:
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
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)
|
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_IMPL(qemuMonitorWatchdogAction, VIR_DOMAIN_EVENT_WATCHDOG_DEBUG + 1,
|
|
|
|
"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)
|
|
|
|
VIR_ENUM_IMPL(qemuMonitorIOErrorAction, VIR_DOMAIN_EVENT_IO_ERROR_REPORT + 1,
|
|
|
|
"ignore", "stop", "report");
|
|
|
|
|
|
|
|
|
|
|
|
static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
const char *device;
|
|
|
|
const char *action;
|
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
|
|
|
const char *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
|
|
|
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
|
|
|
}
|
|
|
|
|
2010-06-03 14:05:30 +00:00
|
|
|
#if 0
|
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
|
|
|
if ((reason = virJSONValueObjectGetString(data, "reason")) == NULL) {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_WARN("missing reason in disk io error event");
|
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
|
|
|
reason = "";
|
|
|
|
}
|
2010-06-03 14:05:30 +00:00
|
|
|
#else
|
|
|
|
reason = "";
|
|
|
|
#endif
|
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)
|
|
|
|
VIR_ENUM_IMPL(qemuMonitorGraphicsAddressFamily, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6 + 1,
|
|
|
|
"ipv4", "ipv6");
|
|
|
|
|
|
|
|
static void qemuMonitorJSONHandleVNC(qemuMonitorPtr mon, virJSONValuePtr data, int phase)
|
|
|
|
{
|
|
|
|
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");
|
|
|
|
|
|
|
|
localFamily = virJSONValueObjectGetString(server, "family");
|
|
|
|
localNode = virJSONValueObjectGetString(server, "host");
|
|
|
|
localService = virJSONValueObjectGetString(server, "service");
|
|
|
|
|
|
|
|
remoteFamily = virJSONValueObjectGetString(client, "family");
|
|
|
|
remoteNode = virJSONValueObjectGetString(client, "host");
|
|
|
|
remoteService = virJSONValueObjectGetString(client, "service");
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
qemuMonitorJSONHandleVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_CONNECT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
qemuMonitorJSONHandleVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
qemuMonitorJSONHandleVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT);
|
|
|
|
}
|
|
|
|
|
2011-06-14 14:36:53 +00:00
|
|
|
static void qemuMonitorJSONHandleBlockPull(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
const char *device;
|
|
|
|
unsigned long long offset, len;
|
|
|
|
int status = VIR_DOMAIN_BLOCK_PULL_FAILED;
|
|
|
|
|
|
|
|
if ((device = virJSONValueObjectGetString(data, "device")) == NULL) {
|
|
|
|
VIR_WARN("missing device in disk io error event");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUlong(data, "offset", &offset) < 0) {
|
|
|
|
VIR_WARN("missing offset in block pull event");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUlong(data, "len", &len) < 0) {
|
|
|
|
VIR_WARN("missing len in block pull event");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset != 0 && offset == len)
|
|
|
|
status = VIR_DOMAIN_BLOCK_PULL_COMPLETED;
|
|
|
|
|
|
|
|
out:
|
|
|
|
qemuMonitorEmitBlockPull(mon, device, status);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONCheckError(cmd, reply))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(obj = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("human monitor command was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reply_str) {
|
|
|
|
const char *data;
|
|
|
|
|
|
|
|
if ((data = virJSONValueGetString(obj)))
|
|
|
|
*reply_str = strdup(data);
|
|
|
|
else
|
|
|
|
*reply_str = strdup("");
|
|
|
|
|
|
|
|
if (!*reply_str) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-30 01:48:29 +00:00
|
|
|
/*
|
|
|
|
* Returns: 0 if human-monitor-command is not supported, +1 if
|
|
|
|
* human-monitor-command worked or -1 on failure
|
|
|
|
*/
|
2011-03-16 19:34:01 +00:00
|
|
|
int
|
|
|
|
qemuMonitorJSONCheckHMP(qemuMonitorPtr mon)
|
|
|
|
{
|
2011-03-30 01:48:29 +00:00
|
|
|
int ret = -1;
|
2011-03-16 19:34:01 +00:00
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-commands", NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
int i, n;
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0 ||
|
|
|
|
qemuMonitorJSONCheckError(cmd, reply) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return")) ||
|
|
|
|
data->type != VIR_JSON_TYPE_ARRAY ||
|
|
|
|
(n = virJSONValueArraySize(data)) <= 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
virJSONValuePtr entry;
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
if (!(entry = virJSONValueArrayGet(data, i)) ||
|
|
|
|
!(name = virJSONValueObjectGetString(entry, "name")))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (STREQ(name, "human-monitor-command")) {
|
|
|
|
ret = 1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-30 01:48:29 +00:00
|
|
|
/* human-monitor-command is not supported */
|
|
|
|
ret = 0;
|
|
|
|
|
2011-03-16 19:34:01 +00:00
|
|
|
cleanup:
|
|
|
|
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;
|
2011-01-13 19:52:23 +00:00
|
|
|
int i = 0, 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
|
|
|
|
qemuMonitorJSONGetStatus(qemuMonitorPtr mon, bool *running)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr data;
|
|
|
|
|
|
|
|
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"))) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-status reply was missing return data"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetBoolean(data, "running", running) < 0) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-status reply was missing running state"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
int i;
|
|
|
|
int *threads = NULL;
|
|
|
|
int ncpus;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(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) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(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) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("cpu information was empty"));
|
2010-01-22 14:52:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(threads, ncpus) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2010-01-22 14:52:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0 ; i < ncpus ; i++) {
|
|
|
|
virJSONValuePtr entry = virJSONValueArrayGet(data, i);
|
|
|
|
int cpu;
|
|
|
|
int thread;
|
|
|
|
if (!entry) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device information was missing aray element"));
|
2010-01-22 14:52:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberInt(entry, "CPU", &cpu) < 0) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("cpu information was missing cpu number"));
|
2010-01-22 14:52:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberInt(entry, "thread_id", &thread) < 0) {
|
2010-02-15 14:41:50 +00:00
|
|
|
/* Only qemu-kvm tree includs thread_id, so treat this as
|
|
|
|
non-fatal, simply returning no data */
|
|
|
|
ret = 0;
|
2010-01-22 14:52:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cpu != i) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected cpu index %d expecting %d"),
|
|
|
|
i, cpu);
|
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
|
|
|
|
|
|
|
cleanup:
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns: 0 if balloon not supported, +1 if balloon query worked
|
|
|
|
* or -1 on failure
|
|
|
|
*/
|
|
|
|
int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon,
|
|
|
|
unsigned long *currmem)
|
|
|
|
{
|
|
|
|
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"))) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(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) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-12 11:31:15 +00:00
|
|
|
int qemuMonitorJSONGetMemoryStats(qemuMonitorPtr mon,
|
|
|
|
virDomainMemoryStatPtr stats,
|
|
|
|
unsigned int nr_stats)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int got = 0;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-balloon",
|
|
|
|
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);
|
|
|
|
|
|
|
|
/* Success */
|
|
|
|
if (ret == 0) {
|
|
|
|
virJSONValuePtr data;
|
|
|
|
unsigned long long mem;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info balloon reply was missing return data"));
|
|
|
|
ret = -1;
|
|
|
|
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
|
|
|
if (virJSONValueObjectHasKey(data, "actual") && (got < nr_stats)) {
|
|
|
|
if (virJSONValueObjectGetNumberUlong(data, "actual", &mem) < 0) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info balloon reply was missing balloon actual"));
|
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
stats[got].tag = VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON;
|
|
|
|
stats[got].val = (mem/1024);
|
|
|
|
got++;
|
|
|
|
}
|
|
|
|
|
2010-04-12 11:31:15 +00:00
|
|
|
if (virJSONValueObjectHasKey(data, "mem_swapped_in") && (got < nr_stats)) {
|
|
|
|
if (virJSONValueObjectGetNumberUlong(data, "mem_swapped_in", &mem) < 0) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info balloon reply was missing balloon mem_swapped_in"));
|
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
stats[got].tag = VIR_DOMAIN_MEMORY_STAT_SWAP_IN;
|
|
|
|
stats[got].val = (mem/1024);
|
|
|
|
got++;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectHasKey(data, "mem_swapped_out") && (got < nr_stats)) {
|
|
|
|
if (virJSONValueObjectGetNumberUlong(data, "mem_swapped_out", &mem) < 0) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info balloon reply was missing balloon mem_swapped_out"));
|
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
stats[got].tag = VIR_DOMAIN_MEMORY_STAT_SWAP_OUT;
|
|
|
|
stats[got].val = (mem/1024);
|
|
|
|
got++;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectHasKey(data, "major_page_faults") && (got < nr_stats)) {
|
|
|
|
if (virJSONValueObjectGetNumberUlong(data, "major_page_faults", &mem) < 0) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info balloon reply was missing balloon major_page_faults"));
|
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
stats[got].tag = VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT;
|
|
|
|
stats[got].val = mem;
|
|
|
|
got++;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectHasKey(data, "minor_page_faults") && (got < nr_stats)) {
|
|
|
|
if (virJSONValueObjectGetNumberUlong(data, "minor_page_faults", &mem) < 0) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info balloon reply was missing balloon minor_page_faults"));
|
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
stats[got].tag = VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT;
|
|
|
|
stats[got].val = mem;
|
|
|
|
got++;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectHasKey(data, "free_mem") && (got < nr_stats)) {
|
|
|
|
if (virJSONValueObjectGetNumberUlong(data, "free_mem", &mem) < 0) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info balloon reply was missing balloon free_mem"));
|
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
stats[got].tag = VIR_DOMAIN_MEMORY_STAT_UNUSED;
|
|
|
|
stats[got].val = (mem/1024);
|
|
|
|
got++;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectHasKey(data, "total_mem") && (got < nr_stats)) {
|
|
|
|
if (virJSONValueObjectGetNumberUlong(data, "total_mem", &mem) < 0) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info balloon reply was missing balloon total_mem"));
|
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
stats[got].tag = VIR_DOMAIN_MEMORY_STAT_AVAILABLE;
|
|
|
|
stats[got].val = (mem/1024);
|
|
|
|
got++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (got > 0)
|
|
|
|
ret = got;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
|
|
|
|
const char *devname,
|
|
|
|
long long *rd_req,
|
|
|
|
long long *rd_bytes,
|
|
|
|
long long *wr_req,
|
|
|
|
long long *wr_bytes,
|
|
|
|
long long *errs)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int i;
|
|
|
|
int found = 0;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-blockstats",
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
virJSONValuePtr devices;
|
|
|
|
|
|
|
|
*rd_req = *rd_bytes = *wr_req = *wr_bytes = *errs = 0;
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
2010-07-12 13:07:02 +00:00
|
|
|
if (ret == 0)
|
2009-11-03 18:59:18 +00:00
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
2010-07-12 13:07:02 +00:00
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
2009-11-03 18:59:18 +00:00
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
devices = virJSONValueObjectGet(reply, "return");
|
|
|
|
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats reply was missing device list"));
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0 ; i < virJSONValueArraySize(devices) ; i++) {
|
|
|
|
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
|
|
|
|
virJSONValuePtr stats;
|
|
|
|
const char *thisdev;
|
|
|
|
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats device entry was not in expected format"));
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats device entry was not in expected format"));
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2010-02-10 17:46:44 +00:00
|
|
|
/* New QEMU has separate names for host & guest side of the disk
|
|
|
|
* and libvirt gives the host side a 'drive-' prefix. The passed
|
|
|
|
* in devname is the guest side though
|
|
|
|
*/
|
|
|
|
if (STRPREFIX(thisdev, QEMU_DRIVE_HOST_PREFIX))
|
|
|
|
thisdev += strlen(QEMU_DRIVE_HOST_PREFIX);
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
if (STRNEQ(thisdev, devname))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
found = 1;
|
|
|
|
if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL ||
|
|
|
|
stats->type != VIR_JSON_TYPE_OBJECT) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats stats entry was not in expected format"));
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberLong(stats, "rd_bytes", rd_bytes) < 0) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"rd_bytes");
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberLong(stats, "rd_operations", rd_req) < 0) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"rd_operations");
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberLong(stats, "wr_bytes", wr_bytes) < 0) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"wr_bytes");
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberLong(stats, "wr_operations", wr_req) < 0) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"wr_operations");
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot find statistics for device '%s'"), devname);
|
2009-11-03 18:59:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-05-14 13:10:01 +00:00
|
|
|
int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
|
|
|
|
const char *devname,
|
|
|
|
unsigned long long *extent)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
int i;
|
|
|
|
int found = 0;
|
|
|
|
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) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats reply was missing device list"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0 ; i < virJSONValueArraySize(devices) ; i++) {
|
|
|
|
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
|
|
|
|
virJSONValuePtr stats;
|
|
|
|
virJSONValuePtr parent;
|
|
|
|
const char *thisdev;
|
|
|
|
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats device entry was not in expected format"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats device entry was not in expected format"));
|
|
|
|
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
|
|
|
|
* in devname is the guest side though
|
|
|
|
*/
|
|
|
|
if (STRPREFIX(thisdev, QEMU_DRIVE_HOST_PREFIX))
|
|
|
|
thisdev += strlen(QEMU_DRIVE_HOST_PREFIX);
|
|
|
|
|
|
|
|
if (STRNEQ(thisdev, devname))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
found = 1;
|
|
|
|
if ((parent = virJSONValueObjectGet(dev, "parent")) == NULL ||
|
|
|
|
parent->type != VIR_JSON_TYPE_OBJECT) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats parent entry was not in expected format"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((stats = virJSONValueObjectGet(parent, "stats")) == NULL ||
|
|
|
|
stats->type != VIR_JSON_TYPE_OBJECT) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats stats entry was not in expected format"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUlong(stats, "wr_highest_offset", extent) < 0) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"wr_highest_offset");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot find statistics for device '%s'"), devname);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
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
|
|
|
|
2011-01-18 18:37:45 +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;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
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,
|
|
|
|
int cpu, int online)
|
|
|
|
{
|
|
|
|
int ret;
|
2010-04-22 15:39:10 +00:00
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("cpu_set",
|
2010-02-08 16:37:17 +00:00
|
|
|
"U:cpu", (unsigned long long)cpu,
|
|
|
|
"s:state", online ? "online" : "offline",
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
2011-03-15 08:53:06 +00:00
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2011-03-21 12:34:16 +00:00
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound") &&
|
|
|
|
qemuMonitorCheckHMP(mon, "cpu_set")) {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_DEBUG("cpu_set command not found, trying HMP");
|
2011-03-15 08:53:06 +00:00
|
|
|
ret = qemuMonitorTextSetCPU(mon, cpu, online);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2010-02-08 16:37:17 +00:00
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
/* XXX See if CPU soft-failed due to lack of ACPI */
|
|
|
|
#if 0
|
|
|
|
if (qemuMonitorJSONHasError(reply, "DeviceNotActive") ||
|
|
|
|
qemuMonitorJSONHasError(reply, "KVMMissingCap"))
|
|
|
|
goto cleanup;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* See if any other fatal error occurred */
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
/* Real success */
|
|
|
|
if (ret == 0)
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon,
|
2010-11-08 17:52:48 +00:00
|
|
|
const char *devname,
|
|
|
|
bool force)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("eject",
|
|
|
|
"s:device", devname,
|
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,
|
|
|
|
const char *devname,
|
|
|
|
const char *newmedia,
|
|
|
|
const char *format)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
if (format)
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("change",
|
|
|
|
"s:device", devname,
|
|
|
|
"s:target", newmedia,
|
|
|
|
"s:arg", format,
|
|
|
|
NULL);
|
|
|
|
else
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("change",
|
|
|
|
"s:device", devname,
|
|
|
|
"s:target", newmedia,
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 18:59:18 +00:00
|
|
|
static int
|
|
|
|
qemuMonitorJSONGetMigrationStatusReply(virJSONValuePtr reply,
|
|
|
|
int *status,
|
|
|
|
unsigned long long *transferred,
|
|
|
|
unsigned long long *remaining,
|
|
|
|
unsigned long long *total)
|
|
|
|
{
|
|
|
|
virJSONValuePtr ret;
|
2010-01-22 13:22:53 +00:00
|
|
|
const char *statusstr;
|
2009-11-03 18:59:18 +00:00
|
|
|
|
|
|
|
if (!(ret = virJSONValueObjectGet(reply, "return"))) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(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"))) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info migration reply was missing return status"));
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((*status = qemuMonitorMigrationStatusTypeFromString(statusstr)) < 0) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected migration status in %s"), statusstr);
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*status == QEMU_MONITOR_MIGRATION_STATUS_ACTIVE) {
|
|
|
|
virJSONValuePtr ram = virJSONValueObjectGet(ret, "ram");
|
|
|
|
if (!ram) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("migration was active, but no RAM info was set"));
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUlong(ram, "transferred", transferred) < 0) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("migration was active, but RAM 'transferred' data was missing"));
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberUlong(ram, "remaining", remaining) < 0) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("migration was active, but RAM 'remaining' data was missing"));
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberUlong(ram, "total", total) < 0) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("migration was active, but RAM 'total' data was missing"));
|
2009-11-03 18:59:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon,
|
|
|
|
int *status,
|
|
|
|
unsigned long long *transferred,
|
|
|
|
unsigned long long *remaining,
|
|
|
|
unsigned long long *total)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
|
|
|
*status = 0;
|
|
|
|
*transferred = *remaining = *total = 0;
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret == 0 &&
|
|
|
|
qemuMonitorJSONGetMigrationStatusReply(reply,
|
|
|
|
status,
|
|
|
|
transferred,
|
|
|
|
remaining,
|
|
|
|
total) < 0)
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
(tlsSubject ? "s:cert-subject" : NULL),
|
|
|
|
(tlsSubject ? tlsSubject : NULL),
|
|
|
|
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
|
|
|
{
|
2010-04-14 14:23:38 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("usb_add not suppported in JSON mode"));
|
|
|
|
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
|
|
|
{
|
2010-04-14 14:23:38 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("usb_add not suppported in JSON mode"));
|
|
|
|
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
|
|
|
{
|
2010-04-14 14:23:38 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("usb_add not suppported in JSON mode"));
|
|
|
|
return -1;
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-14 14:23:38 +00:00
|
|
|
int qemuMonitorJSONAddPCIHostDevice(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
virDomainDevicePCIAddress *hostAddr ATTRIBUTE_UNUSED,
|
|
|
|
virDomainDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
2010-04-14 14:23:38 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_add not suppported in JSON mode"));
|
|
|
|
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,
|
|
|
|
virDomainDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
|
2009-12-07 19:07:39 +00:00
|
|
|
{
|
2010-04-14 14:23:38 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_add not suppported in JSON mode"));
|
|
|
|
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,
|
|
|
|
virDomainDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
2010-04-14 14:23:38 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_add not suppported in JSON mode"));
|
|
|
|
return -1;
|
2009-11-03 18:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-14 14:23:38 +00:00
|
|
|
int qemuMonitorJSONRemovePCIDevice(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
virDomainDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
|
2009-11-03 18:59:18 +00:00
|
|
|
{
|
2010-04-14 14:23:38 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_del not suppported in JSON mode"));
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONAddHostNetwork(qemuMonitorPtr mon,
|
|
|
|
const char *netstr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("host_net_add",
|
|
|
|
"s:device", netstr,
|
|
|
|
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 qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon,
|
|
|
|
int vlan,
|
|
|
|
const char *netname)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("host_net_remove",
|
|
|
|
"i:vlan", vlan,
|
|
|
|
"s:device", netname,
|
|
|
|
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-12-07 19:28:05 +00:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (virJSONValueObjectAppend(cmd, "arguments", args) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
args = NULL; /* obj owns reference to args now */
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(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) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device information was not an array"));
|
2010-01-22 13:22:53 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0 ; i < virJSONValueArraySize(data) ; i++) {
|
|
|
|
virJSONValuePtr entry = virJSONValueArrayGet(data, i);
|
|
|
|
const char *type;
|
|
|
|
const char *id;
|
|
|
|
if (!entry) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device information was missing aray element"));
|
2010-01-22 13:22:53 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(type = virJSONValueObjectGetString(entry, "filename"))) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(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"))) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(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:")) {
|
|
|
|
char *path = strdup(type + strlen("pty:"));
|
|
|
|
if (!path) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2010-01-22 13:22:53 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virHashAddEntry(paths, id, path) < 0) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(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;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
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,
|
|
|
|
virDomainDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
|
2009-12-07 19:28:05 +00:00
|
|
|
{
|
2010-04-14 14:23:38 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_add not suppported in JSON mode"));
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONGetGuestDriveAddress(virJSONValuePtr reply,
|
|
|
|
virDomainDeviceDriveAddress *driveAddr)
|
|
|
|
{
|
|
|
|
virJSONValuePtr addr;
|
|
|
|
|
|
|
|
addr = virJSONValueObjectGet(reply, "return");
|
|
|
|
if (!addr || addr->type != VIR_JSON_TYPE_OBJECT) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("drive_add reply was missing device address"));
|
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
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUint(addr, "bus", &driveAddr->bus) < 0) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("drive_add reply was missing device bus number"));
|
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
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUint(addr, "unit", &driveAddr->unit) < 0) {
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("drive_add reply was missing device unit number"));
|
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
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONAttachDrive(qemuMonitorPtr mon,
|
|
|
|
const char *drivestr,
|
|
|
|
virDomainDevicePCIAddress* controllerAddr,
|
|
|
|
virDomainDeviceDriveAddress* driveAddr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
char *dev;
|
|
|
|
|
|
|
|
if (virAsprintf(&dev, "%.2x:%.2x.%.1x",
|
|
|
|
controllerAddr->bus, controllerAddr->slot, controllerAddr->function) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
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
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("drive_add",
|
|
|
|
"s:pci_addr", dev,
|
|
|
|
"s:opts", drivestr,
|
|
|
|
NULL);
|
|
|
|
VIR_FREE(dev);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret == 0 &&
|
|
|
|
qemuMonitorJSONGetGuestDriveAddress(reply, driveAddr) < 0)
|
2009-12-07 19:28:05 +00:00
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
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)
|
|
|
|
{
|
2010-02-09 18:15:41 +00:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("query-pci not suppported 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;
|
|
|
|
|
|
|
|
if (virJSONValueObjectAppend(cmd, "arguments", args) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
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);
|
|
|
|
|
2010-04-14 15:02:37 +00:00
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(args);
|
2010-01-26 15:34:46 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONAddDrive(qemuMonitorPtr mon,
|
|
|
|
const char *drivestr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("drive_add",
|
|
|
|
"s:pci_addr", "dummy",
|
|
|
|
"s:opts", drivestr,
|
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
2011-03-17 14:41:15 +00:00
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply) < 0))
|
|
|
|
goto cleanup;
|
2010-01-26 15:34:46 +00:00
|
|
|
|
2011-03-17 14:41:15 +00:00
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound") &&
|
|
|
|
qemuMonitorCheckHMP(mon, "drive_add")) {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_DEBUG("drive_add command not found, trying HMP");
|
2011-03-17 14:41:15 +00:00
|
|
|
ret = qemuMonitorTextAddDrive(mon, drivestr);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
2010-01-26 15:34:46 +00:00
|
|
|
|
2011-03-17 14:41:15 +00:00
|
|
|
cleanup:
|
2010-01-26 15:34:46 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
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;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
2011-02-16 23:37:57 +00:00
|
|
|
VIR_DEBUG("JSONDriveDel drivestr=%s", drivestr);
|
2010-12-08 21:30:12 +00:00
|
|
|
cmd = qemuMonitorJSONMakeCommand("drive_del",
|
2010-10-22 14:14:22 +00:00
|
|
|
"s:id", drivestr,
|
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
2011-03-17 14:41:15 +00:00
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
2010-10-22 14:14:22 +00:00
|
|
|
|
2011-03-17 14:41:15 +00:00
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
|
|
|
|
if (qemuMonitorCheckHMP(mon, "drive_del")) {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_DEBUG("drive_del command not found, trying HMP");
|
2011-03-17 14:41:15 +00:00
|
|
|
ret = qemuMonitorTextDriveDel(mon, drivestr);
|
|
|
|
} else {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_ERROR(_("deleting disk is not supported. "
|
2010-10-22 14:14:22 +00:00
|
|
|
"This may leak data if disk is reassigned"));
|
|
|
|
ret = 1;
|
|
|
|
}
|
2011-03-17 14:41:15 +00:00
|
|
|
} else if (qemuMonitorJSONHasError(reply, "DeviceNotFound")) {
|
|
|
|
/* NB: device not found errors mean the drive was
|
|
|
|
* auto-deleted and we ignore the error */
|
|
|
|
ret = 0;
|
|
|
|
} else {
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
2010-10-22 14:14:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (virAsprintf(&drive, "%s%s", QEMU_DRIVE_HOST_PREFIX, alias) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("savevm",
|
|
|
|
"s:name", name,
|
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
2011-02-23 11:12:11 +00:00
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
2010-04-02 14:10:37 +00:00
|
|
|
|
2011-03-21 12:34:16 +00:00
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound") &&
|
|
|
|
qemuMonitorCheckHMP(mon, "savevm")) {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_DEBUG("savevm command not found, trying HMP");
|
2011-02-23 11:12:11 +00:00
|
|
|
ret = qemuMonitorTextCreateSnapshot(mon, name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2010-04-02 14:10:37 +00:00
|
|
|
|
2011-02-23 11:12:11 +00:00
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
cleanup:
|
2010-04-02 14:10:37 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon, const char *name)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("loadvm",
|
|
|
|
"s:name", name,
|
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
2011-02-23 11:12:11 +00:00
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
2010-04-02 14:10:37 +00:00
|
|
|
|
2011-03-21 12:34:16 +00:00
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound") &&
|
|
|
|
qemuMonitorCheckHMP(mon, "loadvm")) {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_DEBUG("loadvm command not found, trying HMP");
|
2011-02-23 11:12:11 +00:00
|
|
|
ret = qemuMonitorTextLoadSnapshot(mon, name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
2010-04-02 14:10:37 +00:00
|
|
|
|
2011-02-23 11:12:11 +00:00
|
|
|
cleanup:
|
2010-04-02 14:10:37 +00:00
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("delvm",
|
|
|
|
"s:name", name,
|
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
2011-02-23 11:12:11 +00:00
|
|
|
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
|
|
|
|
goto cleanup;
|
2010-04-02 14:10:37 +00:00
|
|
|
|
2011-03-21 12:34:16 +00:00
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound") &&
|
|
|
|
qemuMonitorCheckHMP(mon, "delvm")) {
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_DEBUG("delvm command not found, trying HMP");
|
2011-02-23 11:12:11 +00:00
|
|
|
ret = qemuMonitorTextDeleteSnapshot(mon, name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
2010-04-02 14:10:37 +00:00
|
|
|
|
2011-02-23 11:12:11 +00:00
|
|
|
cleanup:
|
2010-04-02 14:10:37 +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) {
|
2011-03-21 12:34:16 +00:00
|
|
|
if (!qemuMonitorCheckHMP(mon, NULL)) {
|
|
|
|
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("HMP passthrough is not supported by qemu"
|
|
|
|
" process; only QMP commands can be used"));
|
|
|
|
return -1;
|
|
|
|
}
|
2011-03-09 20:24:04 +00:00
|
|
|
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
|
|
|
|
2011-02-02 15:37:10 +00:00
|
|
|
if (!(*reply_str = virJSONValueToString(reply)))
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2010-04-17 02:12:45 +00:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONHasError(reply, "CommandNotFound") &&
|
|
|
|
qemuMonitorCheckHMP(mon, "inject-nmi")) {
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
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-06-14 14:36:50 +00:00
|
|
|
|
|
|
|
static int qemuMonitorJSONGetBlockPullInfoOne(virJSONValuePtr entry,
|
|
|
|
const char *device,
|
|
|
|
virDomainBlockPullInfoPtr info)
|
|
|
|
{
|
|
|
|
const char *this_dev;
|
|
|
|
|
|
|
|
if ((this_dev = virJSONValueObjectGetString(entry, "device")) == NULL) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("entry was missing 'device'"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!STREQ(this_dev, device))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUlong(entry, "offset", &info->cur) < 0) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("entry was missing 'offset'"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUlong(entry, "len", &info->end) < 0) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("entry was missing 'len'"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** qemuMonitorJSONGetBlockPullInfo:
|
|
|
|
* Parse Block Pull information.
|
|
|
|
* The reply can be a JSON array of objects or just an object.
|
|
|
|
*/
|
|
|
|
static int qemuMonitorJSONGetBlockPullInfo(virJSONValuePtr reply,
|
|
|
|
const char *device,
|
|
|
|
virDomainBlockPullInfoPtr info)
|
|
|
|
{
|
|
|
|
virJSONValuePtr data;
|
|
|
|
int nr_results, i = 0;
|
|
|
|
|
|
|
|
if (!info)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((data = virJSONValueObjectGet(reply, "return")) == NULL) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("reply was missing block_pull progress information"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->type == VIR_JSON_TYPE_OBJECT) {
|
|
|
|
if (qemuMonitorJSONGetBlockPullInfoOne(data, device, info) != 0)
|
|
|
|
goto not_found;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
} else if (data->type != VIR_JSON_TYPE_ARRAY) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("urecognized format of block pull information"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((nr_results = virJSONValueArraySize(data)) < 0) {
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("unable to determine array size"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < nr_results; i++) {
|
|
|
|
virJSONValuePtr entry = virJSONValueArrayGet(data, i);
|
|
|
|
if (qemuMonitorJSONGetBlockPullInfoOne(entry, device, info) == 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
not_found:
|
|
|
|
qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
|
|
_("No associated information for the specified disk"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONBlockPull(qemuMonitorPtr mon,
|
|
|
|
const char *device,
|
|
|
|
virDomainBlockPullInfoPtr info,
|
|
|
|
int mode)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virJSONValuePtr cmd = NULL;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
int parse_info = 0;
|
|
|
|
|
|
|
|
if (mode == BLOCK_PULL_MODE_ONE) {
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("block_stream", "s:device", device, NULL);
|
|
|
|
parse_info = 1;
|
|
|
|
} else if (mode == BLOCK_PULL_MODE_ALL) {
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("block_stream", "s:device", device,
|
|
|
|
"b:all", 1, NULL);
|
|
|
|
} else if (mode == BLOCK_PULL_MODE_ABORT) {
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("block_stream", "s:device", device,
|
|
|
|
"b:stop", 1, NULL);
|
|
|
|
} else if (mode == BLOCK_PULL_MODE_INFO) {
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("query-block-stream", NULL);
|
|
|
|
parse_info = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0 && virJSONValueObjectHasKey(reply, "error")) {
|
|
|
|
if (qemuMonitorJSONHasError(reply, "DeviceNotActive"))
|
|
|
|
qemuReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("No active operation on device: %s"), device);
|
|
|
|
else if (qemuMonitorJSONHasError(reply, "DeviceInUse"))
|
|
|
|
qemuReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("Device %s in use"), device);
|
|
|
|
else if (qemuMonitorJSONHasError(reply, "NotSupported"))
|
|
|
|
qemuReportError(VIR_ERR_OPERATION_INVALID,
|
|
|
|
_("Operation is not supported for device: %s"), device);
|
|
|
|
else
|
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unexpected error"));
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0 && parse_info)
|
|
|
|
ret = qemuMonitorJSONGetBlockPullInfo(reply, device, info);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|