Difference between revisions of "Entwicklung und Integration eigener Module in Univention Directory Manager"

From Univention Wiki

Jump to: navigation, search
(Moved to Developer Reference)
Tag: Replaced
 
(29 intermediate revisions by 5 users not shown)
Line 1: Line 1:
= Einführung  =
+
=Hinweis=
  
Univention Directory Manager verwendet zur Verwaltung der Daten des Verzeichnisdienstes eine flexible und erweiterbare Struktur aus Python-Modulen. Zusätzlich einzubindende Module werden nach Ablage im Dateisystem automatisch erkannt und zur Verwendung an Kommandozeile und Web-Interface angeboten.
+
Dieser Artikel wurde in die [https://docs.software-univention.de/developer-reference-5.0.html#udm:modules offizielle Dokumentation] integriert, überarbeitet und angepasst. Der Inhalt dieser Seite ist demnach veraltet und wurde entfernt.
 
 
Die Entwicklung eigener Module erlaubt es, den Univention Directory Manager über den Funktionsumfang von erweiterten Attributen hinaus flexibel zu erweitern.
 
 
 
= Univention Directory Manager-Module  =
 
 
 
== Übersicht  ==
 
 
 
Univention Directory Manager (kurz UDM) verwendet zur Abbildung von LDAP-Objekten eine eigene Modulstruktur. Im Regelfall entspricht eines dieser UDM-Module einem LDAP-Objekt (z.B. einem Benutzer, einer Gruppe oder einem Container). Eine recht [http://forum.univention.de/viewtopic.php?p=4278#p4278 kompakte Erläuterung] ist im Univention Forum zu finden.
 
 
 
Die Module sind nach Aufgabenbereichen strukturiert im Verzeichnis ''/usr/lib/python2.4/site-packages/univention/admin/handlers/'' abgelegt. Die Module für die Verwaltung der verschiedenen Rechnerobjekte befinden sich beispielsweise unterhalb des Ordners ''computers''. Dieses Objekt kann von der Kommandozeilenschnittstelle durch ''computers/windows'' angesprochen werden.
 
 
 
Eigene Module sollten nach Möglichkeit in einem eigenen Unterverzeichnis abgelegt werden, um Konflikte mit eventuell später in UCS integrierten Standardmodulen zu vermeiden. Damit die Module initialisiert werden können, muss im Verzeichnis eine Datei ''__init__.py'' existieren.
 
 
 
== Aufbau eines Moduls  ==
 
 
 
{{Note|Der vorgegebene Name '''object''' hat historische Gründe und muß trotz der Namenskollision mit dem [http://docs.python.org/library/functions.html#object Python-Typ object] beibehalten werden.}}
 
Ein Modul besteht aus der Definition der Modul-Attribute und der Definition einer von '''univention.admin.simpleLdap''' abgeleiteten Klasse mit Namen '''object'''.
 
 
 
Im folgenden wird mit einer ausführlichen Beschreibung der zu definierenden Variablen begonnen. Im darauf folgenden Abschnitt [[#Die_object-Klasse]] wird die Klasse '''object''' genauer betrachtet und notwendige Definitionen und Funktionen in der Klasse aufgelistet. Abschließend werden noch zwei optionale Funktionen definiert und erklärt.
 
 
 
=== Globale Variablen  ===
 
 
 
Im folgende werden die globalen Variablen beschrieben, die in einem Univention Directory Manager-Modul besondere Bedeutungen haben. Dabei wird zwischen zwingend notwendigen und optionalen Variablen unterschieden. Die folgende Liste enthält die erforderlichen Variablen, die jedes Modul definieren muss:
 
 
 
==== '''module'''  ====
 
 
 
Ein String, der dem Namen des UDM-Moduls entsprechen muss, z.B. ''computers/computer''
 
 
 
==== '''operations'''  ====
 
 
 
Eine Liste von Strings; enthält alle mit diesem Objekt erlaubten LDAP-Operationen. Mögliche Operationen sind:<span style="font-style: italic;"> </span>''['add','edit','remove','search','move']''
 
 
 
==== '''short_description'''  ====
 
 
 
Diese Beschreibung wird im Web-Frontend des Univention Directory Manager als Name angezeigt. Im Bereich ''Navigation'' wird dieser Text in der Auswahlliste für mögliche Objekttypen angezeigt.  
 
 
 
==== '''childs'''  ====
 
 
 
Gibt an, ob es sich bei diesem LDAP-Objekt um einen Container handelt. In diesem Fall wird diese Variable auf den Wert ''1'' gesetzt und andernfalls auf ''0''.  
 
 
 
==== '''long_description'''  ====
 
 
 
Eine ausführliche Beschreibung des Moduls
 
 
 
==== '''options'''  ====
 
 
 
Über das Python-Dictionary können Attribute (''properties'') verschiedenen Gruppen zugeordnet werden, so daß diese nur angezeigt werden, wenn die Option aktiviert wurde. In diesem Dictionary werden die Optionen (als '''univention.admin.option'''-Objekte) einer eindeutigen Zeichenkette zugeordnet, die später zur Referenzierung verwendet wird (siehe ''[[#property-descriptions]]''). Jede Option kann mehrere Parameter haben:
 
 
 
*'''short_description''': Eine kurze Beschreibung der Option, die beispielsweise im Web-Frontend des Univention Directory Manager als beschreibender Text zu den Eingabefeldern genutzt wird.
 
*'''long_description''': Eine längere Beschreibung der Option.
 
*'''default''' Definiert, ob diese Option standardmäßig aktiviert sein soll. ''True'' steht dabei für aktiv und ''False'' für inaktiv.
 
*'''editable''' Definiert, ob diese Option mehrmalig gesetzt und entfernt werden kann, oder ob diese Option nach einmaliger Aktivierung immer gesetzt bleibt.
 
*'''objectClasses''' Eine Menge von LDAP-Objektklassen, die der LDAP-Eintrag haben muß, damit es mit dieser Option assoziiert wird. Damit ist kein Automatismus verbunden, aber diese Information ist bei der späteren Implementierung der Funktion ''identify()'' (siehe [[#Die Funktionen identify und lookup]]) ggf. hilfreich.
 
 
 
Beispiel:
 
<syntaxhighlight lang=python>options = {
 
'opt1': univention.admin.option(
 
  short_description=_('short description'),
 
  default=True),
 
...}
 
</syntaxhighlight>
 
 
 
==== '''property-descriptions'''  ====
 
 
 
Dieses Python-Dictionary enthält alle Attribute, die dieses Modul zur Verfügung stellt. Dabei werden die Attribute (diesmal als '''univention.admin.property'''-Objekte), über eine eindeutige Zeichenkette als Schlüssel referenziert. Ein solches Modul-Attribut entspricht in der Regel einem LDAP-Attribut, kann aber auch aus anderen Quellen stammen oder berechnet werden. Ein Beispiel:
 
<syntaxhighlight lang=python>property_descriptions = {
 
'prop1': univention.admin.property(
 
short_description='name'
 
long_description='long description',
 
syntax=univention.admin.syntax.string,
 
multivalue=False,
 
required=True,
 
may_change=True,
 
identifies=False,
 
dontsearch=True,
 
default='default value',
 
options=['opt1']),
 
...}
 
</syntaxhighlight>
 
Die Parameter haben folgende Bedeutung:
 
 
 
*'''short_description''' Eine kurze Beschreibung, die beispielsweise im Web-Frontend des Univention Directory Manager als beschreibender Text zu den Eingabefeldern genutzt wird.
 
*'''long_description''': Eine ausführlichere Beschreibung, die im Web-Frontend des Univention Directory Manager für die Tooltips genutzt wird.
 
*'''syntax''': Dieser Parameter gibt den Typ eines Attributs an. Anhand dieser Typ-Definitionen kann der Univention Directory Manager die angegebenen Werte für das Attribut überprüfen und bei ungültigen Werten eine detaillierte Fehlermeldung liefern.<br/>Die nachfolgende Liste enthält die wichtigsten Typen für mögliche Werte, denen jeeils das Prefix ''univention.admin.syntax.'' voranzustellen ist. Zusätzlich zu den vordefinierten Typdefinitionen aus <tt>/usr/share/pyshared/univention/admin/syntax.py</tt> können auch eigene Typen über UDM als '''settings/syntax'''-Objekte oder programmatisch über eigene Dateien im Verzeichnis <tt>/usr/share/pyshared/univention/admin/syntax.d/</tt> definiert werden.
 
**'''string''': eine beliebige Zeichenkette
 
**'''integer''': ein ganzzahliger positiver Wert
 
**'''boolean''': kann nur eine 0 oder eine 1 enthalten
 
**'''filesize''': ein ganzzahliger positiver Wert mit einer optional folgenden Angabe der Einheit. Unterstützt werden die Einheiten: B, KB, MB, GB, die Groß- und Kleinschreibung ist irrelevant
 
**'''phone''': eine Telefonnummer
 
**'''uid''': ein Benutzername
 
**'''uid_umlauts''': ein Benutzername mit Umlauten
 
**'''gid''': ein Gruppenname
 
**'''sharePath''': ein Pfad einer Freigabe
 
**'''passwd''': ein Passwort von min. acht Zeichen Länge
 
**'''userPasswd''': ein Passwort mit min. einem Zeichen
 
**'''hostName''': ein Rechnername nach {{RFC|952}}
 
**'''ipAddress''': eine IPv4-Adresse
 
**'''hostOrIP''': ein Rechnername oder eine IPv4-Adresse
 
**'''netmask''': eine Netzmaske
 
**'''absolutePath''': ein absoluter Pfad nach UNIX-Syntax, d.h. der Wert muss mit einem '/' anfangen
 
**'''emailAddress''': eine Email-Adresse
 
**'''emailAddressTemplate''': eine E-Mail Adresse mit Nutzername
 
**'''date''': ein Datum in verschiedenen Kurzschreibweisen: TT.MM.JJ oder TT.MM.JJJJ oder JJJJ-MM-TT
 
*'''multivalue''' kann die Werte ''True'' oder ''False'' annehmen. Ist dieser Parameter auf ''True'' gesetzt handelt es sich bei dem Wert des Attributs um eine Liste. Der Parameter '''syntax''' gibt in diesem Fall den Typ der Elemente dieser Liste an.
 
*'''required''': Ist dieser Parameter auf ''True'' gesetzt, muß für dieses Attribut ein Wert angegeben werden.
 
*'''may_change''': Ist dieser Parameter auf ''True'' gesetzt, kann der Wert dieses Attributs auch nach der Erstellung des Objektes verändert werden, ansonsten nur einmalig bein Anlegen.
 
*'''editable''': Ist dieser Parameter auf ''False'' gesetzt, dann kann der Wert dieses Attributes schon beim Erstellen nicht angegeben werden. Dies ist i.d.R. nur für automatisch generierte oder berechnete Werte interessant.
 
*'''identifies''': diese Option sollte bei genau einem Attribut eines Moduls auf ''True'' gesetzt sein, um dasjenige Attribut zu kennzeichnen, anhand dessen das Objekt eindeutig zu identifizieren ist.
 
*'''dontsearch''': Ist dieser Parameter auf ''False'' gesetzt, kann das Feld nicht als das Feld für die Suche ausgewählt werden.
 
*'''default''': Ein Vorgabewert für das Attribut, wenn das Objekt über das Web-Frontend angelegt wird.
 
*'''options''': Eine Liste von Schlüsselwörtern, die Optionen identifizieren mit denen dieses Attribut ein- bzw. ausgeblendet werden kann.
 
 
 
==== '''layout'''  ====
 
 
 
Die Attribute eines Objektes können in Gruppen angeordnet werden.
 
Diese werden beispielsweise im Univention Directory Manager als Reiter dargestellt.
 
Für jeden Reiter muß eine Instanz von ''univention.admin.tab'' im Array '''layout''' angelegt werden.
 
Als Parameter wird der Name, eine Beschreibung für den Reiter sowie eine Liste der Zeilen erwartet.
 
In jeder Zeile können sich bis zu zwei Attribute befinden, für die jeweils eine Instanz von ''univention.admin.field'' anzulegen ist.
 
Als Parameter wird jeweils der Name einer Property aus [[#property-descriptions]] erwartet.
 
Über den optionalen Parameter ''colspan=2'' kann das Widget für eine Property auch über beide Spalten ausgedehnt werden.
 
 
 
<syntaxhighlight lang=python>layout = [
 
  univention.admin.tab(_('Tab header'), _('Tab description'), [
 
    [univention.admin.field('prop1'), univention.admin.field('prop3')],
 
    [univention.admin.field('prop3', colspan=2), ],
 
    ...
 
  ], advanced=False),
 
  ...
 
]
 
</syntaxhighlight>
 
 
 
Über die optionale ''advanced=True''-Einstellung kann kontrolliert werden, ob der Reiter standardmäßig angezeigt werden soll oder nur dann, wenn das Anzeigen der ''erweiterten Einstellungen'' aktiviert ist.
 
 
 
==== '''mapping'''  ====
 
 
 
Definiert die Abbildung der Attribute des Univention Directory Manager Moduls auf LDAP-Attribute.
 
Für jedes Attribut muß eine Abbildung registriert werden:
 
<syntaxhighlight lang=python>mapping.register(udm_name, ldap_name, map_value, unmap_value)</syntaxhighlight>
 
 
 
Da die interne Implementierung nicht unterschiedet, ob Attribute mehrwertig sind oder nicht, muß für alle einwertigen Attribute als ''unmap_value''-Funktion (LDAP→UDM) z.B. die Standard-Implementierung ''univention.admin.mapping.ListToString'' angegeben werden, die jeweils den ersten Eintrag zurückliefert.
 
Für ''map_value'' kann ''None'' verwendet werden.
 
 
 
 
 
Folgende Angaben sind optional und müssen nur definiert werden, wenn das Modul diese speziellen Attribute besitzt:
 
 
 
==== '''virtual'''  ====
 
 
 
Module, die diese Variable auf ''1'' setzen sind eine Art Hilfsmodul für andere Module, die keine zugehörigen LDAP-Objekte haben. Ein Beispiel hierfür ist das Modul ''computers/computer'', das als Hilfsmodul für alle Rechnertypen dient.
 
 
 
==== '''template'''  ====
 
 
 
Ein Modul, das diese Variable auf ''1'' setzt, bietet die Möglichkeit Vorgabewerte für die Attribute von anderen Modulen zu definieren. Ein Beispiel hierfür ist die Benutzer-Vorlage (das Modul ''settings/usertemplate''). Eine solche Vorlage kann dann beispielsweise beim Anlegen eines Benutzers ausgewählt werden und die darin definierten Werte werden als Vorgaben in die Eingabemasken übernommen.
 
 
 
=== Die '''object'''-Klasse  ===
 
 
 
Die Klasse '''object''' eines Moduls bildet die Schnittstelle zwischen Univention Directory Manager und den LDAP-Operationen, die beim Anlegen, Verändern, Verschieben oder Löschen eines Objekts ausgelöst werden.
 
Diese Klasse unterstützt den Univention Directory Manager bei der Abbildung der definierten Attribute des Moduls auf LDAP-Objekte und -Attribute.
 
 
 
Dafür ist die vordefinierte API der Klasse einzuhalten.
 
Die Basisklasse '''univention.admin.handlers.simpleLdap''' bietet für einfache LDAP-Objekte die wesentliche Funktionalität, so dass in der Regel nur noch wenige Anpassungen notwendig sind.
 
Eine Instanz (''self'') kapselt alle Informationen eines Objekts, auf die auf verschiedenen Arten zugegriffen werden kann:
 
;self.dn → String
 
:Distinguished Name im LDAP-DIT
 
;self.position → univention.admin.uldap#position
 
:Container-Element im LDAP-DIT
 
;self['UDM-Propertyname'] → [Werte...]
 
:Wrapper um <em>self.info</em>, der Zusätzlichen bei Zuweisung den Wert anhand der Syntax überprüft und Standardwerte beim Lesen liefert.
 
;self.info['UDM-Propertyname'] → [Werte...]
 
:Dictionary mit den aktuell gesetzten Werten der UDM-Properties. Der direkte Zugriff darauf ermöglicht z.B. die Initialisierung von ''editable=False''-Properties.
 
;self.oldinfo['UDM-Propertyname'] → [Werte...]
 
:Dictionary der ursprünglich gelesenen Werte, umgewandelt in UDM-Propertynamen.  Diese Information wird vor allem intern benötigt, um Änderungen am Python-Objekt zurück auf den zugehörigen Eintrag im LDAP zu übertragen.
 
;self.oldattr['LDAP-Attributname'] → [Werte...]
 
:Dictionary der ursprünglich aus dem LDAP gelesenen Attribute. Wird i.d.R. nur dazu genutzt, um die ''objectClass''es auszulesen.
 
;self.oldpolicies → [Policy-DNs...]
 
:Kopie der Liste der DNs der referenzierten univentionPolicyReference...
 
;self.policies → [Policy-DNs...]
 
:Liste der DNs der referenzierten univentionPolicyReference...
 
;self.policyObjects[Policy-DN] → univention.admin.handlers#simplePolicy
 
:Dictionary der geladenen Policies.
 
 
 
Die Klasse '''simpleLdap''' bietet zudem die weitere Möglichkeit, vor und nach der LDAP-Operation durch den Aufruf von Funktionen weitere Anpassungen vorzunehmen.
 
Beispielsweise wird vor dem Anlegen eines LDAP-Objekte die Funktion ''_ldap_pre_create()'' und nach dem Vorgang die Funktion ''_ldap_post_create()'' aufgerufen.
 
Solche Pre- und Post-Funktionen existieren analog auch für die Operationen '''modify''', '''move''' und '''remove'''.
 
Die folgende Tabelle listet alle verwendeten Funktionen in der Aufrufreihenfolge von oben nach unten auf:
 
{| border="1" width="100%" cellspacing="1" cellpadding="1"
 
|+ Aktionen und Hooks
 
|-
 
! Beschreibung<br>
 
! Create<br>
 
! Modify<br>
 
! Remove<br>
 
|-
 
| Vor der Validierung<br>
 
| colspan="2" | _ldap_pre_ready()<br>
 
| <br>
 
|-
 
| Validiert, ob alle Muß-Attribute vorhanden sind<br>
 
| colspan="2" | ready()<br>
 
| <br>
 
|-
 
| <br>
 
| _ldap_pre_create()
 
| _ldap_pre_modify()<br>
 
| _ldap_pre_remove()<br>
 
|-
 
| Policy Copy-on-Write<br>
 
| _update_policies()
 
| _update_policies()
 
| <br>
 
|-
 
| Erweiterungspunkt für ''Erweiterte Attribute''
 
| ''hook_ldap_pre_create()''
 
| ''hook_ldap_pre_modify()''
 
| ''hook_ldap_pre_remove()''
 
|-
 
| Liefert initiale Liste der (LDAP-Attributname, alt, neu)- bzw. (LDAP-Attributname, [neu])-Tupel<br>
 
| _ldap_addlist()
 
| <br>
 
| <br>
 
|-
 
| Erweitert Liste um Diff und andere Änderungen<br>
 
| colspan="2" | _ldap_modlist()<br>
 
| <br>
 
|-
 
| (oc, pname, syntax, ldapMapping, DeleteValues, deleteObjectClas)<br>
 
| colspan="2" | ldap_extra_objectclasses<br>
 
| <br>
 
|-
 
| (prop,)<br>
 
| colspan="2" | extended_udm_attributes<br>
 
| <br>
 
|-
 
| Erweiterungspunkt für ''Erweiterte Attribute''
 
| ''hook_ldap_addlist()''
 
| ''hook_ldap_modlist()''
 
| <br>
 
|-
 
| eigentliche Aktion
 
| '''ADD'''
 
| '''MODIFY'''
 
| '''DELETE'''
 
|-
 
| <br>
 
| _ldap_post_create()
 
| _ldap_post_modify()<br>
 
| _ldap_post_remove()<br>
 
|-
 
| Erweiterungspunkt für ''Erweiterte Attribute''
 
| ''hook_ldap_post_create()''
 
| ''hook_ldap_post_modify()''
 
| ''hook_ldap_post_remove()''
 
|}
 
 
 
Die ''hook_ldap_*''-Funktionen werden genauer in einem gesonderten Artikel über das [[Erstellen eines erweiterten Attributs mit Hook]] beschrieben.
 
 
 
=== Die Funktionen '''identify''' und '''lookup'''  ===
 
 
 
Diese Funktionen werden genutzt, um bei Suchanfragen aus dem Web-Frontend des Univention Directory Manager die dazugehörigen Objekte zu finden ('''lookup''') und um LDAP-Objekte einem Univention Directory Manager-Modul zuzuordnen. Später werden Beispielimplementierungen für diese zwei Funktionen vorgestellt, die für einfache LDAP-Objekte nur leicht modifiziert werden müssen.
 
 
 
= Beispiel-Modul  =
 
 
 
Im folgenden wird ein Beispiel-Modul für den Univention Directory Manager vorgestellt, das auch als Paket ''univention-directory-manager-module-example'' unter [https://forge.univention.org/websvn/dl.php?repname=dev&path=/trunk/ucs/management/univention-directory-manager-module-example/&isdir=1 univention-directory-manager-module.example.tar.gz] verfügbar ist.
 
 
 
Ein Univention Directory Manager-Modul bestimmt in der Regel immer aus zwei Komponenten: Einmal dem Python-Modul, das die Implementierung der Schnittstelle zum Univention Directory Manager enthält und einem LDAP-Schema, das das zu verwaltende LDAP-Objekt definiert. Im folgenden werden beide Teile beschrieben, wobei der Schwerpunkt bei der Erstellung des Python-Moduls liegt.
 
 
 
== Python-Modul des Beispiel-Moduls  ==
 
 
 
Das folgende Modul für den Univention Directory Manager demonstriert die rudimentäre Verwaltung von IP-Telefonen. Dabei wird versucht mit einem einfach gehaltenen Beispiel möglichst viele der Möglichkeiten eines Univention Directory Manager-Moduls aufzuzeigen.
 
 
 
Vor der Definition des eigentlichen Modul-Quellcodes müssen einige Basis-Python-Module importiert werden, die auf jeden Fall notwendig sind:
 
<syntaxhighlight lang=python>import univention.admin.filter
 
import univention.admin.handlers
 
import univention.admin.syntax
 
</syntaxhighlight>
 
Diese Liste von Python-Modulen kann natürlich noch erweitert werden.
 
Wie in Abschnitt [[#Globale Variablen]] beschrieben, werden in einem Univention Directory Manager-Modul zu Beginn einige notwendige globale Variablen definiert, die eine Beschreibung des Moduls liefern:
 
<syntaxhighlight lang=python>module = 'test/ip-phone'
 
childs = 0
 
short_description = u'IP-Telefon'
 
long_description = u'Ein Beispiel-Modul für den \
 
    Univention Directory Manager zur Verwaltung von IP-Telefonen'
 
operations = ['add', 'edit', 'remove', 'search', 'move']
 
</syntaxhighlight>
 
Eine weitere für das Web-Frontend des Univention Directory Manager wichtige globale Variable ist '''layout''':
 
Sie strukturiert die Anordnung der einzelnen Attribute des Objektes auf die Reiter.
 
Es handelt sich dabei um eine Liste aus Elementen vom Typ '''univention.admin.tab''', die jeweils den Inhalt eines Reiters bestimmen.
 
In diesem Fall gibt es einen Reiter ''Allgemein'' und einen weiteren Reiter ''Erweiterungen''.
 
 
 
Achtung:
 
Es können nicht mehr als zwei Felder nebeneinander angeben werden, d.h. ein Listenelement darf nicht aus mehr als zwei Objekten vom Typ '''univention.admin.field''' bestehen!
 
 
 
Als nächstes sollten die Optionen und Attribute des Moduls definiert werden.
 
 
 
In diesem Fall wird eine Option ''extended'' angelegt, deren Funktion später noch erläutert wird.
 
Um die Parameter zu konfigurieren wird dem Objekt '''univention.admin.option''' der Option '''short_description''' für eine Kurzbeschreibung übergeben.
 
Mit '''default''' kann die Vorkonfiguration bestimmt werden, mit ''True'' ist die Option standardmässig aktiviert, mit ''False'' deaktiviert.
 
<syntaxhighlight lang=python>options={
 
    'extended': univention.admin.option(
 
            short_description=u'Erweiterte Einstellungen',
 
            default=True
 
        )
 
}
 
</syntaxhighlight>
 
Nach den Optionen werden die Attribute des Moduls festgelegt.
 
Dabei werden die Attribute durch textuelle Beschreibungen, Syntaxdefinitionen und Anweisungen für das Web-Frontend des Univention Directory Managers definiert.
 
<syntaxhighlight lang=python>property_descriptions={
 
</syntaxhighlight>
 
Das Attribut '''name''' definiert den "Rechnernamen" des IP-Telefons.
 
Mit dem Parameter '''syntax''' wird dem Univention Directory Manager mitgeteilt, dass gültige Werte für dieses Attribut der Syntax eines Rechnernamen entsprechen müssen.
 
Weitere vordefinierte Syntaxdefinitionen finden sich im Abschnitt [[#property-descriptions]].
 
Eine Besonderheit dieses Attributs ist der Parameter '''may_change''', der in diesem Fall auf ''False'' gesetzt ist.
 
Dadurch wird festgelegt, dass dieses Attribut nach dem Anlegen des Objektes nicht mehr geändert werden kann:
 
<syntaxhighlight lang=python>    'name': univention.admin.property(
 
            short_description=u'Name',
 
            long_description=u'Name des Telefons',
 
            syntax=univention.admin.syntax.hostName,
 
            multivalue=False,
 
            options=[],
 
            required=True,
 
            may_change=False,
 
            identifies=True
 
        ),
 
</syntaxhighlight>
 
<br> '''active''' ist ein Beispiel für ein boolsches/binäres Attribut, das nur die Werte ''1'' oder ''0'' annehmen kann.
 
In diesem Beispiel definiert es eine Freischaltung/Sperrung des IP-Telefons.
 
Durch dem Parameter ''default=1'' wird das Telefon initial freigeschaltet:
 
<syntaxhighlight lang=python>    'active': univention.admin.property(
 
            short_description=u'freigeschaltet',
 
            long_description=u'Ein IP-Telefon kann gesperrt werden',
 
            syntax=univention.admin.syntax.boolean,
 
            multivalue=False,
 
            options=[],
 
            required=False,
 
            default='1',
 
            may_change=True,
 
            identifies=False
 
        ),
 
</syntaxhighlight>
 
Das Attribut '''protocol''' legt fest, welches VoIP-Protokoll von dem Telefon unterstützt wird.
 
Dabei wird für dieses Attribut keine Standard-Syntaxdefinition verwendet, sondern eine eigens dafür deklarierte Klasse '''SynVoIP_Protocols'''.
 
(Der Quellcode dieser Klasse folgt in einem späteren Abschnitt).
 
Die Syntax der Klasse definiert eine Auswahlliste mit einer vordefinierten Menge an Möglichkeiten.
 
Durch den Parameter '''default '''wird der Wert mit dem Schlüssel ''sip'' vorausgewählt.
 
<syntaxhighlight lang=python>    'protocol': univention.admin.property(
 
            short_description=u'Protokoll',
 
            long_description=u'Welches VoIP Protokoll \
 
    wird von dem Telefon unterstützt',
 
            syntax=SynVoIP_Protocols,
 
            multivalue=False,
 
            options=[],
 
            required=False,
 
            default='sip',
 
            may_change=True,
 
            identifies=False
 
        ),
 
</syntaxhighlight>
 
Das Attribut '''ip''' legt die IP-Adresse des Telefons fest.
 
Als Syntaxdefinition wird die vordefinierte Klasse '''univention.admin.syntax.ipAddress''' angeben.
 
Zusätzlich wird mit dem Parameter '''required''' erzwungen, dass dieses Attribut zwingend gesetzt sein muss.
 
<syntaxhighlight lang=python>    'ip': univention.admin.property(
 
            short_description=u'IP-Adresse',
 
            long_description=u'',
 
            syntax=univention.admin.syntax.ipAddress,
 
            multivalue=False,
 
            options=[],
 
            required=True,
 
            may_change=True,
 
            identifies=False
 
        ),
 
</syntaxhighlight>
 
Das Attribut '''priuser''' setzt den primären Benutzer des IP-Telefons.
 
Hierfür wird ebenfalls eine eigene Syntax-Definition verwendet.
 
Zum Einsatz kommt dabei eine Klasse, die die gültigen Werte über einen regulären Ausdruck definiert.
 
(Der Quellcode wird später dargestellt)
 
<syntaxhighlight lang=python>    'priuser': univention.admin.property(
 
            short_description=u'primärer Benutzer',
 
            long_description=u'Der primäre Benutzer \
 
    dieses Telefons',
 
            syntax=SynVoIP_Address,
 
            multivalue=False,
 
            options=[],
 
            required=True,
 
            may_change=True,
 
            identifies=False
 
        ),
 
</syntaxhighlight>
 
Das Attribut '''users''' zeigt die Verwendung von Optionen.
 
Mit dem Parameter ''options=['extended']'' wird festgelegt, dass dieses Attribut nur zur Verfügung steht, wenn mindestens eine der aufgelisteten Optionen aktiviert ist.
 
Ist die Liste der angegeben Optionen leer (vordefinierter Wert), wird das Attribut immer angezeigt.
 
<syntaxhighlight lang=python>    'users': univention.admin.property(
 
            short_description=u'weitere Benutzer',
 
            long_description=u'Benutzer, die an diesem \
 
    Telefon registriert sein dürfen',
 
            syntax=SynVoIP_Address,
 
            multivalue=True,
 
            options=['extended'],
 
            required=False,
 
            may_change=True,
 
            identifies=False
 
        )
 
}
 
</syntaxhighlight>
 
Die folgenden zwei Klassen sind die Syntaxdefinitionen, die für die Attribute '''protocols''', '''priuser''' und '''users''' verwendet wurden. '''SynVoIP_Protocols''' basiert auf der vordefinierten Klasse '''univention.admin.syntax.select''', die die Basisfunktionalität für Auswahllisten zur Verfügung stellt.
 
Abgeleitete Klassen müssen, wie in der folgenden Klasse zu sehen ist, nur einen Namen und die Liste der Auswahlmöglichkeiten definieren.
 
 
 
Das Attribut '''choices''' besteht aus einer Liste von Paaren, die aus einem eindeutigen Schlüssel für das Element und dem Text, der angezeigt werden soll, bestehen.
 
<syntaxhighlight lang=python>class SynVoIP_Protocols(univention.admin.syntax.select):
 
    name=u'VoIP_Protocol'
 
    choices=[('sip', u'SIP'), ('h323', u'H.323'), \
 
    ('skype', u'Skype')]
 
</syntaxhighlight>
 
Die andere Syntaxdefinition - '''SynVoIP_Address''' - basiert auf der Klasse '''univention.admin.syntax.simple''', die eine Basisfunktionalität für Syntaxdefinitionen bietet, die mit regulären Ausdrücken arbeiten.
 
Wie bei der anderen Definition muss ein Name vergeben werden.
 
Zusätzlich sind noch die Attribute ''min_length'' und ''max_length'' anzugeben, die eine minimale und eine maximale Länge für zulässige Werte festlegen.
 
Wird eines dieser Attribute auf ''0'' gesetzt, entspricht das einer nicht existenten Grenze in die jeweilige Richtung.
 
Außer den genannten Attributen muss noch die Funktion '''parse()''' definiert werden, die als Parameter den zu prüfenden Wert übergeben bekommt.
 
Mittels des Python Moduls '''re''' wird in diesem Fall geprüft, ob der Wert dem Muster einer VoIP Adresse entspricht, z.B. '''sip:hans@mustermann.de'''.
 
<syntaxhighlight lang=python>class SynVoIP_Address(univention.admin.syntax.simple):
 
    name = 'VoIP_Address'
 
    min_length = 4
 
    max_length = 256
 
    _re = re.compile('((^(sip|h323|skype):)? \
 
    ([a-zA-Z])[a-zA-Z0-9._-]+)@[a-zA-Z0-9._-]+$')
 
 
 
    def parse(self, text):
 
        if self._re.match(text) is not None:
 
            return text
 
        raise univention.admin.uexceptions.valueError,
 
                u'Keine gültige VoIP Adresse'
 
</syntaxhighlight>
 
Anschließend wird die Abbildung von den Modul-Attributen auf die Attribute des zu erzeugenden LDAP-Objektes definiert.
 
Dabei wird die Klasse '''univention.admin.mapping.mapping''' verwendet, die mit der Funktion '''register()''' eine einfache Möglichkeit bietet, für die einzelnen Attribute Abbildungen zu registrieren.
 
Das erste Argument der Funktion ist der Name des Modul-Attributs und das zweite der Name des LDAP-Attribute.
 
Mit den folgenden zwei Argument der Funktion '''register()''' können Abbildungsfunktionen für die Konvertierung von der Modul-Attributen zum LDAP-Attribute und vice versa angegeben werden.
 
<syntaxhighlight lang=python>mapping=univention.admin.mapping.mapping()
 
mapping.register('name', 'cn', None, \
 
                univention.admin.mapping.ListToString)
 
mapping.register('active', 'testPhoneActive', boolToString, stringToBool)
 
mapping.register('protocol', 'testPhoneProtocol', None, \
 
                univention.admin.mapping.ListToString)
 
mapping.register('ip', 'testPhoneIP', None, \
 
                univention.admin.mapping.ListToString)
 
mapping.register('priuser', 'testPhonePrimaryUser', \
 
                None, univention.admin.mapping.ListToString)
 
mapping.register('users', 'testPhoneUsers')
 
</syntaxhighlight>
 
Abschließend muss für das Modul noch eine Klasse ''object'' definiert werden, die den in Abschnitt [[#Aufbau eines Moduls]] definierten Vorgaben entspricht. Für das IP-Telefon würde die Klasse folgendermaßen aussehen:
 
<syntaxhighlight lang=python>class object(univention.admin.handlers.simpleLdap):
 
    module=module
 
 
 
    def __init__(self, co, lo, position, dn='', superordinate=None,
 
            arg=None):
 
        global mapping
 
        global property_descriptions
 
        self.co = co
 
        self.lo = lo
 
        self.dn = dn
 
        self.position = position
 
        self._exists = 0
 
        self.mapping = mapping
 
        self.descriptions = property_descriptions
 
        univention.admin.handlers.simpleLdap.__init__(self, co, lo, \
 
    position, dn, superordinate)
 
 
 
    def exists(self):
 
        return self._exists
 
 
 
    def open(self):
 
        univention.admin.handlers.simpleLdap.open(self)
 
        self.save()
 
 
 
    def _ldap_pre_create(self):
 
        self.dn = '%s=%s,%s' % (mapping.mapName('name'), mapping.mapValue
 
                ('name', self.info['name']), self.position.getDn())
 
 
 
    def _ldap_addlist(self):
 
        return [('objectClass', ['top', 'testPhone' ])]
 
</syntaxhighlight>
 
Damit auch nach Objekten, die dieses Modul verwaltet, gesucht werden kann gibt es noch zwei Funktionen '''lookup()''' und '''identify()'''.
 
Die hier vorgegebenen Funktionen sollten für einfache LDAP-Objekte, die durch eine einzelne '''objectClass''' identifiziert werden können, ausreichen.
 
Für eigene LDAP-Objekte müsste die Objektklasse '''testPhone''' ersetzt werden.
 
<syntaxhighlight lang=python>def lookup(co, lo, filter_s, base='', superordinate=None, scope='sub',
 
        unique=False, required=False, timeout=-1, sizelimit=0):
 
    filter = univention.admin.filter.conjunction('&', [
 
            univention.admin.filter.expression('objectClass', \
 
    'testPhone'),])
 
 
 
    if filter_s:
 
        filter_p = univention.admin.filter.parse(filter_s)
 
        univention.admin.filter.walk(filter_p,
 
                univention.admin.mapping.mapRewrite, arg=mapping)
 
        filter.expressions.append(filter_p)
 
 
 
    res = []
 
    for dn in lo.searchDn(unicode(filter), base, scope, unique, required,
 
            timeout, sizelimit):
 
        res.append(object(co, lo, None, dn))
 
    return res
 
</syntaxhighlight>
 
Diese Funktion prüft, ob das übergebene LDAP-Objekt zu der von diesem Modul verwalteten Menge gehört.
 
<syntaxhighlight lang=python>def identify(dn, attr, canonical=0):
 
    return 'testPhone' in attr.get('objectClass', [])
 
</syntaxhighlight>
 
 
 
== LDAP-Schema-Erweiterung für das Beispiel-Modul  ==
 
 
 
Bevor das entwickelte Modul für den Univention Directory Manager genutzt werden kann, muss dem LDAP-Server erst noch die neue Objektklasse, in diesem Fall '''testPhone''', zusammen mit ihren Attributen bekannt gemacht werden. Solche Objektdefinitionen werden bei LDAP über sogenannte Schemata definiert, die in Dateien beschrieben werden, die wie folgt aussehen:
 
<pre>attributetype ( 1.3.6.1.4.1.10176.9999.1.1 NAME 'testPhoneActive'
 
    DESC 'state of the IP phone'
 
    EQUALITY caseIgnoreIA5Match
 
    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
 
 
 
attributetype ( 1.3.6.1.4.1.10176.9999.1.2 NAME 'testPhoneProtocol'
 
    DESC 'The supported VoIP protocol'
 
    EQUALITY caseExactIA5Match
 
    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
 
 
 
attributetype ( 1.3.6.1.4.1.10176.9999.1.3 NAME 'testPhoneIP'
 
    DESC 'The IP address of the phone'
 
    EQUALITY caseExactIA5Match
 
    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
 
 
 
attributetype ( 1.3.6.1.4.1.10176.9999.1.4 NAME 'testPhonePrimaryUser'
 
    DESC 'The primary user of the phone'
 
    EQUALITY caseIgnoreIA5Match
 
    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
 
 
 
attributetype ( 1.3.6.1.4.1.10176.9999.1.5 NAME 'testPhoneUsers'
 
    DESC 'A list of other users allowed to use the phone'
 
    EQUALITY caseIgnoreIA5Match
 
    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
 
 
 
objectclass ( 1.3.6.1.4.1.10176.9999.2.1 NAME 'testPhone'
 
    DESC 'IP Phone'
 
    SUP top  STRUCTURAL
 
    MUST ( cn $ testPhoneActive $ testPhoneProtocol $ \
 
    testPhoneIP $ testPhonePrimaryUser )
 
    MAY ( testPhoneUsers )
 
    )
 
</pre>
 
Eine ausführliche Dokumentation zur Erstellung von LDAP Schema-Dateien ist auf der Webseite des OpenLDAP Projektes (http://www.openldap.org/) zu finden und ist nicht Schwerpunkt dieser Dokumentation.
 
 
 
= Einrichtung des Moduls  =
 
 
 
Als letzter Schritt müssen das Python-Modul und das LDAP-Schema installiert werden. Im folgenden werden diese beiden Schritte dokumentiert.
 
 
 
Das Python-Modul muss in das Verzeichnis ''/usr/lib/python2.4/site-packages/univention/admin/handlers/'' kopiert werden, damit der Univention Directory Manager es findet. In diesem Verzeichnis ist ein Unterverzeichnis anzulegen. Es sollte dem ersten Teil des Modulnamens entsprechen. Wenn der Name des Moduls beispielsweise '''test/ip-phone''' ist, dann sollte das Verzeichnis '''test''' heißen. Das Python-Modul muss dann in dieses Verzeichnis kopiert werden. Idealerweise wird ein UDM-Modul wie in ein eigenes Paket integriert. Eine Dokumentation dazu findet sich im Artikel [[Paketierung von Software für UCS]]. Das neu angelegte Paket wird dann beim Aufruf von
 
<pre>univention-directory-manager modules
 
</pre>
 
mit angezeigt.
 
 
 
Die Datei, die das LDAP-Schema enthält, kann im Prinzip in ein beliebiges Verzeichnis kopiert werden. Die Schema-Definitionen von Univention werden beispielsweise in dem Verzeichnis ''/usr/share/univention-ldap/schema/'' abgelegt. Damit der LDAP-Server dieses Schema findet muss es in die Konfigurationsdatei ''/etc/ldap/slapd.conf'' eingebunden werden. Da diese Datei unter der Kontrolle von Univention Configuration Registry steht, sollte nicht direkt die Datei editiert werden, sondern ein Univention Configuration Registry-Template erstellt werden (siehe {{Handbuch UCS|UCS-Handbuch}}).
 
 
 
= Download des Beispielcodes =
 
 
 
Die aktuelle Version des Beispielcodes ist im Verzeichnis [https://forge.univention.org/websvn/listing.php?repname=dev&path=%2Ftrunk%2Fucs%2Fmanagement%2Funivention-directory-manager-module-example%2F univention-directory-manager-module-example] im SVN einsehbar. Das Verzeichnis enthält ein Quellpaket im Debian-Format, aus dem bei Paketbau zwei Binärpakete erzeugt werden, ein Schema-Paket, das auf dem Master installiert werden muss und das Paket mit UDM-Modul selbst. Der Beispielcode enthält auch ein Skript ''ip-phone-tool'', das beispielhaft die Verwendung der UDM Python API in einem Python-Skript zeigt.
 
 
 
[[Category:Entwicklung]]
 
[[Category:UDM]]
 

Latest revision as of 11:39, 3 November 2021

Hinweis

Dieser Artikel wurde in die offizielle Dokumentation integriert, überarbeitet und angepasst. Der Inhalt dieser Seite ist demnach veraltet und wurde entfernt.

Personal tools