[libvirt] [PATCH 0/5] storage: implement a skeleton for volume events.

The series add a skeleton for a lifecycle event (and other events in the future) for volumes to cover actions like create and delete. Others events can be added in the future, but the storage driver has some functions to support it. Related: https://bugzilla.redhat.com/show_bug.cgi?id=1578836 Julio Faracco (5): storage: adding a skeleton for libvirt volume events. driver: adding two new driver functions to support volume events. conf: implementing some functions to generate volume events. virsh: implementing 'vol-event' command. examples: adding volume events into event-test example. examples/object-events/event-test.c | 65 ++++++++++ include/libvirt/libvirt-storage.h | 86 +++++++++++++ src/conf/storage_event.c | 155 ++++++++++++++++++++++ src/conf/storage_event.h | 19 +++ src/driver-storage.h | 13 ++ src/libvirt-storage.c | 125 ++++++++++++++++++ src/libvirt_private.syms | 2 + src/libvirt_public.syms | 6 + tools/virsh-completer.c | 27 ++++ tools/virsh-completer.h | 4 + tools/virsh-volume.c | 192 ++++++++++++++++++++++++++++ tools/virsh-volume.h | 8 ++ 12 files changed, 702 insertions(+) -- 2.17.0

This commit adds some basic structures to support events for volumes as libvirt does with pools, networks, domains, secrets, etc. This commit add only lifecycle event to be included at create and delete actions. Signed-off-by: Julio Faracco <jcfaracco@gmail.com> --- include/libvirt/libvirt-storage.h | 86 ++++++++++++++++++++ src/libvirt-storage.c | 125 ++++++++++++++++++++++++++++++ src/libvirt_public.syms | 6 ++ 3 files changed, 217 insertions(+) diff --git a/include/libvirt/libvirt-storage.h b/include/libvirt/libvirt-storage.h index 413d9f6c4c..291742e5ef 100644 --- a/include/libvirt/libvirt-storage.h +++ b/include/libvirt/libvirt-storage.h @@ -496,4 +496,90 @@ typedef void (*virConnectStoragePoolEventLifecycleCallback)(virConnectPtr conn, int detail, void *opaque); +/** + * VIR_STORAGE_VOL_EVENT_CALLBACK: + * + * Used to cast the event specific callback into the generic one + * for use for virConnectStorageVolEventRegisterAny() + */ +# define VIR_STORAGE_VOL_EVENT_CALLBACK(cb)((virConnectStorageVolEventGenericCallback)(cb)) + +/** + * virStorageVolEventID: + * + * An enumeration of supported eventId parameters for + * virConnectStorageVolEventRegisterAny(). Each event id determines which + * signature of callback function will be used. + */ +typedef enum { + VIR_STORAGE_VOL_EVENT_ID_LIFECYCLE = 0, /* virConnectStorageVolEventLifecycleCallback */ + +# ifdef VIR_ENUM_SENTINELS + VIR_STORAGE_VOL_EVENT_ID_LAST +# endif +} virStorageVolEventID; + +/** + * virConnectStorageVolEventGenericCallback: + * @conn: the connection pointer + * @vol: the vol pointer + * @opaque: application specified data + * + * A generic storage vol event callback handler, for use with + * virConnectStorageVolEventRegisterAny(). Specific events usually + * have a customization with extra parameters, often with @opaque being + * passed in a different parameter position; use + * VIR_STORAGE_VOL_EVENT_CALLBACK() when registering an appropriate handler. + */ +typedef void (*virConnectStorageVolEventGenericCallback)(virConnectPtr conn, + virStorageVolPtr vol, + void *opaque); + +/* Use VIR_STORAGE_VOL_EVENT_CALLBACK() to cast the 'cb' parameter */ +int virConnectStorageVolEventRegisterAny(virConnectPtr conn, + virStorageVolPtr vol, /* optional, to filter */ + int eventID, + virConnectStorageVolEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + +int virConnectStorageVolEventDeregisterAny(virConnectPtr conn, + int callbackID); + +/** + * virStorageVolEventLifecycleType: + * + * a virStorageVolEventLifecycleType is emitted during storage vol + * lifecycle events + */ +typedef enum { + VIR_STORAGE_VOL_EVENT_CREATED = 0, + VIR_STORAGE_VOL_EVENT_DELETED = 1, + +# ifdef VIR_ENUM_SENTINELS + VIR_STORAGE_VOL_EVENT_LAST +# endif +} virStorageVolEventLifecycleType; + +/** + * virConnectStorageVolEventLifecycleCallback: + * @conn: connection object + * @vol: vol on which the event occurred + * @event: The specific virStorageVolEventLifeCycleType which occurred + * @detail: contains some details on the reason of the event. + * @opaque: application specified data + * + * This callback is called when a vol lifecycle action is performed, like start + * or stop. + * + * The callback signature to use when registering for an event of type + * VIR_STORAGE_VOL_EVENT_ID_LIFECYCLE with + * virConnectStorageVolEventRegisterAny() + */ +typedef void (*virConnectStorageVolEventLifecycleCallback)(virConnectPtr conn, + virStorageVolPtr vol, + int event, + int detail, + void *opaque); + #endif /* __VIR_LIBVIRT_STORAGE_H__ */ diff --git a/src/libvirt-storage.c b/src/libvirt-storage.c index 1879c6bd60..2bb033c98c 100644 --- a/src/libvirt-storage.c +++ b/src/libvirt-storage.c @@ -2350,3 +2350,128 @@ virConnectStoragePoolEventDeregisterAny(virConnectPtr conn, virDispatchError(conn); return -1; } + +/** + * virConnectStorageVolEventRegisterAny: + * @conn: pointer to the connection + * @vol: pointer to the storage vol + * @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 vol events + * occurring on a storage vol. This function requires that an event loop + * has been previously registered with virEventRegisterImpl() or + * virEventRegisterDefaultImpl(). + * + * If @vol is NULL, then events will be monitored for any storage vol. + * If @vol is non-NULL, then only the specific storage vol 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_VOL_EVENT_CALLBACK() macro to cast the + * supplied function pointer to match the signature of this method. + * + * The virStorageVolPtr 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 vol object after the callback + * returns, it shall take a reference to it, by calling virStorageVolRef(). + * The reference can be released once the object is no longer required + * by calling virStorageVolFree(). + * + * 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 virConnectStorageVolEventDeregisterAny() method. + * + * Returns a callback identifier on success, -1 on failure. + */ +int +virConnectStorageVolEventRegisterAny(virConnectPtr conn, + virStorageVolPtr vol, + int eventID, + virConnectStorageVolEventGenericCallback cb, + void *opaque, + virFreeCallback freecb) +{ + VIR_DEBUG("conn=%p, vol=%p, eventID=%d, cb=%p, opaque=%p, freecb=%p", + conn, vol, eventID, cb, opaque, freecb); + + virResetLastError(); + + virCheckConnectReturn(conn, -1); + if (vol) { + virCheckStorageVolGoto(vol, error); + if (vol->conn != conn) { + virReportInvalidArg(vol, + _("storage vol '%s' in %s must match connection"), + vol->name, __FUNCTION__); + goto error; + } + } + virCheckNonNullArgGoto(cb, error); + virCheckNonNegativeArgGoto(eventID, error); + + if (eventID >= VIR_STORAGE_VOL_EVENT_ID_LAST) { + virReportInvalidArg(eventID, + _("eventID in %s must be less than %d"), + __FUNCTION__, VIR_STORAGE_VOL_EVENT_ID_LAST); + goto error; + } + + if (conn->storageDriver && + conn->storageDriver->connectStorageVolEventRegisterAny) { + int ret; + ret = conn->storageDriver->connectStorageVolEventRegisterAny(conn, + vol, + eventID, + cb, + opaque, + freecb); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return -1; +} + +/** + * virConnectStorageVolEventDeregisterAny: + * @conn: pointer to the connection + * @callbackID: the callback identifier + * + * Removes an event callback. The callbackID parameter should be the + * value obtained from a previous virConnectStorageVolEventRegisterAny() method. + * + * Returns 0 on success, -1 on failure + */ +int +virConnectStorageVolEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + VIR_DEBUG("conn=%p, callbackID=%d", conn, callbackID); + + virResetLastError(); + + virCheckConnectReturn(conn, -1); + virCheckNonNegativeArgGoto(callbackID, error); + + if (conn->storageDriver && + conn->storageDriver->connectStorageVolEventDeregisterAny) { + int ret; + ret = conn->storageDriver->connectStorageVolEventDeregisterAny(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 95df3a0dbc..b0dee76771 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -785,4 +785,10 @@ LIBVIRT_4.1.0 { virStoragePoolLookupByTargetPath; } LIBVIRT_3.9.0; +LIBVIRT_4.4.0 { + global: + virConnectStorageVolEventRegisterAny; + virConnectStorageVolEventDeregisterAny; +} LIBVIRT_4.1.0; + # .... define new API here using predicted next version number .... -- 2.17.0

On Sun, May 27, 2018 at 16:25:35 -0300, Julio Faracco wrote:
This commit adds some basic structures to support events for volumes as libvirt does with pools, networks, domains, secrets, etc. This commit add only lifecycle event to be included at create and delete actions.
Signed-off-by: Julio Faracco <jcfaracco@gmail.com> --- include/libvirt/libvirt-storage.h | 86 ++++++++++++++++++++ src/libvirt-storage.c | 125 ++++++++++++++++++++++++++++++ src/libvirt_public.syms | 6 ++ 3 files changed, 217 insertions(+)
diff --git a/include/libvirt/libvirt-storage.h b/include/libvirt/libvirt-storage.h index 413d9f6c4c..291742e5ef 100644 --- a/include/libvirt/libvirt-storage.h +++ b/include/libvirt/libvirt-storage.h
[...]
+/** + * virStorageVolEventLifecycleType: + * + * a virStorageVolEventLifecycleType is emitted during storage vol + * lifecycle events + */ +typedef enum { + VIR_STORAGE_VOL_EVENT_CREATED = 0, + VIR_STORAGE_VOL_EVENT_DELETED = 1,
How do you plan to handle the case of a storage pool refresh for the filesystem (directory?) pool. Currently if you refresh it libvirt removes all volume metadata and re-detects everything. If a image file was manually deleted from the directory it will vanish after the refresh. This means that if you want to properly emit these you'll need to add a diffing mechanism so that you can regenerate all the events.
+ +# ifdef VIR_ENUM_SENTINELS + VIR_STORAGE_VOL_EVENT_LAST +# endif +} virStorageVolEventLifecycleType; + +/** + * virConnectStorageVolEventLifecycleCallback: + * @conn: connection object + * @vol: vol on which the event occurred + * @event: The specific virStorageVolEventLifeCycleType which occurred + * @detail: contains some details on the reason of the event. + * @opaque: application specified data + * + * This callback is called when a vol lifecycle action is performed, like start + * or stop.
There are no such actions for a volume.
+ * + * The callback signature to use when registering for an event of type + * VIR_STORAGE_VOL_EVENT_ID_LIFECYCLE with + * virConnectStorageVolEventRegisterAny() + */ +typedef void (*virConnectStorageVolEventLifecycleCallback)(virConnectPtr conn, + virStorageVolPtr vol, + int event, + int detail, + void *opaque); + #endif /* __VIR_LIBVIRT_STORAGE_H__ */

On 05/27/2018 03:25 PM, Julio Faracco wrote:
This commit adds some basic structures to support events for volumes as libvirt does with pools, networks, domains, secrets, etc. This commit add only lifecycle event to be included at create and delete actions.
Signed-off-by: Julio Faracco <jcfaracco@gmail.com> --- include/libvirt/libvirt-storage.h | 86 ++++++++++++++++++++ src/libvirt-storage.c | 125 ++++++++++++++++++++++++++++++ src/libvirt_public.syms | 6 ++ 3 files changed, 217 insertions(+)
To "augment" what Peter notes - volumes can be created, deleted, resized, uploaded, downloaded, and wiped. A subclass of creation is that you can create a volume from another volume. Actually in volume terms it's "build" or "buildVolFrom" (see _virStorageBackend for various functions). As for the refresh conundrum - all storage pools do the same thing w/r/t recreating the metadata or volume list on refresh and hence redetects everything. Thus anything you do keep track of a list would need to be generic for all backing store types. It's been a long term goal/desire to "fix" the algorithm to not recreate every time, but rather use "some" mechanism to detect stale entries, but it's never received high enough priority to make it to the top of a todo list. John
diff --git a/include/libvirt/libvirt-storage.h b/include/libvirt/libvirt-storage.h index 413d9f6c4c..291742e5ef 100644 --- a/include/libvirt/libvirt-storage.h +++ b/include/libvirt/libvirt-storage.h @@ -496,4 +496,90 @@ typedef void (*virConnectStoragePoolEventLifecycleCallback)(virConnectPtr conn, int detail, void *opaque);
+/** + * VIR_STORAGE_VOL_EVENT_CALLBACK: + * + * Used to cast the event specific callback into the generic one + * for use for virConnectStorageVolEventRegisterAny() + */ +# define VIR_STORAGE_VOL_EVENT_CALLBACK(cb)((virConnectStorageVolEventGenericCallback)(cb)) + +/** + * virStorageVolEventID: + * + * An enumeration of supported eventId parameters for + * virConnectStorageVolEventRegisterAny(). Each event id determines which + * signature of callback function will be used. + */ +typedef enum { + VIR_STORAGE_VOL_EVENT_ID_LIFECYCLE = 0, /* virConnectStorageVolEventLifecycleCallback */ + +# ifdef VIR_ENUM_SENTINELS + VIR_STORAGE_VOL_EVENT_ID_LAST +# endif +} virStorageVolEventID; + +/** + * virConnectStorageVolEventGenericCallback: + * @conn: the connection pointer + * @vol: the vol pointer + * @opaque: application specified data + * + * A generic storage vol event callback handler, for use with + * virConnectStorageVolEventRegisterAny(). Specific events usually + * have a customization with extra parameters, often with @opaque being + * passed in a different parameter position; use + * VIR_STORAGE_VOL_EVENT_CALLBACK() when registering an appropriate handler. + */ +typedef void (*virConnectStorageVolEventGenericCallback)(virConnectPtr conn, + virStorageVolPtr vol, + void *opaque); + +/* Use VIR_STORAGE_VOL_EVENT_CALLBACK() to cast the 'cb' parameter */ +int virConnectStorageVolEventRegisterAny(virConnectPtr conn, + virStorageVolPtr vol, /* optional, to filter */ + int eventID, + virConnectStorageVolEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + +int virConnectStorageVolEventDeregisterAny(virConnectPtr conn, + int callbackID); + +/** + * virStorageVolEventLifecycleType: + * + * a virStorageVolEventLifecycleType is emitted during storage vol + * lifecycle events + */ +typedef enum { + VIR_STORAGE_VOL_EVENT_CREATED = 0, + VIR_STORAGE_VOL_EVENT_DELETED = 1, + +# ifdef VIR_ENUM_SENTINELS + VIR_STORAGE_VOL_EVENT_LAST +# endif +} virStorageVolEventLifecycleType; + +/** + * virConnectStorageVolEventLifecycleCallback: + * @conn: connection object + * @vol: vol on which the event occurred + * @event: The specific virStorageVolEventLifeCycleType which occurred + * @detail: contains some details on the reason of the event. + * @opaque: application specified data + * + * This callback is called when a vol lifecycle action is performed, like start + * or stop. + * + * The callback signature to use when registering for an event of type + * VIR_STORAGE_VOL_EVENT_ID_LIFECYCLE with + * virConnectStorageVolEventRegisterAny() + */ +typedef void (*virConnectStorageVolEventLifecycleCallback)(virConnectPtr conn, + virStorageVolPtr vol, + int event, + int detail, + void *opaque); + #endif /* __VIR_LIBVIRT_STORAGE_H__ */ diff --git a/src/libvirt-storage.c b/src/libvirt-storage.c index 1879c6bd60..2bb033c98c 100644 --- a/src/libvirt-storage.c +++ b/src/libvirt-storage.c @@ -2350,3 +2350,128 @@ virConnectStoragePoolEventDeregisterAny(virConnectPtr conn, virDispatchError(conn); return -1; } + +/** + * virConnectStorageVolEventRegisterAny: + * @conn: pointer to the connection + * @vol: pointer to the storage vol + * @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 vol events + * occurring on a storage vol. This function requires that an event loop + * has been previously registered with virEventRegisterImpl() or + * virEventRegisterDefaultImpl(). + * + * If @vol is NULL, then events will be monitored for any storage vol. + * If @vol is non-NULL, then only the specific storage vol 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_VOL_EVENT_CALLBACK() macro to cast the + * supplied function pointer to match the signature of this method. + * + * The virStorageVolPtr 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 vol object after the callback + * returns, it shall take a reference to it, by calling virStorageVolRef(). + * The reference can be released once the object is no longer required + * by calling virStorageVolFree(). + * + * 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 virConnectStorageVolEventDeregisterAny() method. + * + * Returns a callback identifier on success, -1 on failure. + */ +int +virConnectStorageVolEventRegisterAny(virConnectPtr conn, + virStorageVolPtr vol, + int eventID, + virConnectStorageVolEventGenericCallback cb, + void *opaque, + virFreeCallback freecb) +{ + VIR_DEBUG("conn=%p, vol=%p, eventID=%d, cb=%p, opaque=%p, freecb=%p", + conn, vol, eventID, cb, opaque, freecb); + + virResetLastError(); + + virCheckConnectReturn(conn, -1); + if (vol) { + virCheckStorageVolGoto(vol, error); + if (vol->conn != conn) { + virReportInvalidArg(vol, + _("storage vol '%s' in %s must match connection"), + vol->name, __FUNCTION__); + goto error; + } + } + virCheckNonNullArgGoto(cb, error); + virCheckNonNegativeArgGoto(eventID, error); + + if (eventID >= VIR_STORAGE_VOL_EVENT_ID_LAST) { + virReportInvalidArg(eventID, + _("eventID in %s must be less than %d"), + __FUNCTION__, VIR_STORAGE_VOL_EVENT_ID_LAST); + goto error; + } + + if (conn->storageDriver && + conn->storageDriver->connectStorageVolEventRegisterAny) { + int ret; + ret = conn->storageDriver->connectStorageVolEventRegisterAny(conn, + vol, + eventID, + cb, + opaque, + freecb); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return -1; +} + +/** + * virConnectStorageVolEventDeregisterAny: + * @conn: pointer to the connection + * @callbackID: the callback identifier + * + * Removes an event callback. The callbackID parameter should be the + * value obtained from a previous virConnectStorageVolEventRegisterAny() method. + * + * Returns 0 on success, -1 on failure + */ +int +virConnectStorageVolEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + VIR_DEBUG("conn=%p, callbackID=%d", conn, callbackID); + + virResetLastError(); + + virCheckConnectReturn(conn, -1); + virCheckNonNegativeArgGoto(callbackID, error); + + if (conn->storageDriver && + conn->storageDriver->connectStorageVolEventDeregisterAny) { + int ret; + ret = conn->storageDriver->connectStorageVolEventDeregisterAny(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 95df3a0dbc..b0dee76771 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -785,4 +785,10 @@ LIBVIRT_4.1.0 { virStoragePoolLookupByTargetPath; } LIBVIRT_3.9.0;
+LIBVIRT_4.4.0 { + global: + virConnectStorageVolEventRegisterAny; + virConnectStorageVolEventDeregisterAny; +} LIBVIRT_4.1.0; + # .... define new API here using predicted next version number ....

This commit adds two functions into the structure of the driver to support volume events: 'connectStorageVolEventRegisterAny' and 'connectStorageVolEventDeregisterAny'. Signed-off-by: Julio Faracco <jcfaracco@gmail.com> --- src/driver-storage.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/driver-storage.h b/src/driver-storage.h index 146eb88b2c..f332b9918f 100644 --- a/src/driver-storage.h +++ b/src/driver-storage.h @@ -213,10 +213,21 @@ typedef int void *opaque, virFreeCallback freecb); +typedef int +(*virDrvConnectStorageVolEventRegisterAny)(virConnectPtr conn, + virStorageVolPtr vol, + int eventID, + virConnectStorageVolEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + typedef int (*virDrvConnectStoragePoolEventDeregisterAny)(virConnectPtr conn, int callbackID); +typedef int +(*virDrvConnectStorageVolEventDeregisterAny)(virConnectPtr conn, + int callbackID); typedef struct _virStorageDriver virStorageDriver; typedef virStorageDriver *virStorageDriverPtr; @@ -237,6 +248,8 @@ struct _virStorageDriver { virDrvConnectFindStoragePoolSources connectFindStoragePoolSources; virDrvConnectStoragePoolEventRegisterAny connectStoragePoolEventRegisterAny; virDrvConnectStoragePoolEventDeregisterAny connectStoragePoolEventDeregisterAny; + virDrvConnectStorageVolEventRegisterAny connectStorageVolEventRegisterAny; + virDrvConnectStorageVolEventDeregisterAny connectStorageVolEventDeregisterAny; virDrvStoragePoolLookupByName storagePoolLookupByName; virDrvStoragePoolLookupByUUID storagePoolLookupByUUID; virDrvStoragePoolLookupByVolume storagePoolLookupByVolume; -- 2.17.0

This commit defines some functions to create new events for volumes according the events available for volumes. Signed-off-by: Julio Faracco <jcfaracco@gmail.com> --- src/conf/storage_event.c | 155 +++++++++++++++++++++++++++++++++++++++ src/conf/storage_event.h | 19 +++++ src/libvirt_private.syms | 2 + 3 files changed, 176 insertions(+) diff --git a/src/conf/storage_event.c b/src/conf/storage_event.c index 32a12eb63f..8d77b2a6cb 100644 --- a/src/conf/storage_event.c +++ b/src/conf/storage_event.c @@ -56,6 +56,24 @@ struct _virStoragePoolEventRefresh { typedef struct _virStoragePoolEventRefresh virStoragePoolEventRefresh; typedef virStoragePoolEventRefresh *virStoragePoolEventRefreshPtr; +struct _virStorageVolEvent { + virObjectEvent parent; + + /* Unused attribute to allow for subclass creation */ + bool dummy; +}; +typedef struct _virStorageVolEvent virStorageVolEvent; +typedef virStorageVolEvent *virStorageVolEventPtr; + +struct _virStorageVolEventLifecycle { + virStorageVolEvent parent; + + int type; + int detail; +}; +typedef struct _virStorageVolEventLifecycle virStorageVolEventLifecycle; +typedef virStorageVolEventLifecycle *virStorageVolEventLifecyclePtr; + static virClassPtr virStoragePoolEventClass; static virClassPtr virStoragePoolEventLifecycleClass; static virClassPtr virStoragePoolEventRefreshClass; @@ -63,6 +81,11 @@ static void virStoragePoolEventDispose(void *obj); static void virStoragePoolEventLifecycleDispose(void *obj); static void virStoragePoolEventRefreshDispose(void *obj); +static virClassPtr virStorageVolEventClass; +static virClassPtr virStorageVolEventLifecycleClass; +static void virStorageVolEventDispose(void *obj); +static void virStorageVolEventLifecycleDispose(void *obj); + static int virStoragePoolEventsOnceInit(void) { @@ -78,7 +101,20 @@ virStoragePoolEventsOnceInit(void) return 0; } +static int +virStorageVolEventsOnceInit(void) +{ + if (!VIR_CLASS_NEW(virStorageVolEvent, virClassForObjectEvent())) + return -1; + + if (!VIR_CLASS_NEW(virStorageVolEventLifecycle, virStorageVolEventClass)) + return -1; + + return 0; +} + VIR_ONCE_GLOBAL_INIT(virStoragePoolEvents) +VIR_ONCE_GLOBAL_INIT(virStorageVolEvents) static void virStoragePoolEventDispose(void *obj) @@ -103,6 +139,20 @@ virStoragePoolEventRefreshDispose(void *obj) VIR_DEBUG("obj=%p", event); } +static void +virStorageVolEventDispose(void *obj) +{ + virStorageVolEventPtr event = obj; + VIR_DEBUG("obj=%p", event); +} + + +static void +virStorageVolEventLifecycleDispose(void *obj) +{ + virStorageVolEventLifecyclePtr event = obj; + VIR_DEBUG("obj=%p", event); +} static void virStoragePoolEventDispatchDefaultFunc(virConnectPtr conn, @@ -147,6 +197,40 @@ virStoragePoolEventDispatchDefaultFunc(virConnectPtr conn, } +static void +virStorageVolEventDispatchDefaultFunc(virConnectPtr conn, + virObjectEventPtr event, + virConnectObjectEventGenericCallback cb ATTRIBUTE_UNUSED, + void *cbopaque ATTRIBUTE_UNUSED) +{ + virStorageVolPtr vol = virStorageVolLookupByKey(conn, + event->meta.key); + if (!vol) + return; + + switch ((virStorageVolEventID)event->eventID) { + case VIR_STORAGE_VOL_EVENT_ID_LIFECYCLE: + { + virStorageVolEventLifecyclePtr storageVolLifecycleEvent; + + storageVolLifecycleEvent = (virStorageVolEventLifecyclePtr)event; + ((virConnectStorageVolEventLifecycleCallback)cb)(conn, vol, + storageVolLifecycleEvent->type, + storageVolLifecycleEvent->detail, + cbopaque); + goto cleanup; + } + + case VIR_STORAGE_VOL_EVENT_ID_LAST: + break; + } + VIR_WARN("Unexpected event ID %d", event->eventID); + + cleanup: + virObjectUnref(vol); +} + + /** * virStoragePoolEventStateRegisterID: * @conn: connection to associate with callback @@ -189,6 +273,44 @@ virStoragePoolEventStateRegisterID(virConnectPtr conn, false, callbackID, false); } +/** + * virStorageVolEventStateRegisterID: + * @conn: connection to associate with callback + * @state: object event state + * @vol: storage vol to filter on or NULL for all storage volumes + * @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 +virStorageVolEventStateRegisterID(virConnectPtr conn, + virObjectEventStatePtr state, + virStorageVolPtr vol, + int eventID, + virConnectStorageVolEventGenericCallback cb, + void *opaque, + virFreeCallback freecb, + int *callbackID) +{ + if (virStorageVolEventsInitialize() < 0) + return -1; + + return virObjectEventStateRegisterID(conn, state, vol ? vol->key : NULL, + NULL, NULL, + virStorageVolEventClass, eventID, + VIR_OBJECT_EVENT_CALLBACK(cb), + opaque, freecb, + false, callbackID, false); +} + /** * virStoragePoolEventStateRegisterClient: @@ -268,6 +390,39 @@ virStoragePoolEventLifecycleNew(const char *name, return (virObjectEventPtr)event; } +/** + * virStorageVolEventLifecycleNew: + * @name: name of the storage volume object the event describes + * @key: key of the storage volume object the event describes + * @type: type of lifecycle event + * @detail: more details about @type + * + * Create a new storage volume lifecycle event. + */ +virObjectEventPtr +virStorageVolEventLifecycleNew(const char *pool, + const char *name, + const unsigned char *key, + int type, + int detail) +{ + virStorageVolEventLifecyclePtr event; + + if (virStorageVolEventsInitialize() < 0) + return NULL; + + if (!(event = virObjectEventNew(virStorageVolEventLifecycleClass, + virStorageVolEventDispatchDefaultFunc, + VIR_STORAGE_VOL_EVENT_ID_LIFECYCLE, + 0, name, key, pool))) + return NULL; + + event->type = type; + event->detail = detail; + + return (virObjectEventPtr)event; +} + /** * virStoragePoolEventRefreshNew: diff --git a/src/conf/storage_event.h b/src/conf/storage_event.h index ea726911fa..59adf622d7 100644 --- a/src/conf/storage_event.h +++ b/src/conf/storage_event.h @@ -39,6 +39,18 @@ virStoragePoolEventStateRegisterID(virConnectPtr conn, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5) ATTRIBUTE_NONNULL(8); +int +virStorageVolEventStateRegisterID(virConnectPtr conn, + virObjectEventStatePtr state, + virStorageVolPtr vol, + int eventID, + virConnectStorageVolEventGenericCallback 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, @@ -61,4 +73,11 @@ virObjectEventPtr virStoragePoolEventRefreshNew(const char *name, const unsigned char *uuid); +virObjectEventPtr +virStorageVolEventLifecycleNew(const char *pool, + const char *name, + const unsigned char *key, + int type, + int detail); + #endif diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a97b7fe223..4e62a02c02 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -940,6 +940,8 @@ virStorageVolTypeToString; virStoragePoolEventLifecycleNew; virStoragePoolEventRefreshNew; virStoragePoolEventStateRegisterID; +virStorageVolEventLifecycleNew; +virStorageVolEventStateRegisterID; # conf/virchrdev.h -- 2.17.0

As we have some basics events for volumes, virsh can have the 'vol-event' command to check some events related to volumes. Signed-off-by: Julio Faracco <jcfaracco@gmail.com> --- tools/virsh-completer.c | 27 ++++++ tools/virsh-completer.h | 4 + tools/virsh-volume.c | 192 ++++++++++++++++++++++++++++++++++++++++ tools/virsh-volume.h | 8 ++ 4 files changed, 231 insertions(+) diff --git a/tools/virsh-completer.c b/tools/virsh-completer.c index d3effe59ea..30cc595376 100644 --- a/tools/virsh-completer.c +++ b/tools/virsh-completer.c @@ -26,6 +26,7 @@ #include "virsh-domain.h" #include "virsh.h" #include "virsh-pool.h" +#include "virsh-volume.h" #include "virsh-nodedev.h" #include "virsh-util.h" #include "virsh-secret.h" @@ -732,6 +733,32 @@ virshPoolEventNameCompleter(vshControl *ctl ATTRIBUTE_UNUSED, } +char ** +virshVolEventNameCompleter(vshControl *ctl ATTRIBUTE_UNUSED, + const vshCmd *cmd ATTRIBUTE_UNUSED, + unsigned int flags) +{ + size_t i = 0; + char **ret = NULL; + + virCheckFlags(0, NULL); + + if (VIR_ALLOC_N(ret, VIR_STORAGE_VOL_EVENT_ID_LAST) < 0) + goto error; + + for (i = 0; i < VIR_STORAGE_VOL_EVENT_ID_LAST; i++) { + if (VIR_STRDUP(ret[i], virshVolEventCallbacks[i].name) < 0) + goto error; + } + + return ret; + + error: + virStringListFree(ret); + return NULL; +} + + char ** virshNodedevEventNameCompleter(vshControl *ctl ATTRIBUTE_UNUSED, const vshCmd *cmd ATTRIBUTE_UNUSED, diff --git a/tools/virsh-completer.h b/tools/virsh-completer.h index ee7eec68c5..831be73d7e 100644 --- a/tools/virsh-completer.h +++ b/tools/virsh-completer.h @@ -90,6 +90,10 @@ char ** virshPoolEventNameCompleter(vshControl *ctl, const vshCmd *cmd, unsigned int flags); +char ** virshVolEventNameCompleter(vshControl *ctl, + const vshCmd *cmd, + unsigned int flags); + char ** virshNodedevEventNameCompleter(vshControl *ctl, const vshCmd *cmd, unsigned int flags); diff --git a/tools/virsh-volume.c b/tools/virsh-volume.c index 9d6ebd2325..63497b7d8c 100644 --- a/tools/virsh-volume.c +++ b/tools/virsh-volume.c @@ -42,6 +42,7 @@ #include "virsh-pool.h" #include "virxml.h" #include "virstring.h" +#include "virtime.h" #define VIRSH_COMMON_OPT_POOL_FULL \ VIRSH_COMMON_OPT_POOL(N_("pool name or uuid"), \ @@ -1772,6 +1773,191 @@ cmdVolPath(vshControl *ctl, const vshCmd *cmd) return true; } +/* + * "vol-event" command + */ +VIR_ENUM_DECL(virshVolEvent) +VIR_ENUM_IMPL(virshVolEvent, + VIR_STORAGE_VOL_EVENT_LAST, + N_("Created"), + N_("Deleted")) + +static const char * +virshVolEventToString(int event) +{ + const char *str = virshVolEventTypeToString(event); + return str ? _(str) : _("unknown"); +} + +struct virshVolEventData { + vshControl *ctl; + bool loop; + bool timestamp; + int count; + virshVolEventCallback *cb; +}; +typedef struct virshVolEventData virshVolEventData; + + +static void +vshEventLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virStorageVolPtr vol, + int event, + int detail ATTRIBUTE_UNUSED, + void *opaque) +{ + virshVolEventData *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 vol %s: %s\n"), + timestamp, + virStorageVolGetName(vol), + virshVolEventToString(event)); + } else { + vshPrint(data->ctl, _("event 'lifecycle' for storage vol %s: %s\n"), + virStorageVolGetName(vol), + virshVolEventToString(event)); + } + + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +virshVolEventCallback virshVolEventCallbacks[] = { + { "lifecycle", + VIR_STORAGE_VOL_EVENT_CALLBACK(vshEventLifecyclePrint), } +}; +verify(VIR_STORAGE_VOL_EVENT_ID_LAST == ARRAY_CARDINALITY(virshVolEventCallbacks)); + + +static const vshCmdInfo info_vol_event[] = { + {.name = "help", + .data = N_("Storage Vol Events") + }, + {.name = "desc", + .data = N_("List event types, or wait for storage vol events to occur") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_vol_event[] = { + {.name = "vol", + .type = VSH_OT_STRING, + .help = N_("filter by storage vol name or uuid") + }, + {.name = "event", + .type = VSH_OT_STRING, + .completer = virshVolEventNameCompleter, + .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 +cmdVolEvent(vshControl *ctl, const vshCmd *cmd) +{ + virStorageVolPtr vol = NULL; + bool ret = false; + int eventId = -1; + int timeout = 0; + virshVolEventData data; + const char *eventName = NULL; + int event; + virshControlPtr priv = ctl->privData; + + if (vshCommandOptBool(cmd, "list")) { + size_t i; + + for (i = 0; i < VIR_STORAGE_VOL_EVENT_ID_LAST; i++) + vshPrint(ctl, "%s\n", virshVolEventCallbacks[i].name); + 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; + } + + for (event = 0; event < VIR_STORAGE_VOL_EVENT_ID_LAST; event++) + if (STREQ(eventName, virshVolEventCallbacks[event].name)) + break; + if (event == VIR_STORAGE_VOL_EVENT_ID_LAST) { + 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; + data.cb = &virshVolEventCallbacks[event]; + if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) + return false; + + if (vshCommandOptBool(cmd, "vol")) + vol = virshCommandOptVolBy(ctl, cmd, "vol", NULL, NULL, + VIRSH_BYUUID); + + if (vshEventStart(ctl, timeout) < 0) + goto cleanup; + + if ((eventId = virConnectStorageVolEventRegisterAny(priv->conn, vol, event, + data.cb->cb, + &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 && + virConnectStorageVolEventDeregisterAny(priv->conn, eventId) < 0) + ret = false; + if (vol) + virStorageVolFree(vol); + return ret; +} + const vshCmdDef storageVolCmds[] = { {.name = "vol-clone", .handler = cmdVolClone, @@ -1869,5 +2055,11 @@ const vshCmdDef storageVolCmds[] = { .info = info_vol_wipe, .flags = 0 }, + {.name = "vol-event", + .handler = cmdVolEvent, + .opts = opts_vol_event, + .info = info_vol_event, + .flags = 0 + }, {.name = NULL} }; diff --git a/tools/virsh-volume.h b/tools/virsh-volume.h index 60f647776e..7869d3bfaf 100644 --- a/tools/virsh-volume.h +++ b/tools/virsh-volume.h @@ -38,6 +38,14 @@ virStorageVolPtr virshCommandOptVolBy(vshControl *ctl, const vshCmd *cmd, virshCommandOptVolBy(_ctl, _cmd, _optname, _pooloptname, _name, \ VIRSH_BYUUID | VIRSH_BYNAME) +struct virshVolEventCallback { + const char *name; + virConnectStorageVolEventGenericCallback cb; +}; +typedef struct virshVolEventCallback virshVolEventCallback; + +extern virshVolEventCallback virshVolEventCallbacks[]; + extern const vshCmdDef storageVolCmds[]; #endif /* VIRSH_VOLUME_H */ -- 2.17.0

After including volume events, event-test inside examples needs to be updated with some volume events too. Signed-off-by: Julio Faracco <jcfaracco@gmail.com> --- examples/object-events/event-test.c | 65 +++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/examples/object-events/event-test.c b/examples/object-events/event-test.c index 8499e0b38e..605490fa68 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -366,6 +366,20 @@ storagePoolEventToString(int event) return "unknown"; } +static const char * +storageVolEventToString(int event) +{ + switch ((virStorageVolEventLifecycleType) event) { + case VIR_STORAGE_VOL_EVENT_CREATED: + return "Created"; + case VIR_STORAGE_VOL_EVENT_DELETED: + return "Deleted"; + case VIR_STORAGE_VOL_EVENT_LAST: + break; + } + return "unknown"; +} + static const char * nodeDeviceEventToString(int event) { @@ -730,6 +744,19 @@ myStoragePoolEventRefreshCallback(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int +myStorageVolEventCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virStorageVolPtr vol, + int event, + int detail, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Storage volume %s %s %d\n", __func__, + virStorageVolGetName(vol), + storageVolEventToString(event), + detail); + return 0; +} static int myNodeDeviceEventCallback(virConnectPtr conn ATTRIBUTE_UNUSED, @@ -1099,6 +1126,20 @@ struct storagePoolEventData storagePoolEvents[] = { STORAGE_POOL_EVENT(VIR_STORAGE_POOL_EVENT_ID_REFRESH, myStoragePoolEventRefreshCallback), }; +struct storageVolEventData { + int event; + int id; + virConnectStorageVolEventGenericCallback cb; + const char *name; +}; + +#define STORAGE_VOL_EVENT(event, callback) \ + {event, -1, VIR_STORAGE_VOL_EVENT_CALLBACK(callback), #event} + +struct storageVolEventData storageVolEvents[] = { + STORAGE_VOL_EVENT(VIR_STORAGE_VOL_EVENT_ID_LIFECYCLE, myStorageVolEventCallback), +}; + struct nodeDeviceEventData { int event; int id; @@ -1132,6 +1173,7 @@ struct secretEventData secretEvents[] = { /* 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); +verify(ARRAY_CARDINALITY(storageVolEvents) == VIR_STORAGE_VOL_EVENT_ID_LAST); verify(ARRAY_CARDINALITY(nodeDeviceEvents) == VIR_NODE_DEVICE_EVENT_ID_LAST); verify(ARRAY_CARDINALITY(secretEvents) == VIR_SECRET_EVENT_ID_LAST); @@ -1225,6 +1267,22 @@ main(int argc, char **argv) } } + /* register common storage volume callbacks */ + for (i = 0; i < ARRAY_CARDINALITY(storageVolEvents); i++) { + struct storageVolEventData *event = storageVolEvents + i; + + event->id = virConnectStorageVolEventRegisterAny(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; + } + } + /* register common node device callbacks */ for (i = 0; i < ARRAY_CARDINALITY(nodeDeviceEvents); i++) { struct nodeDeviceEventData *event = nodeDeviceEvents + i; @@ -1293,6 +1351,13 @@ main(int argc, char **argv) } + printf("Deregistering storage volume event callbacks\n"); + for (i = 0; i < ARRAY_CARDINALITY(storageVolEvents); i++) { + if (storageVolEvents[i].id > 0) + virConnectStorageVolEventDeregisterAny(dconn, storageVolEvents[i].id); + } + + printf("Deregistering node device event callbacks\n"); for (i = 0; i < ARRAY_CARDINALITY(nodeDeviceEvents); i++) { if (nodeDeviceEvents[i].id > 0) -- 2.17.0
participants (3)
-
John Ferlan
-
Julio Faracco
-
Peter Krempa