[libvirt] add a qemu-specific event register API supported in remote driver

shaohef at linux.vnet.ibm.com shaohef at linux.vnet.ibm.com
Fri Dec 16 16:31:26 UTC 2011


From: ShaoHe Feng <shaohef at linux.vnet.ibm.com>


Signed-off-by: ShaoHe Feng <shaohef at linux.vnet.ibm.com>
---
 daemon/libvirtd.h            |   10 +++
 daemon/remote.c              |  172 +++++++++++++++++++++++++++++++++++++++++-
 src/remote/qemu_protocol.x   |   29 +++++++-
 src/remote/remote_driver.c   |  162 ++++++++++++++++++++++++++++++++++++++-
 src/remote/remote_protocol.x |    6 ++
 src/remote_protocol-structs  |    5 +
 6 files changed, 376 insertions(+), 8 deletions(-)

diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h
index c8d3ca2..fab7290 100644
--- a/daemon/libvirtd.h
+++ b/daemon/libvirtd.h
@@ -38,6 +38,15 @@
 # endif
 # include "virnetserverprogram.h"
 
+/* limit the number unknow event of an conncet can register */
+#define VIR_DOMAIN_EVENT_NAME_LAST  512
+struct domainEventNameCallBackStatus {
+    /* counter the number of unknow event registered */
+    int eventNameCallBackCounter;
+    /* Stores the ID of the unknow event registered */
+    int eventNameCallback[VIR_DOMAIN_EVENT_NAME_LAST];
+};
+typedef struct domainEventNameCallBackStatus domainEventNameCallBackStatus;
 typedef struct daemonClientStream daemonClientStream;
 typedef daemonClientStream *daemonClientStreamPtr;
 typedef struct daemonClientPrivate daemonClientPrivate;
@@ -49,6 +58,7 @@ struct daemonClientPrivate {
     virMutex lock;
 
     int domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LAST];
+    domainEventNameCallBackStatus domainEventNameCallBack;
 
 # if HAVE_SASL
     virNetSASLSessionPtr sasl;
diff --git a/daemon/remote.c b/daemon/remote.c
index e1d208c..f444c3d 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -421,6 +421,53 @@ mem_error:
     return -1;
 }
 
+static int remoteRelayDomainEventUnknown(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                         virDomainPtr dom,
+                                         const char *eventName, /* The JSON event name */
+                                         const char *eventArgs, /* The JSON string of args */
+                                         void *opaque)
+{
+    virNetServerClientPtr client = opaque;
+    qemu_domain_events_unknown_event_msg data;
+
+    if (!client)
+        return -1;
+
+    VIR_DEBUG("Relaying domain: %s id: %d, unknown event: %s arguments: %s",
+              dom->name, dom->id, eventName, eventArgs);
+
+    /* build return data */
+    memset(&data, 0, sizeof data);
+    if (eventName == NULL)
+        goto mem_error3;
+    data.eventName = (char *)strdup(eventName);
+    if (data.eventName == NULL)
+        goto mem_error2;
+    if (eventArgs != NULL) {
+        data.eventArgs = (char *)strdup(eventArgs);
+        if (data.eventArgs == NULL)
+            goto mem_error1;
+    }
+    else {
+        data.eventArgs = (char *)strdup("NULL");
+        if (data.eventArgs == NULL)
+            goto mem_error1;
+    }
+    make_nonnull_domain(&data.dom, dom);
+    remoteDispatchDomainEventSend(client, qemuProgram,
+                                  QEMU_PROC_DOMAIN_EVENTS_UNKNOWN_EVENT,
+                                  (xdrproc_t)xdr_qemu_domain_events_unknown_event_msg, &data);
+
+    return 0;
+
+mem_error1:
+    VIR_FREE(data.eventArgs);
+mem_error2:
+    VIR_FREE(data.eventName);
+mem_error3:
+    virReportOOMError();
+    return -1;
+}
 
 static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSED,
                                               virDomainPtr dom,
@@ -509,6 +556,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
     VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError),
     VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob),
     VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDiskChange),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventUnknown),
 };
 
 verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST);
@@ -526,10 +574,21 @@ static void remoteClientFreeFunc(void *data)
 
     /* Deregister event delivery callback */
     if (priv->conn) {
-        int i;
+        int i, j;
 
         for (i = 0 ; i < VIR_DOMAIN_EVENT_ID_LAST ; i++) {
-            if (priv->domainEventCallbackID[i] != -1) {
+            if (i == VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN && priv->domainEventCallbackID[i] != -1) {
+                for (j = 0 ; j < VIR_DOMAIN_EVENT_NAME_LAST ; j++){
+                    if (priv->domainEventNameCallBack.eventNameCallback[j] != -1) {
+                        VIR_DEBUG("Deregistering to relay remote events %d", i);
+                        virConnectDomainQemuEventDeregister(priv->conn,
+                                                            priv->domainEventNameCallBack.eventNameCallback[j]);
+                    }
+                    priv->domainEventNameCallBack.eventNameCallback[j] == -1;
+                }
+                priv->domainEventNameCallBack.eventNameCallBackCounter = 0;
+            }
+            else if (priv->domainEventCallbackID[i] != -1) {
                 VIR_DEBUG("Deregistering to relay remote events %d", i);
                 virConnectDomainEventDeregisterAny(priv->conn,
                                                    priv->domainEventCallbackID[i]);
@@ -572,6 +631,10 @@ int remoteClientInitHook(virNetServerPtr srv ATTRIBUTE_UNUSED,
     for (i = 0 ; i < VIR_DOMAIN_EVENT_ID_LAST ; i++)
         priv->domainEventCallbackID[i] = -1;
 
+    priv->domainEventNameCallBack.eventNameCallBackCounter = 0;
+    for (i = 0 ; i < VIR_DOMAIN_EVENT_NAME_LAST ; i++)
+        priv->domainEventNameCallBack.eventNameCallback[i] = -1;
+
     virNetServerClientSetPrivateData(client, priv,
                                      remoteClientFreeFunc);
     virNetServerClientSetCloseHook(client, remoteClientCloseFunc);
@@ -2991,6 +3054,111 @@ cleanup:
 }
 
 static int
+qemuDispatchDomainEventsRegister(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                 virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                 virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                 virNetMessageErrorPtr rerr,
+                                 qemu_domain_events_register_args *args,
+                                 qemu_domain_events_register_ret *ret ATTRIBUTE_UNUSED)
+{
+    int callbackID = -1;
+    int rv = -1;
+    int eventIdx = 0;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    if (!priv->conn) {
+        virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    if (args->eventName == NULL) {
+        virNetError(VIR_ERR_INTERNAL_ERROR, _("event Name is NULL"));
+        goto cleanup;
+    }
+    if (priv->domainEventNameCallBack.eventNameCallBackCounter >= VIR_DOMAIN_EVENT_NAME_LAST) {
+        virNetError(VIR_ERR_INTERNAL_ERROR,
+                    _("domain event %s is not registered, the register number has exceeded limit number %d"),
+                    args->eventName,
+                    VIR_DOMAIN_EVENT_NAME_LAST);
+        goto cleanup;
+    }
+
+    if ((callbackID = virConnectDomainQemuEventRegister(priv->conn,
+                                                        NULL,
+                                                        args->eventName,
+                                                        (virConnectDomainQemuEventCallback)remoteRelayDomainEventUnknown,
+                                                        client,
+                                                        NULL)) < 0)
+        goto cleanup;
+
+    for (eventIdx = 0 ; eventIdx < VIR_DOMAIN_EVENT_NAME_LAST ; eventIdx++) {
+        if (priv->domainEventNameCallBack.eventNameCallback[eventIdx] == -1) {
+            priv->domainEventNameCallBack.eventNameCallback[eventIdx] = callbackID;
+            priv->domainEventNameCallBack.eventNameCallBackCounter++;
+            ret->callbackID = eventIdx;
+            break;
+        }
+    }
+    priv->domainEventCallbackID[VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN] = callbackID;
+
+    rv = 0;
+
+cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+static int
+qemuDispatchDomainEventsDeregister(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                   virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                                   virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                   virNetMessageErrorPtr rerr,
+                                   qemu_domain_events_deregister_args *args,
+                                   qemu_domain_events_deregister_ret *ret ATTRIBUTE_UNUSED)
+{
+    int callbackID = -1;
+    int rv = -1;
+    int eventIdx = args->callbackID;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+
+    if (!priv->conn) {
+        virNetError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    if (eventIdx >= VIR_DOMAIN_EVENT_NAME_LAST ||
+        (callbackID = priv->domainEventNameCallBack.eventNameCallback[eventIdx]) < 0) {
+
+        virNetError(VIR_ERR_INTERNAL_ERROR, _("callbakcID %d is not register"), eventIdx);
+        goto cleanup;
+    }
+
+    if (virConnectDomainQemuEventDeregister(priv->conn, callbackID) < 0)
+        goto cleanup;
+    ret->callbackID = callbackID;
+
+    priv->domainEventNameCallBack.eventNameCallback[eventIdx] = -1;
+    priv->domainEventNameCallBack.eventNameCallBackCounter--;
+    if (priv->domainEventNameCallBack.eventNameCallBackCounter == 0)
+        priv->domainEventCallbackID[VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN] = -1;
+
+    rv = 0;
+
+cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+static int
 qemuDispatchMonitorCommand(virNetServerPtr server ATTRIBUTE_UNUSED,
                            virNetServerClientPtr client ATTRIBUTE_UNUSED,
                            virNetMessagePtr msg ATTRIBUTE_UNUSED,
diff --git a/src/remote/qemu_protocol.x b/src/remote/qemu_protocol.x
index 39f9adf..54f5734 100644
--- a/src/remote/qemu_protocol.x
+++ b/src/remote/qemu_protocol.x
@@ -47,6 +47,30 @@ struct qemu_domain_attach_ret {
     remote_nonnull_domain dom;
 };
 
+struct qemu_domain_events_register_args {
+    remote_nonnull_string eventName;
+};
+
+struct qemu_domain_events_deregister_args {
+    remote_nonnull_string eventName;
+    int callbackID;
+};
+
+struct qemu_domain_events_register_ret {
+    int callbackID;
+};
+
+struct qemu_domain_events_deregister_ret {
+    int callbackID;
+};
+
+struct qemu_domain_events_unknown_event_msg {
+    remote_nonnull_domain dom;
+    remote_nonnull_string eventName;
+    remote_nonnull_string eventArgs;
+};
+
+
 /* Define the program number, protocol version and procedure numbers here. */
 const QEMU_PROGRAM = 0x20008087;
 const QEMU_PROTOCOL_VERSION = 1;
@@ -61,5 +85,8 @@ enum qemu_procedure {
      * are some exceptions to this rule, e.g. domainDestroy. Other APIs MAY
      * be marked as high priority. If in doubt, it's safe to choose low. */
     QEMU_PROC_MONITOR_COMMAND = 1, /* skipgen skipgen priority:low */
-    QEMU_PROC_DOMAIN_ATTACH = 2 /* autogen autogen priority:low */
+    QEMU_PROC_DOMAIN_ATTACH = 2, /* autogen autogen priority:low */
+    QEMU_PROC_DOMAIN_EVENTS_REGISTER = 3, /* skipgen skipgen priority:low */
+    QEMU_PROC_DOMAIN_EVENTS_DEREGISTER = 4, /* skipgen skipgen priority:low */
+    QEMU_PROC_DOMAIN_EVENTS_UNKNOWN_EVENT = 5 /* skipgen skipgen */
 };
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index ff2d4b4..a347eb2 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -238,6 +238,10 @@ static void
 remoteDomainBuildEventDiskChange(virNetClientProgramPtr prog,
                                  virNetClientPtr client,
                                  void *evdata, void *opaque);
+static void
+remoteQemuDomainBuildEventDefaultEvent(virNetClientProgramPtr prog,
+                                       virNetClientPtr client,
+                                       void *evdata, void *opaque);
 
 static virNetClientProgramEvent remoteDomainEvents[] = {
     { REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE,
@@ -281,7 +285,12 @@ static virNetClientProgramEvent remoteDomainEvents[] = {
       sizeof(remote_domain_event_disk_change_msg),
       (xdrproc_t)xdr_remote_domain_event_disk_change_msg },
 };
-
+static virNetClientProgramEvent qemuDomainEvents[] = {
+    { QEMU_PROC_DOMAIN_EVENTS_UNKNOWN_EVENT,
+      remoteQemuDomainBuildEventDefaultEvent,
+      sizeof(qemu_domain_events_unknown_event_msg),
+      (xdrproc_t)xdr_qemu_domain_events_unknown_event_msg},
+};
 enum virDrvOpenRemoteFlags {
     VIR_DRV_OPEN_REMOTE_RO = (1 << 0),
     VIR_DRV_OPEN_REMOTE_USER      = (1 << 1), /* Use the per-user socket path */
@@ -663,9 +672,9 @@ doRemoteOpen (virConnectPtr conn,
         goto failed;
     if (!(priv->qemuProgram = virNetClientProgramNew(QEMU_PROGRAM,
                                                      QEMU_PROTOCOL_VERSION,
-                                                     NULL,
-                                                     0,
-                                                     NULL)))
+                                                     qemuDomainEvents,
+                                                     ARRAY_CARDINALITY(remoteDomainEvents),
+                                                     conn)))
         goto failed;
 
     if (virNetClientAddProgram(priv->client, priv->remoteProgram) < 0 ||
@@ -3345,6 +3354,29 @@ remoteDomainBuildEventBlockJob(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
 }
 
 static void
+remoteQemuDomainBuildEventDefaultEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
+                                       virNetClientPtr client ATTRIBUTE_UNUSED,
+                                       void *evdata, void *opaque)
+{
+    virConnectPtr conn = opaque;
+    struct private_data *priv = conn->privateData;
+    //remote_domain_event_default_event_msg *msg = evdata;
+    qemu_domain_events_unknown_event_msg *msg = evdata;
+    virDomainPtr dom;
+    virDomainEventPtr event = NULL;
+
+    dom = get_nonnull_domain(conn, msg->dom);
+    if (!dom)
+        return;
+
+    event = virDomainEventUnknownNewFromDom(dom, msg->eventName, msg->eventArgs);
+
+    virDomainFree(dom);
+
+    remoteDomainEventQueue(priv, event);
+}
+
+static void
 remoteDomainBuildEventGraphics(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
                                virNetClientPtr client ATTRIBUTE_UNUSED,
                                void *evdata, void *opaque)
@@ -3800,7 +3832,6 @@ done:
     return rv;
 }
 
-
 static int remoteDomainEventDeregisterAny(virConnectPtr conn,
                                           int callbackID)
 {
@@ -3843,6 +3874,125 @@ done:
     return rv;
 }
 
+static int
+remoteDomainQemuEventRegister(virConnectPtr conn,
+                              virDomainPtr dom,
+                              const char *eventName,
+                              virConnectDomainEventGenericCallback callback,
+                              void *opaque,
+                              virFreeCallback freecb)
+{
+    int rv = -1;
+    struct private_data *priv = conn->privateData;
+    qemu_domain_events_register_args args;
+    qemu_domain_events_register_ret ret;
+    int callbackID;
+
+    remoteDriverLock(priv);
+
+    if (priv->domainEventState->timer < 0) {
+         remoteError(VIR_ERR_NO_SUPPORT, "%s", _("no event support"));
+         goto done;
+    }
+
+    if ((callbackID = virDomainEventCallbackListAddName(conn,
+                                                      priv->domainEventState->callbacks,
+                                                      dom, eventName,
+                                                      VIR_QEMU_DOMAIN_EVENT_ID_UNKNOWN,
+                                                      callback, opaque, freecb)) < 0) {
+         remoteError(VIR_ERR_RPC, "%s", _("adding cb to list"));
+         goto done;
+    }
+
+    /* If this is the first callback for this eventID, we need to enable
+     * events on the server */
+    if (virDomainEventCallbackListCountName(conn,
+                                          priv->domainEventState->callbacks,
+                                          eventName) == 1) {
+        args.eventName= (char *)eventName;
+
+        if (call (conn, priv, REMOTE_CALL_QEMU, QEMU_PROC_DOMAIN_EVENTS_REGISTER,
+                  (xdrproc_t) xdr_qemu_domain_events_register_args, (char *) &args,
+                  (xdrproc_t) xdr_qemu_domain_events_register_ret, (char *) &ret) == -1) {
+            virDomainEventCallbackListRemoveID(conn,
+                                               priv->domainEventState->callbacks,
+                                               callbackID);
+            goto done;
+        }
+    }
+    virDomainEventCallbackListAddQemuCallbackID(conn,
+                                                priv->domainEventState->callbacks,
+                                                callbackID,
+                                                ret.callbackID);
+    rv = callbackID;
+
+done:
+    remoteDriverUnlock(priv);
+    return rv;
+}
+
+static int remoteDomainQemuEventDeregister(virConnectPtr conn,
+                                     int callbackID)
+{
+    struct private_data *priv = conn->privateData;
+    int rv = -1;
+    qemu_domain_events_deregister_args args;
+    qemu_domain_events_deregister_ret ret;
+    char *eventName = NULL;
+    ret.callbackID = -1;
+
+    remoteDriverLock(priv);
+
+    if ((eventName = (char *)virDomainEventCallbackListEventName(conn,
+                                                         priv->domainEventState->callbacks,
+                                                         callbackID)) == NULL) {
+        remoteError(VIR_ERR_RPC, _("unable to find callback ID %d"), callbackID);
+        goto done;
+    }
+    eventName = strdup(eventName);
+    if (eventName == NULL)
+        goto done;
+
+    if ((args.callbackID = virDomainEventCallbackListEventQemuCallbackID(conn,
+                                                         priv->domainEventState->callbacks,
+                                                         callbackID)) == -1) {
+        remoteError(VIR_ERR_RPC, _("unable to find callback ID %d"), callbackID);
+        goto done;
+    }
+
+    if (virDomainQemuEventStateDeregister(conn,
+                                          priv->domainEventState,
+                                          callbackID) < 0)
+        goto done;
+
+    /* If that was the last callback for this eventName, we need to disable
+     * events on the server */
+    if (virDomainEventCallbackListCountName(conn,
+                                          priv->domainEventState->callbacks,
+                                          eventName) == 0) {
+        args.eventName = eventName;
+
+        if (call (conn, priv, REMOTE_CALL_QEMU, QEMU_PROC_DOMAIN_EVENTS_DEREGISTER,
+                  (xdrproc_t) xdr_qemu_domain_events_deregister_args, (char *) &args,
+                  (xdrproc_t) xdr_qemu_domain_events_deregister_ret, (char *) &ret) == -1) {
+            goto done;
+        }
+
+        if ( ret.callbackID == -1 ) {
+            remoteError(VIR_ERR_RPC, _("remote sever deregeiter remote callbackID:%d, and the client callbackID%d"),
+                        args.callbackID,
+                        callbackID);
+            goto done;
+        }
+        rv = ret.callbackID;
+    }
+
+
+done:
+    remoteDriverUnlock(priv);
+    VIR_FREE(eventName);
+    return rv;
+}
 
 /*----------------------------------------------------------------------*/
 
@@ -4627,6 +4777,8 @@ static virDriver remote_driver = {
     .nodeGetFreeMemory = remoteNodeGetFreeMemory, /* 0.3.3 */
     .domainEventRegister = remoteDomainEventRegister, /* 0.5.0 */
     .domainEventDeregister = remoteDomainEventDeregister, /* 0.5.0 */
+    .qemuDomainQemuEventRegister = remoteDomainQemuEventRegister, /* 0.9.9 */
+    .qemuDomainQemuEventDeregister = remoteDomainQemuEventDeregister, /* 0.9.9 */
     .domainMigratePrepare2 = remoteDomainMigratePrepare2, /* 0.5.0 */
     .domainMigrateFinish2 = remoteDomainMigrateFinish2, /* 0.5.0 */
     .nodeDeviceDettach = remoteNodeDeviceDettach, /* 0.6.1 */
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 509a20b..02154bc 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -2049,6 +2049,12 @@ struct remote_domain_event_disk_change_msg {
     int reason;
 };
 
+struct remote_domain_event_default_event_msg {
+    remote_nonnull_domain dom;
+    remote_nonnull_string eventName;
+    remote_nonnull_string eventArgs;
+};
+
 struct remote_domain_managed_save_args {
     remote_nonnull_domain dom;
     unsigned int flags;
diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs
index a9d4296..17b3896 100644
--- a/src/remote_protocol-structs
+++ b/src/remote_protocol-structs
@@ -1546,6 +1546,10 @@ struct remote_domain_event_disk_change_msg {
         remote_nonnull_string      devAlias;
         int                        reason;
 };
+struct remote_domain_event_default_event_msg {
+        remote_nonnull_domain      dom;
+        remote_nonnull_string      rawEvent;
+};
 struct remote_domain_managed_save_args {
         remote_nonnull_domain      dom;
         u_int                      flags;
@@ -2044,4 +2048,5 @@ enum remote_procedure {
         REMOTE_PROC_DOMAIN_BLOCK_RESIZE = 251,
         REMOTE_PROC_DOMAIN_SET_BLOCK_IO_TUNE = 252,
         REMOTE_PROC_DOMAIN_GET_BLOCK_IO_TUNE = 253,
+        REMOTE_PROC_QEMU_DOMAIN_EVENT_UNKNOWN_EVENT = 254,
 };
-- 
1.7.5.4




More information about the libvir-list mailing list