2009-11-03 12:59:18 -06:00
|
|
|
/*
|
|
|
|
* qemu_monitor_json.c: interaction with QEMU monitor console
|
|
|
|
*
|
2010-01-21 08:12:12 -06:00
|
|
|
* Copyright (C) 2006-2010 Red Hat, Inc.
|
2009-11-03 12:59:18 -06: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>
|
|
|
|
|
|
|
|
#include "qemu_monitor_json.h"
|
|
|
|
#include "qemu_conf.h"
|
|
|
|
#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 07:06:24 -06: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 13:28:15 -05: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 14:07:48 -05: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 14:37:44 -05:00
|
|
|
static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr data);
|
2009-11-26 07:06:24 -06:00
|
|
|
|
|
|
|
struct {
|
|
|
|
const char *type;
|
|
|
|
void (*handler)(qemuMonitorPtr mon, virJSONValuePtr data);
|
|
|
|
} eventHandlers[] = {
|
|
|
|
{ "SHUTDOWN", qemuMonitorJSONHandleShutdown, },
|
|
|
|
{ "RESET", qemuMonitorJSONHandleReset, },
|
|
|
|
{ "POWERDOWN", qemuMonitorJSONHandlePowerdown, },
|
|
|
|
{ "STOP", qemuMonitorJSONHandleStop, },
|
2010-03-18 13:28:15 -05: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 14:07:48 -05:00
|
|
|
{ "WATCHDOG", qemuMonitorJSONHandleWatchdog, },
|
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 14:37:44 -05:00
|
|
|
{ "DISK_IO_ERROR", qemuMonitorJSONHandleIOError, },
|
2009-11-26 07:06:24 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon,
|
|
|
|
virJSONValuePtr obj)
|
|
|
|
{
|
2010-01-22 07:22:53 -06:00
|
|
|
const char *type;
|
2009-11-26 07:06:24 -06:00
|
|
|
int i;
|
|
|
|
VIR_DEBUG("mon=%p obj=%p", mon, obj);
|
|
|
|
|
|
|
|
type = virJSONValueObjectGetString(obj, "event");
|
|
|
|
if (!type) {
|
|
|
|
VIR_WARN0("missing event type in message");
|
|
|
|
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 12:59:18 -06:00
|
|
|
static int
|
2009-11-26 07:06:24 -06:00
|
|
|
qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon,
|
2009-11-03 12:59:18 -06:00
|
|
|
const char *line,
|
|
|
|
qemuMonitorMessagePtr msg)
|
|
|
|
{
|
|
|
|
virJSONValuePtr obj = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
VIR_DEBUG("Line [%s]", line);
|
|
|
|
|
|
|
|
if (!(obj = virJSONValueFromString(line))) {
|
|
|
|
VIR_DEBUG0("Parsing JSON string failed");
|
|
|
|
errno = EINVAL;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj->type != VIR_JSON_TYPE_OBJECT) {
|
|
|
|
VIR_DEBUG0("Parsed JSON string isn't an object");
|
|
|
|
errno = EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectHasKey(obj, "QMP") == 1) {
|
|
|
|
VIR_DEBUG0("Got QMP capabilities data");
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectHasKey(obj, "event") == 1) {
|
2009-11-26 07:06:24 -06:00
|
|
|
ret = qemuMonitorJSONIOProcessEvent(mon, obj);
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg) {
|
|
|
|
if (!(msg->rxBuffer = strdup(line))) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
msg->rxLength = strlen(line);
|
|
|
|
msg->finished = 1;
|
|
|
|
} else {
|
|
|
|
VIR_DEBUG("Ignoring unexpected JSON message [%s]", line);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(obj);
|
|
|
|
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) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
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;
|
|
|
|
|
|
|
|
*reply = NULL;
|
|
|
|
|
|
|
|
memset(&msg, 0, sizeof msg);
|
|
|
|
|
|
|
|
if (!(cmdstr = virJSONValueToString(cmd))) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (virAsprintf(&msg.txBuffer, "%s\r\n", cmdstr) < 0) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06: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);
|
|
|
|
|
|
|
|
VIR_DEBUG("Receive command reply ret=%d errno=%d %d bytes '%s'",
|
|
|
|
ret, msg.lastErrno, msg.rxLength, msg.rxBuffer);
|
|
|
|
|
|
|
|
|
|
|
|
/* If we got ret==0, but not reply data something rather bad
|
|
|
|
* went wrong, so lets fake an EIO error */
|
|
|
|
if (!msg.rxBuffer && ret == 0) {
|
|
|
|
msg.lastErrno = EIO;
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
if (!((*reply) = virJSONValueFromString(msg.rxBuffer))) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse JSON doc '%s'"), msg.rxBuffer);
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0)
|
2010-02-04 14:02:58 -06:00
|
|
|
virReportSystemError(msg.lastErrno,
|
2009-11-03 12:59:18 -06:00
|
|
|
_("cannot send monitor command '%s'"), cmdstr);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cmdstr);
|
|
|
|
VIR_FREE(msg.txBuffer);
|
|
|
|
VIR_FREE(msg.rxBuffer);
|
|
|
|
|
|
|
|
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 10:45:11 -06:00
|
|
|
static const char *
|
|
|
|
qemuMonitorJSONStringifyError(virJSONValuePtr error)
|
2009-11-03 12:59:18 -06:00
|
|
|
{
|
2010-01-22 07:22:53 -06:00
|
|
|
const char *klass = virJSONValueObjectGetString(error, "class");
|
2010-02-12 10:45:11 -06:00
|
|
|
const char *detail = NULL;
|
2009-11-03 12:59:18 -06:00
|
|
|
|
2010-02-12 10:45:11 -06: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 12:59:18 -06: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 10:45:11 -06: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 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
2010-02-12 10:45:11 -06:00
|
|
|
_("unable to execute QEMU command '%s'"),
|
|
|
|
qemuMonitorJSONCommandName(cmd));
|
|
|
|
else
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
2010-02-12 10:45:11 -06:00
|
|
|
_("unable to execute QEMU command '%s': %s"),
|
|
|
|
qemuMonitorJSONCommandName(cmd),
|
|
|
|
qemuMonitorJSONStringifyError(error));
|
|
|
|
|
2009-11-03 12:59:18 -06: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 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
2010-02-12 10:45:11 -06:00
|
|
|
_("unable to execute QEMU command '%s'"),
|
|
|
|
qemuMonitorJSONCommandName(cmd));
|
2009-11-03 12:59:18 -06:00
|
|
|
VIR_FREE(cmdstr);
|
|
|
|
VIR_FREE(replystr);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONHasError(virJSONValuePtr reply,
|
|
|
|
const char *klass)
|
|
|
|
{
|
|
|
|
virJSONValuePtr error;
|
2010-01-22 07:22:53 -06:00
|
|
|
const char *thisklass;
|
2009-11-03 12:59:18 -06: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 int
|
|
|
|
qemuMonitorJSONCommandAddTimestamp(virJSONValuePtr obj)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
virJSONValuePtr timestamp = NULL;
|
|
|
|
|
|
|
|
if (gettimeofday(&tv, NULL) < 0) {
|
2010-02-04 14:02:58 -06:00
|
|
|
virReportSystemError(errno, "%s",
|
2009-11-03 12:59:18 -06:00
|
|
|
_("cannot query time of day"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(timestamp = virJSONValueNewObject()))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (virJSONValueObjectAppendNumberLong(timestamp, "seconds", tv.tv_sec) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (virJSONValueObjectAppendNumberLong(timestamp, "microseconds", tv.tv_usec) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (virJSONValueObjectAppend(obj, "timestamp", timestamp) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
virJSONValueFree(timestamp);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (qemuMonitorJSONCommandAddTimestamp(obj) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
while ((key = va_arg(args, char *)) != NULL) {
|
|
|
|
int ret;
|
|
|
|
char type;
|
|
|
|
|
|
|
|
if (strlen(key) < 3) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("argument key '%s' is too short, missing type prefix"),
|
|
|
|
key);
|
2009-11-03 12:59:18 -06: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': {
|
|
|
|
unsigned long long val = va_arg(args, unsigned long long);
|
|
|
|
ret = virJSONValueObjectAppendNumberUlong(jargs, key, val);
|
|
|
|
} 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 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unsupported data type '%c' for arg '%s'"), type, key - 2);
|
2009-11-03 12:59:18 -06: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 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
error:
|
|
|
|
virJSONValueFree(obj);
|
|
|
|
virJSONValueFree(jargs);
|
|
|
|
va_end(args);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-26 07:06:24 -06: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 13:28:15 -05:00
|
|
|
static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
long long offset = 0;
|
|
|
|
if (virJSONValueObjectGetNumberLong(data, "offset", &offset) < 0) {
|
|
|
|
VIR_WARN0("missing offset in RTC change event");
|
|
|
|
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 14:07:48 -05: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 14:37:44 -05: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 14:07:48 -05:00
|
|
|
|
|
|
|
static void qemuMonitorJSONHandleWatchdog(qemuMonitorPtr mon, virJSONValuePtr data)
|
|
|
|
{
|
|
|
|
const char *action;
|
|
|
|
int actionID;
|
|
|
|
if (!(action = virJSONValueObjectGetString(data, "action"))) {
|
|
|
|
VIR_WARN0("missing action in watchdog event");
|
|
|
|
}
|
|
|
|
if (action) {
|
|
|
|
if ((actionID = qemuMonitorWatchdogActionTypeFromString(action)) < 0) {
|
|
|
|
VIR_WARN("unknown action %s in watchdog event", action);
|
|
|
|
actionID = VIR_DOMAIN_EVENT_WATCHDOG_NONE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
actionID = VIR_DOMAIN_EVENT_WATCHDOG_NONE;
|
|
|
|
}
|
|
|
|
qemuMonitorEmitRTCChange(mon, actionID);
|
|
|
|
}
|
|
|
|
|
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 14:37:44 -05: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;
|
|
|
|
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) {
|
|
|
|
VIR_WARN0("Missing action in disk io error event");
|
|
|
|
action = "ignore";
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((device = virJSONValueObjectGetString(data, "device")) == NULL) {
|
|
|
|
VIR_WARN0("missing device in disk io error event");
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((actionID = qemuMonitorIOErrorActionTypeFromString(action)) < 0) {
|
|
|
|
VIR_WARN("unknown disk io error action '%s'", action);
|
|
|
|
actionID = VIR_DOMAIN_EVENT_IO_ERROR_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
qemuMonitorEmitIOError(mon, device, actionID);
|
|
|
|
}
|
|
|
|
|
2009-11-26 07:06:24 -06:00
|
|
|
|
2010-02-12 07:45:20 -06:00
|
|
|
int
|
|
|
|
qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("qmp_capabilities", NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 12:59:18 -06:00
|
|
|
int
|
|
|
|
qemuMonitorJSONStartCPUs(qemuMonitorPtr mon,
|
|
|
|
virConnectPtr conn ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("cont", 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
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 08:52:05 -06: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 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("cpu reply was missing return data"));
|
2010-01-22 08:52:05 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->type != VIR_JSON_TYPE_ARRAY) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("cpu information was not an array"));
|
2010-01-22 08:52:05 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ncpus = virJSONValueArraySize(data)) <= 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("cpu information was empty"));
|
2010-01-22 08:52:05 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(threads, ncpus) < 0) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2010-01-22 08:52:05 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0 ; i < ncpus ; i++) {
|
|
|
|
virJSONValuePtr entry = virJSONValueArrayGet(data, i);
|
|
|
|
int cpu;
|
|
|
|
int thread;
|
|
|
|
if (!entry) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device information was missing aray element"));
|
2010-01-22 08:52:05 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberInt(entry, "CPU", &cpu) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("cpu information was missing cpu number"));
|
2010-01-22 08:52:05 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberInt(entry, "thread_id", &thread) < 0) {
|
2010-02-15 08:41:50 -06:00
|
|
|
/* Only qemu-kvm tree includs thread_id, so treat this as
|
|
|
|
non-fatal, simply returning no data */
|
|
|
|
ret = 0;
|
2010-01-22 08:52:05 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cpu != i) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected cpu index %d expecting %d"),
|
|
|
|
i, cpu);
|
2010-01-22 08:52:05 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
threads[i] = thread;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pids = threads;
|
|
|
|
threads = NULL;
|
2010-03-10 12:22:02 -06:00
|
|
|
ret = ncpus;
|
2010-01-22 08:52:05 -06:00
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(threads);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 12:59:18 -06: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 08:52:05 -06:00
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONExtractCPUInfo(reply, pids);
|
2009-11-03 12:59:18 -06: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 07:22:53 -06:00
|
|
|
virJSONValuePtr data;
|
2009-11-03 12:59:18 -06:00
|
|
|
unsigned long long mem;
|
|
|
|
|
2010-01-22 07:22:53 -06:00
|
|
|
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info balloon reply was missing return data"));
|
2010-01-22 07:22:53 -06:00
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2010-02-16 10:31:19 -06:00
|
|
|
if (virJSONValueObjectGetNumberUlong(data, "actual", &mem) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info balloon reply was missing balloon data"));
|
2009-11-03 12:59:18 -06:00
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2010-02-16 10:31:19 -06:00
|
|
|
*currmem = (mem/1024);
|
2009-11-03 12:59:18 -06:00
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
devices = virJSONValueObjectGet(reply, "return");
|
|
|
|
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats reply was missing device list"));
|
2009-11-03 12:59:18 -06: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 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats device entry was not in expected format"));
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats device entry was not in expected format"));
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2010-02-10 11:46:44 -06: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 12:59:18 -06:00
|
|
|
if (STRNEQ(thisdev, devname))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
found = 1;
|
|
|
|
if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL ||
|
|
|
|
stats->type != VIR_JSON_TYPE_OBJECT) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("blockstats stats entry was not in expected format"));
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberLong(stats, "rd_bytes", rd_bytes) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"rd_bytes");
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberLong(stats, "rd_operations", rd_req) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"rd_operations");
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberLong(stats, "wr_bytes", wr_bytes) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"wr_bytes");
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberLong(stats, "wr_operations", wr_req) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot read %s statistic"),
|
|
|
|
"wr_operations");
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot find statistics for device '%s'"), devname);
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 10:31:19 -06:00
|
|
|
"U:value", ((unsigned long long)newmem)*1024,
|
2009-11-03 12:59:18 -06: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 10:37:17 -06: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;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("balloon",
|
|
|
|
"U:cpu", (unsigned long long)cpu,
|
|
|
|
"s:state", online ? "online" : "offline",
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
cleanup:
|
|
|
|
#endif
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-03 12:59:18 -06:00
|
|
|
int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon,
|
|
|
|
const char *devname)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("eject",
|
|
|
|
"s:device", devname,
|
|
|
|
"i:force", 0,
|
|
|
|
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;
|
|
|
|
char *bandwidthstr;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (virAsprintf(&bandwidthstr, "%lum", bandwidth) < 0) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("migrate_set_speed",
|
|
|
|
"s:value", bandwidthstr,
|
|
|
|
NULL);
|
|
|
|
VIR_FREE(bandwidthstr);
|
|
|
|
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 10:53:14 -05:00
|
|
|
int qemuMonitorJSONSetMigrationDowntime(qemuMonitorPtr mon,
|
|
|
|
unsigned long long downtime)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
char *downtimestr;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
if (virAsprintf(&downtimestr, "%llums", downtime) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("migrate_set_downtime",
|
|
|
|
"s:value", downtimestr,
|
|
|
|
NULL);
|
|
|
|
VIR_FREE(downtimestr);
|
|
|
|
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 12:59:18 -06: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 07:22:53 -06:00
|
|
|
const char *statusstr;
|
2009-11-03 12:59:18 -06:00
|
|
|
|
|
|
|
if (!(ret = virJSONValueObjectGet(reply, "return"))) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info migration reply was missing return data"));
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(statusstr = virJSONValueObjectGetString(ret, "status"))) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("info migration reply was missing return status"));
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((*status = qemuMonitorMigrationStatusTypeFromString(statusstr)) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected migration status in %s"), statusstr);
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*status == QEMU_MONITOR_MIGRATION_STATUS_ACTIVE) {
|
|
|
|
virJSONValuePtr ram = virJSONValueObjectGet(ret, "ram");
|
|
|
|
if (!ram) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("migration was active, but no RAM info was set"));
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUlong(ram, "transferred", transferred) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("migration was active, but RAM 'transferred' data was missing"));
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberUlong(ram, "remaining", remaining) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("migration was active, but RAM 'remaining' data was missing"));
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberUlong(ram, "total", total) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("migration was active, but RAM 'total' data was missing"));
|
2009-11-03 12:59:18 -06: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 11:07:50 -06:00
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-migrate",
|
2009-11-03 12:59:18 -06: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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int qemuMonitorJSONMigrate(qemuMonitorPtr mon,
|
|
|
|
int background,
|
|
|
|
const char *uri)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("migrate",
|
|
|
|
"i:detach", background ? 1 : 0,
|
|
|
|
"s:uri", uri,
|
|
|
|
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 qemuMonitorJSONMigrateToHost(qemuMonitorPtr mon,
|
|
|
|
int background,
|
|
|
|
const char *hostname,
|
|
|
|
int port)
|
|
|
|
{
|
|
|
|
char *uri = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (virAsprintf(&uri, "tcp:%s:%d", hostname, port) < 0) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONMigrate(mon, background, uri);
|
|
|
|
|
|
|
|
VIR_FREE(uri);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONMigrateToCommand(qemuMonitorPtr mon,
|
|
|
|
int background,
|
|
|
|
const char * const *argv,
|
|
|
|
const char *target)
|
|
|
|
{
|
|
|
|
char *argstr;
|
|
|
|
char *dest = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
char *safe_target = NULL;
|
|
|
|
|
|
|
|
argstr = virArgvToString(argv);
|
|
|
|
if (!argstr) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Migrate to file */
|
|
|
|
safe_target = qemuMonitorEscapeShell(target);
|
|
|
|
if (!safe_target) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&dest, "exec:%s >>%s 2>/dev/null", argstr, safe_target) < 0) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONMigrate(mon, background, dest);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(safe_target);
|
|
|
|
VIR_FREE(argstr);
|
|
|
|
VIR_FREE(dest);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemuMonitorJSONMigrateToUnix(qemuMonitorPtr mon,
|
|
|
|
int background,
|
|
|
|
const char *unixfile)
|
|
|
|
{
|
|
|
|
char *dest = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&dest, "unix:%s", unixfile) < 0) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONMigrate(mon, background, dest);
|
|
|
|
|
|
|
|
VIR_FREE(dest);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int qemuMonitorJSONAddUSB(qemuMonitorPtr mon,
|
|
|
|
const char *dev)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("usb_add",
|
|
|
|
"s:devname", dev,
|
|
|
|
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 qemuMonitorJSONAddUSBDisk(qemuMonitorPtr mon,
|
|
|
|
const char *path)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
char *disk;
|
|
|
|
|
|
|
|
if (virAsprintf(&disk, "disk:%s", path) < 0) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONAddUSB(mon, disk);
|
|
|
|
|
|
|
|
VIR_FREE(disk);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONAddUSBDeviceExact(qemuMonitorPtr mon,
|
|
|
|
int bus,
|
|
|
|
int dev)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
char *addr;
|
|
|
|
|
|
|
|
if (virAsprintf(&addr, "host:%.3d.%.3d", bus, dev) < 0) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONAddUSB(mon, addr);
|
|
|
|
|
|
|
|
VIR_FREE(addr);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONAddUSBDeviceMatch(qemuMonitorPtr mon,
|
|
|
|
int vendor,
|
|
|
|
int product)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
char *addr;
|
|
|
|
|
|
|
|
if (virAsprintf(&addr, "host:%.4x:%.4x", vendor, product) < 0) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONAddUSB(mon, addr);
|
|
|
|
|
|
|
|
VIR_FREE(addr);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
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 11:57:09 -06:00
|
|
|
qemuMonitorJSONGetGuestPCIAddress(virJSONValuePtr reply,
|
|
|
|
virDomainDevicePCIAddress *guestAddr)
|
2009-11-03 12:59:18 -06:00
|
|
|
{
|
|
|
|
virJSONValuePtr addr;
|
|
|
|
|
|
|
|
addr = virJSONValueObjectGet(reply, "return");
|
|
|
|
if (!addr || addr->type != VIR_JSON_TYPE_OBJECT) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_add reply was missing device address"));
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-12-07 13:07:39 -06:00
|
|
|
if (virJSONValueObjectGetNumberUint(addr, "domain", &guestAddr->domain) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_add reply was missing device domain number"));
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-12-07 13:07:39 -06:00
|
|
|
if (virJSONValueObjectGetNumberUint(addr, "bus", &guestAddr->bus) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_add reply was missing device bus number"));
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-12-07 13:07:39 -06:00
|
|
|
if (virJSONValueObjectGetNumberUint(addr, "slot", &guestAddr->slot) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_add reply was missing device slot number"));
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-12-07 13:07:39 -06:00
|
|
|
if (virJSONValueObjectGetNumberUint(addr, "function", &guestAddr->function) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("pci_add reply was missing device function number"));
|
2009-12-07 13:07:39 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-11-03 12:59:18 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONAddPCIHostDevice(qemuMonitorPtr mon,
|
2009-12-07 13:07:39 -06:00
|
|
|
virDomainDevicePCIAddress *hostAddr,
|
|
|
|
virDomainDevicePCIAddress *guestAddr)
|
2009-11-03 12:59:18 -06:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
char *dev;
|
|
|
|
|
2009-12-07 13:07:39 -06:00
|
|
|
memset(guestAddr, 0, sizeof(*guestAddr));
|
2009-11-03 12:59:18 -06:00
|
|
|
|
|
|
|
/* XXX hostDomain */
|
|
|
|
if (virAsprintf(&dev, "host=%.2x:%.2x.%.1x",
|
2009-12-07 13:07:39 -06:00
|
|
|
hostAddr->bus, hostAddr->slot, hostAddr->function) < 0) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("pci_add",
|
|
|
|
"s:pci_addr", "auto"
|
|
|
|
"s:type", "host",
|
|
|
|
"s:opts", dev,
|
|
|
|
NULL);
|
|
|
|
VIR_FREE(dev);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret == 0 &&
|
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 11:57:09 -06:00
|
|
|
qemuMonitorJSONGetGuestPCIAddress(reply, guestAddr) < 0)
|
2009-11-03 12:59:18 -06:00
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONAddPCIDisk(qemuMonitorPtr mon,
|
|
|
|
const char *path,
|
|
|
|
const char *bus,
|
2009-12-07 13:07:39 -06:00
|
|
|
virDomainDevicePCIAddress *guestAddr)
|
|
|
|
{
|
2009-11-03 12:59:18 -06:00
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
char *dev;
|
|
|
|
|
2009-12-07 13:07:39 -06:00
|
|
|
memset(guestAddr, 0, sizeof(*guestAddr));
|
2009-11-03 12:59:18 -06:00
|
|
|
|
|
|
|
if (virAsprintf(&dev, "file=%s,if=%s", path, bus) < 0) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("pci_add",
|
|
|
|
"s:pci_addr", "auto",
|
|
|
|
"s:type", "storage",
|
|
|
|
"s:opts", dev,
|
|
|
|
NULL);
|
|
|
|
VIR_FREE(dev);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret == 0 &&
|
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 11:57:09 -06:00
|
|
|
qemuMonitorJSONGetGuestPCIAddress(reply, guestAddr) < 0)
|
2009-11-03 12:59:18 -06:00
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONAddPCINetwork(qemuMonitorPtr mon,
|
|
|
|
const char *nicstr,
|
2009-12-07 13:07:39 -06:00
|
|
|
virDomainDevicePCIAddress *guestAddr)
|
2009-11-03 12:59:18 -06:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("pci_add",
|
|
|
|
"s:pci_addr", "auto",
|
|
|
|
"s:type", "nic",
|
|
|
|
"s:opts", nicstr,
|
|
|
|
NULL);
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
2009-12-07 13:07:39 -06:00
|
|
|
memset(guestAddr, 0, sizeof(*guestAddr));
|
2009-11-03 12:59:18 -06:00
|
|
|
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret == 0 &&
|
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 11:57:09 -06:00
|
|
|
qemuMonitorJSONGetGuestPCIAddress(reply, guestAddr) < 0)
|
2009-11-03 12:59:18 -06:00
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONRemovePCIDevice(qemuMonitorPtr mon,
|
2009-12-07 13:07:39 -06:00
|
|
|
virDomainDevicePCIAddress *guestAddr)
|
2009-11-03 12:59:18 -06:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
char *addr;
|
|
|
|
|
2009-12-07 13:07:39 -06:00
|
|
|
/* XXX what about function ? */
|
2009-11-03 12:59:18 -06:00
|
|
|
if (virAsprintf(&addr, "%.4x:%.2x:%.2x",
|
2009-12-07 13:07:39 -06:00
|
|
|
guestAddr->domain, guestAddr->bus, guestAddr->slot) < 0) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-11-03 12:59:18 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("pci_del",
|
|
|
|
"s:pci_addr", addr,
|
|
|
|
NULL);
|
|
|
|
VIR_FREE(addr);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 13:28:05 -06:00
|
|
|
|
|
|
|
|
2010-01-22 07:22:53 -06: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 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device reply was missing return data"));
|
2010-01-22 07:22:53 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->type != VIR_JSON_TYPE_ARRAY) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device information was not an array"));
|
2010-01-22 07:22:53 -06: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 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device information was missing aray element"));
|
2010-01-22 07:22:53 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(type = virJSONValueObjectGetString(entry, "filename"))) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device information was missing filename"));
|
2010-01-22 07:22:53 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(id = virJSONValueObjectGetString(entry, "label"))) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("character device information was missing filename"));
|
2010-01-22 07:22:53 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STRPREFIX(type, "pty:")) {
|
|
|
|
char *path = strdup(type + strlen("pty:"));
|
|
|
|
if (!path) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2010-01-22 07:22:53 -06:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virHashAddEntry(paths, id, path) < 0) {
|
2010-02-09 12:15:41 -06:00
|
|
|
qemuReportError(VIR_ERR_OPERATION_FAILED,
|
|
|
|
_("failed to save chardev path '%s'"), path);
|
2010-01-22 07:22:53 -06: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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-07 13:28:05 -06:00
|
|
|
int qemuMonitorJSONAttachPCIDiskController(qemuMonitorPtr mon,
|
|
|
|
const char *bus,
|
|
|
|
virDomainDevicePCIAddress *guestAddr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
char *dev;
|
|
|
|
|
|
|
|
memset(guestAddr, 0, sizeof(*guestAddr));
|
|
|
|
|
|
|
|
if (virAsprintf(&dev, "if=%s", bus) < 0) {
|
2010-02-04 12:19:08 -06:00
|
|
|
virReportOOMError();
|
2009-12-07 13:28:05 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("pci_add",
|
|
|
|
"s:pci_addr", "auto",
|
|
|
|
"s:type", "storage",
|
|
|
|
"s:opts", dev,
|
|
|
|
NULL);
|
|
|
|
VIR_FREE(dev);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
if (ret == 0 &&
|
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 11:57:09 -06:00
|
|
|
qemuMonitorJSONGetGuestPCIAddress(reply, guestAddr) < 0)
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuMonitorJSONGetGuestDriveAddress(virJSONValuePtr reply,
|
|
|
|
virDomainDeviceDriveAddress *driveAddr)
|
|
|
|
{
|
|
|
|
virJSONValuePtr addr;
|
|
|
|
|
|
|
|
addr = virJSONValueObjectGet(reply, "return");
|
|
|
|
if (!addr || addr->type != VIR_JSON_TYPE_OBJECT) {
|
2010-02-09 12:15:41 -06: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 11:57:09 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUint(addr, "bus", &driveAddr->bus) < 0) {
|
2010-02-09 12:15:41 -06: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 11:57:09 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUint(addr, "unit", &driveAddr->unit) < 0) {
|
2010-02-09 12:15:41 -06: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 11:57:09 -06: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 12:19:08 -06: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 11:57:09 -06: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 13:28:05 -06: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 15:59:04 -06:00
|
|
|
|
|
|
|
|
|
|
|
int qemuMonitorJSONGetAllPCIAddresses(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
qemuMonitorPCIAddress **addrs ATTRIBUTE_UNUSED)
|
|
|
|
{
|
2010-02-09 12:15:41 -06: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 15:59:04 -06:00
|
|
|
return -1;
|
|
|
|
}
|
2010-01-26 09:34:46 -06:00
|
|
|
|
|
|
|
|
2010-03-02 02:40:51 -06:00
|
|
|
int qemuMonitorJSONDelDevice(qemuMonitorPtr mon,
|
|
|
|
const char *devicestr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("device_del",
|
|
|
|
"s:config", devicestr,
|
|
|
|
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 09:34:46 -06:00
|
|
|
int qemuMonitorJSONAddDevice(qemuMonitorPtr mon,
|
|
|
|
const char *devicestr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
virJSONValuePtr cmd;
|
|
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("device_add",
|
|
|
|
"s:config", devicestr,
|
|
|
|
NULL);
|
|
|
|
if (!cmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
|
|
|
|
virJSONValueFree(cmd);
|
|
|
|
virJSONValueFree(reply);
|
|
|
|
return ret;
|
|
|
|
}
|
2010-02-11 08:28:16 -06: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;
|
|
|
|
}
|