[libvirt] [PATCH 5/9] remote: implement node device lifecycle event APIs

Jovanka Gulicoska jovanka.gulicoska at gmail.com
Wed Jul 20 13:50:43 UTC 2016


---
 daemon/libvirtd.h            |   2 +
 daemon/remote.c              | 206 +++++++++++++++++++++++++++++++++++++++++++
 src/remote/remote_driver.c   | 139 +++++++++++++++++++++++++++++
 src/remote/remote_protocol.x |  43 ++++++++-
 src/remote_protocol-structs  |  19 ++++
 5 files changed, 408 insertions(+), 1 deletion(-)

diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h
index cc91266..09d505d 100644
--- a/daemon/libvirtd.h
+++ b/daemon/libvirtd.h
@@ -62,6 +62,8 @@ struct daemonClientPrivate {
     size_t nqemuEventCallbacks;
     daemonClientEventCallbackPtr *storageEventCallbacks;
     size_t nstorageEventCallbacks;
+    daemonClientEventCallbackPtr *nodeDeviceEventCallbacks;
+    size_t nnodeDeviceEventCallbacks;
     bool closeRegistered;
 
 # if WITH_SASL
diff --git a/daemon/remote.c b/daemon/remote.c
index 4aa43c2..d07cacd 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -91,6 +91,7 @@ static virStorageVolPtr get_nonnull_storage_vol(virConnectPtr conn, remote_nonnu
 static virSecretPtr get_nonnull_secret(virConnectPtr conn, remote_nonnull_secret secret);
 static virNWFilterPtr get_nonnull_nwfilter(virConnectPtr conn, remote_nonnull_nwfilter nwfilter);
 static virDomainSnapshotPtr get_nonnull_domain_snapshot(virDomainPtr dom, remote_nonnull_domain_snapshot snapshot);
+static virNodeDevicePtr get_nonnull_node_device(virConnectPtr conn, remote_nonnull_node_device dev);
 static void make_nonnull_domain(remote_nonnull_domain *dom_dst, virDomainPtr dom_src);
 static void make_nonnull_network(remote_nonnull_network *net_dst, virNetworkPtr net_src);
 static void make_nonnull_interface(remote_nonnull_interface *interface_dst, virInterfacePtr interface_src);
@@ -209,6 +210,32 @@ remoteRelayStoragePoolEventCheckACL(virNetServerClientPtr client,
 }
 
 static bool
+remoteRelayNodeDeviceEventCheckACL(virNetServerClientPtr client,
+                                   virConnectPtr conn,
+                                   virNodeDevicePtr dev)
+{
+    virNodeDeviceDef def;
+    virIdentityPtr identity = NULL;
+    bool ret = false;
+
+    /* For now, we just create a virNodeDeviceDef with enough contents to
+     * satisfy what viraccessdriverpolkit.c references.  This is a bit
+     * fragile, but I don't know of anything better.  */
+    def.name = dev->name;
+
+    if (!(identity = virNetServerClientGetIdentity(client)))
+        goto cleanup;
+    if (virIdentitySetCurrent(identity) < 0)
+        goto cleanup;
+    ret = virConnectNodeDeviceEventRegisterAnyCheckACL(conn, &def);
+
+ cleanup:
+    ignore_value(virIdentitySetCurrent(NULL));
+    virObjectUnref(identity);
+    return ret;
+}
+
+static bool
 remoteRelayDomainQemuMonitorEventCheckACL(virNetServerClientPtr client,
                                           virConnectPtr conn, virDomainPtr dom)
 {
@@ -1329,6 +1356,44 @@ static virConnectStoragePoolEventGenericCallback storageEventCallbacks[] = {
 
 verify(ARRAY_CARDINALITY(storageEventCallbacks) == VIR_STORAGE_POOL_EVENT_ID_LAST);
 
+static int
+remoteRelayNodeDeviceEventLifecycle(virConnectPtr conn,
+                                    virNodeDevicePtr dev,
+                                    int event,
+                                    int detail,
+                                    void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_node_device_event_lifecycle_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayNodeDeviceEventCheckACL(callback->client, conn, dev))
+        return -1;
+
+    VIR_DEBUG("Relaying node device lifecycle event %d, detail %d, callback %d",
+              event, detail, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    make_nonnull_node_device(&data.dev, dev);
+    data.callbackID = callback->callbackID;
+    data.event = event;
+    data.detail = detail;
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_NODE_DEVICE_EVENT_LIFECYCLE,
+                                  (xdrproc_t)xdr_remote_node_device_event_lifecycle_msg,
+                                  &data);
+
+    return 0;
+}
+
+static virConnectNodeDeviceEventGenericCallback nodeDeviceEventCallbacks[] = {
+    VIR_NODE_DEVICE_EVENT_CALLBACK(remoteRelayNodeDeviceEventLifecycle),
+};
+
+verify(ARRAY_CARDINALITY(nodeDeviceEventCallbacks) == VIR_NODE_DEVICE_EVENT_ID_LAST);
+
 static void
 remoteRelayDomainQemuMonitorEvent(virConnectPtr conn,
                                   virDomainPtr dom,
@@ -1451,6 +1516,21 @@ void remoteClientFreeFunc(void *data)
         }
         VIR_FREE(priv->storageEventCallbacks);
 
+        for (i = 0; i < priv->nnodeDeviceEventCallbacks; i++) {
+            int callbackID = priv->nodeDeviceEventCallbacks[i]->callbackID;
+            if (callbackID < 0) {
+                VIR_WARN("unexpected incomplete node device callback %zu", i);
+                continue;
+            }
+            VIR_DEBUG("Deregistering remote node device event relay %d",
+                      callbackID);
+            priv->nodeDeviceEventCallbacks[i]->callbackID = -1;
+            if (virConnectNodeDeviceEventDeregisterAny(priv->conn,
+                                                       callbackID) < 0)
+                VIR_WARN("unexpected node device event deregister failure");
+        }
+        VIR_FREE(priv->nodeDeviceEventCallbacks);
+
         for (i = 0; i < priv->nqemuEventCallbacks; i++) {
             int callbackID = priv->qemuEventCallbacks[i]->callbackID;
             if (callbackID < 0) {
@@ -5656,6 +5736,126 @@ remoteDispatchConnectStoragePoolEventDeregisterAny(virNetServerPtr server ATTRIB
     return rv;
 }
 
+static int
+remoteDispatchConnectNodeDeviceEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                                virNetServerClientPtr client,
+                                                virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                                virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                                remote_connect_node_device_event_register_any_args *args,
+                                                remote_connect_node_device_event_register_any_ret *ret)
+{
+    int callbackID;
+    int rv = -1;
+    daemonClientEventCallbackPtr callback = NULL;
+    daemonClientEventCallbackPtr ref;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    virNodeDevicePtr  dev = NULL;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    virMutexLock(&priv->lock);
+
+    if (args->dev &&
+        !(dev = get_nonnull_node_device(priv->conn, *args->dev)))
+        goto cleanup;
+
+    if (args->eventID >= VIR_NODE_DEVICE_EVENT_ID_LAST || args->eventID < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unsupported storage pool event ID %d"), args->eventID);
+        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->eventID = args->eventID;
+    callback->callbackID = -1;
+    ref = callback;
+    if (VIR_APPEND_ELEMENT(priv->nodeDeviceEventCallbacks,
+                           priv->nnodeDeviceEventCallbacks,
+                           callback) < 0)
+        goto cleanup;
+
+    if ((callbackID = virConnectNodeDeviceEventRegisterAny(priv->conn,
+                                                           dev,
+                                                           args->eventID,
+                                                           nodeDeviceEventCallbacks[args->eventID],
+                                                           ref,
+                                                           remoteEventCallbackFree)) < 0) {
+        VIR_SHRINK_N(priv->nodeDeviceEventCallbacks,
+                     priv->nnodeDeviceEventCallbacks, 1);
+        callback = ref;
+        goto cleanup;
+    }
+
+    ref->callbackID = callbackID;
+    ret->callbackID = callbackID;
+
+    rv = 0;
+
+ cleanup:
+    VIR_FREE(callback);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virObjectUnref(dev);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
+
+static int
+remoteDispatchConnectNodeDeviceEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+                                                  virNetServerClientPtr client,
+                                                  virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                                                  virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED,
+                                                  remote_connect_node_device_event_deregister_any_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->nnodeDeviceEventCallbacks; i++) {
+        if (priv->nodeDeviceEventCallbacks[i]->callbackID == args->callbackID)
+            break;
+    }
+    if (i == priv->nnodeDeviceEventCallbacks) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("node device event callback %d not registered"),
+                       args->callbackID);
+        goto cleanup;
+    }
+
+    if (virConnectNodeDeviceEventDeregisterAny(priv->conn, args->callbackID) < 0)
+        goto cleanup;
+
+    VIR_DELETE_ELEMENT(priv->nodeDeviceEventCallbacks, i,
+                       priv->nnodeDeviceEventCallbacks);
+
+    rv = 0;
+
+ cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virMutexUnlock(&priv->lock);
+    return rv;
+}
 
 static int
 qemuDispatchConnectDomainMonitorEventRegister(virNetServerPtr server ATTRIBUTE_UNUSED,
@@ -6425,6 +6625,12 @@ get_nonnull_domain_snapshot(virDomainPtr dom, remote_nonnull_domain_snapshot sna
     return virGetDomainSnapshot(dom, snapshot.name);
 }
 
+static virNodeDevicePtr
+get_nonnull_node_device(virConnectPtr conn, remote_nonnull_node_device dev)
+{
+    return virGetNodeDevice(conn, dev.name);
+}
+
 /* Make remote_nonnull_domain and remote_nonnull_network. */
 static void
 make_nonnull_domain(remote_nonnull_domain *dom_dst, virDomainPtr dom_src)
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 29552aa..fbaec80 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -35,6 +35,7 @@
 #include "domain_event.h"
 #include "network_event.h"
 #include "storage_event.h"
+#include "node_device_event.h"
 #include "driver.h"
 #include "virbuffer.h"
 #include "remote_driver.h"
@@ -151,6 +152,8 @@ static void make_nonnull_network(remote_nonnull_network *net_dst, virNetworkPtr
 static void make_nonnull_interface(remote_nonnull_interface *interface_dst, virInterfacePtr interface_src);
 static void make_nonnull_storage_pool(remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr vol_src);
 static void make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src);
+static void
+make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src);
 static void make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr secret_src);
 static void make_nonnull_nwfilter(remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr nwfilter_src);
 static void make_nonnull_domain_snapshot(remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src);
@@ -367,6 +370,11 @@ remoteStoragePoolBuildEventRefresh(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
                                    void *evdata, void *opaque);
 
 static void
+remoteNodeDeviceBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
+                                    virNetClientPtr client ATTRIBUTE_UNUSED,
+                                    void *evdata, void *opaque);
+
+static void
 remoteConnectNotifyEventConnectionClosed(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
                                          virNetClientPtr client ATTRIBUTE_UNUSED,
                                          void *evdata, void *opaque);
@@ -553,6 +561,10 @@ static virNetClientProgramEvent remoteEvents[] = {
       remoteStoragePoolBuildEventRefresh,
       sizeof(remote_storage_pool_event_refresh_msg),
       (xdrproc_t)xdr_remote_storage_pool_event_refresh_msg },
+    { REMOTE_PROC_NODE_DEVICE_EVENT_LIFECYCLE,
+      remoteNodeDeviceBuildEventLifecycle,
+      sizeof(remote_node_device_event_lifecycle_msg),
+      (xdrproc_t)xdr_remote_node_device_event_lifecycle_msg },
 };
 
 static void
@@ -3147,6 +3159,103 @@ remoteConnectStoragePoolEventDeregisterAny(virConnectPtr conn,
 
 
 static int
+remoteConnectNodeDeviceEventRegisterAny(virConnectPtr conn,
+                                        virNodeDevicePtr dev,
+                                        int eventID,
+                                        virConnectNodeDeviceEventGenericCallback callback,
+                                        void *opaque,
+                                        virFreeCallback freecb)
+{
+    int rv = -1;
+    struct private_data *priv = conn->privateData;
+    remote_connect_node_device_event_register_any_args args;
+    remote_connect_node_device_event_register_any_ret ret;
+    int callbackID;
+    int count;
+    remote_nonnull_node_device node_device;
+
+    remoteDriverLock(priv);
+
+    if ((count = virNodeDeviceEventStateRegisterClient(conn, priv->eventState,
+                                                       dev, eventID, callback,
+                                                       opaque, freecb,
+                                                       &callbackID)) < 0)
+        goto done;
+
+    /* If this is the first callback for this eventID, we need to enable
+     * events on the server */
+    if (count == 1) {
+        args.eventID = eventID;
+        if (dev) {
+            make_nonnull_node_device(&node_device, dev);
+            args.dev = &node_device;
+        } else {
+            args.dev = NULL;
+        }
+
+        memset(&ret, 0, sizeof(ret));
+        if (call(conn, priv, 0, REMOTE_PROC_CONNECT_NODE_DEVICE_EVENT_REGISTER_ANY,
+                 (xdrproc_t) xdr_remote_connect_node_device_event_register_any_args, (char *) &args,
+                 (xdrproc_t) xdr_remote_connect_node_device_event_register_any_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
+remoteConnectNodeDeviceEventDeregisterAny(virConnectPtr conn,
+                                          int callbackID)
+{
+    struct private_data *priv = conn->privateData;
+    int rv = -1;
+    remote_connect_node_device_event_deregister_any_args args;
+    int eventID;
+    int remoteID;
+    int count;
+
+    remoteDriverLock(priv);
+
+    if ((eventID = 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 eventID, we need to disable
+     * events on the server */
+    if (count == 0) {
+        args.callbackID = remoteID;
+
+        if (call(conn, priv, 0, REMOTE_PROC_CONNECT_NODE_DEVICE_EVENT_DEREGISTER_ANY,
+                 (xdrproc_t) xdr_remote_connect_node_device_event_deregister_any_args, (char *) &args,
+                 (xdrproc_t) xdr_void, (char *) NULL) == -1)
+            goto done;
+
+    }
+
+    rv = 0;
+
+ done:
+    remoteDriverUnlock(priv);
+    return rv;
+}
+
+
+static int
 remoteConnectDomainQemuMonitorEventRegister(virConnectPtr conn,
                                             virDomainPtr dom,
                                             const char *event,
@@ -5155,6 +5264,28 @@ remoteStoragePoolBuildEventRefresh(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
 }
 
 static void
+remoteNodeDeviceBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
+                                    virNetClientPtr client ATTRIBUTE_UNUSED,
+                                    void *evdata, void *opaque)
+{
+    virConnectPtr conn = opaque;
+    struct private_data *priv = conn->privateData;
+    remote_node_device_event_lifecycle_msg *msg = evdata;
+    virNodeDevicePtr dev;
+    virObjectEventPtr event = NULL;
+
+    dev = get_nonnull_node_device(conn, msg->dev);
+    if (!dev)
+        return;
+
+    event = virNodeDeviceEventLifecycleNew(dev->name, msg->event,
+                                           msg->detail);
+    virObjectUnref(dev);
+
+    remoteEventQueue(priv, event, msg->callbackID);
+}
+
+static void
 remoteDomainBuildQemuMonitorEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
                                   virNetClientPtr client ATTRIBUTE_UNUSED,
                                   void *evdata, void *opaque)
@@ -7753,6 +7884,12 @@ make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr secret_src)
 }
 
 static void
+make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src)
+{
+    dev_dst->name = dev_src->name;
+}
+
+static void
 make_nonnull_nwfilter(remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr nwfilter_src)
 {
     nwfilter_dst->name = nwfilter_src->name;
@@ -8103,6 +8240,8 @@ static virSecretDriver secret_driver = {
 };
 
 static virNodeDeviceDriver node_device_driver = {
+    .connectNodeDeviceEventDeregisterAny = remoteConnectNodeDeviceEventDeregisterAny, /* 2.1.0 */
+    .connectNodeDeviceEventRegisterAny = remoteConnectNodeDeviceEventRegisterAny, /* 2.1.0 */
     .nodeNumOfDevices = remoteNodeNumOfDevices, /* 0.5.0 */
     .nodeListDevices = remoteNodeListDevices, /* 0.5.0 */
     .connectListAllNodeDevices  = remoteConnectListAllNodeDevices, /* 0.10.2 */
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 4403714..b4fd057 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -3131,6 +3131,26 @@ struct remote_storage_pool_event_refresh_msg {
     remote_nonnull_storage_pool pool;
 };
 
+struct remote_connect_node_device_event_register_any_args {
+    int eventID;
+    remote_node_device dev;
+};
+
+struct remote_connect_node_device_event_register_any_ret {
+    int callbackID;
+};
+
+struct remote_connect_node_device_event_deregister_any_args {
+    int callbackID;
+};
+
+struct remote_node_device_event_lifecycle_msg {
+    int callbackID;
+    remote_nonnull_node_device dev;
+    int event;
+    int detail;
+};
+
 struct remote_domain_fsfreeze_args {
     remote_nonnull_domain dom;
     remote_nonnull_string mountpoints<REMOTE_DOMAIN_FSFREEZE_MOUNTPOINTS_MAX>; /* (const char **) */
@@ -5882,5 +5902,26 @@ enum remote_procedure {
      * @generate: both
      * @acl: none
      */
-    REMOTE_PROC_STORAGE_POOL_EVENT_REFRESH = 373
+    REMOTE_PROC_STORAGE_POOL_EVENT_REFRESH = 373,
+
+    /**
+     * @generate: none
+     * @priority: high
+     * @acl: connect:search_node_devices
+     * @aclfilter: node_device:getattr
+     */
+    REMOTE_PROC_CONNECT_NODE_DEVICE_EVENT_REGISTER_ANY = 374,
+
+    /**
+     * @generate: none
+     * @priority: high
+     * @acl: connect:read
+     */
+    REMOTE_PROC_CONNECT_NODE_DEVICE_EVENT_DEREGISTER_ANY = 375,
+
+    /**
+     * @generate: both
+     * @acl: none
+     */
+    REMOTE_PROC_NODE_DEVICE_EVENT_LIFECYCLE = 376
 };
diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs
index 46e798b..0e66fc5 100644
--- a/src/remote_protocol-structs
+++ b/src/remote_protocol-structs
@@ -2571,6 +2571,22 @@ struct remote_storage_pool_event_refresh_msg {
         int                        callbackID;
         remote_nonnull_storage_pool pool;
 };
+struct remote_connect_node_device_event_register_any_args {
+         int                            eventID;
+         remote_node_device             dev;
+};
+struct remote_connect_node_device_event_register_any_ret {
+         int                        callbackID;
+};
+struct remote_connect_node_device_event_deregister_any_args {
+         int                        callbackID;
+};
+struct remote_node_device_event_lifecycle_msg {
+         int                             callbackID;
+         remote_nonnull_node_device      dev;
+         int                             event;
+         int                             detail;
+};
 struct remote_domain_fsfreeze_args {
         remote_nonnull_domain      dom;
         struct {
@@ -3145,4 +3161,7 @@ enum remote_procedure {
         REMOTE_PROC_DOMAIN_GET_GUEST_VCPUS = 371,
         REMOTE_PROC_DOMAIN_SET_GUEST_VCPUS = 372,
         REMOTE_PROC_STORAGE_POOL_EVENT_REFRESH = 373,
+        REMOTE_PROC_CONNECT_NODE_DEVICE_EVENT_REGISTER_ANY = 374,
+        REMOTE_PROC_CONNECT_NODE_DEVICE_EVENT_DEREGISTER_ANY = 375,
+        REMOTE_PROC_NODE_DEVICE_EVENT_LIFECYCLE = 376,
 };
-- 
2.7.4




More information about the libvir-list mailing list