Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
292ea622a7 |
No files matched your search
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "tesm"
|
name = "tesm"
|
||||||
version = "2.8.0"
|
version = "2.8.1"
|
||||||
description = "TESM -- Verwaltung und Ueberwachung von PoE-Endgeraeten und Aruba-Switchen"
|
description = "TESM -- Verwaltung und Ueberwachung von PoE-Endgeraeten und Aruba-Switchen"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|||||||
@@ -16,10 +16,18 @@ eingelesen. Ein eigener Schreibpfad fuer Altdaten waere eine zweite Stelle, an
|
|||||||
der Fremdeingaben in die Datenbank gelangen -- und die zweite Stelle ist die,
|
der Fremdeingaben in die Datenbank gelangen -- und die zweite Stelle ist die,
|
||||||
die niemand pflegt.
|
die niemand pflegt.
|
||||||
|
|
||||||
Uebersetzt werden Clients, Switche, Zugangsdaten und die DHCP-Konfiguration.
|
Uebersetzt werden Clients, Switche, Zugangsdaten, die DHCP-Konfiguration und
|
||||||
Was in der Datei sonst noch steht, wird nicht stillschweigend verworfen,
|
die Freigabe-Zuordnungen. Was in der Datei sonst noch steht, wird nicht
|
||||||
sondern im Bericht benannt -- damit sichtbar bleibt, was von Hand nachzuziehen
|
stillschweigend verworfen, sondern im Bericht benannt -- damit sichtbar bleibt,
|
||||||
ist.
|
was von Hand nachzuziehen ist.
|
||||||
|
|
||||||
|
Die Freigaben standen bis 2.8.0 auf der Liste "von Hand nachtragen", obwohl es
|
||||||
|
dafuer keinen Grund gab: der Vorgaenger fuehrt sie in
|
||||||
|
``ldap_fileshare_mappings`` mit denselben vier Angaben, die diese Anwendung in
|
||||||
|
``fileshare_mappings`` braucht (Verzeichnisgruppe, deren Name, Beschriftung,
|
||||||
|
UNC-Pfad), und einen Import dafuer gibt es hier ohnehin. Was sich uebernehmen
|
||||||
|
laesst, soll man nicht abtippen muessen -- ein UNC-Pfad von Hand ist ein
|
||||||
|
Tippfehler, der erst auffaellt, wenn jemand die Freigabe braucht.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -35,8 +43,10 @@ from typing import Any
|
|||||||
LEGACY_KDF_ITERATIONS = 390000
|
LEGACY_KDF_ITERATIONS = 390000
|
||||||
LEGACY_APP_TAG = "tesm"
|
LEGACY_APP_TAG = "tesm"
|
||||||
|
|
||||||
#: Diese Abschnitte werden uebersetzt.
|
#: Diese Abschnitte werden uebersetzt. "ldap" nur zum Teil -- daraus kommen die
|
||||||
SUPPORTED = ("credentials", "switches", "devices", "dhcp")
|
#: Freigabe-Zuordnungen, waehrend Servereinstellungen und Gruppen-Zuordnungen
|
||||||
|
#: dort bleiben (siehe KNOWN_UNSUPPORTED).
|
||||||
|
SUPPORTED = ("credentials", "switches", "devices", "dhcp", "ldap")
|
||||||
|
|
||||||
#: Was der alte Export sonst enthaelt. Wird gemeldet, nicht uebersetzt.
|
#: Was der alte Export sonst enthaelt. Wird gemeldet, nicht uebersetzt.
|
||||||
KNOWN_UNSUPPORTED: dict[str, str] = {
|
KNOWN_UNSUPPORTED: dict[str, str] = {
|
||||||
@@ -46,9 +56,12 @@ KNOWN_UNSUPPORTED: dict[str, str] = {
|
|||||||
"Zweck. Konten neu anlegen."
|
"Zweck. Konten neu anlegen."
|
||||||
),
|
),
|
||||||
"groups": "Gruppen und Rechte: die Rechteschlüssel wurden neu geschnitten.",
|
"groups": "Gruppen und Rechte: die Rechteschlüssel wurden neu geschnitten.",
|
||||||
|
# Nur noch der Teil, der wirklich nicht uebertragbar ist: Server, Bind-Konto
|
||||||
|
# und die Gruppen-Zuordnung (dort zeigen die Namen auf Gruppen, deren
|
||||||
|
# Rechteschluessel neu geschnitten wurden). Die Freigaben kommen mit.
|
||||||
"ldap": (
|
"ldap": (
|
||||||
"Verzeichnisdienst: Einstellungen, Gruppen- und Freigabe-Zuordnungen von "
|
"Verzeichnisdienst: Server, Bind-Konto und die Gruppen-Zuordnung von Hand "
|
||||||
"Hand übertragen."
|
"übertragen. Die Freigabe-Zuordnungen wurden übernommen."
|
||||||
),
|
),
|
||||||
"nginx": "Webserver: Domain, Ports und Zertifikat von Hand übertragen.",
|
"nginx": "Webserver: Domain, Ports und Zertifikat von Hand übertragen.",
|
||||||
"logs": "Auditlog: bleibt bewusst beim alten System.",
|
"logs": "Auditlog: bleibt bewusst beim alten System.",
|
||||||
@@ -272,6 +285,48 @@ def _devices(items: Any, credentials: list[dict[str, Any]], report: Report) -> l
|
|||||||
return ergebnis
|
return ergebnis
|
||||||
|
|
||||||
|
|
||||||
|
def _freigaben(data: Any, report: Report) -> list[dict[str, Any]]:
|
||||||
|
"""Freigabe-Zuordnungen aus dem Abschnitt "ldap" des alten Exports.
|
||||||
|
|
||||||
|
Der Vorgaenger legt sie unter ``ldap.fileshare_mappings`` ab; beim Lesen
|
||||||
|
einer Installation stehen sie unmittelbar in der Tabelle. Beides landet
|
||||||
|
hier, damit es nur eine Stelle gibt, die die Felder umbenennt.
|
||||||
|
"""
|
||||||
|
if isinstance(data, dict):
|
||||||
|
eintraege = _liste(data.get("fileshare_mappings"))
|
||||||
|
else:
|
||||||
|
eintraege = _liste(data)
|
||||||
|
ergebnis: list[dict[str, Any]] = []
|
||||||
|
ohne_pfad = 0
|
||||||
|
for eintrag in eintraege:
|
||||||
|
# "share_label"/"share_unc" heissen hier "label"/"unc" -- dieselbe
|
||||||
|
# Angabe, kuerzerer Name, weil die Tabelle schon "fileshare" heisst.
|
||||||
|
unc = str(eintrag.get("share_unc") or eintrag.get("unc") or "").strip()
|
||||||
|
gruppe = str(eintrag.get("ad_group_dn") or "").strip()
|
||||||
|
if not unc or not gruppe:
|
||||||
|
ohne_pfad += 1
|
||||||
|
continue
|
||||||
|
beschriftung = str(eintrag.get("share_label") or eintrag.get("label") or "").strip()
|
||||||
|
ergebnis.append(
|
||||||
|
{
|
||||||
|
"ad_group_dn": gruppe,
|
||||||
|
"ad_group_name": str(eintrag.get("ad_group_name") or "").strip() or gruppe,
|
||||||
|
# Ohne Beschriftung der letzte Teil des Pfades -- besser als
|
||||||
|
# eine Freigabe ohne Namen. Beide Trenner: der Vorgaenger
|
||||||
|
# speichert "//server/share", getippt wird "\server\share".
|
||||||
|
"label": beschriftung
|
||||||
|
or unc.replace(chr(92), "/").rstrip("/").rsplit("/", 1)[-1],
|
||||||
|
"unc": unc,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if ohne_pfad:
|
||||||
|
report.note(
|
||||||
|
f"{ohne_pfad} Freigabe-Zuordnung(en) ohne Verzeichnisgruppe oder UNC-Pfad "
|
||||||
|
"übersprungen."
|
||||||
|
)
|
||||||
|
return ergebnis
|
||||||
|
|
||||||
|
|
||||||
def _dhcp(data: Any, report: Report) -> tuple[dict[str, Any], dict[str, str]]:
|
def _dhcp(data: Any, report: Report) -> tuple[dict[str, Any], dict[str, str]]:
|
||||||
"""DHCP-Teil uebersetzen. Rueckgabe: ``(dhcp, settings)``."""
|
"""DHCP-Teil uebersetzen. Rueckgabe: ``(dhcp, settings)``."""
|
||||||
if not isinstance(data, dict):
|
if not isinstance(data, dict):
|
||||||
@@ -382,6 +437,10 @@ def convert(legacy: dict[str, Any]) -> tuple[dict[str, Any], Report]:
|
|||||||
if devices:
|
if devices:
|
||||||
payload["devices"] = devices
|
payload["devices"] = devices
|
||||||
|
|
||||||
|
freigaben = _freigaben(legacy.get("ldap") or legacy.get("fileshare"), report)
|
||||||
|
if freigaben:
|
||||||
|
payload["fileshare"] = freigaben
|
||||||
|
|
||||||
dhcp, settings = _dhcp(legacy.get("dhcp"), report)
|
dhcp, settings = _dhcp(legacy.get("dhcp"), report)
|
||||||
if dhcp:
|
if dhcp:
|
||||||
payload["dhcp"] = dhcp
|
payload["dhcp"] = dhcp
|
||||||
@@ -417,8 +476,8 @@ def convert(legacy: dict[str, Any]) -> tuple[dict[str, Any], Report]:
|
|||||||
|
|
||||||
if not payload:
|
if not payload:
|
||||||
raise LegacyTransferError(
|
raise LegacyTransferError(
|
||||||
"Die Datei enthält keine Clients, Switche, Zugangsdaten oder DHCP-Daten. "
|
"Die Datei enthält keine Clients, Switche, Zugangsdaten, DHCP-Daten oder "
|
||||||
"Beim Export im alten TESM diese Kategorien mitwählen."
|
"Freigaben. Beim Export im alten TESM diese Kategorien mitwählen."
|
||||||
)
|
)
|
||||||
return payload, report
|
return payload, report
|
||||||
|
|
||||||
@@ -634,6 +693,21 @@ def read_installation(root: Path) -> tuple[dict[str, Any], Report]:
|
|||||||
else:
|
else:
|
||||||
report.note("Tabelle 'credentials' fehlt in der alten Datenbank.")
|
report.note("Tabelle 'credentials' fehlt in der alten Datenbank.")
|
||||||
|
|
||||||
|
# Die Freigaben reisen im Abschnitt "ldap" -- genau dort, wo der alte
|
||||||
|
# Export sie auch hat. So liest die Uebersetzung beide Wege gleich.
|
||||||
|
if "ldap_fileshare_mappings" in vorhanden:
|
||||||
|
freigaben = [
|
||||||
|
dict(row)
|
||||||
|
for row in conn.execute(
|
||||||
|
"SELECT ad_group_dn, ad_group_name, share_label, share_unc "
|
||||||
|
"FROM ldap_fileshare_mappings"
|
||||||
|
).fetchall()
|
||||||
|
]
|
||||||
|
if freigaben:
|
||||||
|
payload["ldap"] = {"fileshare_mappings": freigaben}
|
||||||
|
else:
|
||||||
|
report.note("Tabelle 'ldap_fileshare_mappings' fehlt in der alten Datenbank.")
|
||||||
|
|
||||||
dhcp = _alt_dhcp(conn, vorhanden)
|
dhcp = _alt_dhcp(conn, vorhanden)
|
||||||
if dhcp:
|
if dhcp:
|
||||||
payload["dhcp"] = dhcp
|
payload["dhcp"] = dhcp
|
||||||
@@ -644,9 +718,12 @@ def read_installation(root: Path) -> tuple[dict[str, Any], Report]:
|
|||||||
if not payload:
|
if not payload:
|
||||||
raise LegacyTransferError(
|
raise LegacyTransferError(
|
||||||
f"In {root} stehen keine übernehmbaren Daten (Clients, Switche, "
|
f"In {root} stehen keine übernehmbaren Daten (Clients, Switche, "
|
||||||
"Zugangsdaten, DHCP)."
|
"Zugangsdaten, DHCP, Freigaben)."
|
||||||
)
|
)
|
||||||
for abschnitt, inhalt in payload.items():
|
for abschnitt, inhalt in payload.items():
|
||||||
|
if abschnitt == "ldap" and isinstance(inhalt, dict):
|
||||||
|
report.took("Freigaben", len(_liste(inhalt.get("fileshare_mappings"))))
|
||||||
|
continue
|
||||||
report.took(abschnitt, len(inhalt) if isinstance(inhalt, list) else 1)
|
report.took(abschnitt, len(inhalt) if isinstance(inhalt, list) else 1)
|
||||||
return payload, report
|
return payload, report
|
||||||
|
|
||||||
|
|||||||
@@ -534,19 +534,33 @@ def apply_import(
|
|||||||
|
|
||||||
if "fileshare" in wanted:
|
if "fileshare" in wanted:
|
||||||
for entry in payload.get("fileshare", []):
|
for entry in payload.get("fileshare", []):
|
||||||
|
gruppe = str(entry.get("ad_group_dn") or "")
|
||||||
|
pfad = str(entry.get("unc") or "")
|
||||||
|
if not gruppe or not pfad:
|
||||||
|
bump("fileshare", "skipped")
|
||||||
|
continue
|
||||||
|
# Vorher nachsehen, damit der Bericht die Wahrheit sagt: bisher
|
||||||
|
# stand dort auch bei einer frischen Installation "2 aktualisiert",
|
||||||
|
# obwohl zwei Zuordnungen **angelegt** wurden. Wer einen Import
|
||||||
|
# prueft, liest genau diese Zahlen.
|
||||||
|
vorhanden = Database.value(
|
||||||
|
conn,
|
||||||
|
"SELECT 1 FROM fileshare_mappings WHERE ad_group_dn=? AND unc=? LIMIT 1",
|
||||||
|
(gruppe, pfad),
|
||||||
|
)
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT INTO fileshare_mappings (ad_group_dn, ad_group_name, label, unc, created_at) "
|
"INSERT INTO fileshare_mappings (ad_group_dn, ad_group_name, label, unc, created_at) "
|
||||||
"VALUES (?,?,?,?,?) ON CONFLICT(ad_group_dn, unc) DO UPDATE SET "
|
"VALUES (?,?,?,?,?) ON CONFLICT(ad_group_dn, unc) DO UPDATE SET "
|
||||||
"label=excluded.label, ad_group_name=excluded.ad_group_name",
|
"label=excluded.label, ad_group_name=excluded.ad_group_name",
|
||||||
(
|
(
|
||||||
str(entry.get("ad_group_dn") or ""),
|
gruppe,
|
||||||
str(entry.get("ad_group_name") or ""),
|
str(entry.get("ad_group_name") or ""),
|
||||||
str(entry.get("label") or ""),
|
str(entry.get("label") or ""),
|
||||||
str(entry.get("unc") or ""),
|
pfad,
|
||||||
stamp,
|
stamp,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
bump("fileshare", "updated")
|
bump("fileshare", "updated" if vorhanden else "created")
|
||||||
|
|
||||||
if "groups" in wanted:
|
if "groups" in wanted:
|
||||||
for entry in payload.get("groups", []):
|
for entry in payload.get("groups", []):
|
||||||
|
|||||||
Reference in new issue
Block a user