- Backend-Wechsel von isc-dhcp-server (seit 2022 EOL) auf Kea DHCP (kea-dhcp4-server) — aktiv weiterentwickelt, bildet "globaler Wert, pro Client überschreibbar" nativ über Kea-Reservation-Option-Data ab. - Neue Tabellen dhcp_option_defs/dhcp_option_values: eigene/herstellerspezifische Options mit Code/Name/Typ/Beschreibung, Wert global sowie optional pro Client (device_mac="" für global, siehe Kommentar zur SQLite-NULL- UNIQUE-Falle). UI dafür: Optionen-Tabelle + "Neue Option"-Modal + Pro-Client-Overrides-Modal je Reservierung. - Kea-JSON-Generator (_render_kea_config): option-def für jede eigene Option, globale Werte im Top-Level option-data, Client-Overrides im option-data der jeweiligen Reservierung. - Reservierungen werden jetzt zusätzlich auf Zugehörigkeit zum konfigurierten Subnet gefiltert (_dhcp_reservation_candidates) — beim Live-Test gegen echtes Kea gefunden: Kea lehnt Reservierungen außerhalb ihres Subnets als Konfigurationsfehler ab, das muss also schon bei der Generierung berücksichtigt werden statt erst beim Laden zu crashen. - Neue, einzeln bestätigte Aktionen: "kea-dhcp4-server installieren" (apt-get), "Aktivieren & (neu) starten" sowie "Stoppen" (systemctl) — bewusst getrennt von "Konfiguration speichern"/"In Datei schreiben". - Live gegen eine echte, frisch installierte Kea-3.0.3-Instanz verifiziert: Installation erfolgreich, generierte Config besteht "kea-dhcp4 -t", Dienst übernimmt sie beim Neustart fehlerfrei (DHCP4_CONFIG_COMPLETE), globaler Options-Wert und Client-Override erscheinen korrekt getrennt. Danach wieder gestoppt; Paket bleibt installiert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
174 lines
5.4 KiB
Python
174 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Initialisiert die SQLite-Datenbank für den PoE Manager."""
|
|
import os
|
|
import sqlite3
|
|
|
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
DB_PATH = os.environ.get("POE_DB_PATH", os.path.join(BASE_DIR, "sqlite.db"))
|
|
|
|
conn = sqlite3.connect(DB_PATH)
|
|
c = conn.cursor()
|
|
|
|
# Zugangsdaten (wiederverwendbare SSH-Logins, mehrere Switche können sich
|
|
# dieselben teilen; Passwort wird verschlüsselt gespeichert)
|
|
c.execute("""
|
|
CREATE TABLE IF NOT EXISTS credentials (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT UNIQUE NOT NULL,
|
|
username TEXT NOT NULL,
|
|
password TEXT NOT NULL
|
|
);
|
|
""")
|
|
|
|
# Switches (Aruba-Switche) — referenzieren ihre Zugangsdaten statt sie
|
|
# direkt zu speichern
|
|
c.execute("""
|
|
CREATE TABLE IF NOT EXISTS switches (
|
|
hostname TEXT PRIMARY KEY,
|
|
ip TEXT NOT NULL,
|
|
ssh_port INTEGER,
|
|
credential_id INTEGER,
|
|
last_modified_by TEXT,
|
|
last_modified_at TEXT,
|
|
FOREIGN KEY (credential_id) REFERENCES credentials(id)
|
|
);
|
|
""")
|
|
|
|
# Devices
|
|
c.execute("""
|
|
CREATE TABLE IF NOT EXISTS devices (
|
|
mac TEXT PRIMARY KEY,
|
|
rpi_ip TEXT NOT NULL,
|
|
switch_hostname TEXT,
|
|
port TEXT,
|
|
name TEXT NOT NULL,
|
|
is_active INTEGER DEFAULT 1,
|
|
last_modified_by TEXT,
|
|
last_modified_at TEXT,
|
|
FOREIGN KEY (switch_hostname) REFERENCES switches(hostname)
|
|
);
|
|
""")
|
|
|
|
# Benutzer
|
|
c.execute("""
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT UNIQUE NOT NULL,
|
|
password TEXT NOT NULL,
|
|
is_admin INTEGER DEFAULT 0,
|
|
first_name TEXT,
|
|
last_name TEXT,
|
|
avatar_filename TEXT
|
|
);
|
|
""")
|
|
|
|
# Gruppen (Rechtesystem) — Admins besitzen ohnehin alle Rechte; Gruppen
|
|
# dienen dazu, normalen Benutzern gezielt einzelne Verwaltungsrechte für
|
|
# Devices/Switches zu geben, ohne sie zu Admins zu machen.
|
|
c.execute("""
|
|
CREATE TABLE IF NOT EXISTS groups (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT UNIQUE NOT NULL,
|
|
is_default INTEGER DEFAULT 0,
|
|
is_system INTEGER DEFAULT 0
|
|
);
|
|
""")
|
|
|
|
# Welche Berechtigungen eine Gruppe gewährt (siehe app.py: PERMISSIONS)
|
|
c.execute("""
|
|
CREATE TABLE IF NOT EXISTS group_permissions (
|
|
group_id INTEGER NOT NULL,
|
|
permission TEXT NOT NULL,
|
|
PRIMARY KEY (group_id, permission),
|
|
FOREIGN KEY (group_id) REFERENCES groups(id)
|
|
);
|
|
""")
|
|
|
|
# Gruppenmitgliedschaft (ein Benutzer kann in mehreren Gruppen sein;
|
|
# effektive Rechte = Vereinigung aller Gruppen)
|
|
c.execute("""
|
|
CREATE TABLE IF NOT EXISTS user_groups (
|
|
user_id INTEGER NOT NULL,
|
|
group_id INTEGER NOT NULL,
|
|
PRIMARY KEY (user_id, group_id),
|
|
FOREIGN KEY (user_id) REFERENCES users(id),
|
|
FOREIGN KEY (group_id) REFERENCES groups(id)
|
|
);
|
|
""")
|
|
|
|
# Settings (z.B. Prüfintervall)
|
|
c.execute("""
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL
|
|
);
|
|
""")
|
|
|
|
# DHCP: eigene Options-Definitionen (analog "Vordefinierte Optionen und
|
|
# Werte" bei einem Windows-DHCP-Server) — Code/Name/Typ/Beschreibung.
|
|
c.execute("""
|
|
CREATE TABLE IF NOT EXISTS dhcp_option_defs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
code INTEGER NOT NULL UNIQUE,
|
|
name TEXT NOT NULL UNIQUE,
|
|
type TEXT NOT NULL,
|
|
description TEXT
|
|
);
|
|
""")
|
|
|
|
# DHCP: Werte je Option — device_mac='' bedeutet global (gilt für den
|
|
# ganzen Scope), ein gesetzter device_mac überschreibt den globalen Wert
|
|
# für genau dieses Gerät. Bewusst '' statt NULL für den globalen Fall,
|
|
# da SQLite NULL in UNIQUE-Constraints nicht als gleich behandelt (zwei
|
|
# NULL-Zeilen wären sonst kein Konflikt) — mit '' funktioniert ON CONFLICT
|
|
# zuverlässig für den Upsert des globalen Werts.
|
|
c.execute("""
|
|
CREATE TABLE IF NOT EXISTS dhcp_option_values (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
option_def_id INTEGER NOT NULL,
|
|
device_mac TEXT NOT NULL DEFAULT '',
|
|
value TEXT NOT NULL,
|
|
FOREIGN KEY (option_def_id) REFERENCES dhcp_option_defs(id),
|
|
UNIQUE (option_def_id, device_mac)
|
|
);
|
|
""")
|
|
|
|
# Änderungslog (Audit-Log) — wer hat wann was geändert. Bewusst OHNE
|
|
# PoE-Neustarts (die stehen im Live-Log von poe.sh).
|
|
c.execute("""
|
|
CREATE TABLE IF NOT EXISTS audit_log (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
ts TEXT NOT NULL,
|
|
username TEXT NOT NULL,
|
|
action TEXT NOT NULL,
|
|
target TEXT,
|
|
details TEXT
|
|
);
|
|
""")
|
|
|
|
# Standard-Setting: Prüfintervall 5 Minuten (interval = Minuten, check_interval = Sekunden)
|
|
c.execute("INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)", ("interval", "5"))
|
|
c.execute("INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)", ("check_interval", "300"))
|
|
|
|
# Standardgruppe 'Benutzer' — Systemgruppe (is_system), Name und Rechte
|
|
# sind über die UI fest und nicht änderbar. Jeder neu angelegte (nicht-admin)
|
|
# Benutzer landet automatisch darin (siehe app.py: users()). Der genaue
|
|
# Rechtesatz muss mit DEFAULT_GROUP_PERMISSIONS in app.py übereinstimmen.
|
|
cur = c.execute("INSERT OR IGNORE INTO groups (name, is_default, is_system) VALUES (?, 1, 1)", ("Benutzer",))
|
|
if cur.rowcount > 0:
|
|
default_group_id = cur.lastrowid
|
|
c.executemany(
|
|
"INSERT OR IGNORE INTO group_permissions (group_id, permission) VALUES (?, ?)",
|
|
[
|
|
(default_group_id, "devices_group.view"),
|
|
(default_group_id, "devices.view"),
|
|
(default_group_id, "switches.view"),
|
|
(default_group_id, "logs_group.view"),
|
|
(default_group_id, "logs_live.view"),
|
|
],
|
|
)
|
|
|
|
conn.commit()
|
|
conn.close()
|
|
print(f"Datenbank '{DB_PATH}' wurde initialisiert inklusive Settings.")
|