]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
vici: Add decorators to Python bindings to simplify listening for events
authorTobias Brunner <tobias@strongswan.org>
Thu, 2 Oct 2025 14:25:58 +0000 (16:25 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 7 Oct 2025 07:20:57 +0000 (09:20 +0200)
src/libcharon/plugins/vici/python/README.rst
src/libcharon/plugins/vici/python/vici/__init__.py
src/libcharon/plugins/vici/python/vici/event_listener.py [new file with mode: 0644]

index 3990f630024d50960f7952d02d32fb80338d80dc..b9c0cebcb4c7c375fdea9484b3284f804a568908 100644 (file)
@@ -7,8 +7,8 @@ side implementation of the VICI protocol, well suited to script automated tasks
 in a reliable way.
 
 
-Example Usage
--------------
+Basic Usage
+-----------
 
 .. code-block:: python
 
@@ -22,3 +22,26 @@ Example Usage
     >>> s.get_pools()
     OrderedDict([('p1', OrderedDict([('base', b'10.0.0.0'), ('size', b'254'),
     ('online', b'0'), ('offline', b'0')]))])
+
+Event Handling
+--------------
+
+Either use the convenient decorators provided by EventListener or directly call
+listen() on a Session object to loop over received events and dispatch them
+manually.
+
+.. code-block:: python
+
+    >>> import vici
+    >>> s = vici.Session()
+    >>> l = vici.EventListener(s)
+    >>> @l.on_events(['ike-updown', 'ike-rekey'])
+    ... def ike_events(name, data):
+    ...   """Handle event with given 'name' and 'data'."""
+    ...   print(name, data)
+    ...
+    >>> @l.on_events(['child-updown', 'child-rekey'])
+    ... def child_events(name, data):
+    ...   print(name, data)
+    ...
+    >>> l.listen()
index d314325b6cf5f09002c7839c6424bb9cbd6fb87a..12f84b36678c0ab49c53ae58264937b96f41661a 100644 (file)
@@ -1 +1,2 @@
+from .event_listener import EventListener
 from .session import Session
diff --git a/src/libcharon/plugins/vici/python/vici/event_listener.py b/src/libcharon/plugins/vici/python/vici/event_listener.py
new file mode 100644 (file)
index 0000000..45030bb
--- /dev/null
@@ -0,0 +1,85 @@
+from functools import wraps
+
+
+class EventListener(object):
+    def __init__(self, session=None):
+        """Create an event listener instance, which provides decorator methods
+        to make listening for events and the disconnection of the vici session
+        more convenient.
+
+        The session is optional here, but one must be set via
+        :func:`~set_session()` before calling :func:`~listen()`.
+
+        :param session: optional vici session to use
+        :type session: :class:`~vici.session.Session` or None
+        """
+        self.event_map = {}
+        self.disconnect_list = []
+        self.session = session
+
+    def set_session(self, session):
+        """Set the session that's used to listen for events. Only has an effect
+        when set before calling :func:`~listen()`.
+
+        :param session: vici session to use
+        :type session: :class:`~vici.session.Session`
+        """
+        self.session = session
+
+    def on_events(self, events):
+        """Decorator to mark a function as a listener for specific events.
+
+        The decorated function is expected to receive the name of the event and
+        the data as arguments.
+
+        :param events: events to register and call decorated function for
+        :type events: list
+        :return: decorator function
+        :rtype: any
+        """
+        def decorator(func):
+            self.event_map.update({event: func for event in events})
+
+            @wraps(func)
+            def wrapper(*args, **kwargs):
+                return func(*args, **kwargs)
+            return wrapper
+        return decorator
+
+    def on_disconnected(self):
+        """Decorator to mark a function as a listener for when the daemon
+        disconnects the vici session. This listener instance is passed to the
+        decorated function.
+
+        :return: decorator function
+        :rtype: any
+        """
+        def decorator(func):
+            self.disconnect_list.append(func)
+
+            @wraps(func)
+            def wrapper(*args, **kwargs):
+                return func(*args, **kwargs)
+            return wrapper
+        return decorator
+
+    def listen(self):
+        """Dispatch events registered via decorators of this instance.
+
+        This method does not return unless the daemon disconnects or an
+        exception occurs.
+
+        An active session has to be set before calling this. After getting
+        disconnected, a new session may be set via :func:`~set_session()`
+        before calling this again.
+        """
+        try:
+            if self.session is None:
+                return
+            for label, event in self.session.listen(self.event_map.keys()):
+                name = label.decode()
+                if name in self.event_map:
+                    self.event_map[name](name, event)
+        except IOError:
+            for func in self.disconnect_list:
+                func(self)