[libvirt] [PATCH python v4 0/7] Introducing storage pool lifecycle event APIs

Introducing implementation of storage pool event APIs. Code changes follow network event APIs. Implemented functions: virStoragePoolEventRegisterAny(), virStoragePoolEventDeregisterAny(), virStoragePoolLifeCycleEventNew(), introduced STARTED, STOPPED, DEFINE, UNDEFINE and REFRESHED. STARTED signal is emiited in storagePoolCreateXML() and storagePoolCreate() DEFINED signal is emitted in storagePoolDefineXML() UNDEFINED signal is emitted in storagePoolUndefine() STOPPED signal is emitted in storagePoolDestroy() and storagePoolRefresh() REFRESHED signal is emitted in storagePoolRefresh() There are also test as well as unittests for the new functions and signals. This is part of a GSOC project: Asynchronous lifecycle events for storage objects As part of the project there should also be implementation for storage volume events. For now there's no signal when volumes are created or deleted, they can also be implemented, but probably the easiest way is to have apps watch for REFRESH signal, and later extend storage driver code to refresh a pool after volume APIs are called. Changes since v1: small changes following comments Changes since v2: #1 - fix broken alignment in datatypes and fix typos in docstring #2 - rename STORAGE_POOL_EVENT_SOURCES to STORAGE_EVENT_SOURCES, change copyright #3 - fixed spacing issue in comments in test driver #4 - fix comment formating, change in comment, move REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE to buttom of list #5 - include storageEventState pointer, change version from 1.3.6 to 2.0.0, use callbackID insted of ret in storageConnectStoragePoolEventRegisterAny() #6 - use domain like lifecycle event callback Changes since v3: moved storage_conf.h from patch #2 to #6, indentation in remote_protocol-stucts (Martin's suggestion) Jovanka Gulicoska (7): Introduce storage lifecycle event APIs conf: add storage_event handling test: implement storage lifecycle event APIs remote: implement storage lifecycle event APIs storage: implement storage lifecycle event APIs event-test: support storage lifecycle event APIs virsh: Introduce virsh-pool command daemon/libvirtd.h | 2 + daemon/remote.c | 207 ++++++++++++++++++++++++++++++- examples/object-events/event-test.c | 73 +++++++++++ include/libvirt/libvirt-storage.h | 93 ++++++++++++++ src/Makefile.am | 5 + src/conf/storage_conf.h | 4 + src/conf/storage_event.c | 237 ++++++++++++++++++++++++++++++++++++ src/conf/storage_event.h | 60 +++++++++ src/datatypes.h | 13 ++ src/driver-storage.h | 13 ++ src/libvirt-storage.c | 125 +++++++++++++++++++ src/libvirt_private.syms | 5 + src/libvirt_public.syms | 6 + src/remote/remote_driver.c | 128 +++++++++++++++++++ src/remote/remote_protocol.x | 43 ++++++- src/remote_protocol-structs | 19 +++ src/storage/storage_driver.c | 110 +++++++++++++++++ src/test/test_driver.c | 71 +++++++++++ tests/objecteventtest.c | 177 +++++++++++++++++++++++++++ tools/virsh-pool.c | 184 ++++++++++++++++++++++++++++ 20 files changed, 1571 insertions(+), 4 deletions(-) create mode 100644 src/conf/storage_event.c create mode 100644 src/conf/storage_event.h -- 2.5.5

Storage pool lifecycle event API entry points for registering and deregistering storage pool events, as well as types of events associated with storage pools. These entry points will be used for implementing asynchronous lifecycle events. Storage pool API: virConnectStoragePoolEventRegisterAny virConnectStoragePoolEventDeregisterAny virStoragePoolEventLifecycleType which has events STARTED, STOPPED, DEFINED, UNDEFINED, and REFRESHED --- include/libvirt/libvirt-storage.h | 93 ++++++++++++++++++++++++++++ src/datatypes.h | 13 ++++ src/driver-storage.h | 13 ++++ src/libvirt-storage.c | 125 ++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 6 ++ 5 files changed, 250 insertions(+) diff --git a/include/libvirt/libvirt-storage.h b/include/libvirt/libvirt-storage.h index db6f2b4..a67a97f 100644 --- a/include/libvirt/libvirt-storage.h +++ b/include/libvirt/libvirt-storage.h @@ -377,5 +377,98 @@ int virStorageVolResize (virStorageVolPtr vol, int virStoragePoolIsActive(virStoragePoolPtr pool); int virStoragePoolIsPersistent(virStoragePoolPtr pool); +/** + * VIR_STORAGE_POOL_EVENT_CALLBACK: + * + * Used to cast the event specific callback into the generic one + * for use for virConnectStoragePoolEventRegisterAny() + */ +# define VIR_STORAGE_POOL_EVENT_CALLBACK(cb)((virConnectStoragePoolEventGenericCallback)(cb)) + +/** + * virStoragePoolEventID: + * + * An enumeration of supported eventId parameters for + * virConnectStoragePoolEventRegisterAny(). Each event id determines which + * signature of callback function will be used. + */ +typedef enum { + VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE = 0, /* virConnectStoragePoolEventLifecycleCallback */ + +# ifdef VIR_ENUM_SENTINELS + VIR_STORAGE_POOL_EVENT_ID_LAST + /* + * NB: this enum value will increase over time as new events are + * added to the libvirt API. It reflects the last event ID supported + * by this version of the libvirt API. + */ +# endif +} virStoragePoolEventID; + +/** + * virConnectStoragePoolEventGenericCallback: + * @conn: the connection pointer + * @pool: the pool pointer + * @opaque: application specified data + * + * A generic storage pool event callback handler, for use with + * virConnectStoragePoolEventRegisterAny(). Specific events usually + * have a customization with extra parameters, often with @opaque being + * passed in a different parameter position; use + * VIR_STORAGE_POOL_EVENT_CALLBACK() when registering an appropriate handler. + */ +typedef void (*virConnectStoragePoolEventGenericCallback)(virConnectPtr conn, + virStoragePoolPtr pool, + void *opaque); + +/* Use VIR_STORAGE_POOL_EVENT_CALLBACK() to cast the 'cb' parameter */ +int virConnectStoragePoolEventRegisterAny(virConnectPtr conn, + virStoragePoolPtr pool, /* optional, to filter */ + int eventID, + virConnectStoragePoolEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + +int virConnectStoragePoolEventDeregisterAny(virConnectPtr conn, + int callbackID); + +/** + * virStoragePoolEventLifecycleType: + * + * a virStoragePoolEventLifecycleType is emitted during storage pool + * lifecycle events + */ +typedef enum { + VIR_STORAGE_POOL_EVENT_DEFINED = 0, + VIR_STORAGE_POOL_EVENT_UNDEFINED = 1, + VIR_STORAGE_POOL_EVENT_STARTED = 2, + VIR_STORAGE_POOL_EVENT_STOPPED = 3, + VIR_STORAGE_POOL_EVENT_REFRESHED = 4, + +# ifdef VIR_ENUM_SENTINELS + VIR_STORAGE_POOL_EVENT_LAST +# endif +} virStoragePoolEventLifecycleType; + +/** + * virConnectStoragePoolEventLifecycleCallback: + * @conn: connection object + * @pool: pool on which the event occurred + * @event: The specific virStoragePoolEventLifeCycleType which occurred + * @detail: contains some details on the reason of the event. + * @opaque: application specified data + * + * This callback is called when a pool lifecycle action is performed, like start + * or stop. + * + * The callback signature to use when registering for an event of type + * VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE with + * virConnectStoragePoolEventRegisterAny() + */ +typedef void (*virConnectStoragePoolEventLifecycleCallback)(virConnectPtr conn, + virStoragePoolPtr pool, + int event, + int detail, + void *opaque); #endif /* __VIR_LIBVIRT_STORAGE_H__ */ diff --git a/src/datatypes.h b/src/datatypes.h index 8ccc7b0..996506b 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -143,6 +143,19 @@ extern virClassPtr virAdmClientClass; } \ } while (0) +# define virCheckStoragePoolGoto(obj, label) \ + do { \ + virStoragePoolPtr _pool= (obj); \ + if (!virObjectIsClass(_pool, virStoragePoolClass) || \ + !virObjectIsClass(_pool->conn, virConnectClass)) { \ + virReportErrorHelper(VIR_FROM_STORAGE, \ + VIR_ERR_INVALID_STORAGE_POOL, \ + __FILE__, __FUNCTION__, __LINE__, \ + __FUNCTION__); \ + goto label; \ + } \ + } while (0) + # define virCheckStorageVolReturn(obj, retval) \ do { \ virStorageVolPtr _vol = (obj); \ diff --git a/src/driver-storage.h b/src/driver-storage.h index 0489647..afcb12b 100644 --- a/src/driver-storage.h +++ b/src/driver-storage.h @@ -196,6 +196,17 @@ typedef int typedef int (*virDrvStoragePoolIsPersistent)(virStoragePoolPtr pool); +typedef int +(*virDrvConnectStoragePoolEventRegisterAny)(virConnectPtr conn, + virStoragePoolPtr pool, + int eventID, + virConnectStoragePoolEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + +typedef int +(*virDrvConnectStoragePoolEventDeregisterAny)(virConnectPtr conn, + int callbackID); typedef struct _virStorageDriver virStorageDriver; @@ -215,6 +226,8 @@ struct _virStorageDriver { virDrvConnectListDefinedStoragePools connectListDefinedStoragePools; virDrvConnectListAllStoragePools connectListAllStoragePools; virDrvConnectFindStoragePoolSources connectFindStoragePoolSources; + virDrvConnectStoragePoolEventRegisterAny connectStoragePoolEventRegisterAny; + virDrvConnectStoragePoolEventDeregisterAny connectStoragePoolEventDeregisterAny; virDrvStoragePoolLookupByName storagePoolLookupByName; virDrvStoragePoolLookupByUUID storagePoolLookupByUUID; virDrvStoragePoolLookupByVolume storagePoolLookupByVolume; diff --git a/src/libvirt-storage.c b/src/libvirt-storage.c index 1ce6745..cebe02f 100644 --- a/src/libvirt-storage.c +++ b/src/libvirt-storage.c @@ -2124,3 +2124,128 @@ virStoragePoolIsPersistent(virStoragePoolPtr pool) virDispatchError(pool->conn); return -1; } + +/** + * virConnectStoragePoolEventRegisterAny: + * @conn: pointer to the connection + * @pool: pointer to the storage pool + * @eventID: the event type to receive + * @cb: callback to the function handling network events + * @opaque: opaque data to pass on to the callback + * @freecb: optional function to deallocate opaque when not used anymore + * + * Adds a callback to receive notifications of arbitrary storage pool events + * occurring on a storage pool. This function requires that an event loop + * has been previously registered with virEventRegisterImpl() or + * virEventRegisterDefaultImpl(). + * + * If @pool is NULL, then events will be monitored for any storage pool. + * If @pool is non-NULL, then only the specific storage pool will be monitored. + * + * Most types of events have a callback providing a custom set of parameters + * for the event. When registering an event, it is thus necessary to use + * the VIR_STORAGE_POOL_EVENT_CALLBACK() macro to cast the + * supplied function pointer to match the signature of this method. + * + * The virStoragePoolPtr object handle passed into the callback upon delivery + * of an event is only valid for the duration of execution of the callback. + * If the callback wishes to keep the storage pool object after the callback + * returns, it shall take a reference to it, by calling virStoragePoolRef(). + * The reference can be released once the object is no longer required + * by calling virStoragePoolFree(). + * + * The return value from this method is a positive integer identifier + * for the callback. To unregister a callback, this callback ID should + * be passed to the virConnectStoragePoolEventDeregisterAny() method. + * + * Returns a callback identifier on success, -1 on failure. + */ +int +virConnectStoragePoolEventRegisterAny(virConnectPtr conn, + virStoragePoolPtr pool, + int eventID, + virConnectStoragePoolEventGenericCallback cb, + void *opaque, + virFreeCallback freecb) +{ + VIR_DEBUG("conn=%p, pool=%p, eventID=%d, cb=%p, opaque=%p, freecb=%p", + conn, pool, eventID, cb, opaque, freecb); + + virResetLastError(); + + virCheckConnectReturn(conn, -1); + if (pool) { + virCheckStoragePoolGoto(pool, error); + if (pool->conn != conn) { + virReportInvalidArg(pool, + _("storage pool '%s' in %s must match connection"), + pool->name, __FUNCTION__); + goto error; + } + } + virCheckNonNullArgGoto(cb, error); + virCheckNonNegativeArgGoto(eventID, error); + + if (eventID >= VIR_STORAGE_POOL_EVENT_ID_LAST) { + virReportInvalidArg(eventID, + _("eventID in %s must be less than %d"), + __FUNCTION__, VIR_STORAGE_POOL_EVENT_ID_LAST); + goto error; + } + + if (conn->storageDriver && + conn->storageDriver->connectStoragePoolEventRegisterAny) { + int ret; + ret = conn->storageDriver->connectStoragePoolEventRegisterAny(conn, + pool, + eventID, + cb, + opaque, + freecb); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return -1; +} + +/** + * virConnectStoragePoolEventDeregisterAny: + * @conn: pointer to the connection + * @callbackID: the callback identifier + * + * Removes an event callback. The callbackID parameter should be the + * value obtained from a previous virConnectStoragePoolEventRegisterAny() method. + * + * Returns 0 on success, -1 on failure + */ +int +virConnectStoragePoolEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + VIR_DEBUG("conn=%p, callbackID=%d", conn, callbackID); + + virResetLastError(); + + virCheckConnectReturn(conn, -1); + virCheckNonNegativeArgGoto(callbackID, error); + + if (conn->storageDriver && + conn->storageDriver->connectStoragePoolEventDeregisterAny) { + int ret; + ret = conn->storageDriver->connectStoragePoolEventDeregisterAny(conn, + callbackID); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 1e920d6..06011ce 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -732,4 +732,10 @@ LIBVIRT_1.3.3 { virDomainSetPerfEvents; } LIBVIRT_1.2.19; +LIBVIRT_2.0.0 { + global: + virConnectStoragePoolEventRegisterAny; + virConnectStoragePoolEventDeregisterAny; +} LIBVIRT_1.3.3; + # .... define new API here using predicted next version number .... -- 2.5.5

On Thu, Jun 16, 2016 at 10:08:55AM +0200, Jovanka Gulicoska wrote:
Storage pool lifecycle event API entry points for registering and deregistering storage pool events, as well as types of events associated with storage pools. These entry points will be used for implementing asynchronous lifecycle events.
Storage pool API: virConnectStoragePoolEventRegisterAny virConnectStoragePoolEventDeregisterAny virStoragePoolEventLifecycleType which has events STARTED, STOPPED, DEFINED, UNDEFINED, and REFRESHED
This is wrong. A storage pool refresh is *not* a lifecycle change, as there is no transition involved - the pool is simply running the whole time. We should have a completely separate event for notifying of refreshes. This is critical to fix before the 2.0.0 release, so I will shortly send patches to fix the API in this respect. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 06/24/2016 12:31 PM, Daniel P. Berrange wrote:
On Thu, Jun 16, 2016 at 10:08:55AM +0200, Jovanka Gulicoska wrote:
Storage pool lifecycle event API entry points for registering and deregistering storage pool events, as well as types of events associated with storage pools. These entry points will be used for implementing asynchronous lifecycle events.
Storage pool API: virConnectStoragePoolEventRegisterAny virConnectStoragePoolEventDeregisterAny virStoragePoolEventLifecycleType which has events STARTED, STOPPED, DEFINED, UNDEFINED, and REFRESHED
This is wrong. A storage pool refresh is *not* a lifecycle change, as there is no transition involved - the pool is simply running the whole time.
Hmm, in my head I was thinking more 'we emit DEFINED event when guest XML is redefined/changed, refresh often implies changed XML, so make it a LIFECYCLE event liked DEFINED', but indeed it doesn't really have a true state component to it so I'll defer to your judgement here.
We should have a completely separate event for notifying of refreshes.
This is critical to fix before the 2.0.0 release, so I will shortly send patches to fix the API in this respect.
Reviewing your patch now... Thanks, Cole

Add storage event handling infrastructure to storage_event.[ch], following the network_event.[ch] pattern. --- src/Makefile.am | 5 + src/conf/storage_event.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++ src/conf/storage_event.h | 60 ++++++++++++ src/libvirt_private.syms | 5 + 4 files changed, 307 insertions(+) create mode 100644 src/conf/storage_event.c create mode 100644 src/conf/storage_event.h diff --git a/src/Makefile.am b/src/Makefile.am index 1aff57d..1aaa00e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -343,6 +343,9 @@ DOMAIN_EVENT_SOURCES = \ NETWORK_EVENT_SOURCES = \ conf/network_event.c conf/network_event.h +STORAGE_EVENT_SOURCES = \ + conf/storage_event.c conf/storage_event.h + # Network driver generic impl APIs NETWORK_CONF_SOURCES = \ conf/network_conf.c conf/network_conf.h \ @@ -393,6 +396,7 @@ CONF_SOURCES = \ $(OBJECT_EVENT_SOURCES) \ $(DOMAIN_EVENT_SOURCES) \ $(NETWORK_EVENT_SOURCES) \ + $(STORAGE_EVENT_SOURCES) \ $(NETWORK_CONF_SOURCES) \ $(NWFILTER_CONF_SOURCES) \ $(NODE_DEVICE_CONF_SOURCES) \ @@ -2360,6 +2364,7 @@ libvirt_setuid_rpc_client_la_SOURCES = \ conf/domain_event.c \ conf/network_event.c \ conf/object_event.c \ + conf/storage_event.c \ rpc/virnetsocket.c \ rpc/virnetsocket.h \ rpc/virnetmessage.h \ diff --git a/src/conf/storage_event.c b/src/conf/storage_event.c new file mode 100644 index 0000000..c5688be --- /dev/null +++ b/src/conf/storage_event.c @@ -0,0 +1,237 @@ +/* + * storage_event.c: storage event queue processing helpers + * + * Copyright (C) 2010-2014 Red Hat, Inc. + * Copyright (C) 2008 VirtualIron + * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. + * + * 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, see + * <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include "storage_event.h" +#include "object_event.h" +#include "object_event_private.h" +#include "datatypes.h" +#include "virlog.h" + +VIR_LOG_INIT("conf.storage_event"); + +struct _virStoragePoolEvent { + virObjectEvent parent; + + /* Unused attribute to allow for subclass creation */ + bool dummy; +}; +typedef struct _virStoragePoolEvent virStoragePoolEvent; +typedef virStoragePoolEvent *virStoragePoolEventPtr; + +struct _virStoragePoolEventLifecycle { + virStoragePoolEvent parent; + + int type; + int detail; +}; +typedef struct _virStoragePoolEventLifecycle virStoragePoolEventLifecycle; +typedef virStoragePoolEventLifecycle *virStoragePoolEventLifecyclePtr; + +static virClassPtr virStoragePoolEventClass; +static virClassPtr virStoragePoolEventLifecycleClass; +static void virStoragePoolEventDispose(void *obj); +static void virStoragePoolEventLifecycleDispose(void *obj); + +static int +virStoragePoolEventsOnceInit(void) +{ + if (!(virStoragePoolEventClass = + virClassNew(virClassForObjectEvent(), + "virStoragePoolEvent", + sizeof(virStoragePoolEvent), + virStoragePoolEventDispose))) + return -1; + if (!(virStoragePoolEventLifecycleClass = + virClassNew(virStoragePoolEventClass, + "virStoragePoolEventLifecycle", + sizeof(virStoragePoolEventLifecycle), + virStoragePoolEventLifecycleDispose))) + return -1; + return 0; +} + +VIR_ONCE_GLOBAL_INIT(virStoragePoolEvents) + +static void +virStoragePoolEventDispose(void *obj) +{ + virStoragePoolEventPtr event = obj; + VIR_DEBUG("obj=%p", event); +} + + +static void +virStoragePoolEventLifecycleDispose(void *obj) +{ + virStoragePoolEventLifecyclePtr event = obj; + VIR_DEBUG("obj=%p", event); +} + + +static void +virStoragePoolEventDispatchDefaultFunc(virConnectPtr conn, + virObjectEventPtr event, + virConnectObjectEventGenericCallback cb, + void *cbopaque) +{ + virStoragePoolPtr pool = virGetStoragePool(conn, + event->meta.name, + event->meta.uuid, + NULL, NULL); + if (!pool) + return; + + switch ((virStoragePoolEventID)event->eventID) { + case VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE: + { + virStoragePoolEventLifecyclePtr storagePoolLifecycleEvent; + + storagePoolLifecycleEvent = (virStoragePoolEventLifecyclePtr)event; + ((virConnectStoragePoolEventLifecycleCallback)cb)(conn, pool, + storagePoolLifecycleEvent->type, + storagePoolLifecycleEvent->detail, + cbopaque); + goto cleanup; + } + + case VIR_STORAGE_POOL_EVENT_ID_LAST: + break; + } + VIR_WARN("Unexpected event ID %d", event->eventID); + + cleanup: + virObjectUnref(pool); +} + + +/** + * virStoragePoolEventStateRegisterID: + * @conn: connection to associate with callback + * @state: object event state + * @pool: storage pool to filter on or NULL for all storage pools + * @eventID: ID of the event type to register for + * @cb: function to invoke when event occurs + * @opaque: data blob to pass to @callback + * @freecb: callback to free @opaque + * @callbackID: filled with callback ID + * + * Register the function @cb with connection @conn, from @state, for + * events of type @eventID, and return the registration handle in + * @callbackID. + * + * Returns: the number of callbacks now registered, or -1 on error + */ +int +virStoragePoolEventStateRegisterID(virConnectPtr conn, + virObjectEventStatePtr state, + virStoragePoolPtr pool, + int eventID, + virConnectStoragePoolEventGenericCallback cb, + void *opaque, + virFreeCallback freecb, + int *callbackID) +{ + if (virStoragePoolEventsInitialize() < 0) + return -1; + + return virObjectEventStateRegisterID(conn, state, pool ? pool->uuid : NULL, + NULL, NULL, + virStoragePoolEventClass, eventID, + VIR_OBJECT_EVENT_CALLBACK(cb), + opaque, freecb, + false, callbackID, false); +} + + +/** + * virStoragePoolEventStateRegisterClient: + * @conn: connection to associate with callback + * @state: object event state + * @pool: storage pool to filter on or NULL for all storage pools + * @eventID: ID of the event type to register for + * @cb: function to invoke when event occurs + * @opaque: data blob to pass to @callback + * @freecb: callback to free @opaque + * @callbackID: filled with callback ID + * + * Register the function @cb with connection @conn, from @state, for + * events of type @eventID, and return the registration handle in + * @callbackID. This version is intended for use on the client side + * of RPC. + * + * Returns: the number of callbacks now registered, or -1 on error + */ +int +virStoragePoolEventStateRegisterClient(virConnectPtr conn, + virObjectEventStatePtr state, + virStoragePoolPtr pool, + int eventID, + virConnectStoragePoolEventGenericCallback cb, + void *opaque, + virFreeCallback freecb, + int *callbackID) +{ + if (virStoragePoolEventsInitialize() < 0) + return -1; + + return virObjectEventStateRegisterID(conn, state, pool ? pool->uuid : NULL, + NULL, NULL, + virStoragePoolEventClass, eventID, + VIR_OBJECT_EVENT_CALLBACK(cb), + opaque, freecb, + false, callbackID, true); +} + + +/** + * virStoragePoolEventLifecycleNew: + * @name: name of the storage pool object the event describes + * @uuid: uuid of the storage pool object the event describes + * @type: type of lifecycle event + * @detail: more details about @type + * + * Create a new storage pool lifecycle event. + */ +virObjectEventPtr +virStoragePoolEventLifecycleNew(const char *name, + const unsigned char *uuid, + int type, + int detail) +{ + virStoragePoolEventLifecyclePtr event; + + if (virStoragePoolEventsInitialize() < 0) + return NULL; + + if (!(event = virObjectEventNew(virStoragePoolEventLifecycleClass, + virStoragePoolEventDispatchDefaultFunc, + VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE, + 0, name, uuid))) + return NULL; + + event->type = type; + event->detail = detail; + + return (virObjectEventPtr)event; +} diff --git a/src/conf/storage_event.h b/src/conf/storage_event.h new file mode 100644 index 0000000..7c8ccb7 --- /dev/null +++ b/src/conf/storage_event.h @@ -0,0 +1,60 @@ +/* + * storage_event.h: storage event queue processing helpers + * + * Copyright (C) 2010-2014 Red Hat, Inc. + * Copyright (C) 2008 VirtualIron + * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. + * + * 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, see + * <http://www.gnu.org/licenses/>. +*/ + +#include "internal.h" +#include "object_event.h" +#include "object_event_private.h" + +#ifndef __STORAGE_EVENT_H__ +# define __STORAGE_EVENT_H__ + +int +virStoragePoolEventStateRegisterID(virConnectPtr conn, + virObjectEventStatePtr state, + virStoragePoolPtr pool, + int eventID, + virConnectStoragePoolEventGenericCallback cb, + void *opaque, + virFreeCallback freecb, + int *callbackID) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5) + ATTRIBUTE_NONNULL(8); + +int +virStoragePoolEventStateRegisterClient(virConnectPtr conn, + virObjectEventStatePtr state, + virStoragePoolPtr pool, + int eventID, + virConnectStoragePoolEventGenericCallback cb, + void *opaque, + virFreeCallback freecb, + int *callbackID) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5) + ATTRIBUTE_NONNULL(8); + +virObjectEventPtr +virStoragePoolEventLifecycleNew(const char *name, + const unsigned char *uuid, + int type, + int detail); + +#endif diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e939de3..af30476 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -878,6 +878,11 @@ virStorageVolTypeFromString; virStorageVolTypeToString; +# conf/storage_event.h +virStoragePoolEventLifecycleNew; +virStoragePoolEventStateRegisterID; + + # conf/virchrdev.h virChrdevAlloc; virChrdevFree; -- 2.5.5

Also includes unittests for storage pool lifecycle events API --- src/test/test_driver.c | 71 +++++++++++++++++++ tests/objecteventtest.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 248 insertions(+) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 6ab939a..29dd58b 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -49,6 +49,7 @@ #include "snapshot_conf.h" #include "fdstream.h" #include "storage_conf.h" +#include "storage_event.h" #include "node_device_conf.h" #include "virxml.h" #include "virthread.h" @@ -4114,6 +4115,7 @@ testStoragePoolCreate(virStoragePoolPtr pool, testDriverPtr privconn = pool->conn->privateData; virStoragePoolObjPtr privpool; int ret = -1; + virObjectEventPtr event = NULL; virCheckFlags(0, -1); @@ -4134,9 +4136,14 @@ testStoragePoolCreate(virStoragePoolPtr pool, } privpool->active = 1; + + event = virStoragePoolEventLifecycleNew(pool->name, pool->uuid, + VIR_STORAGE_POOL_EVENT_STARTED, + 0); ret = 0; cleanup: + testObjectEventQueue(privconn, event); if (privpool) virStoragePoolObjUnlock(privpool); return ret; @@ -4204,6 +4211,7 @@ testStoragePoolCreateXML(virConnectPtr conn, virStoragePoolDefPtr def; virStoragePoolObjPtr pool = NULL; virStoragePoolPtr ret = NULL; + virObjectEventPtr event = NULL; virCheckFlags(0, NULL); @@ -4231,11 +4239,16 @@ testStoragePoolCreateXML(virConnectPtr conn, } pool->active = 1; + event = virStoragePoolEventLifecycleNew(pool->def->name, pool->def->uuid, + VIR_STORAGE_POOL_EVENT_STARTED, + 0); + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid, NULL, NULL); cleanup: virStoragePoolDefFree(def); + testObjectEventQueue(privconn, event); if (pool) virStoragePoolObjUnlock(pool); testDriverUnlock(privconn); @@ -4251,6 +4264,7 @@ testStoragePoolDefineXML(virConnectPtr conn, virStoragePoolDefPtr def; virStoragePoolObjPtr pool = NULL; virStoragePoolPtr ret = NULL; + virObjectEventPtr event = NULL; virCheckFlags(0, NULL); @@ -4266,6 +4280,10 @@ testStoragePoolDefineXML(virConnectPtr conn, goto cleanup; def = NULL; + event = virStoragePoolEventLifecycleNew(pool->def->name, pool->def->uuid, + VIR_STORAGE_POOL_EVENT_DEFINED, + 0); + if (testStoragePoolObjSetDefaults(pool) == -1) { virStoragePoolObjRemove(&privconn->pools, pool); pool = NULL; @@ -4277,6 +4295,7 @@ testStoragePoolDefineXML(virConnectPtr conn, cleanup: virStoragePoolDefFree(def); + testObjectEventQueue(privconn, event); if (pool) virStoragePoolObjUnlock(pool); testDriverUnlock(privconn); @@ -4289,6 +4308,7 @@ testStoragePoolUndefine(virStoragePoolPtr pool) testDriverPtr privconn = pool->conn->privateData; virStoragePoolObjPtr privpool; int ret = -1; + virObjectEventPtr event = NULL; testDriverLock(privconn); privpool = virStoragePoolObjFindByName(&privconn->pools, @@ -4305,6 +4325,10 @@ testStoragePoolUndefine(virStoragePoolPtr pool) goto cleanup; } + event = virStoragePoolEventLifecycleNew(pool->name, pool->uuid, + VIR_STORAGE_POOL_EVENT_UNDEFINED, + 0); + virStoragePoolObjRemove(&privconn->pools, privpool); privpool = NULL; ret = 0; @@ -4312,6 +4336,7 @@ testStoragePoolUndefine(virStoragePoolPtr pool) cleanup: if (privpool) virStoragePoolObjUnlock(privpool); + testObjectEventQueue(privconn, event); testDriverUnlock(privconn); return ret; } @@ -4356,6 +4381,7 @@ testStoragePoolDestroy(virStoragePoolPtr pool) testDriverPtr privconn = pool->conn->privateData; virStoragePoolObjPtr privpool; int ret = -1; + virObjectEventPtr event = NULL; testDriverLock(privconn); privpool = virStoragePoolObjFindByName(&privconn->pools, @@ -4373,6 +4399,9 @@ testStoragePoolDestroy(virStoragePoolPtr pool) } privpool->active = 0; + event = virStoragePoolEventLifecycleNew(privpool->def->name, privpool->def->uuid, + VIR_STORAGE_POOL_EVENT_STOPPED, + 0); if (privpool->configFile == NULL) { virStoragePoolObjRemove(&privconn->pools, privpool); @@ -4381,6 +4410,7 @@ testStoragePoolDestroy(virStoragePoolPtr pool) ret = 0; cleanup: + testObjectEventQueue(privconn, event); if (privpool) virStoragePoolObjUnlock(privpool); testDriverUnlock(privconn); @@ -4430,6 +4460,7 @@ testStoragePoolRefresh(virStoragePoolPtr pool, testDriverPtr privconn = pool->conn->privateData; virStoragePoolObjPtr privpool; int ret = -1; + virObjectEventPtr event = NULL; virCheckFlags(0, -1); @@ -4448,9 +4479,14 @@ testStoragePoolRefresh(virStoragePoolPtr pool, _("storage pool '%s' is not active"), pool->name); goto cleanup; } + + event = virStoragePoolEventLifecycleNew(pool->name, pool->uuid, + VIR_STORAGE_POOL_EVENT_REFRESHED, + 0); ret = 0; cleanup: + testObjectEventQueue(privconn, event); if (privpool) virStoragePoolObjUnlock(privpool); return ret; @@ -5644,6 +5680,39 @@ testConnectNetworkEventDeregisterAny(virConnectPtr conn, return ret; } +static int +testConnectStoragePoolEventRegisterAny(virConnectPtr conn, + virStoragePoolPtr pool, + int eventID, + virConnectStoragePoolEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + testDriverPtr driver = conn->privateData; + int ret; + + if (virStoragePoolEventStateRegisterID(conn, driver->eventState, + pool, eventID, callback, + opaque, freecb, &ret) < 0) + ret = -1; + + return ret; +} + +static int +testConnectStoragePoolEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + testDriverPtr driver = conn->privateData; + int ret = 0; + + if (virObjectEventStateDeregisterID(conn, driver->eventState, + callbackID) < 0) + ret = -1; + + return ret; +} + static int testConnectListAllDomains(virConnectPtr conn, virDomainPtr **domains, unsigned int flags) @@ -6751,6 +6820,8 @@ static virStorageDriver testStorageDriver = { .connectListDefinedStoragePools = testConnectListDefinedStoragePools, /* 0.5.0 */ .connectListAllStoragePools = testConnectListAllStoragePools, /* 0.10.2 */ .connectFindStoragePoolSources = testConnectFindStoragePoolSources, /* 0.5.0 */ + .connectStoragePoolEventRegisterAny = testConnectStoragePoolEventRegisterAny, /* 2.0.0 */ + .connectStoragePoolEventDeregisterAny = testConnectStoragePoolEventDeregisterAny, /* 2.0.0 */ .storagePoolLookupByName = testStoragePoolLookupByName, /* 0.5.0 */ .storagePoolLookupByUUID = testStoragePoolLookupByUUID, /* 0.5.0 */ .storagePoolLookupByVolume = testStoragePoolLookupByVolume, /* 0.5.0 */ diff --git a/tests/objecteventtest.c b/tests/objecteventtest.c index c77b0cd..ac8cce3 100644 --- a/tests/objecteventtest.c +++ b/tests/objecteventtest.c @@ -53,12 +53,21 @@ static const char networkDef[] = " </ip>\n" "</network>\n"; +static const char storagePoolDef[] = +"<pool type='dir'>\n" +" <name>P</name>\n" +" <target>\n" +" <path>/target-path</path>\n" +" </target>\n" +"</pool>\n"; + typedef struct { int startEvents; int stopEvents; int defineEvents; int undefineEvents; int unexpectedEvents; + int refreshEvents; } lifecycleEventCounter; static void @@ -69,11 +78,13 @@ lifecycleEventCounter_reset(lifecycleEventCounter *counter) counter->defineEvents = 0; counter->undefineEvents = 0; counter->unexpectedEvents = 0; + counter->refreshEvents = 0; } typedef struct { virConnectPtr conn; virNetworkPtr net; + virStoragePoolPtr pool; } objecteventTest; @@ -125,6 +136,26 @@ networkLifecycleCb(virConnectPtr conn ATTRIBUTE_UNUSED, counter->undefineEvents++; } +static void +storagePoolLifecycleCb(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolPtr pool ATTRIBUTE_UNUSED, + int event, + int detail ATTRIBUTE_UNUSED, + void* opaque) +{ + lifecycleEventCounter *counter = opaque; + + if (event == VIR_STORAGE_POOL_EVENT_STARTED) + counter->startEvents++; + else if (event == VIR_STORAGE_POOL_EVENT_STOPPED) + counter->stopEvents++; + else if (event == VIR_STORAGE_POOL_EVENT_DEFINED) + counter->defineEvents++; + else if (event == VIR_STORAGE_POOL_EVENT_UNDEFINED) + counter->undefineEvents++; + else if (event == VIR_STORAGE_POOL_EVENT_REFRESHED) + counter->refreshEvents++; +} static int testDomainCreateXMLOld(const void *data) @@ -523,6 +554,130 @@ testNetworkStartStopEvent(const void *data) return ret; } +static int +testStoragePoolCreateXML(const void *data) +{ + const objecteventTest *test = data; + lifecycleEventCounter counter; + virStoragePoolPtr pool; + int id; + int ret = 0; + + lifecycleEventCounter_reset(&counter); + + id = virConnectStoragePoolEventRegisterAny(test->conn, NULL, + VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE, + VIR_STORAGE_POOL_EVENT_CALLBACK(&storagePoolLifecycleCb), + &counter, NULL); + pool = virStoragePoolCreateXML(test->conn, storagePoolDef, 0); + + if (!pool || virEventRunDefaultImpl() < 0) { + ret = -1; + goto cleanup; + } + + if (counter.startEvents != 1 || counter.unexpectedEvents > 0) { + ret = -1; + goto cleanup; + } + + cleanup: + virConnectStoragePoolEventDeregisterAny(test->conn, id); + if (pool) { + virStoragePoolDestroy(pool); + virStoragePoolFree(pool); + } + return ret; +} + +static int +testStoragePoolDefine(const void *data) +{ + const objecteventTest *test = data; + lifecycleEventCounter counter; + virStoragePoolPtr pool; + int id; + int ret = 0; + + lifecycleEventCounter_reset(&counter); + + id = virConnectStoragePoolEventRegisterAny(test->conn, NULL, + VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE, + VIR_STORAGE_POOL_EVENT_CALLBACK(&storagePoolLifecycleCb), + &counter, NULL); + + /* Make sure the define event is triggered */ + pool = virStoragePoolDefineXML(test->conn, storagePoolDef, 0); + + if (!pool || virEventRunDefaultImpl() < 0) { + ret = -1; + goto cleanup; + } + + if (counter.defineEvents != 1 || counter.unexpectedEvents > 0) { + ret = -1; + goto cleanup; + } + + /* Make sure the undefine event is triggered */ + virStoragePoolUndefine(pool); + + if (virEventRunDefaultImpl() < 0) { + ret = -1; + goto cleanup; + } + + if (counter.undefineEvents != 1 || counter.unexpectedEvents > 0) { + ret = -1; + goto cleanup; + } + + + cleanup: + virConnectStoragePoolEventDeregisterAny(test->conn, id); + if (pool) + virStoragePoolFree(pool); + + return ret; +} + +static int +testStoragePoolStartStopEvent(const void *data) +{ + const objecteventTest *test = data; + lifecycleEventCounter counter; + int id; + int ret = 0; + + if (!test->pool) + return -1; + + lifecycleEventCounter_reset(&counter); + + id = virConnectStoragePoolEventRegisterAny(test->conn, test->pool, + VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE, + VIR_STORAGE_POOL_EVENT_CALLBACK(&storagePoolLifecycleCb), + &counter, NULL); + virStoragePoolCreate(test->pool, 0); + virStoragePoolRefresh(test->pool, 0); + virStoragePoolDestroy(test->pool); + + if (virEventRunDefaultImpl() < 0) { + ret = -1; + goto cleanup; + } + + if (counter.startEvents != 1 || counter.stopEvents != 1 || + counter.refreshEvents != 1 || counter.unexpectedEvents > 0) { + ret = -1; + goto cleanup; + } + + cleanup: + virConnectStoragePoolEventDeregisterAny(test->conn, id); + return ret; +} + static void timeout(int id ATTRIBUTE_UNUSED, void *opaque ATTRIBUTE_UNUSED) { @@ -581,6 +736,28 @@ mymain(void) virNetworkUndefine(test.net); virNetworkFree(test.net); } + + /* Storage pool event tests */ + if (virTestRun("Storage pool createXML start event ", + testStoragePoolCreateXML, &test) < 0) + ret = EXIT_FAILURE; + if (virTestRun("Storage pool (un)define events", + testStoragePoolDefine, &test) < 0) + ret = EXIT_FAILURE; + + /* Define a test storage pool */ + if (!(test.pool = virStoragePoolDefineXML(test.conn, storagePoolDef, 0))) + ret = EXIT_FAILURE; + if (virTestRun("Storage pool start stop events ", + testStoragePoolStartStopEvent, &test) < 0) + ret = EXIT_FAILURE; + + /* Cleanup */ + if (test.pool) { + virStoragePoolUndefine(test.pool); + virStoragePoolFree(test.pool); + } + virConnectClose(test.conn); virEventRemoveTimeout(timer); -- 2.5.5

--- daemon/libvirtd.h | 2 + daemon/remote.c | 207 ++++++++++++++++++++++++++++++++++++++++++- src/remote/remote_driver.c | 128 ++++++++++++++++++++++++++ src/remote/remote_protocol.x | 43 ++++++++- src/remote_protocol-structs | 19 ++++ 5 files changed, 395 insertions(+), 4 deletions(-) diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index 7271b0f..cc91266 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -60,6 +60,8 @@ struct daemonClientPrivate { size_t nnetworkEventCallbacks; daemonClientEventCallbackPtr *qemuEventCallbacks; size_t nqemuEventCallbacks; + daemonClientEventCallbackPtr *storageEventCallbacks; + size_t nstorageEventCallbacks; bool closeRegistered; # if WITH_SASL diff --git a/daemon/remote.c b/daemon/remote.c index b2a420b..e5c9036 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -181,6 +181,32 @@ remoteRelayNetworkEventCheckACL(virNetServerClientPtr client, return ret; } +static bool +remoteRelayStoragePoolEventCheckACL(virNetServerClientPtr client, + virConnectPtr conn, + virStoragePoolPtr pool) +{ + virStoragePoolDef def; + virIdentityPtr identity = NULL; + bool ret = false; + + /* For now, we just create a virStoragePoolDef with enough contents to + * satisfy what viraccessdriverpolkit.c references. This is a bit + * fragile, but I don't know of anything better. */ + def.name = pool->name; + memcpy(def.uuid, pool->uuid, VIR_UUID_BUFLEN); + + if (!(identity = virNetServerClientGetIdentity(client))) + goto cleanup; + if (virIdentitySetCurrent(identity) < 0) + goto cleanup; + ret = virConnectStoragePoolEventRegisterAnyCheckACL(conn, &def); + + cleanup: + ignore_value(virIdentitySetCurrent(NULL)); + virObjectUnref(identity); + return ret; +} static bool remoteRelayDomainQemuMonitorEventCheckACL(virNetServerClientPtr client, @@ -208,7 +234,6 @@ remoteRelayDomainQemuMonitorEventCheckACL(virNetServerClientPtr client, return ret; } - static int remoteRelayDomainEventLifecycle(virConnectPtr conn, virDomainPtr dom, @@ -1236,6 +1261,44 @@ static virConnectNetworkEventGenericCallback networkEventCallbacks[] = { verify(ARRAY_CARDINALITY(networkEventCallbacks) == VIR_NETWORK_EVENT_ID_LAST); +static int +remoteRelayStoragePoolEventLifecycle(virConnectPtr conn, + virStoragePoolPtr pool, + int event, + int detail, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_storage_pool_event_lifecycle_msg data; + + if (callback->callbackID < 0 || + !remoteRelayStoragePoolEventCheckACL(callback->client, conn, pool)) + return -1; + + VIR_DEBUG("Relaying storage pool lifecycle event %d, detail %d, callback %d", + event, detail, callback->callbackID); + + /* build return data */ + memset(&data, 0, sizeof(data)); + make_nonnull_storage_pool(&data.pool, pool); + data.callbackID = callback->callbackID; + data.event = event; + data.detail = detail; + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE, + (xdrproc_t)xdr_remote_storage_pool_event_lifecycle_msg, + &data); + + return 0; +} + +static virConnectStoragePoolEventGenericCallback storageEventCallbacks[] = { + VIR_STORAGE_POOL_EVENT_CALLBACK(remoteRelayStoragePoolEventLifecycle), +}; + +verify(ARRAY_CARDINALITY(storageEventCallbacks) == VIR_STORAGE_POOL_EVENT_ID_LAST); + static void remoteRelayDomainQemuMonitorEvent(virConnectPtr conn, virDomainPtr dom, @@ -1343,6 +1406,21 @@ void remoteClientFreeFunc(void *data) } VIR_FREE(priv->networkEventCallbacks); + for (i = 0; i < priv->nstorageEventCallbacks; i++) { + int callbackID = priv->storageEventCallbacks[i]->callbackID; + if (callbackID < 0) { + VIR_WARN("unexpected incomplete storage pool callback %zu", i); + continue; + } + VIR_DEBUG("Deregistering remote storage pool event relay %d", + callbackID); + priv->storageEventCallbacks[i]->callbackID = -1; + if (virConnectStoragePoolEventDeregisterAny(priv->conn, + callbackID) < 0) + VIR_WARN("unexpected storage pool event deregister failure"); + } + VIR_FREE(priv->storageEventCallbacks); + for (i = 0; i < priv->nqemuEventCallbacks; i++) { int callbackID = priv->qemuEventCallbacks[i]->callbackID; if (callbackID < 0) { @@ -3528,8 +3606,10 @@ remoteDispatchConnectDomainEventRegister(virNetServerPtr server ATTRIBUTE_UNUSED * 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. */ + * our callback; or you can use VIR_APPEND_ELEMENT_COPY to avoid + * clearing 'callback' and having to juggle the pointer + * between 'ref' and 'callback'. + */ if (VIR_ALLOC(callback) < 0) goto cleanup; callback->client = client; @@ -5434,6 +5514,127 @@ remoteDispatchConnectNetworkEventDeregisterAny(virNetServerPtr server ATTRIBUTE_ return rv; } +static int +remoteDispatchConnectStoragePoolEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, + remote_connect_storage_pool_event_register_any_args *args, + remote_connect_storage_pool_event_register_any_ret *ret) +{ + int callbackID; + int rv = -1; + daemonClientEventCallbackPtr callback = NULL; + daemonClientEventCallbackPtr ref; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + virStoragePoolPtr pool = NULL; + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + virMutexLock(&priv->lock); + + if (args->pool && + !(pool = get_nonnull_storage_pool(priv->conn, *args->pool))) + goto cleanup; + + if (args->eventID >= VIR_STORAGE_POOL_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->storageEventCallbacks, + priv->nstorageEventCallbacks, + callback) < 0) + goto cleanup; + + if ((callbackID = virConnectStoragePoolEventRegisterAny(priv->conn, + pool, + args->eventID, + storageEventCallbacks[args->eventID], + ref, + remoteEventCallbackFree)) < 0) { + VIR_SHRINK_N(priv->storageEventCallbacks, + priv->nstorageEventCallbacks, 1); + callback = ref; + goto cleanup; + } + + ref->callbackID = callbackID; + ret->callbackID = callbackID; + + rv = 0; + + cleanup: + VIR_FREE(callback); + if (rv < 0) + virNetMessageSaveError(rerr); + virObjectUnref(pool); + virMutexUnlock(&priv->lock); + return rv; +} + +static int +remoteDispatchConnectStoragePoolEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, + remote_connect_storage_pool_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->nstorageEventCallbacks; i++) { + if (priv->storageEventCallbacks[i]->callbackID == args->callbackID) + break; + } + if (i == priv->nstorageEventCallbacks) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("storage pool event callback %d not registered"), + args->callbackID); + goto cleanup; + } + + if (virConnectStoragePoolEventDeregisterAny(priv->conn, args->callbackID) < 0) + goto cleanup; + + VIR_DELETE_ELEMENT(priv->storageEventCallbacks, i, + priv->nstorageEventCallbacks); + + rv = 0; + + cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + static int qemuDispatchConnectDomainMonitorEventRegister(virNetServerPtr server ATTRIBUTE_UNUSED, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index f494cbf..62deecd 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -34,6 +34,7 @@ #include "datatypes.h" #include "domain_event.h" #include "network_event.h" +#include "storage_event.h" #include "driver.h" #include "virbuffer.h" #include "remote_driver.h" @@ -356,6 +357,11 @@ remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, void *evdata, void *opaque); static void +remoteStoragePoolBuildEventLifecycle(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); @@ -534,6 +540,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteDomainBuildEventCallbackDeviceRemovalFailed, sizeof(remote_domain_event_callback_device_removal_failed_msg), (xdrproc_t)xdr_remote_domain_event_callback_device_removal_failed_msg }, + { REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE, + remoteStoragePoolBuildEventLifecycle, + sizeof(remote_storage_pool_event_lifecycle_msg), + (xdrproc_t)xdr_remote_storage_pool_event_lifecycle_msg }, }; static void @@ -3040,6 +3050,101 @@ remoteConnectNetworkEventDeregisterAny(virConnectPtr conn, return rv; } +static int +remoteConnectStoragePoolEventRegisterAny(virConnectPtr conn, + virStoragePoolPtr pool, + int eventID, + virConnectStoragePoolEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + int rv = -1; + struct private_data *priv = conn->privateData; + remote_connect_storage_pool_event_register_any_args args; + remote_connect_storage_pool_event_register_any_ret ret; + int callbackID; + int count; + remote_nonnull_storage_pool storage_pool; + + remoteDriverLock(priv); + + if ((count = virStoragePoolEventStateRegisterClient(conn, priv->eventState, + pool, 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 (pool) { + make_nonnull_storage_pool(&storage_pool, pool); + args.pool = &storage_pool; + } else { + args.pool = NULL; + } + + memset(&ret, 0, sizeof(ret)); + if (call(conn, priv, 0, REMOTE_PROC_CONNECT_STORAGE_POOL_EVENT_REGISTER_ANY, + (xdrproc_t) xdr_remote_connect_storage_pool_event_register_any_args, (char *) &args, + (xdrproc_t) xdr_remote_connect_storage_pool_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 +remoteConnectStoragePoolEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + struct private_data *priv = conn->privateData; + int rv = -1; + remote_connect_storage_pool_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_STORAGE_POOL_EVENT_DEREGISTER_ANY, + (xdrproc_t) xdr_remote_connect_storage_pool_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, @@ -5013,6 +5118,27 @@ remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, remoteEventQueue(priv, event, msg->callbackID); } +static void +remoteStoragePoolBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_storage_pool_event_lifecycle_msg *msg = evdata; + virStoragePoolPtr pool; + virObjectEventPtr event = NULL; + + pool = get_nonnull_storage_pool(conn, msg->pool); + if (!pool) + return; + + event = virStoragePoolEventLifecycleNew(pool->name, pool->uuid, msg->event, + msg->detail); + virObjectUnref(pool); + + remoteEventQueue(priv, event, msg->callbackID); +} static void remoteDomainBuildQemuMonitorEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, @@ -7908,6 +8034,8 @@ static virStorageDriver storage_driver = { .connectListDefinedStoragePools = remoteConnectListDefinedStoragePools, /* 0.4.1 */ .connectListAllStoragePools = remoteConnectListAllStoragePools, /* 0.10.2 */ .connectFindStoragePoolSources = remoteConnectFindStoragePoolSources, /* 0.4.5 */ + .connectStoragePoolEventDeregisterAny = remoteConnectStoragePoolEventDeregisterAny, /* 1.3.6 */ + .connectStoragePoolEventRegisterAny = remoteConnectStoragePoolEventRegisterAny, /* 1.3.6 */ .storagePoolLookupByName = remoteStoragePoolLookupByName, /* 0.4.1 */ .storagePoolLookupByUUID = remoteStoragePoolLookupByUUID, /* 0.4.1 */ .storagePoolLookupByVolume = remoteStoragePoolLookupByVolume, /* 0.4.1 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index bab8ef2..0170c0c 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3098,6 +3098,26 @@ struct remote_network_event_lifecycle_msg { int detail; }; +struct remote_connect_storage_pool_event_register_any_args { + int eventID; + remote_storage_pool pool; +}; + +struct remote_connect_storage_pool_event_register_any_ret { + int callbackID; +}; + +struct remote_connect_storage_pool_event_deregister_any_args { + int callbackID; +}; + +struct remote_storage_pool_event_lifecycle_msg { + int callbackID; + remote_nonnull_storage_pool pool; + int event; + int detail; +}; + struct remote_domain_fsfreeze_args { remote_nonnull_domain dom; remote_nonnull_string mountpoints<REMOTE_DOMAIN_FSFREEZE_MOUNTPOINTS_MAX>; /* (const char **) */ @@ -5793,5 +5813,26 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_REMOVAL_FAILED = 367 + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_REMOVAL_FAILED = 367, + + /** + * @generate: none + * @priority: high + * @acl: connect:search_storage_pools + * @aclfilter: storage_pool:getattr + */ + REMOTE_PROC_CONNECT_STORAGE_POOL_EVENT_REGISTER_ANY = 368, + + /** + * @generate: none + * @priority: high + * @acl: connect:read + */ + REMOTE_PROC_CONNECT_STORAGE_POOL_EVENT_DEREGISTER_ANY = 369, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE = 370 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index fe1b8a8..2d7299d 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2551,6 +2551,22 @@ struct remote_network_event_lifecycle_msg { int event; int detail; }; +struct remote_connect_storage_pool_event_register_any_args { + int eventID; + remote_storage_pool pool; +}; +struct remote_connect_storage_pool_event_register_any_ret { + int callbackID; +}; +struct remote_connect_storage_pool_event_deregister_any_args { + int callbackID; +}; +struct remote_storage_pool_event_lifecycle_msg { + int callbackID; + remote_nonnull_storage_pool pool; + int event; + int detail; +}; struct remote_domain_fsfreeze_args { remote_nonnull_domain dom; struct { @@ -3103,4 +3119,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_PERF_EVENTS = 365, REMOTE_PROC_DOMAIN_SET_PERF_EVENTS = 366, REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_REMOVAL_FAILED = 367, + REMOTE_PROC_CONNECT_STORAGE_POOL_EVENT_REGISTER_ANY = 368, + REMOTE_PROC_CONNECT_STORAGE_POOL_EVENT_DEREGISTER_ANY = 369, + REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE = 370, }; -- 2.5.5

Something weird happened with the series titles, they have 'python' in the name! Did you trigger send-email from the libvirt-python.git directory, or maybe you edited .git/config in the wrong repo? Anyways, I've pushed patches 1-6 with the following changes to this patch: On 06/16/2016 04:08 AM, Jovanka Gulicoska wrote:
--- daemon/libvirtd.h | 2 + daemon/remote.c | 207 ++++++++++++++++++++++++++++++++++++++++++- src/remote/remote_driver.c | 128 ++++++++++++++++++++++++++ src/remote/remote_protocol.x | 43 ++++++++- src/remote_protocol-structs | 19 ++++ 5 files changed, 395 insertions(+), 4 deletions(-) diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index 7271b0f..cc91266 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -208,7 +234,6 @@ remoteRelayDomainQemuMonitorEventCheckACL(virNetServerClientPtr client, return ret; }
- static int remoteRelayDomainEventLifecycle(virConnectPtr conn, virDomainPtr dom,
Dropped this spurious change
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index f494cbf..62deecd 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -7908,6 +8034,8 @@ static virStorageDriver storage_driver = { .connectListDefinedStoragePools = remoteConnectListDefinedStoragePools, /* 0.4.1 */ .connectListAllStoragePools = remoteConnectListAllStoragePools, /* 0.10.2 */ .connectFindStoragePoolSources = remoteConnectFindStoragePoolSources, /* 0.4.5 */ + .connectStoragePoolEventDeregisterAny = remoteConnectStoragePoolEventDeregisterAny, /* 1.3.6 */ + .connectStoragePoolEventRegisterAny = remoteConnectStoragePoolEventRegisterAny, /* 1.3.6 */ .storagePoolLookupByName = remoteStoragePoolLookupByName, /* 0.4.1 */ .storagePoolLookupByUUID = remoteStoragePoolLookupByUUID, /* 0.4.1 */ .storagePoolLookupByVolume = remoteStoragePoolLookupByVolume, /* 0.4.1 */
Switched this reference to 2.0.0
diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index fe1b8a8..2d7299d 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2551,6 +2551,22 @@ struct remote_network_event_lifecycle_msg { int event; int detail; }; +struct remote_connect_storage_pool_event_register_any_args { + int eventID; + remote_storage_pool pool; +}; +struct remote_connect_storage_pool_event_register_any_ret { + int callbackID; +}; +struct remote_connect_storage_pool_event_deregister_any_args { + int callbackID; +}; +struct remote_storage_pool_event_lifecycle_msg { + int callbackID; + remote_nonnull_storage_pool pool; + int event; + int detail; +}; struct remote_domain_fsfreeze_args { remote_nonnull_domain dom; struct { @@ -3103,4 +3119,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_PERF_EVENTS = 365, REMOTE_PROC_DOMAIN_SET_PERF_EVENTS = 366, REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_REMOVAL_FAILED = 367, + REMOTE_PROC_CONNECT_STORAGE_POOL_EVENT_REGISTER_ANY = 368, + REMOTE_PROC_CONNECT_STORAGE_POOL_EVENT_DEREGISTER_ANY = 369, + REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE = 370, };
I installed latest pdwtags so I can actually run the protocol-structs test, and it was still missing this diff, which is squashed in now: diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 2d7299d..3934e07 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2552,20 +2552,20 @@ struct remote_network_event_lifecycle_msg { int detail; }; struct remote_connect_storage_pool_event_register_any_args { - int eventID; - remote_storage_pool pool; + int eventID; + remote_storage_pool pool; }; struct remote_connect_storage_pool_event_register_any_ret { - int callbackID; + int callbackID; }; struct remote_connect_storage_pool_event_deregister_any_args { - int callbackID; + int callbackID;git }; struct remote_storage_pool_event_lifecycle_msg { - int callbackID; - remote_nonnull_storage_pool pool; - int event; - int detail; + int callbackID; + remote_nonnull_storage_pool pool; + int event; + int detail; }; Thanks, Cole

Implement storage pool event callbacks for START, STOP, DEFINE, UNDEFINED and REFRESHED in functions when a storage pool is created/started/stopped etc. accordingly --- src/conf/storage_conf.h | 4 ++ src/storage/storage_driver.c | 110 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h index 54116a6..185ae5e 100644 --- a/src/conf/storage_conf.h +++ b/src/conf/storage_conf.h @@ -31,6 +31,7 @@ # include "virthread.h" # include "device_conf.h" # include "node_device_conf.h" +# include "object_event.h" # include <libxml/tree.h> @@ -296,6 +297,9 @@ struct _virStorageDriverState { char *autostartDir; char *stateDir; bool privileged; + + /* Immutable pointer, self-locking APIs */ + virObjectEventStatePtr storageEventState; }; typedef struct _virStoragePoolSourceList virStoragePoolSourceList; diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index fb1b1a2..e2d729f 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -41,6 +41,7 @@ #include "driver.h" #include "storage_driver.h" #include "storage_conf.h" +#include "storage_event.h" #include "viralloc.h" #include "storage_backend.h" #include "virlog.h" @@ -276,6 +277,8 @@ storageStateInitialize(bool privileged, storagePoolUpdateAllState(); + driver->storageEventState = virObjectEventStateNew(); + storageDriverUnlock(); ret = 0; @@ -344,6 +347,8 @@ storageStateCleanup(void) storageDriverLock(); + virObjectEventStateFree(driver->storageEventState); + /* free inactive pools */ virStoragePoolObjListFree(&driver->pools); @@ -668,6 +673,7 @@ storagePoolCreateXML(virConnectPtr conn, virStoragePoolObjPtr pool = NULL; virStoragePoolPtr ret = NULL; virStorageBackendPtr backend; + virObjectEventPtr event = NULL; char *stateFile = NULL; unsigned int build_flags = 0; @@ -735,6 +741,12 @@ storagePoolCreateXML(virConnectPtr conn, pool = NULL; goto cleanup; } + + event = virStoragePoolEventLifecycleNew(pool->def->name, + pool->def->uuid, + VIR_STORAGE_POOL_EVENT_STARTED, + 0); + VIR_INFO("Creating storage pool '%s'", pool->def->name); pool->active = true; @@ -744,6 +756,8 @@ storagePoolCreateXML(virConnectPtr conn, cleanup: VIR_FREE(stateFile); virStoragePoolDefFree(def); + if (event) + virObjectEventStateQueue(driver->storageEventState, event); if (pool) virStoragePoolObjUnlock(pool); storageDriverUnlock(); @@ -758,6 +772,7 @@ storagePoolDefineXML(virConnectPtr conn, virStoragePoolDefPtr def; virStoragePoolObjPtr pool = NULL; virStoragePoolPtr ret = NULL; + virObjectEventPtr event = NULL; virCheckFlags(0, NULL); @@ -786,6 +801,11 @@ storagePoolDefineXML(virConnectPtr conn, pool = NULL; goto cleanup; } + + event = virStoragePoolEventLifecycleNew(def->name, def->uuid, + VIR_STORAGE_POOL_EVENT_DEFINED, + 0); + def = NULL; VIR_INFO("Defining storage pool '%s'", pool->def->name); @@ -793,6 +813,8 @@ storagePoolDefineXML(virConnectPtr conn, NULL, NULL); cleanup: + if (event) + virObjectEventStateQueue(driver->storageEventState, event); virStoragePoolDefFree(def); if (pool) virStoragePoolObjUnlock(pool); @@ -804,6 +826,7 @@ static int storagePoolUndefine(virStoragePoolPtr obj) { virStoragePoolObjPtr pool; + virObjectEventPtr event = NULL; int ret = -1; storageDriverLock(); @@ -847,12 +870,19 @@ storagePoolUndefine(virStoragePoolPtr obj) VIR_FREE(pool->configFile); VIR_FREE(pool->autostartLink); + event = virStoragePoolEventLifecycleNew(pool->def->name, + pool->def->uuid, + VIR_STORAGE_POOL_EVENT_UNDEFINED, + 0); + VIR_INFO("Undefining storage pool '%s'", pool->def->name); virStoragePoolObjRemove(&driver->pools, pool); pool = NULL; ret = 0; cleanup: + if (event) + virObjectEventStateQueue(driver->storageEventState, event); if (pool) virStoragePoolObjUnlock(pool); storageDriverUnlock(); @@ -865,6 +895,7 @@ storagePoolCreate(virStoragePoolPtr obj, { virStoragePoolObjPtr pool; virStorageBackendPtr backend; + virObjectEventPtr event = NULL; int ret = -1; char *stateFile = NULL; unsigned int build_flags = 0; @@ -926,11 +957,18 @@ storagePoolCreate(virStoragePoolPtr obj, goto cleanup; } + event = virStoragePoolEventLifecycleNew(pool->def->name, + pool->def->uuid, + VIR_STORAGE_POOL_EVENT_STARTED, + 0); + pool->active = true; ret = 0; cleanup: VIR_FREE(stateFile); + if (event) + virObjectEventStateQueue(driver->storageEventState, event); if (pool) virStoragePoolObjUnlock(pool); return ret; @@ -976,6 +1014,7 @@ storagePoolDestroy(virStoragePoolPtr obj) { virStoragePoolObjPtr pool; virStorageBackendPtr backend; + virObjectEventPtr event = NULL; char *stateFile = NULL; int ret = -1; @@ -1024,6 +1063,11 @@ storagePoolDestroy(virStoragePoolPtr obj) virStoragePoolObjClearVols(pool); + event = virStoragePoolEventLifecycleNew(pool->def->name, + pool->def->uuid, + VIR_STORAGE_POOL_EVENT_STOPPED, + 0); + pool->active = false; if (pool->configFile == NULL) { @@ -1038,6 +1082,8 @@ storagePoolDestroy(virStoragePoolPtr obj) ret = 0; cleanup: + if (event) + virObjectEventStateQueue(driver->storageEventState, event); if (pool) virStoragePoolObjUnlock(pool); storageDriverUnlock(); @@ -1109,6 +1155,7 @@ storagePoolRefresh(virStoragePoolPtr obj, virStoragePoolObjPtr pool; virStorageBackendPtr backend; int ret = -1; + virObjectEventPtr event = NULL; virCheckFlags(0, -1); @@ -1146,6 +1193,10 @@ storagePoolRefresh(virStoragePoolPtr obj, if (backend->stopPool) backend->stopPool(obj->conn, pool); + event = virStoragePoolEventLifecycleNew(pool->def->name, + pool->def->uuid, + VIR_STORAGE_POOL_EVENT_STOPPED, + 0); pool->active = false; if (pool->configFile == NULL) { @@ -1154,9 +1205,16 @@ storagePoolRefresh(virStoragePoolPtr obj, } goto cleanup; } + + event = virStoragePoolEventLifecycleNew(pool->def->name, + pool->def->uuid, + VIR_STORAGE_POOL_EVENT_REFRESHED, + 0); ret = 0; cleanup: + if (event) + virObjectEventStateQueue(driver->storageEventState, event); if (pool) virStoragePoolObjUnlock(pool); storageDriverUnlock(); @@ -2266,6 +2324,7 @@ virStorageVolPoolRefreshThread(void *opaque) virStorageVolStreamInfoPtr cbdata = opaque; virStoragePoolObjPtr pool = NULL; virStorageBackendPtr backend; + virObjectEventPtr event = NULL; storageDriverLock(); if (cbdata->vol_path) { @@ -2283,7 +2342,14 @@ virStorageVolPoolRefreshThread(void *opaque) if (backend->refreshPool(NULL, pool) < 0) VIR_DEBUG("Failed to refresh storage pool"); + event = virStoragePoolEventLifecycleNew(pool->def->name, + pool->def->uuid, + VIR_STORAGE_POOL_EVENT_REFRESHED, + 0); + cleanup: + if (event) + virObjectEventStateQueue(driver->storageEventState, event); if (pool) virStoragePoolObjUnlock(pool); storageDriverUnlock(); @@ -2662,6 +2728,48 @@ storageConnectListAllStoragePools(virConnectPtr conn, return ret; } +static int +storageConnectStoragePoolEventRegisterAny(virConnectPtr conn, + virStoragePoolPtr pool, + int eventID, + virConnectStoragePoolEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + int callbackID = -1; + + if (virConnectStoragePoolEventRegisterAnyEnsureACL(conn) < 0) + goto cleanup; + + if (virStoragePoolEventStateRegisterID(conn, driver->storageEventState, + pool, eventID, callback, + opaque, freecb, &callbackID) < 0) + callbackID = -1; + cleanup: + return callbackID; +} + +static int +storageConnectStoragePoolEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + int ret = -1; + + if (virConnectStoragePoolEventDeregisterAnyEnsureACL(conn) < 0) + goto cleanup; + + if (virObjectEventStateDeregisterID(conn, + driver->storageEventState, + callbackID) < 0) + goto cleanup; + + ret = 0; + + cleanup: + return ret; +} + + static virStorageDriver storageDriver = { .name = "storage", @@ -2670,6 +2778,8 @@ static virStorageDriver storageDriver = { .connectNumOfDefinedStoragePools = storageConnectNumOfDefinedStoragePools, /* 0.4.0 */ .connectListDefinedStoragePools = storageConnectListDefinedStoragePools, /* 0.4.0 */ .connectListAllStoragePools = storageConnectListAllStoragePools, /* 0.10.2 */ + .connectStoragePoolEventRegisterAny = storageConnectStoragePoolEventRegisterAny, /* 2.0.0 */ + .connectStoragePoolEventDeregisterAny = storageConnectStoragePoolEventDeregisterAny, /* 2.0.0 */ .connectFindStoragePoolSources = storageConnectFindStoragePoolSources, /* 0.4.0 */ .storagePoolLookupByName = storagePoolLookupByName, /* 0.4.0 */ .storagePoolLookupByUUID = storagePoolLookupByUUID, /* 0.4.0 */ -- 2.5.5

--- examples/object-events/event-test.c | 73 +++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/examples/object-events/event-test.c b/examples/object-events/event-test.c index c1ff4a7..215e026 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -337,6 +337,26 @@ guestAgentLifecycleEventReasonToString(int event) return "unknown"; } +static const char * +storagePoolEventToString(int event) +{ + switch ((virStoragePoolEventLifecycleType) event) { + case VIR_STORAGE_POOL_EVENT_DEFINED: + return "Defined"; + case VIR_STORAGE_POOL_EVENT_UNDEFINED: + return "Undefined"; + case VIR_STORAGE_POOL_EVENT_STARTED: + return "Started"; + case VIR_STORAGE_POOL_EVENT_STOPPED: + return "Stopped"; + case VIR_STORAGE_POOL_EVENT_REFRESHED: + return "Refreshed"; + case VIR_STORAGE_POOL_EVENT_LAST: + break; + } + return "unknown"; +} + static int myDomainEventCallback1(virConnectPtr conn ATTRIBUTE_UNUSED, @@ -644,6 +664,21 @@ myNetworkEventCallback(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int +myStoragePoolEventCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolPtr pool, + int event, + int detail, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Storage pool %s %s %d\n", __func__, + virStoragePoolGetName(pool), + storagePoolEventToString(event), + detail); + return 0; +} + + static void eventTypedParamsPrint(virTypedParameterPtr params, int nparams) @@ -889,8 +924,23 @@ struct domainEventData domainEvents[] = { DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED, myDomainEventDeviceRemovalFailedCallback), }; +struct storagePoolEventData { + int event; + int id; + virConnectStoragePoolEventGenericCallback cb; + const char *name; +}; + +#define STORAGE_POOL_EVENT(event, callback) \ + {event, -1, VIR_STORAGE_POOL_EVENT_CALLBACK(callback), #event} + +struct storagePoolEventData storagePoolEvents[] = { + STORAGE_POOL_EVENT(VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE, myStoragePoolEventCallback), +}; + /* make sure that the events are kept in sync */ verify(ARRAY_CARDINALITY(domainEvents) == VIR_DOMAIN_EVENT_ID_LAST); +verify(ARRAY_CARDINALITY(storagePoolEvents) == VIR_STORAGE_POOL_EVENT_ID_LAST); int main(int argc, char **argv) @@ -966,6 +1016,22 @@ main(int argc, char **argv) VIR_NETWORK_EVENT_CALLBACK(myNetworkEventCallback), strdup("net callback"), myFreeFunc); + /* register common storage pool callbacks */ + for (i = 0; i < ARRAY_CARDINALITY(storagePoolEvents); i++) { + struct storagePoolEventData *event = storagePoolEvents + i; + + event->id = virConnectStoragePoolEventRegisterAny(dconn, NULL, + event->event, + event->cb, + strdup(event->name), + myFreeFunc); + + if (event->id < 0) { + fprintf(stderr, "Failed to register event '%s'\n", event->name); + goto cleanup; + } + } + if ((callback1ret == -1) || (callback16ret == -1)) goto cleanup; @@ -994,6 +1060,13 @@ main(int argc, char **argv) virConnectDomainEventDeregisterAny(dconn, domainEvents[i].id); } + + printf("Deregistering storage pool event callbacks\n"); + for (i = 0; i < ARRAY_CARDINALITY(storagePoolEvents); i++) { + if (storagePoolEvents[i].id > 0) + virConnectStoragePoolEventDeregisterAny(dconn, storagePoolEvents[i].id); + } + virConnectUnregisterCloseCallback(dconn, connectClose); ret = EXIT_SUCCESS; -- 2.5.5

Add virsh-pool support for storage pool lifecycle events --- tools/virsh-pool.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/tools/virsh-pool.c b/tools/virsh-pool.c index f9299e2..cd40749 100644 --- a/tools/virsh-pool.c +++ b/tools/virsh-pool.c @@ -32,6 +32,7 @@ #include "virfile.h" #include "conf/storage_conf.h" #include "virstring.h" +#include "virtime.h" #define VIRSH_COMMON_OPT_POOL_FULL \ VIRSH_COMMON_OPT_POOL(N_("pool name or uuid")) \ @@ -1889,6 +1890,183 @@ cmdPoolEdit(vshControl *ctl, const vshCmd *cmd) return ret; } +/* + * "pool-event" command + */ +VIR_ENUM_DECL(virshPoolEvent) +VIR_ENUM_IMPL(virshPoolEvent, + VIR_STORAGE_POOL_EVENT_LAST, + N_("Defined"), + N_("Undefined"), + N_("Started"), + N_("Stopped"), + N_("Refreshed")) + +static const char * +virshPoolEventToString(int event) +{ + const char *str = virshPoolEventTypeToString(event); + return str ? _(str) : _("unknown"); +} + +struct virshPoolEventData { + vshControl *ctl; + bool loop; + bool timestamp; + int count; +}; +typedef struct virshPoolEventData virshPoolEventData; + +VIR_ENUM_DECL(virshPoolEventId) +VIR_ENUM_IMPL(virshPoolEventId, + VIR_STORAGE_POOL_EVENT_ID_LAST, + "lifecycle") + +static void +vshEventLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolPtr pool, + int event, + int detail ATTRIBUTE_UNUSED, + void *opaque) +{ + virshPoolEventData *data = opaque; + + if (!data->loop && data->count) + return; + + if (data->timestamp) { + char timestamp[VIR_TIME_STRING_BUFLEN]; + + if (virTimeStringNowRaw(timestamp) < 0) + timestamp[0] = '\0'; + + vshPrint(data->ctl, _("%s: event 'lifecycle' for storage pool %s: %s\n"), + timestamp, + virStoragePoolGetName(pool), + virshPoolEventToString(event)); + } else { + vshPrint(data->ctl, _("event 'lifecycle' for storage pool %s: %s\n"), + virStoragePoolGetName(pool), + virshPoolEventToString(event)); + } + + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static const vshCmdInfo info_pool_event[] = { + {.name = "help", + .data = N_("Storage Pool Events") + }, + {.name = "desc", + .data = N_("List event types, or wait for storage pool events to occur") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_pool_event[] = { + {.name = "storage pool", + .type = VSH_OT_STRING, + .help = N_("filter by storage pool name or uuid") + }, + {.name = "event", + .type = VSH_OT_STRING, + .help = N_("which event type to wait for") + }, + {.name = "loop", + .type = VSH_OT_BOOL, + .help = N_("loop until timeout or interrupt, rather than one-shot") + }, + {.name = "timeout", + .type = VSH_OT_INT, + .help = N_("timeout seconds") + }, + {.name = "list", + .type = VSH_OT_BOOL, + .help = N_("list valid event types") + }, + {.name = "timestamp", + .type = VSH_OT_BOOL, + .help = N_("show timestamp for each printed event") + }, + {.name = NULL} +}; + +static bool +cmdPoolEvent(vshControl *ctl, const vshCmd *cmd) +{ + virStoragePoolPtr pool = NULL; + bool ret = false; + int eventId = -1; + int timeout = 0; + virshPoolEventData data; + const char *eventName = NULL; + int event; + virshControlPtr priv = ctl->privData; + + if (vshCommandOptBool(cmd, "list")) { + size_t i; + + for (i = 0; i < VIR_STORAGE_POOL_EVENT_ID_LAST; i++) + vshPrint(ctl, "%s\n", virshPoolEventIdTypeToString(i)); + return true; + } + + if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0) + return false; + if (!eventName) { + vshError(ctl, "%s", _("either --list or event type is required")); + return false; + } + if ((event = virshPoolEventIdTypeFromString(eventName)) < 0) { + vshError(ctl, _("unknown event type %s"), eventName); + return false; + } + + data.ctl = ctl; + data.loop = vshCommandOptBool(cmd, "loop"); + data.timestamp = vshCommandOptBool(cmd, "timestamp"); + data.count = 0; + if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) + return false; + + if (vshCommandOptBool(cmd, "pool")) + pool = virshCommandOptPool(ctl, cmd, "pool", NULL); + if (vshEventStart(ctl, timeout) < 0) + goto cleanup; + + if ((eventId = virConnectStoragePoolEventRegisterAny(priv->conn, pool, event, + VIR_STORAGE_POOL_EVENT_CALLBACK(vshEventLifecyclePrint), + &data, NULL)) < 0) + goto cleanup; + switch (vshEventWait(ctl)) { + case VSH_EVENT_INTERRUPT: + vshPrint(ctl, "%s", _("event loop interrupted\n")); + break; + case VSH_EVENT_TIMEOUT: + vshPrint(ctl, "%s", _("event loop timed out\n")); + break; + case VSH_EVENT_DONE: + break; + default: + goto cleanup; + } + vshPrint(ctl, _("events received: %d\n"), data.count); + if (data.count) + ret = true; + + cleanup: + vshEventCleanup(ctl); + if (eventId >= 0 && + virConnectStoragePoolEventDeregisterAny(priv->conn, eventId) < 0) + ret = false; + if (pool) + virStoragePoolFree(pool); + return ret; +} + + const vshCmdDef storagePoolCmds[] = { {.name = "find-storage-pool-sources-as", .handler = cmdPoolDiscoverSourcesAs, @@ -2004,5 +2182,11 @@ const vshCmdDef storagePoolCmds[] = { .info = info_pool_uuid, .flags = 0 }, + {.name = "pool-event", + .handler = cmdPoolEvent, + .opts = opts_pool_event, + .info = info_pool_event, + .flags = 0 + }, {.name = NULL} }; -- 2.5.5

On 06/16/2016 04:09 AM, Jovanka Gulicoska wrote:
Add virsh-pool support for storage pool lifecycle events --- tools/virsh-pool.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+)
I needed this diff to get things working: diff --git a/tools/virsh-pool.c b/tools/virsh-pool.c index cd40749..18e218c 100644 --- a/tools/virsh-pool.c +++ b/tools/virsh-pool.c @@ -1966,7 +1966,7 @@ static const vshCmdInfo info_pool_event[] = { }; static const vshCmdOptDef opts_pool_event[] = { - {.name = "storage pool", + {.name = "pool", .type = VSH_OT_STRING, .help = N_("filter by storage pool name or uuid") }, Also, please extend tools/virsh.pod so this option is documented in the man page. Thanks, Cole
participants (3)
-
Cole Robinson
-
Daniel P. Berrange
-
Jovanka Gulicoska