8.4. Local system module#

The UMC server provides management services that are provided by so called UMC modules. These modules are implemented in Python (backend) and in JavaScript (web frontend). The following page provides information about developing and packaging of UMC modules. It is important to know the details of Architecture.

The package univention-management-console-dev provides the command umc-create-module, which can be used to create a template for a custom UMC module.

8.4.1. Python API#

The Python API for the UMC is defined in the Python module univention.management.console.base.

8.4.2. UMC module API (Python and JavaScript)#

A UMC module consists of three components

  • A XML document containing the definition.

  • The Python module defining the command functions.

  • The JavaScript frontend providing the web frontend.

8.4.2.1. XML definition#

The UMC server knows three types of resources that define the functionality it can provide:

UMC modules

provide commands that can be executed if the required permission is given.

Syntax types

can be used to verify the correctness of command attributes defined by the UMC client in the request message or return values provided by the UMC modules.

Categories

help to define a structure and to sort the UMC modules by its type of functionality.

All these resources are defined in XML files. The details are described in the following sections

8.4.2.1.1. Module definition#

The UMC server does not load the Python modules to get the details about the modules name, description and functionality. Therefore, each UMC module must provide an XML file containing this kind of information. The following example defines a module with the id udm:

<?xml version="1.0" encoding="UTF-8"?>
<umc version="2.0">
  <module id="udm" icon="udm/module" version="1.0">
    <name>Univention Directory Manager</name>
    <description>Manages all UDM modules</description>
    <flavor icon="udm-users" id="users/user">
      <name>Users</name>
      <description>Managing users</description>
    </flavor>
    <categories>
      <category name="domain"/>
    </categories>
    <command name="udm/query" function="query"/>
    <command name="udm/containers" function="containers"/>
  </module>
</umc>

The element module defines the basic details of a UMC module.

id

This identifier must be unique among the modules of an UMC server. Other files may extend the definition of a module by adding more flavors or categories.

icon

The value of this attribute defines an identifier for the icon that should be used for the module. Details for installing icons can be found in the Packaging.

The child elements name and description define the English human readable name and description of the module. For other translations the build tools will create translation files. Details can be found in the Packaging.

This example defines a so called flavor. A flavor defines a new name, description and icon for the same UMC module. This can be used to show several virtual modules in the overview of the web frontend. Additionally, the flavor is passed to the UMC server with each request i.e. the UMC module has the possibility to act differently for a specific flavor.

As the next element categories is defined in the example. The child elements category set the categories within the overview where the module should be shown. Each module can be part of multiple categories. The attribute name is the internal identifier of a category.

At the end of the definition file a list of commands is specified. The UMC server only passes commands to a UMC module that are defined. A command definition has two attributes:

name

is the name of the command that is passed to the UMC module. Within the HTTP request it is the URL path after /univention/command/.

function

defines the method to be invoked within the Python module when the command is called.

8.4.2.1.2. Category definition#

The predefined set of categories can be extended by each module.

Listing 8.4 UMC module category examples#
<?xml version="1.0" encoding="UTF-8"?>
<umc version="2.0">
  <categories>
    <category id="favorites">
      <name>Favorites</name>
    </category>
    <category id="system">
      <name>System</name>
    </category>
    <category id="wizards">
      <name>Wizards</name>
    </category>
    <category id="monitor">
      <name>Surveillance</name>
    </category>
  </categories>
</umc>

8.4.2.2. Python module#

The Python API for UMC modules primarily consists of one base class that must be implemented. As an addition the Python API provides some helpers:

  • Exception classes

  • Translation support

  • Logging functions

  • UCR access

In the definition file, the UMC module specifies functions for the commands provided by the module. These functions must be implemented as methods of the class Instance that inherits from univention.management.console.base.Base.

The following Python code example matches the definition in the previous section:

# SPDX-FileCopyrightText: 2021-2024 Univention GmbH
#
# SPDX-License-Identifier: AGPL-3.0-only

from univention.management.console import Translation
from univention.management.console.base import Base, UMC_Error
from univention.management.console.config import ucr
from univention.management.console.log import MODULE
from univention.management.console.modules.decorators import sanitize
from univention.management.console.modules.sanitizers import IntegerSanitizer


_ = Translation('univention-management-console-modules-udm').translate


class Instance(Base):

    def init(self):
        """Initialize the module with some values"""
        super(Instance, self).init()
        self.data = [int(x) for x in ucr.get('some/examle/ucr/variable', '1,2,3').split(',')]

    def query(self, request):
        """get all values of self.data"""
        self.finished(request.id, self.data)

    @sanitize(item=IntegerSanitizer(required=True))
    def get(self, request):
        """get a specific item of self.data"""
        try:
            item = self.data[request.options['item']]
        except IndexError:
            MODULE.error('A invalid item was accessed.')
            raise UMC_Error(_('The item %d does not exists.') % (request.options['item'],), status=400)
        self.finished(request.id, self.data[item])

    @sanitize(IntegerSanitizer(required=True))
    def put(self, request):
        """replace all data with the list provided in request.options"""
        self.data = request.options
        self.finished(request.id, None)

Each command methods has one parameter that contains the UMC request. Such an object has the following properties:

id

the unique identifier of the request.

options

contains the arguments for the command. For most commands it is a dictionary.

flavor

the name of the flavor that was used to invoke the command. This might be None.

The method init() in the example is invoked when the module process starts. It could for example be used to initialize a database connection.

The other methods in the example will serve specific requests. To respond to a request the function finished() must be invoked. To validate the request body the decorator @sanitize might be used with various sanitizers defined in univention.management.console.modules.sanitizers.

For a way to send an error message back to the client the UMC_Error can be raised with the error message as argument and an optional HTTP status code. The base class for modules provides some properties and methods that could be useful when writing UMC modules:

username

The username of the owner of this session.

user_dn

The DN of the user or None if the user is only a local user.

password

The password of the user.

init()

Is invoked after the module process has been initialized. At that moment, the settings, like locale and username and password are available.

destroy()

Is invoked before the module process shuts down.

8.4.2.3. UMC store API#

In order to encapsulate and ease the access to module data from the JavaScript side, a store object offers a unified way to query and modify module data.

The UMC JavaScript API comes with an object store implementation of the Dojo store API. This allows the JavaScript code to access/modify module data and to observe changes on the data in order to react immediately. The following methods are supported:

get(id)#

Returns a dictionary of all properties for the object with the specified identifier.

put(dictionary, options)#

modifies an object with the corresponding properties and an optional dictionary of options.

add(dictionary, options)#

Adds a new object with the corresponding properties and an optional dictionary of options.

remove(id)#

Removes the object with the specified identifier.

query(dictionary)#

Queries a list of objects (returned as list of dictionaries) corresponding to the given query which is represented as dictionary. Note that not all object properties need to be returned in order to save bandwidth.

The UMC object store class in JavaScript will be able to communicate directly with the Python module if the following methods are implemented:

module/get()#

Expects as input a list if unique IDs (as strings) and returns a list of dictionaries as result. Each dictionary entry holds all object properties.

module/put()#

Expects as input a list of dictionaries where each entry has the properties object and options. The property object holds all object properties to be set (i.e., this may also be a subset of all possible properties) as a dictionary. The second property options is an optional dictionary that holds additional options as a dictionary.

module/add()#

Expects similar input values as module/put().

module/remove()#

Expects as input a list of dictionaries where each entry has the properties object (containing the object’s unique ID (as string)) and options. The options property can be necessary as a removal might be executed in different ways (recursively, shallow removal etc.).

module/query()#

Expects as input a dictionary with entries that specify the query parameters and returns a list of dictionaries. Each entry may hold only a subset of all possible object properties.

Further references:

8.4.3. Packaging#

A UMC module consists of several files that must be installed at a specific location. As this mechanism is always the same there are debhelper tools making package creation for UMC modules very easy.

The following example is based on the package for the UMC module UCR.

A UMC module may be part of a source package with multiple binary packages. The examples uses a own source package for the module.

As a first step create a source package with the following directories and files:

  • univention-management-console-module-ucr/

  • univention-management-console-module-ucr/debian/

  • univention-management-console-module-ucr/debian/univention-management-console-module-ucr.umc-modules

  • univention-management-console-module-ucr/debian/rules

  • univention-management-console-module-ucr/debian/changelog

  • univention-management-console-module-ucr/debian/control

  • univention-management-console-module-ucr/debian/copyright

All these files are standard Debian packaging files except univention-management-console-module-ucr.umc-modules. This file contains information about the locations of the UMC module source files:

Module: ucr
Python: umc/python
Definition: umc/ucr.xml
Syntax: umc/syntax/ucr.xml
Javascript: umc/js
Icons: umc/icons

The keys in this file of the following meaning:

Module

The internal name of the module

Python

A directory that contains the Python package for the UMC module

Definition

The filename of the XML file with the module definition

Javascript

A directory containing the JavaScript source code

Icons

A directory containing the icons required by the modules web frontend

Syntax (optional)

The filename of the XML file with the syntax definitions

Category (optional)

The filename of the XML file with the category definitions

The directory structure for such a UMC module file would look like this:

  • univention-management-console-module-ucr/umc/

  • univention-management-console-module-ucr/umc/syntax/

  • univention-management-console-module-ucr/umc/syntax/ucr.xml

  • univention-management-console-module-ucr/umc/js/

  • univention-management-console-module-ucr/umc/js/ucr.js

  • univention-management-console-module-ucr/umc/js/de.po

  • univention-management-console-module-ucr/umc/de.po

  • univention-management-console-module-ucr/umc/icons/

  • univention-management-console-module-ucr/umc/icons/16x16/

  • univention-management-console-module-ucr/umc/icons/16x16/ucr.png

  • univention-management-console-module-ucr/umc/icons/24x24/

  • univention-management-console-module-ucr/umc/icons/24x24/ucr.png

  • univention-management-console-module-ucr/umc/icons/64x64/

  • univention-management-console-module-ucr/umc/icons/64x64/ucr.png

  • univention-management-console-module-ucr/umc/icons/32x32/

  • univention-management-console-module-ucr/umc/icons/32x32/ucr.png

  • univention-management-console-module-ucr/umc/ucr.xml

  • univention-management-console-module-ucr/umc/python/

  • univention-management-console-module-ucr/umc/python/ucr/

  • univention-management-console-module-ucr/umc/python/ucr/de.po

  • univention-management-console-module-ucr/umc/python/ucr/__init__.py

If such a package has been created a few things need to be adjusted

debian/rules
#!/usr/bin/make -f
%:
	dh $@ --with umc,python3
debian/control
Source: univention-management-console-module-ucr
Section: univention
Priority: optional
Maintainer: Univention GmbH <packages@univention.de>
Build-Depends:
 debhelper-compat (= 12),
 dh-python,
 python3-all,
 univention-management-console-dev (>= 12.0.2),
Standards-Version: 4.3.0.3

Package: univention-management-console-module-ucr
Architecture: all
Depends:
 univention-management-console-server,
Provides:
 ${python3:Provides},
Description: UMC module for UCR
 This package contains the UMC module for Univention Configuration Registry