Simplified everything

This commit is contained in:
2026-06-12 14:49:11 +02:00
parent ef17a370fc
commit 3d4a3e29ad
2 changed files with 81 additions and 76 deletions
+3 -1
View File
@@ -14,7 +14,9 @@ services:
# Beginning of name of the downloaded archivebroup information file
- BEGIN_NAME_GROUPINFO_JSON_FILE=size_by_ownergroup_and_number_of_copies_
- ERROR_LOGFILE=error.log
# To connect with AD
# AD variables
- AD_SERVER=d.psi.ch
- AD_SEARCH_BASE=DC=d,DC=psi,DC=ch
- AD_USER=${AD_USER}
- AD_PASSWORD=${AD_PASSWORD}
ports:
+78 -75
View File
@@ -1,16 +1,22 @@
"""
Dieses Skript liest eine JSON-Datei mit Gruppeninformationen ein und fragt für jede Gruppe
das zugehörige Department im Active Directory (AD) ab. Department ist in Form der entsprechenden
Zahl (z.B. 6000 fuer CPS) angegeben.
das zugehörige Department im Active Directory (AD) ab. Das Department ist in Form der entsprechenden
Zahl (z.B. 6000 für CPS) angegeben.
Es unterscheidet dabei zwischen zwei Hauptfällen:
Die JSON-Datei ist eine Liste von Dictionaries, welche folgende Form haben:
{"ownerGroup":"p18973","copies":"oneCopyBig","size":"6.85468397803E11","packedSize":"6.8563388416E11"}
Es werden zwei Hauptfälle unterscheidet:
1. a-Gruppen (z.B. a-groupname): Diese Gruppen enthalten direkt das Attribut 'department'.
2. p-Gruppen (z.B. p12345): Diese Gruppen sind jeweils einer Beamline zugeordnet. Jede
Beamline besitzt ein 'department' Attribut. Das Skripte ermittelt zuerst die Beamline
und danach das Department. Die Zuordnung Beamline -> Department wird gecached, was
AD Anfragen spart.
Beamline besitzt ein 'department'-Attribut. Das Skript ermittelt zuerst die Beamline
und danach das Department. Die Zuordnung von Beamline zu Department wird gecached,
um AD-Anfragen zu minimieren, da es viele p-Gruppen aber nur wenige Beamlines gibt.
Die Ergebnisse (oder Fehlermeldungen für nicht zuordenbare Gruppen) werden ausgegeben.
Am Schluss wird eine Liste der eingelesenen Dictionaries ausgegeben, die allerdings durch
einen 'department' Eintrag ergänzt wurden. Bei den p-Gruppen wird zusätzlich noch ein 'beamline'
Eintrag hinzugefügt.
"""
import json
import os
@@ -21,42 +27,45 @@ from ldap3 import ALL, Connection, Server
# --- Konfiguration ---
AD_SERVER = 'd.psi.ch'
AD_SERVER = os.environ.get('AD_SERVER')
AD_USER = os.environ.get('AD_USER')
AD_PASSWORD = os.environ.get('AD_PASSWORD')
AD_SEARCH_BASE = 'DC=d,DC=psi,DC=ch'
AD_SEARCH_BASE = os.environ.get('AD_SEARCH_BASE')
FILE_PATH = os.environ.get('GROUPINFO_JSON_LNK_NAME_WITH_PATH')
# FILE_PATH = "/data/" + os.environ.get('GROUPINFO_JSON_LNK_NAME')
def lookup_ad():
try:
# 1. Datenbasis aus lokaler JSON-Datei laden
# Datenbasis aus der via Umgebungsvariable definierten JSON-Datei laden
if FILE_PATH is None:
print("ERROR: Environment variable GROUPINFO_JSON_LNK_NAME_WITH_PATH is not set.", file=sys.stderr, flush=True)
print("ERROR: Environment variable GROUPINFO_JSON_LNK_NAME_WITH_PATH is not set.",
file=sys.stderr, flush=True)
return
if not os.path.exists(FILE_PATH):
print(f"ERROR: Datei {FILE_PATH} nicht gefunden!", file=sys.stderr, flush=True)
print(f"ERROR: File {FILE_PATH} not found!", file=sys.stderr, flush=True)
return
with open(FILE_PATH, 'r') as f:
items = json.load(f)
print(f"INFO: {len(items)} Einträge geladen.", file=sys.stderr, flush=True)
print(f"INFO: {len(items)} entries loaded.", file=sys.stderr, flush=True)
# 2. AD-Verbindung initialisieren (SSL, Port 636)
# AD-Verbindung via SSL (Port 636) initialisieren
server = Server(AD_SERVER, port=636, use_ssl=True, get_info=ALL, connect_timeout=10)
with Connection(server, user=AD_USER, password=AD_PASSWORD, auto_bind=True) as conn:
# Interner Cache zur Speicherung der Zuordnung von p-Gruppe zu Beamline
beamline2department_cache = {}
# Cache für die Zuordnung von Beamline zu Department
beamline_department_cache = {}
count = 0
for item in items:
count += 1
group_name = item.get('ownerGroup', '')
# --- Fall A: Direkte a-Gruppen ---
# --- Fall A: direkte a-Gruppen ---
if group_name.startswith('a-'):
search_filter = f'(&(objectClass=group)(cn={group_name}))'
conn.search(
@@ -71,86 +80,80 @@ def lookup_ad():
else:
item['department'] = "not_found"
# --- Fall B: Zweistufige p-Gruppen ---
# --- Fall B: zweistufige p-Gruppen ---
elif group_name.startswith('p') and group_name[1:2].isdigit():
if group_name in beamline2department_cache:
beamline = beamline2department_cache[group_name]
else:
# Stufe 1: memberOf-Attribut des p-Objekts abfragen, um übergeordnete Gruppe zu finden
p_filter = f'(cn={group_name})'
conn.search(
search_base=AD_SEARCH_BASE,
search_filter=p_filter,
attributes=['memberOf']
)
beamline = "not_found" # Initialisierung
beamline = "not_found"
if conn.entries and 'memberOf' in conn.entries[0] and conn.entries[0].memberOf:
# Suche spezifisch nach dem Beamline-Serviceordner
selected_dn = None
for dn in conn.entries[0].memberOf:
dn_str = str(dn)
if 'OU=Beamlines,OU=Experiment,OU=IT' in dn_str:
selected_dn = dn_str
break
# Fallback, falls der spezifische Ordner nicht in memberOf gefunden wurde
if selected_dn is None:
selected_dn = str(conn.entries[0].memberOf[0])
# Zielgruppe (CN) aus dem Distinguished Name extrahieren
try:
beamline = selected_dn.split(',')[0].split('=')[1]
beamline2department_cache[group_name] = beamline
except IndexError:
print(f"WARN: Format von DN '{selected_dn}' unerwartet.", file=sys.stderr, flush=True)
beamline = "format_error"
item['beamline'] = beamline
# Stufe 2: Department anhand der ermittelten beamline (aus memberOf) bestimmen
search_filter = f'(&(objectClass=group)(cn={beamline}))'
# Stufe 1: 'memberOf' der p-Gruppe abfragen, um die zugehörige Beamline-Gruppe zu finden
p_filter = f'(cn={group_name})'
conn.search(
search_base=AD_SEARCH_BASE,
search_filter=search_filter,
attributes=['department']
search_filter=p_filter,
attributes=['memberOf']
)
if conn.entries:
res = str(conn.entries[0].department) if 'department' in conn.entries[0] else "no-dept"
item['department'] = res
if conn.entries and 'memberOf' in conn.entries[0] and conn.entries[0].memberOf:
selected_dn = None
for dn in conn.entries[0].memberOf:
dn_str = str(dn)
if 'OU=Beamlines,OU=Experiment,OU=IT' in dn_str:
selected_dn = dn_str
break
if selected_dn is None:
selected_dn = str(conn.entries[0].memberOf[0])
try:
beamline = selected_dn.split(',')[0].split('=')[1]
except IndexError:
print(f"WARN: Format von DN '{selected_dn}' unerwartet.", file=sys.stderr, flush=True)
beamline = "format_error"
item['beamline'] = beamline
# Stufe 2: Department für die ermittelte Beamline abfragen (mit Caching)
if beamline in beamline_department_cache:
item['department'] = beamline_department_cache[beamline]
else:
item['department'] = "not_found"
search_filter = f'(&(objectClass=group)(cn={beamline}))'
conn.search(
search_base=AD_SEARCH_BASE,
search_filter=search_filter,
attributes=['department']
)
if conn.entries:
res = str(conn.entries[0].department) if 'department' in conn.entries[0] else "no-dept"
beamline_department_cache[beamline] = res # Department im Cache speichern
item['department'] = res
else:
beamline_department_cache[beamline] = "not_found"
item['department'] = "not_found"
# --- Fall C: Abweichende Schemata ---
else:
print(f"WARN: Unbekanntes Gruppen-Schema: {group_name}", file=sys.stderr)
print(f"WARN: Unbekanntes Gruppen-Schema: {group_name}", file=sys.stderr, flush=True)
item['department'] = "unknown_schema"
continue
# Fortschritt alle 500 Einträge im stderr protokollieren
if count % 500 == 0:
print(f"INFO: progress {count}/{len(items)}...", file=sys.stderr, flush=True)
# Fortschritt alle 500 Einträge im stderr protokollieren
if count % 500 == 0:
print(f"Fortschritt: {count}/{len(items)}...", file=sys.stderr, flush=True)
# Kompletter JSON-Output (momentan auskommentiert):
print(json.dumps(items))
# 3. Finales Ergebnis auswerten
# Kompletten JSON-Output drucken (momentan auskommentiert):
# print(json.dumps(items))
# Test-Output: Alle nicht gefundenen bzw. leeren Zuweisungen drucken
# Alle Gruppen mit leerem Department-Feld ausgeben
for item in items:
# Debug (auskommentiert):
# print(f"group: {item['ownerGroup']} -> {item['department']} -> type: {type(item['department'])}")
if item['department'] == "[]":
if item.get('department') == "[]":
if 'beamline' in item:
print(f"LEER -> {item['ownerGroup']} -> {item['beamline']}")
print(f"LEER -> {item['ownerGroup']} (Beamline: {item['beamline']})", file=sys.stderr, flush=True)
else:
print(f"LEER -> {item['ownerGroup']} has no beamline")
print(f"LEER -> {item['ownerGroup']} (hat keine Beamline)", file=sys.stderr, flush=True)
except Exception as e:
print(f"ERROR: {str(e)}", file=sys.stderr, flush=True)
# Bei Fehler optional Items als JSON ausgeben (auskommentiert)
# if 'items' in locals():
# print(json.dumps(items))
if __name__ == "__main__":