qemu: wire up RPC for qemu monitor events

These are the first async events in the qemu protocol, so this
patch looks rather big compared to most RPC additions.  However,
a large majority of this patch is just mechanical copy-and-paste
from recently-added network events.  It didn't help that this
is also the first virConnect rather than virDomain prefix
associated with a qemu-specific API.

* src/remote/qemu_protocol.x (qemu_*_domain_monitor_event_*): New
structs and RPC messages.
* src/rpc/gendispatch.pl: Adjust naming conventions.
* daemon/libvirtd.h (daemonClientPrivate): Track qemu events.
* daemon/remote.c (remoteClientFreeFunc): Likewise.
(remoteRelayDomainQemuMonitorEvent)
(qemuDispatchConnectDomainMonitorEventRegister)
(qemuDispatchConnectDomainMonitorEventDeregister): New functions.
* src/remote/remote_driver.c (qemuEvents): Handle qemu events.
(doRemoteOpen): Register for events.
(remoteNetworkBuildEventLifecycle)
(remoteConnectDomainQemuMonitorEventRegister)
(remoteConnectDomainQemuMonitorEventDeregister): New functions.
* src/qemu_protocol-structs: Regenerate.

Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
Eric Blake 2014-01-29 15:30:44 -07:00
parent 8059afca62
commit e7708a1c47
6 changed files with 428 additions and 11 deletions

View File

@ -54,6 +54,8 @@ struct daemonClientPrivate {
size_t ndomainEventCallbacks;
daemonClientEventCallbackPtr *networkEventCallbacks;
size_t nnetworkEventCallbacks;
daemonClientEventCallbackPtr *qemuEventCallbacks;
size_t nqemuEventCallbacks;
# if WITH_SASL
virNetSASLSessionPtr sasl;

View File

@ -54,6 +54,7 @@
#include "network_conf.h"
#include "virprobe.h"
#include "viraccessapicheck.h"
#include "viraccessapicheckqemu.h"
#define VIR_FROM_THIS VIR_FROM_RPC
@ -189,6 +190,33 @@ cleanup:
}
static bool
remoteRelayDomainQemuMonitorEventCheckACL(virNetServerClientPtr client,
virConnectPtr conn, virDomainPtr dom)
{
virDomainDef def;
virIdentityPtr identity = NULL;
bool ret = false;
/* For now, we just create a virDomainDef with enough contents to
* satisfy what viraccessdriverpolkit.c references. This is a bit
* fragile, but I don't know of anything better. */
def.name = dom->name;
memcpy(def.uuid, dom->uuid, VIR_UUID_BUFLEN);
if (!(identity = virNetServerClientGetIdentity(client)))
goto cleanup;
if (virIdentitySetCurrent(identity) < 0)
goto cleanup;
ret = virConnectDomainQemuMonitorEventRegisterCheckACL(conn, &def);
cleanup:
ignore_value(virIdentitySetCurrent(NULL));
virObjectUnref(identity);
return ret;
}
static int
remoteRelayDomainEventLifecycle(virConnectPtr conn,
virDomainPtr dom,
@ -961,6 +989,52 @@ static virConnectNetworkEventGenericCallback networkEventCallbacks[] = {
verify(ARRAY_CARDINALITY(networkEventCallbacks) == VIR_NETWORK_EVENT_ID_LAST);
static void
remoteRelayDomainQemuMonitorEvent(virConnectPtr conn,
virDomainPtr dom,
const char *event,
long long seconds,
unsigned int micros,
const char *details,
void *opaque)
{
daemonClientEventCallbackPtr callback = opaque;
qemu_domain_monitor_event_msg data;
char **details_p = NULL;
if (callback->callbackID < 0 ||
!remoteRelayDomainQemuMonitorEventCheckACL(callback->client, conn,
dom))
return;
VIR_DEBUG("Relaying qemu monitor event %s %s, callback %d",
event, details, callback->callbackID);
/* build return data */
memset(&data, 0, sizeof(data));
data.callbackID = callback->callbackID;
if (VIR_STRDUP(data.event, event) < 0)
goto error;
data.seconds = seconds;
data.micros = micros;
if (details &&
((VIR_ALLOC(details_p) < 0) ||
VIR_STRDUP(*details_p, details) < 0))
goto error;
data.details = details_p;
make_nonnull_domain(&data.dom, dom);
remoteDispatchObjectEventSend(callback->client, qemuProgram,
QEMU_PROC_DOMAIN_MONITOR_EVENT,
(xdrproc_t)xdr_qemu_domain_monitor_event_msg,
&data);
return;
error:
VIR_FREE(data.event);
VIR_FREE(details_p);
}
/*
* You must hold lock for at least the client
* We don't free stuff here, merely disconnect the client's
@ -1008,6 +1082,21 @@ void remoteClientFreeFunc(void *data)
}
VIR_FREE(priv->networkEventCallbacks);
for (i = 0; i < priv->nqemuEventCallbacks; i++) {
int callbackID = priv->qemuEventCallbacks[i]->callbackID;
if (callbackID < 0) {
VIR_WARN("unexpected incomplete qemu monitor callback %zu", i);
continue;
}
VIR_DEBUG("Deregistering remote qemu monitor event relay %d",
callbackID);
priv->qemuEventCallbacks[i]->callbackID = -1;
if (virConnectDomainQemuMonitorEventDeregister(priv->conn,
callbackID) < 0)
VIR_WARN("unexpected qemu monitor event deregister failure");
}
VIR_FREE(priv->qemuEventCallbacks);
virConnectClose(priv->conn);
virIdentitySetCurrent(NULL);
@ -5869,6 +5958,126 @@ cleanup:
}
static int
qemuDispatchConnectDomainMonitorEventRegister(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
qemu_connect_domain_monitor_event_register_args *args,
qemu_connect_domain_monitor_event_register_ret *ret)
{
int callbackID;
int rv = -1;
daemonClientEventCallbackPtr callback = NULL;
daemonClientEventCallbackPtr ref;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
virDomainPtr dom = NULL;
const char *event = args->event ? *args->event : NULL;
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
virMutexLock(&priv->lock);
if (args->dom &&
!(dom = get_nonnull_domain(priv->conn, *args->dom)))
goto cleanup;
/* If we call register first, we could append a complete callback
* to our array, but on OOM append failure, we'd have to then hope
* deregister works to undo our register. So instead we append an
* incomplete callback to our array, then register, then fix up
* our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
* success, we use 'ref' to save a copy of the pointer. */
if (VIR_ALLOC(callback) < 0)
goto cleanup;
callback->client = client;
callback->callbackID = -1;
ref = callback;
if (VIR_APPEND_ELEMENT(priv->qemuEventCallbacks,
priv->nqemuEventCallbacks,
callback) < 0)
goto cleanup;
if ((callbackID = virConnectDomainQemuMonitorEventRegister(priv->conn,
dom,
event,
remoteRelayDomainQemuMonitorEvent,
ref,
remoteEventCallbackFree,
args->flags)) < 0) {
VIR_SHRINK_N(priv->qemuEventCallbacks,
priv->nqemuEventCallbacks, 1);
callback = ref;
goto cleanup;
}
ref->callbackID = callbackID;
ret->callbackID = callbackID;
rv = 0;
cleanup:
VIR_FREE(callback);
if (rv < 0)
virNetMessageSaveError(rerr);
if (dom)
virDomainFree(dom);
virMutexUnlock(&priv->lock);
return rv;
}
static int
qemuDispatchConnectDomainMonitorEventDeregister(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
qemu_connect_domain_monitor_event_deregister_args *args)
{
int rv = -1;
size_t i;
struct daemonClientPrivate *priv =
virNetServerClientGetPrivateData(client);
if (!priv->conn) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
goto cleanup;
}
virMutexLock(&priv->lock);
for (i = 0; i < priv->nqemuEventCallbacks; i++) {
if (priv->qemuEventCallbacks[i]->callbackID == args->callbackID)
break;
}
if (i == priv->nqemuEventCallbacks) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("qemu monitor event callback %d not registered"),
args->callbackID);
goto cleanup;
}
if (virConnectDomainQemuMonitorEventDeregister(priv->conn,
args->callbackID) < 0)
goto cleanup;
VIR_DELETE_ELEMENT(priv->qemuEventCallbacks, i,
priv->nqemuEventCallbacks);
rv = 0;
cleanup:
if (rv < 0)
virNetMessageSaveError(rerr);
virMutexUnlock(&priv->lock);
return rv;
}
/*----- Helpers. -----*/
/* get_nonnull_domain and get_nonnull_network turn an on-wire

View File

@ -28,8 +28,30 @@ struct qemu_domain_agent_command_args {
struct qemu_domain_agent_command_ret {
remote_string result;
};
struct qemu_connect_domain_monitor_event_register_args {
remote_domain dom;
remote_string event;
u_int flags;
};
struct qemu_connect_domain_monitor_event_register_ret {
int callbackID;
};
struct qemu_connect_domain_monitor_event_deregister_args {
int callbackID;
};
struct qemu_domain_monitor_event_msg {
int callbackID;
remote_nonnull_domain dom;
remote_nonnull_string event;
int64_t seconds;
u_int micros;
remote_string details;
};
enum qemu_procedure {
QEMU_PROC_DOMAIN_MONITOR_COMMAND = 1,
QEMU_PROC_DOMAIN_ATTACH = 2,
QEMU_PROC_DOMAIN_AGENT_COMMAND = 3,
QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_REGISTER = 4,
QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_DEREGISTER = 5,
QEMU_PROC_DOMAIN_MONITOR_EVENT = 6,
};

View File

@ -3,7 +3,7 @@
* remote_internal driver and libvirtd. This protocol is
* internal and may change at any time.
*
* Copyright (C) 2010-2012 Red Hat, Inc.
* Copyright (C) 2010-2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -58,6 +58,30 @@ struct qemu_domain_agent_command_ret {
remote_string result;
};
struct qemu_connect_domain_monitor_event_register_args {
remote_domain dom;
remote_string event;
unsigned int flags;
};
struct qemu_connect_domain_monitor_event_register_ret {
int callbackID;
};
struct qemu_connect_domain_monitor_event_deregister_args {
int callbackID;
};
struct qemu_domain_monitor_event_msg {
int callbackID;
remote_nonnull_domain dom;
remote_nonnull_string event;
hyper seconds;
unsigned int micros;
remote_string details;
};
/* Define the program number, protocol version and procedure numbers here. */
const QEMU_PROGRAM = 0x20008087;
const QEMU_PROTOCOL_VERSION = 1;
@ -108,5 +132,27 @@ enum qemu_procedure {
* @priority: low
* @acl: domain:write
*/
QEMU_PROC_DOMAIN_AGENT_COMMAND = 3
QEMU_PROC_DOMAIN_AGENT_COMMAND = 3,
/**
* @generate: none
* @priority: high
* @acl: connect:search_domains
* @acl: connect:write
* @aclfilter: domain:getattr
*/
QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_REGISTER = 4,
/**
* @generate: none
* @priority: high
* @acl: connect:write
*/
QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_DEREGISTER = 5,
/**
* @generate: both
* @acl: none
*/
QEMU_PROC_DOMAIN_MONITOR_EVENT = 6
};

View File

@ -500,6 +500,19 @@ static virNetClientProgramEvent remoteEvents[] = {
(xdrproc_t)xdr_remote_domain_event_callback_device_removed_msg },
};
static void
remoteDomainBuildQemuMonitorEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
virNetClientPtr client ATTRIBUTE_UNUSED,
void *evdata, void *opaque);
static virNetClientProgramEvent qemuEvents[] = {
{ QEMU_PROC_DOMAIN_MONITOR_EVENT,
remoteDomainBuildQemuMonitorEvent,
sizeof(qemu_domain_monitor_event_msg),
(xdrproc_t)xdr_qemu_domain_monitor_event_msg },
};
enum virDrvOpenRemoteFlags {
VIR_DRV_OPEN_REMOTE_RO = (1 << 0),
VIR_DRV_OPEN_REMOTE_USER = (1 << 1), /* Use the per-user socket path */
@ -977,9 +990,9 @@ doRemoteOpen(virConnectPtr conn,
goto failed;
if (!(priv->qemuProgram = virNetClientProgramNew(QEMU_PROGRAM,
QEMU_PROTOCOL_VERSION,
NULL,
0,
NULL)))
qemuEvents,
ARRAY_CARDINALITY(qemuEvents),
conn)))
goto failed;
if (virNetClientAddProgram(priv->client, priv->remoteProgram) < 0 ||
@ -3175,6 +3188,103 @@ done:
}
static int
remoteConnectDomainQemuMonitorEventRegister(virConnectPtr conn,
virDomainPtr dom,
const char *event,
virConnectDomainQemuMonitorEventCallback callback,
void *opaque,
virFreeCallback freecb,
unsigned int flags)
{
int rv = -1;
struct private_data *priv = conn->privateData;
qemu_connect_domain_monitor_event_register_args args;
qemu_connect_domain_monitor_event_register_ret ret;
int callbackID;
int count;
remote_nonnull_domain domain;
remoteDriverLock(priv);
if ((count = virDomainQemuMonitorEventStateRegisterID(conn,
priv->eventState,
dom, event, callback,
opaque, freecb, -1,
&callbackID)) < 0)
goto done;
/* If this is the first callback for this event, we need to enable
* events on the server */
if (count == 1) {
if (dom) {
make_nonnull_domain(&domain, dom);
args.dom = &domain;
} else {
args.dom = NULL;
}
args.event = event ? (char **) &event : NULL;
args.flags = flags;
memset(&ret, 0, sizeof(ret));
if (call(conn, priv, REMOTE_CALL_QEMU, QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_REGISTER,
(xdrproc_t) xdr_qemu_connect_domain_monitor_event_register_args, (char *) &args,
(xdrproc_t) xdr_qemu_connect_domain_monitor_event_register_ret, (char *) &ret) == -1) {
virObjectEventStateDeregisterID(conn, priv->eventState,
callbackID);
goto done;
}
virObjectEventStateSetRemote(conn, priv->eventState, callbackID,
ret.callbackID);
}
rv = callbackID;
done:
remoteDriverUnlock(priv);
return rv;
}
static int
remoteConnectDomainQemuMonitorEventDeregister(virConnectPtr conn,
int callbackID)
{
struct private_data *priv = conn->privateData;
int rv = -1;
qemu_connect_domain_monitor_event_deregister_args args;
int remoteID;
int count;
remoteDriverLock(priv);
if (virObjectEventStateEventID(conn, priv->eventState,
callbackID, &remoteID) < 0)
goto done;
if ((count = virObjectEventStateDeregisterID(conn, priv->eventState,
callbackID)) < 0)
goto done;
/* If that was the last callback for this event, we need to disable
* events on the server */
if (count == 0) {
args.callbackID = remoteID;
if (call(conn, priv, REMOTE_CALL_QEMU, QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_DEREGISTER,
(xdrproc_t) xdr_qemu_connect_domain_monitor_event_deregister_args, (char *) &args,
(xdrproc_t) xdr_void, (char *) NULL) == -1)
goto done;
}
rv = 0;
done:
remoteDriverUnlock(priv);
return rv;
}
static int
remoteConnectListAllInterfaces(virConnectPtr conn,
virInterfacePtr **ifaces,
@ -5410,6 +5520,31 @@ remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
}
static void
remoteDomainBuildQemuMonitorEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
virNetClientPtr client ATTRIBUTE_UNUSED,
void *evdata, void *opaque)
{
virConnectPtr conn = opaque;
struct private_data *priv = conn->privateData;
qemu_domain_monitor_event_msg *msg = evdata;
virDomainPtr dom;
virObjectEventPtr event = NULL;
dom = get_nonnull_domain(conn, msg->dom);
if (!dom)
return;
event = virDomainQemuMonitorEventNew(dom->id, dom->name, dom->uuid,
msg->event, msg->seconds,
msg->micros,
msg->details ? *msg->details : NULL);
virDomainFree(dom);
remoteEventQueue(priv, event, msg->callbackID);
}
static virDrvOpenStatus ATTRIBUTE_NONNULL(1)
remoteSecretOpen(virConnectPtr conn, virConnectAuthPtr auth,
unsigned int flags)
@ -7620,6 +7755,8 @@ static virDriver remote_driver = {
.domainQemuMonitorCommand = remoteDomainQemuMonitorCommand, /* 0.8.3 */
.domainQemuAttach = remoteDomainQemuAttach, /* 0.9.4 */
.domainQemuAgentCommand = remoteDomainQemuAgentCommand, /* 0.10.0 */
.connectDomainQemuMonitorEventRegister = remoteConnectDomainQemuMonitorEventRegister, /* 1.2.3 */
.connectDomainQemuMonitorEventDeregister = remoteConnectDomainQemuMonitorEventDeregister, /* 1.2.3 */
.domainOpenConsole = remoteDomainOpenConsole, /* 0.8.6 */
.domainOpenChannel = remoteDomainOpenChannel, /* 1.0.2 */
.domainOpenGraphics = remoteDomainOpenGraphics, /* 0.9.7 */

View File

@ -1,6 +1,6 @@
#!/usr/bin/perl -w
#
# Copyright (C) 2010-2013 Red Hat, Inc.
# Copyright (C) 2010-2014 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -927,8 +927,9 @@ elsif ($mode eq "server") {
push(@args_list, "priv->conn");
}
if ($structprefix eq "qemu" && $call->{ProcName} =~ /^Domain/) {
$proc_name =~ s/^(Domain)/${1}Qemu/;
if ($structprefix eq "qemu" &&
$call->{ProcName} =~ /^(Connect)?Domain/) {
$proc_name =~ s/^((Connect)?Domain)/${1}Qemu/;
}
if ($structprefix eq "lxc" && $call->{ProcName} =~ /^Domain/) {
$proc_name =~ s/^(Domain)/${1}Lxc/;
@ -1704,7 +1705,7 @@ elsif ($mode eq "client") {
if ($mode eq "aclsym") {
my $apiname = "vir" . $call->{ProcName};
if ($structprefix eq "qemu") {
$apiname =~ s/virDomain/virDomainQemu/;
$apiname =~ s/(vir(Connect)?Domain)/${1}Qemu/;
} elsif ($structprefix eq "lxc") {
$apiname =~ s/virDomain/virDomainLxc/;
}
@ -1744,7 +1745,7 @@ elsif ($mode eq "client") {
my $apiname = "vir" . $call->{ProcName};
if ($structprefix eq "qemu") {
$apiname =~ s/virDomain/virDomainQemu/;
$apiname =~ s/(vir(Connect)?Domain)/${1}Qemu/;
} elsif ($structprefix eq "lxc") {
$apiname =~ s/virDomain/virDomainLxc/;
}
@ -1856,7 +1857,7 @@ elsif ($mode eq "client") {
my $apiname = "vir" . $call->{ProcName};
if ($structprefix eq "qemu") {
$apiname =~ s/virDomain/virDomainQemu/;
$apiname =~ s/(vir(Connect)?Domain)/${1}Qemu/;
} elsif ($structprefix eq "lxc") {
$apiname =~ s/virDomain/virDomainLxc/;
}