univention.listener package

Contents

univention.listener package#

Listener module API

To create a listener module (LM) with this API, create a Python file in /usr/lib/univention-directory-listener/system/ which includes:

  1. a subclass of ListenerModuleHandler

  2. with an inner class Configuration that has at least the class attributes name, description and ldap_filter

See /usr/share/doc/univention-directory-listener/examples/ for examples.

class univention.listener.ListenerModuleAdapter(module_configuration: ListenerModuleConfiguration, *args: Any, **kwargs: Any)[source]#

Bases: object

Adapter to convert the univention.listener.listener_module interface to the existing listener module interface.

Use in a classic listener module like this:

globals().update(ListenerModuleAdapter(MyListenerModuleConfiguration()).get_globals())

Parameters:

module_configuration (ListenerModuleConfiguration) – configuration object

get_globals() dict[str, Any][source]#

Returns the variables to be written to the module namespace, that make up the legacy listener module interface.

Returns:

a mapping with keys: name, description, filter_s, attributes, modrdn, handler, initialize, clean, prerun, postrun, setdata, ..

Return type:

dict

class univention.listener.ListenerModuleConfiguration(*args: Any, **kwargs: Any)[source]#

Bases: object

Interface class for accessing the configuration and code of a listener module.

Subclass this and set the class attributes or pass them through __init__. If more logic is needed, overwrite the corresponding get_<attribute> method. Setting name, description, ldap_filter and listener_module_class is mandatory.

To extend the configuration, add key names in get_configuration_keys() and create a get_<attribute> method.

The listener server will use an object of your subclass to access your listener module through get_listener_module_instance().

attributes: list[str] = []#
description = ''#
get_active() bool[source]#

If this listener module should run. Determined by the value of listener/module/<name>/deactivate.

Returns:

whether the listener module should be activated

Return type:

bool

get_attributes() list[str][source]#
Returns:

attributes of matching LDAP objects the module will be notified about if changed

Return type:

list(str)

classmethod get_configuration_keys() list[str][source]#

List of known configuration keys. Subclasses can expand this to support additional attributes.

Returns:

list of known configuration keys

Return type:

list(str)

get_description() str[source]#
Returns:

description string of module

Return type:

str

get_ldap_filter() str[source]#
Returns:

LDAP filter of module

Return type:

str

get_listener_module_class() type[ListenerModuleHandler][source]#

Get the class to instantiate for a listener module.

Returns:

subclass of univention.listener.ListenerModuleHandler

Return type:

ListenerModuleHandler

get_listener_module_instance(*args: Any, **kwargs: Any) ListenerModuleHandler[source]#

Get an instance of the listener module.

Parameters:
Returns:

instance of ListenerModuleHandler

Return type:

ListenerModuleHandler

get_name() str[source]#
Returns:

name of module

Return type:

str

get_priority() float[source]#
Returns:

priority of the handler. Defines the order in which this module is executed inside the listener

Return type:

float

ldap_filter = ''#
listener_module_class: type[ListenerModuleHandler] = None#
name = ''#
exception univention.listener.ListenerModuleConfigurationError[source]#

Bases: ListenerModuleError

class univention.listener.ListenerModuleHandler(*args: str, **kwargs: str)[source]#

Bases: object

Listener module base class.

Subclass this to implement the logic of your listener module and have ListenerModuleConfiguration.get_listener_module_class() return the name of your subclass.

This class is not intended to be used directly. It should only be instantiated by ListenerModuleConfiguration.get_listener_module_instance().

When subclassing, in __init__() first call must be:

super(.., self).__init__(*args, **kwargs)

self.config will be set by the metaclass.

class Configuration(*args: Any, **kwargs: Any)[source]#

Bases: ListenerModuleConfiguration

Overwrite this with your own class of the same name. It can be any Python class with just the required attributes (description, ldap_filter) or a subclass of ListenerModuleConfiguration.

static as_root() Iterator[None][source]#

Contextmanager to temporarily change the effective UID of the current process to 0:

with self.as_root():

do something

Use listener.setuid() for any other user than root. But be aware that listener.unsetuid() will not be possible afterwards, as that requires root privileges.

clean() None[source]#

Called once when the Univention Directory Listener loads the module for the first time or when a resync it triggered.

config: ListenerModuleConfiguration | None = None#
create(dn: str, new: Mapping[str, Sequence[bytes]]) None[source]#

Called when a new object was created.

Parameters:
  • dn (str) – current objects DN

  • new (dict) – new LDAP objects attributes

classmethod diff(old: Mapping[str, Sequence[bytes]], new: Mapping[str, Sequence[bytes]], keys: Iterable[str] | None = None, ignore_metadata: bool = True) dict[str, tuple[Sequence[bytes] | None, Sequence[bytes] | None]][source]#

Find differences in old and new. Returns dict with keys pointing to old and new values.

Parameters:
  • old (dict) – previous LDAP objects attributes

  • new (dict) – new LDAP objects attributes

  • keys (list) – consider only those keys in comparison

  • ignore_metadata (bool) – ignore changed metadata attributes (if keys is not set)

Returns:

key -> (old[key], new[key]) mapping

Return type:

dict

error_handler(dn: str, old: Mapping[str, Sequence[bytes]], new: Mapping[str, Sequence[bytes]], command: str, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_traceback: TracebackType | None) None[source]#

Will be called for unhandled exceptions in create/modify/remove.

Parameters:
  • dn (str) – current objects DN

  • old (dict) – previous LDAP objects attributes

  • new (dict) – new LDAP objects attributes

  • command (str) – LDAP modification type

  • exc_type (type) – exception class

  • exc_value (BaseException) – exception object

  • exc_traceback (traceback) – traceback object

initialize() None[source]#

Called once when the Univention Directory Listener loads the module for the first time or when a resync it triggered.

property lo: access#

LDAP connection object.

Returns:

uldap.access object

Return type:

univention.admin.uldap.access

modify(dn: str, old: Mapping[str, Sequence[bytes]], new: Mapping[str, Sequence[bytes]], old_dn: str | None) None[source]#

Called when an existing object was modified or moved.

A move can be be detected by looking at old_dn. Attributes can be modified during a move.

Parameters:
  • dn (str) – current objects DN

  • old (dict) – previous LDAP objects attributes

  • new (dict) – new LDAP objects attributes

  • old_dn (str or None) – previous DN if object was moved/renamed, None otherwise

property po: position#

Get a LDAP position object for the base DN (ldap/base).

Returns:

uldap.position object

Return type:

univention.admin.uldap.position

post_run() None[source]#

Called only, when no change happens for 15 seconds - for any listener module.

Use for example to close an LDAP connection.

pre_run() None[source]#

Called before create/modify/remove if either the Univention Directory Listener has been restarted or when post_run() has run before.

Use for example to open an LDAP connection.

remove(dn: str, old: Mapping[str, Sequence[bytes]]) None[source]#

Called when an object was deleted.

Parameters:
  • dn (str) – current objects DN

  • old (dict) – previous LDAP objects attributes

ucr = <univention.config_registry.backend.ConfigRegistry object>#
exception univention.listener.ListenerModuleRuntimeError[source]#

Bases: ListenerModuleError

Submodules#

univention.listener.api_adapter module#

class univention.listener.api_adapter.ListenerModuleAdapter(module_configuration: ListenerModuleConfiguration, *args: Any, **kwargs: Any)[source]#

Bases: object

Adapter to convert the univention.listener.listener_module interface to the existing listener module interface.

Use in a classic listener module like this:

globals().update(ListenerModuleAdapter(MyListenerModuleConfiguration()).get_globals())

Parameters:

module_configuration (ListenerModuleConfiguration) – configuration object

get_globals() dict[str, Any][source]#

Returns the variables to be written to the module namespace, that make up the legacy listener module interface.

Returns:

a mapping with keys: name, description, filter_s, attributes, modrdn, handler, initialize, clean, prerun, postrun, setdata, ..

Return type:

dict

univention.listener.exceptions module#

exception univention.listener.exceptions.ListenerModuleError[source]#

Bases: Exception

exception univention.listener.exceptions.ListenerModuleConfigurationError[source]#

Bases: ListenerModuleError

exception univention.listener.exceptions.ListenerModuleRuntimeError[source]#

Bases: ListenerModuleError

univention.listener.handler module#

class univention.listener.handler.HandlerMetaClass(clsname: str, bases: tuple[type, ...], attrs: dict[str, Any])[source]#

Bases: type

Read handler configuration, invoke adapter and set global variables in module to fulfill original API.

class univention.listener.handler.ListenerModuleHandler(*args: str, **kwargs: str)[source]#

Bases: object

Listener module base class.

Subclass this to implement the logic of your listener module and have ListenerModuleConfiguration.get_listener_module_class() return the name of your subclass.

This class is not intended to be used directly. It should only be instantiated by ListenerModuleConfiguration.get_listener_module_instance().

When subclassing, in __init__() first call must be:

super(.., self).__init__(*args, **kwargs)

self.config will be set by the metaclass.

config: ListenerModuleConfiguration | None = None#
ucr = <univention.config_registry.backend.ConfigRegistry object>#
class Configuration(*args: Any, **kwargs: Any)[source]#

Bases: ListenerModuleConfiguration

Overwrite this with your own class of the same name. It can be any Python class with just the required attributes (description, ldap_filter) or a subclass of ListenerModuleConfiguration.

create(dn: str, new: Mapping[str, Sequence[bytes]]) None[source]#

Called when a new object was created.

Parameters:
  • dn (str) – current objects DN

  • new (dict) – new LDAP objects attributes

modify(dn: str, old: Mapping[str, Sequence[bytes]], new: Mapping[str, Sequence[bytes]], old_dn: str | None) None[source]#

Called when an existing object was modified or moved.

A move can be be detected by looking at old_dn. Attributes can be modified during a move.

Parameters:
  • dn (str) – current objects DN

  • old (dict) – previous LDAP objects attributes

  • new (dict) – new LDAP objects attributes

  • old_dn (str or None) – previous DN if object was moved/renamed, None otherwise

remove(dn: str, old: Mapping[str, Sequence[bytes]]) None[source]#

Called when an object was deleted.

Parameters:
  • dn (str) – current objects DN

  • old (dict) – previous LDAP objects attributes

initialize() None[source]#

Called once when the Univention Directory Listener loads the module for the first time or when a resync it triggered.

clean() None[source]#

Called once when the Univention Directory Listener loads the module for the first time or when a resync it triggered.

pre_run() None[source]#

Called before create/modify/remove if either the Univention Directory Listener has been restarted or when post_run() has run before.

Use for example to open an LDAP connection.

post_run() None[source]#

Called only, when no change happens for 15 seconds - for any listener module.

Use for example to close an LDAP connection.

static as_root() Iterator[None][source]#

Contextmanager to temporarily change the effective UID of the current process to 0:

with self.as_root():

do something

Use listener.setuid() for any other user than root. But be aware that listener.unsetuid() will not be possible afterwards, as that requires root privileges.

classmethod diff(old: Mapping[str, Sequence[bytes]], new: Mapping[str, Sequence[bytes]], keys: Iterable[str] | None = None, ignore_metadata: bool = True) dict[str, tuple[Sequence[bytes] | None, Sequence[bytes] | None]][source]#

Find differences in old and new. Returns dict with keys pointing to old and new values.

Parameters:
  • old (dict) – previous LDAP objects attributes

  • new (dict) – new LDAP objects attributes

  • keys (list) – consider only those keys in comparison

  • ignore_metadata (bool) – ignore changed metadata attributes (if keys is not set)

Returns:

key -> (old[key], new[key]) mapping

Return type:

dict

error_handler(dn: str, old: Mapping[str, Sequence[bytes]], new: Mapping[str, Sequence[bytes]], command: str, exc_type: type[BaseException] | None, exc_value: BaseException | None, exc_traceback: TracebackType | None) None[source]#

Will be called for unhandled exceptions in create/modify/remove.

Parameters:
  • dn (str) – current objects DN

  • old (dict) – previous LDAP objects attributes

  • new (dict) – new LDAP objects attributes

  • command (str) – LDAP modification type

  • exc_type (type) – exception class

  • exc_value (BaseException) – exception object

  • exc_traceback (traceback) – traceback object

property lo: access#

LDAP connection object.

Returns:

uldap.access object

Return type:

univention.admin.uldap.access

property po: position#

Get a LDAP position object for the base DN (ldap/base).

Returns:

uldap.position object

Return type:

univention.admin.uldap.position

univention.listener.handler_configuration module#

class univention.listener.handler_configuration.ListenerModuleConfiguration(*args: Any, **kwargs: Any)[source]#

Bases: object

Interface class for accessing the configuration and code of a listener module.

Subclass this and set the class attributes or pass them through __init__. If more logic is needed, overwrite the corresponding get_<attribute> method. Setting name, description, ldap_filter and listener_module_class is mandatory.

To extend the configuration, add key names in get_configuration_keys() and create a get_<attribute> method.

The listener server will use an object of your subclass to access your listener module through get_listener_module_instance().

name = ''#
description = ''#
ldap_filter = ''#
listener_module_class: type[ListenerModuleHandler] = None#
attributes: list[str] = []#
classmethod get_configuration_keys() list[str][source]#

List of known configuration keys. Subclasses can expand this to support additional attributes.

Returns:

list of known configuration keys

Return type:

list(str)

get_name() str[source]#
Returns:

name of module

Return type:

str

get_description() str[source]#
Returns:

description string of module

Return type:

str

get_ldap_filter() str[source]#
Returns:

LDAP filter of module

Return type:

str

get_attributes() list[str][source]#
Returns:

attributes of matching LDAP objects the module will be notified about if changed

Return type:

list(str)

get_priority() float[source]#
Returns:

priority of the handler. Defines the order in which this module is executed inside the listener

Return type:

float

get_listener_module_instance(*args: Any, **kwargs: Any) ListenerModuleHandler[source]#

Get an instance of the listener module.

Parameters:
  • args (tuple) – passed to __init__ of ListenerModuleHandler

  • kwargs (dict) – : passed to __init__ of ListenerModuleHandler

Returns:

instance of ListenerModuleHandler

Return type:

ListenerModuleHandler

get_listener_module_class() type[ListenerModuleHandler][source]#

Get the class to instantiate for a listener module.

Returns:

subclass of univention.listener.ListenerModuleHandler

Return type:

ListenerModuleHandler

get_active() bool[source]#

If this listener module should run. Determined by the value of listener/module/<name>/deactivate.

Returns:

whether the listener module should be activated

Return type:

bool

univention.listener.handler_logging module#

Get a Python logging object below a listener module root logger. The new logging object can log to a stream or a file. The listener module root logger will log messages of all of its children additionally to the common listener.log.

class univention.listener.handler_logging.UniFileHandler(filename, mode='a', encoding=None, delay=False, errors=None)[source]#

Bases: WatchedFileHandler

Used by listener modules using the univention.listener API to write log files below /var/log/univention/listener_log/. Configuration can be done through the handler_kwargs argument of get_listener_logger().

Open the specified file and use it as the stream for logging.

class univention.listener.handler_logging.ModuleHandler(level: int = 0, udebug_facility: int = 8)[source]#

Bases: Handler

Used by listener modules using the univention.listener API to write log messages through univention.debug to /var/log/univention/listener.log

Initializes the instance - basically setting the formatter to None and the filter list to empty.

LOGGING_TO_UDEBUG = {'CRITICAL': 0, 'DEBUG': 3, 'ERROR': 0, 'INFO': 2, 'NOTSET': 3, 'WARN': 1, 'WARNING': 1}#
emit(record: LogRecord) None[source]#

Do whatever it takes to actually log the specified logging record.

This version is intended to be implemented by subclasses and so raises a NotImplementedError.

univention.listener.handler_logging.get_logger(name: str, path: str | None = None) Logger[source]#

Get a logging instance. Caching wrapper for get_listener_logger().

Parameters:
  • name (str) – name of the logger instance will be <root loggers name>.name

  • path (str) – path to log file to create. If unset will be /var/log/univention/listener_modules/<name>.log.

Returns:

a Python logging object

Return type:

logging.Logger

univention.listener.handler_logging.calculate_loglevel(name: str) str[source]#

Returns the higher of listener/debug/level and listener/module/<name>/debug/level which is the lower log level.

Parameters:

name (str) – name of logger instance

Returns:

log level

Return type:

int

univention.listener.handler_logging.get_listener_logger(name: str, filename: str, level: str | None = None, handler_kwargs: dict[str, Any] | None = None, formatter_kwargs: dict[str, Any] | None = None) Logger[source]#

Get a logger object below the listener module root logger. The logger will additionally log to the common listener.log.

  • The logger will use UniFileHandler(TimedRotatingFileHandler) for files if

    not configured differently through handler_kwargs[cls].

  • A call with the same name will return the same logging object.

  • There is only one handler per name-target combination.

  • If name and target are the same, and only the log level changes, it will

    return the logging object with the same handlers and change both the log level of the respective handler and of the logger object to be the lowest of the previous and the new level.

  • The loglevel will be the lowest one of INFO and the UCRVs

    listener/debug/level and listener/module/<name>/debug/level.

  • Complete output customization is possible, setting kwargs for the

    constructors of the handler and formatter.

  • Using custom handler and formatter classes is possible by configuring

    the cls key of handler_kwargs and formatter_kwargs.

Parameters:
  • name (str) – name of the logger instance will be <root loggers name>.name

  • level (str) – loglevel (DEBUG, INFO etc) or if not set it will be chosen automatically (see above)

  • target (str) – (file path)

  • handler_kwargs (dict) – will be passed to the handlers constructor. It cannot be used to modify a handler, as it is only used at creation time. If it has a key cls it will be used as handler instead of UniFileHandler or UniStreamHandler. It should be a subclass of one of those!

  • formatter_kwargs (dict) – will be passed to the formatters constructor, if it has a key cls it will be used to create a formatter instead of :py:class`logging.Formatter`.

Returns:

a Python logging object

Return type:

logging.Logger

univention.listener.handler_logging.info_to_syslog(msg: str) None[source]#