diff --git a/README.md b/README.md index 8dcae6a..c326dd9 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,15 @@ Löschen ist nur möglich, wenn kein Switch mehr auf den Eintrag verweist. Bestehende Datenbanken aus einer älteren Version (Username/Passwort direkt am Switch) werden beim ersten Start automatisch migriert. +Jeder Switch kann außerdem einen individuellen **SSH-Port** hinterlegen +(Feld „SSH-Port“ beim Anlegen/Bearbeiten) — bleibt er leer, wird überall +automatisch **Port 22** angenommen (`SWITCH_DEFAULT_SSH_PORT` in `app.py`). +Der Port wird konsistent an allen drei Stellen verwendet, an denen die App +sich per SSH mit einem Switch verbindet: dem Web-Terminal (Verbindungstest), +`generate_ips.py` (liefert ihn als eigenes Feld an `poe.sh`) und den +`expect`-Aufrufen in `poe.sh` selbst (`ssh -p ...`) für automatische +wie manuelle PoE-Neustarts. + ## Änderungslog Jede Anlage, Bearbeitung, Löschung sowie jedes Aktivieren/Deaktivieren von diff --git a/srv/poe_manager/app.py b/srv/poe_manager/app.py index 4bafaf1..d6c1980 100644 --- a/srv/poe_manager/app.py +++ b/srv/poe_manager/app.py @@ -77,6 +77,10 @@ FERNET_KEY_PATH = os.environ.get("POE_FERNET_KEY", os.path.join(BASE_DIR, "ferne SSH_KNOWN_HOSTS_PATH = os.environ.get("POE_KNOWN_HOSTS", os.path.join(BASE_DIR, "known_hosts")) SECRET_KEY_PATH = os.environ.get("POE_SECRET_KEY_FILE", os.path.join(BASE_DIR, "secret.key")) AVATAR_DIR = os.path.join(BASE_DIR, "static", "uploads", "avatars") +# Wird verwendet, wenn ein Switch keinen eigenen ssh_port hinterlegt hat +# (Feld leer gelassen) — deckt den Standardfall ab, ohne dass er überall +# explizit eingetragen werden muss. +SWITCH_DEFAULT_SSH_PORT = 22 ALLOWED_AVATAR_EXT = {"png", "jpg", "jpeg", "gif", "webp"} os.makedirs(AVATAR_DIR, exist_ok=True) @@ -519,6 +523,10 @@ def _ensure_schema(): conn.execute("ALTER TABLE switches ADD COLUMN last_modified_by TEXT") if "last_modified_at" not in switch_cols: conn.execute("ALTER TABLE switches ADD COLUMN last_modified_at TEXT") + # SSH-Port je Switch (falls nicht Standard 22) — NULL/leer bedeutet + # überall "22 verwenden" (siehe SWITCH_DEFAULT_SSH_PORT-Fallback). + if "ssh_port" not in switch_cols: + conn.execute("ALTER TABLE switches ADD COLUMN ssh_port INTEGER") # Migration: bestehende, direkt am Switch hinterlegte Zugangsdaten # (ältere DB-Version) in eigene Credentials-Datensätze überführen. @@ -1509,6 +1517,18 @@ def _resolve_credential_choice(conn): return None, "Bitte Zugangsdaten auswählen oder neue anlegen." +def _parse_ssh_port(form): + """Liest das optionale SSH-Port-Feld aus dem Formular. Leer -> None (= + Fallback auf SWITCH_DEFAULT_SSH_PORT überall, wo der Port gebraucht + wird). Gibt (port_or_none, error_message) zurück.""" + raw = (form.get("ssh_port") or "").strip() + if not raw: + return None, None + if not raw.isdigit() or not (1 <= int(raw) <= 65535): + return None, "SSH-Port muss eine Zahl zwischen 1 und 65535 sein (oder leer für Standard 22)!" + return int(raw), None + + @app.route("/switches", methods=["GET", "POST"]) @login_required def switches(): @@ -1524,14 +1544,17 @@ def switches(): return redirect(url_for("switches")) hostname = request.form["hostname"] ip = request.form["ip"] + ssh_port, port_error = _parse_ssh_port(request.form) credential_id, cred_error = _resolve_credential_choice(conn) - if cred_error: + if port_error: + flash(port_error, "danger") + elif cred_error: flash(cred_error, "danger") else: try: conn.execute( - "INSERT INTO switches (hostname, ip, credential_id) VALUES (?, ?, ?)", - (hostname, ip, credential_id), + "INSERT INTO switches (hostname, ip, ssh_port, credential_id) VALUES (?, ?, ?, ?)", + (hostname, ip, ssh_port, credential_id), ) touch_record(conn, "switches", "hostname", hostname) conn.commit() @@ -1547,14 +1570,17 @@ def switches(): old_hostname = request.form["old_hostname"] hostname = request.form["hostname"] ip = request.form["ip"] + ssh_port, port_error = _parse_ssh_port(request.form) credential_id, cred_error = _resolve_credential_choice(conn) - if cred_error: + if port_error: + flash(port_error, "danger") + elif cred_error: flash(cred_error, "danger") else: try: conn.execute( - "UPDATE switches SET hostname=?, ip=?, credential_id=? WHERE hostname=?", - (hostname, ip, credential_id, old_hostname), + "UPDATE switches SET hostname=?, ip=?, ssh_port=?, credential_id=? WHERE hostname=?", + (hostname, ip, ssh_port, credential_id, old_hostname), ) if hostname != old_hostname: conn.execute( @@ -1569,7 +1595,7 @@ def switches(): flash("Hostname existiert bereits oder Eingabefehler!", "danger") switch_rows = conn.execute(""" - SELECT switches.hostname, switches.ip, switches.credential_id, + SELECT switches.hostname, switches.ip, switches.ssh_port, switches.credential_id, credentials.name AS credential_name, credentials.username AS credential_username FROM switches LEFT JOIN credentials ON credentials.id = switches.credential_id diff --git a/srv/poe_manager/create_db.py b/srv/poe_manager/create_db.py index 0f80c76..883f140 100644 --- a/srv/poe_manager/create_db.py +++ b/srv/poe_manager/create_db.py @@ -26,6 +26,7 @@ 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, diff --git a/srv/poe_manager/generate_ips.py b/srv/poe_manager/generate_ips.py index d666386..3bf6f3c 100644 --- a/srv/poe_manager/generate_ips.py +++ b/srv/poe_manager/generate_ips.py @@ -15,7 +15,7 @@ def generate_ips_list(): switches = { row["hostname"]: row for row in conn.execute(""" - SELECT switches.hostname, switches.ip, + SELECT switches.hostname, switches.ip, switches.ssh_port, credentials.username AS username, credentials.password AS password FROM switches LEFT JOIN credentials ON credentials.id = switches.credential_id @@ -33,10 +33,12 @@ def generate_ips_list(): switch = switches.get(dev["switch_hostname"]) if switch and switch["password"]: switch_ip = switch["ip"] + switch_ssh_port = switch["ssh_port"] or 22 switch_user = switch["username"] switch_pass = decrypt_password(switch["password"]) else: switch_ip = switch["ip"] if switch else "" + switch_ssh_port = (switch["ssh_port"] if switch else None) or 22 switch_user = "" switch_pass = "" @@ -45,6 +47,7 @@ def generate_ips_list(): f"{dev['rpi_ip']}|" f"{dev['name']}|" f"{switch_ip}|" + f"{switch_ssh_port}|" f"{dev['switch_hostname'] or 'kein Switch'}|" f"{port}|" f"{switch_user}|" diff --git a/srv/poe_manager/templates/switches.html b/srv/poe_manager/templates/switches.html index ec1c942..36377f5 100644 --- a/srv/poe_manager/templates/switches.html +++ b/srv/poe_manager/templates/switches.html @@ -37,15 +37,20 @@ Hostname IP-Adresse + SSH-Port Zugangsdaten Aktionen {% for s in switches %} - + {{ s['hostname'] }} {{ s['ip'] }} + + {{ s['ssh_port'] or 22 }} + {% if not s['ssh_port'] %}(Standard){% endif %} + {% if s['credential_name'] %} {{ s['credential_name'] }} ({{ s['credential_username'] }}) @@ -89,6 +94,10 @@
Bitte eine gültige IP-Adresse eingeben.
+
+ +
Leer lassen, wenn der Switch den Standard-Port 22 verwendet.
+
Bitte eine gültige IP-Adresse eingeben.
+
+ +
Leer lassen, wenn der Switch den Standard-Port 22 verwendet.
+