4. Szenario: Eine Quelle, partielle Importe#

Das Szenario wird auch single source, parial import - SiSoPi genannt.

Neu in Version 4.3v5: Seit UCS@school 4.3 v5 wird ein Szenario unterstützt, in dem es eine Quelldatenbank mit den Mitgliedern aller Schulen gibt, bei dem aber nicht zentral, sondern an allen Schulen einzeln importiert wird.

Der Import ist sowohl über die Kommandozeile als auch mit dem UMC-Modul, welches im Hintergrund die Import-HTTP-API verwendet, möglich.

4.1. Features#

  • OU übergreifende Benutzerkonten (ein Benutzer kann in mehreren Schulen sein)

  • Jede Schule kann ihre Benutzer einzeln und zu einem beliebigen Zeitpunkt importieren.

4.2. Voraussetzungen#

  • Eine Datenbasis, die alle Benutzer mit je einem domänenweit eindeutigen Schlüssel (record_uid), enthält.

  • Die Quelldatenbank exportiert separate CSV Dateien pro Schule und Benutzerrolle.

  • Die Importe können in zufälliger Reihenfolge stattfinden. Es ist möglich, dass beim Verschieben eines Benutzers dieser zuerst in einer Schule gelöscht und in einer anderen Schule später angelegt wird. Das Benutzerkonto darf in der Zwischenzeit nicht gelöscht werden.

4.3. Implementierung#

Um das Verschieben eines Benutzers von Schule A nach Schule B in zwei Schritten zu ermöglichen - einschließlich der Möglichkeit, dass der Benutzer zuerst in Schule A gelöscht und später in Schule B angelegt wird - wird eine temporäre Schule verwendet: die sogenannte limbo_ou. Es handelt sich dabei um eine gewöhnliche Schule (OU), deren Name konfigurierbar ist (Standard ist limbo).

Benutzerkonten, die von ihrer letzten oder einzigen Schule (A) entfernt wurden, werden

  1. sofort deaktiviert und

  2. in die temporäre Schule (limbo_ou) verschoben.

Soll ein Benutzer während eines Imports (an Schule B) erstellt werden, existiert jedoch bereits ein Konto mit dessen record_uid in der limbo OU, so wird dieses Konto stattdessen von dort zur Schule „B“ verschoben und das Konto reaktiviert.

4.4. Installation und Konfiguration#

Der Inhalt von /usr/share/ucs-school-import/configs/user_import_sisopi.json muss der Importkonfiguration (in /var/lib/ucs-school-import/configs/user_import.json) hinzugefügt werden.

Folgende Einstellungen sollten angepasst werden:

  • deletion_grace_period:deactivation muss 0 sein.

  • deletion_grace_period:deletion sollte (deutlich) größer als 0 sein. Es sollte die maximale Anzahl an Tagen sein, die ein Import von zwei Schulen auseinander liegen kann. Das ist die Zeit, die ein Konto in der limbo OU verbringt, bevor es endgültig gelöscht wird.

Der Name der limbo OU kann mit der Einstellung limbo_ou gesetzt werden.

Darüber hinaus muss die Univention Configuration Registry-Variable ucsschool/import/http_api/set_source_uid auf no gesetzt und der Import-HTTP-API-Server neu gestartet werden:

$ ucr set ucsschool/import/http_api/set_source_uid=no
$ service ucs-school-import-http-api restart

4.5. Beispielaufbau#

Für den Testaufbau werden zunächst zwei reguläre und die temporäre Schule erstellt:

$ /usr/share/ucs-school-import/scripts/create_ou schuleA
$ /usr/share/ucs-school-import/scripts/create_ou schuleB
$ /usr/share/ucs-school-import/scripts/create_ou limbo

Nach dem Sichern der ursprünglichen Konfiguration wird die SiSoPi-Konfiguration aktiviert. Üblicherweise wird die neue Konfiguration anschließend an die individuellen Erfordernisse angepasst. Für den Testaufbau wurden csv, scheme und source_uid hinzugefügt.

$ cp -v /var/lib/ucs-school-import/configs/user_import.json{,.bak}
$ cp -v /usr/share/ucs-school-import/configs/user_import_sisopi.json /var/lib/ucs-school-import/configs/user_import.json
$ $EDITOR /var/lib/ucs-school-import/configs/user_import.json

Tipp

Mit folgendem Befehl kann die syntaktische Korrektheit der JSON-Datei geprüft werden. Wenn die Datei syntaktisch korrekt ist, wird ihr Inhalt ausgegeben, bei einem Fehler wird stattdessen dieser angezeigt.

$ cat /var/lib/ucs-school-import/configs/user_import.json | python3 -m json.tool
{
    "classes": {
        "user_importer": "ucsschool.importer.mass_import.sisopi_user_import.SingleSourcePartialUserImport"
    },
    "configuration_checks": [
        "defaults",
        "sisopi"
    ],
    "csv": {
        "mapping": {
            "Beschreibung": "description",
            "EMail": "email",
            "Klassen": "school_classes",
            "Nachname": "lastname",
            "Schule": "school",
            "Telefon": "phone",
            "Vorname": "firstname"
        }
    },
    "deletion_grace_period": {
        "deactivation": 0,
        "deletion": 90
    },
    "limbo_ou": "limbo",
    "scheme": {
        "record_uid": "<firstname>.<lastname>",
        "username": {
            "default": "<:umlauts><firstname>.<lastname><:lower>[COUNTER2]"
        }
    },
    "source_uid": "Test"
}
$ ucr set ucsschool/import/http_api/set_source_uid=no
$ service ucs-school-import-http-api restart

Nun wird für jede Schule eine zu importierende CSV Datei erzeugt:

$ /usr/share/ucs-school-import/scripts/ucs-school-testuser-import \
  --csvfile test_users_A-1.csv \
  --nostart \
  --httpapi \
  --teachers 4 \
  --classes 1 \
  --inclasses 1 \
  --schools 1 \
  --verbose \
  schuleA
$ /usr/share/ucs-school-import/scripts/ucs-school-testuser-import \
  --csvfile test_users_B-1.csv \
  --nostart \
  --httpapi \
  --teachers 4 \
  --classes 1 \
  --inclasses 1 \
  --schools 1 \
  --verbose \
  schuleB
$ cat test_users_A-1.csv

"Schule","Vorname","Nachname","Klassen","Beschreibung","Telefon","EMail"
"schuleA","Yola","Lenz","1a","A teacher.","+74-686-445678",""
"schuleA","Iphigenie","Lemgo","1a","A teacher.","+63-727-768248",""
"schuleA","Felix","Adams","1a","A teacher.","+15-263-530094",""
"schuleA","Radomila","Meygger","1a","A teacher.","+11-364-599925",""
$ cat test_users_B-1.csv

"Schule","Vorname","Nachname","Klassen","Beschreibung","Telefon","EMail"
"schuleB","Stan","Kinker","1a","A teacher.","+91-299-143803",""
"schuleB","Jonathan","Heuelmann","1a","A teacher.","+74-656-351455",""
"schuleB","Ingward","Bohnenkämper","1a","A teacher.","+24-351-217608",""
"schuleB","Vincent","Störtländer","1a","A teacher.","+67-303-103581",""

Der Import würde regulär über das UMC-Modul statt finden, wird für diesen Test aber an der Kommandozeile durchgeführt. Beim Import an den beiden Schulen werden je vier Lehrer angelegt:

$ /usr/share/ucs-school-import/scripts/ucs-school-user-import \
  --verbose \
  --user_role teacher \
  --infile test_users_A-1.csv \
  --school \
  schuleA

------ User import statistics ------
Read users from input data: 4
Created ImportTeacher: 4
  ['yola.lenz', 'iphigenie.lemgo', 'felix.adams', 'radomila.meygger']
Modified ImportTeacher: 0
Deleted ImportTeacher: 0
Errors: 0
------ End of user import statistics ------

$ /usr/share/ucs-school-import/scripts/ucs-school-user-import \
  --verbose \
  --user_role teacher \
  --infile test_users_B-1.csv \
  --school \
  schuleB

------ User import statistics ------
Read users from input data: 4
Created ImportTeacher: 4
  ['stan.kinker', 'jonathan.heuelman', 'ingward.bohnenkae', 'vincent.stoertlae']
Modified ImportTeacher: 0
Deleted ImportTeacher: 0
Errors: 0
------ End of user import statistics ------

Nun soll yola.lenz von schuleA nach schuleB verschoben werden. Dazu wird eine CSV Datei test_users_A-2.csv ohne die Zeile mit "Yola","Lenz" aus test_users_A-1.csv erzeugt, sowie eben diese Zeile in test_users_B-2.csv eingefügt. Dort muss schuleA noch durch schuleB ersetzt werden. Die neuen Dateien sehen wie folgt aus:

$ cat test_users_A-2.csv

"Schule","Vorname","Nachname","Klassen","Beschreibung","Telefon","EMail"
"schuleA","Iphigenie","Lemgo","1a","A teacher.","+63-727-768248",""
"schuleA","Felix","Adams","1a","A teacher.","+15-263-530094",""
"schuleA","Radomila","Meygger","1a","A teacher.","+11-364-599925",""
$ cat test_users_B-2.csv

"Schule","Vorname","Nachname","Klassen","Beschreibung","Telefon","EMail"
"schuleB","Stan","Kinker","1a","A teacher.","+91-299-143803",""
"schuleB","Jonathan","Heuelmann","1a","A teacher.","+74-656-351455",""
"schuleB","Ingward","Bohnenkämper","1a","A teacher.","+24-351-217608",""
"schuleB","Vincent","Störtländer","1a","A teacher.","+67-303-103581",""
"schuleB","Yola","Lenz","1a","A teacher.","+74-686-445678",""

Beim Import an schuleA wird yola.lenz scheinbar gelöscht. Tatsächlich wird sie aber in die Schule limbo verschoben:

$ udm users/user list --filter uid=yola.lenz | egrep 'DN|school:'
DN: uid=yola.lenz,cn=lehrer,cn=users,ou=schuleA,<base dn>
  school: schuleA

$ /usr/share/ucs-school-import/scripts/ucs-school-user-import \
  --verbose \
  --user_role teacher \
  --infile test_users_A-2.csv \
  --school \
  schuleA

[..]
------ Deleting 1 users... ------
Removing ImportTeacher(name='yola.lenz', school='schuleA', dn='uid=yola.lenz,cn=lehrer,cn=users,ou=schuleA,<base dn>') from school 'schuleA'...
Moving ImportTeacher(name='yola.lenz', school='schuleA', dn='uid=yola.lenz,cn=lehrer,cn=users,ou=schuleA,<base dn>') to limbo school u'limbo'.
[..]

------ User import statistics ------
Read users from input data: 3
Modified ImportTeacher: 3
  ['iphigenie.lemgo', 'felix.adams', 'radomila.meygger']
Deleted ImportTeacher: 1
  ['yola.lenz']
Modified ImportTeacher: 0
Deleted ImportTeacher: 0
Errors: 0
------ End of user import statistics ------

$ udm users/user list --filter uid=yola.lenz | egrep 'DN|school:'
DN: uid=yola.lenz,cn=lehrer,cn=users,ou=limbo,<base dn>
  school: limbo

Beim Import an schuleB wird yola.lenz aus der Schule limbo dort hin verschoben:

$ /usr/share/ucs-school-import/scripts/ucs-school-user-import \
  --verbose \
  --user_role teacher \
  --infile test_users_B-2.csv \
  --school \
  schuleB

[..]
User ImportTeacher(name='yola.lenz', school='limbo', dn='uid=yola.lenz,cn=lehrer,cn=users,ou=limbo,<base dn>') is in limbo school u'limbo', moving to 'schuleB'.
Reactivating ImportTeacher(name=None, school='schuleB', dn=None)...
User will change school. Previous school: 'limbo', new school: 'schuleB'.
Moving ImportTeacher(name='yola.lenz', school='limbo', dn='uid=yola.lenz,cn=lehrer,cn=users,ou=limbo,<base dn>') from school 'limbo' to 'schuleB'...
[..]

------ User import statistics ------
Read users from input data: 5
Modified ImportTeacher: 5
  ['stan.kinker', 'jonathan.heuelman', 'ingward.bohnenkae', 'vincent.stoertlae']
  ['yola.lenz']
Modified ImportTeacher: 0
Deleted ImportTeacher: 0
Errors: 0
------ End of user import statistics ------

$ udm users/user list --filter uid=yola.lenz | egrep 'DN|school:'
DN: uid=yola.lenz,cn=lehrer,cn=users,ou=schuleB,<base dn>
  school: schuleB

Der umgekehrte Fall, in dem ein zu verschiebender Benutzer an der Zielschule importiert wird, bevor er an der ursprünglichen Schule gelöscht wird, kann z.B. folgendermaßen erzeugt werden: Die Zeile von "Iphigenie","Lemgo" wird in das CSV der schuleB kopiert, wobei die Spalte "Schule" angepasst und aus dem CSV der schuleA entfernt wird. Der Import wird nun an schuleB vor schuleA durchgeführt. Zwischendurch wird die Lehrerin Mitglied beider Schulen sein. Das Benutzerkonto würde sich folgendermaßen ändern:

# vor dem Import:
DN: uid=iphigenie.lemgo,cn=lehrer,cn=users,ou=schuleA,<base dn>
  school: schuleA

# nach dem Import an schuleB:
DN: uid=iphigenie.lemgo,cn=lehrer,cn=users,ou=schuleA,<base dn>
  school: schuleA
  school: schuleB

# nach dem Import an schuleA:
DN: uid=iphigenie.lemgo,cn=lehrer,cn=users,ou=schuleB,<base dn>
  school: schuleB