Compare commits

..
6 Commits
Author SHA1 Message Date
alientim 3c3fbd8f23 update.sh: In-Place-Update statt Fresh-Install, wenn sich das DB-Schema nicht geändert hat
Neue Datei SCHEMA_VERSION (neben VERSION) markiert den DB-Schema-Stand.
update.sh laedt/entpackt das Release-Paket jetzt VOR der Bestaetigungs-
abfrage und vergleicht die SCHEMA_VERSION der bestehenden Installation
mit der des neuen Pakets:

- Stimmen beide ueberein (und existiert bereits eine sqlite.db) --
  In-Place-Update: /srv/tesm bleibt bestehen, install.sh rsynct nur den
  neuen Code darueber (sqlite.db/fernet.key/secret.key waren dort schon
  immer von rsync ausgeschlossen). Geraete/Switche/Zugangsdaten/Benutzer/
  Einstellungen bleiben vollstaendig erhalten, kein manuelles Neuanlegen
  oder Re-Import noetig.
- Unterscheiden sie sich (oder ist eine der beiden Versionen unbekannt,
  z.B. eine Installation von vor dieser Aenderung) -- weiterhin der
  bisherige sichere Weg: /srv/tesm wird beiseite verschoben (nicht
  geloescht) und komplett frisch installiert.

/srv/poe_manager (voellig andere App/Schema) wird davon unabhaengig
immer beiseite verschoben, nie in-place uebernommen.

create_db.py bekommt einen Hinweis-Kommentar, SCHEMA_VERSION bei jeder
Schema-Aenderung mit hochzuzaehlen.
2026-08-14 00:20:52 +02:00
alientim d77327cee1 skripts/: Admin-Hilfsskripte für Bulk-Aktionen ins Repo aufgenommen
- assign_credential_to_all_devices.py: weist allen aktiven Geraeten auf
  einen Schlag dieselben Zugangsdaten zu (Name als optionales Argument,
  Standard 'HAPS'), inkl. Eintrag im Aenderungsverlauf.
- accept_all_hostkeys.py: akzeptiert die SSH-Host-Keys aller aktiven
  Geraete in einem Rutsch (paralleler Verbindungsaufbau + Schluesseltausch,
  kein Login) -- nutzt dieselbe known_hosts-Datei wie die App, ein
  geaenderter (statt unbekannter) Key wird bewusst NICHT automatisch
  uebernommen, sondern separat als Warnung ausgegeben.

Beide live gegen POETEST verifiziert.
2026-08-14 00:13:11 +02:00
alientim d72db26a86 Versionsnummer in der UI anzeigen (Login-Seite + Sidebar-Footer)
VERSION-Datei (neben app.py, wird von install.sh 1:1 nach /srv/tesm
mitkopiert) plus Context-Processor inject_tesm_version() -- die
zugehoerige app.py-Aenderung war bereits im vorherigen Commit
enthalten. Damit laesst sich der installierte Stand direkt in der
Oberflaeche ablesen, ohne Datei-Zeitstempel oder Commit-Hashes
vergleichen zu muessen.
2026-08-13 23:56:32 +02:00
alientim 0b9b92a228 Wartung/Update: MODULES=most beim apt upgrade + autoremove/clean danach
_apt_upgrade_command() setzt jetzt zusaetzlich MODULES=most (initramfs-
tools) fuer den Update-Aufruf -- falls dabei ein neuer Kernel installiert
und ein initramfs neu gebaut wird, landen vorsorglich ALLE verfuegbaren
Treiber darin statt nur die per Abhaengigkeitserkennung ('dep') erkannten,
damit der Client nach einem Kernel-Wechsel zuverlaessig bootet. Am Ende
zusaetzlich 'apt-get autoremove'/'apt-get clean', damit nicht mehr
benoetigte Kernel/Pakete sowie der .deb-Cache nicht auf jedem Client
unbegrenzt Platz belegen. Live gegen einen echten Ubuntu-Client
verifiziert (Exit-Code 0, komplette Kette lief durch).
2026-08-13 23:55:56 +02:00
alientim 2a092af508 Merge: poe.sh-Revert von origin/main mit lokaler Kommentar-Aktualisierung zusammenführen 2026-08-13 23:26:39 +02:00
alientim 9f4195b800 usr/local/bin/custom/poe.sh aktualisiert 2026-08-13 23:13:39 +02:00
9 changed files with 330 additions and 76 deletions

No files matched your search

+104
View File
@@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""Akzeptiert die SSH-Host-Keys aller aktiven Geraete (Clients) in einem
Rutsch -- macht technisch dasselbe wie "Verbindung testen" im Browser-
Terminal (Verbindungsaufbau -> Host-Key abfragen -> "yes" -> trennen),
nur automatisiert fuer alle Geraete statt eines einzelnen.
Nutzt bewusst dieselbe known_hosts-Datei/dasselbe Format wie die App
(SSH_KNOWN_HOSTS_PATH in app.py) -- Geraete, deren Key hier akzeptiert
wird, muessen danach in der Weboberflaeche NICHT mehr einzeln bestaetigt
werden (SSH-Verbindungstest und Wartungs-Aktionen teilen sich dieselbe
Datei).
Aufruf auf dem Server: sudo /srv/tesm/venv/bin/python3 accept_all_hostkeys.py
"""
import socket
import sqlite3
from concurrent.futures import ThreadPoolExecutor, as_completed
import paramiko
DB_PATH = "/srv/tesm/sqlite.db"
KNOWN_HOSTS_PATH = "/srv/tesm/known_hosts"
DEFAULT_SSH_PORT = 22
CONNECT_TIMEOUT = 4
MAX_WORKERS = 32
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
devices = conn.execute(
"SELECT name, ip, ssh_port FROM devices WHERE deleted_at IS NULL ORDER BY name"
).fetchall()
conn.close()
def fetch_key(dev):
"""Nur der Handshake (Verbindungsaufbau + Schluesseltausch), KEIN Login
-- exakt der Teil, der im Browser-Terminal vor der Passwortabfrage
passiert. Parallel je Gerät, sonst dauert eine Liste mit vielen
offline Geraeten (je bis zu CONNECT_TIMEOUT Sekunden) unnoetig lange."""
host = dev["ip"]
port = dev["ssh_port"] or DEFAULT_SSH_PORT
try:
sock = socket.create_connection((host, port), timeout=CONNECT_TIMEOUT)
transport = paramiko.Transport(sock)
transport.start_client(timeout=CONNECT_TIMEOUT)
server_key = transport.get_remote_server_key()
transport.close()
return dev, server_key, None
except (OSError, paramiko.SSHException, EOFError) as e:
return dev, None, str(e)
hostkeys = paramiko.HostKeys()
try:
hostkeys.load(KNOWN_HOSTS_PATH)
except (OSError, paramiko.SSHException):
pass
accepted, already_known, changed, unreachable = [], [], [], []
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as pool:
futures = [pool.submit(fetch_key, dev) for dev in devices]
for future in as_completed(futures):
dev, server_key, err = future.result()
host = dev["ip"]
port = dev["ssh_port"] or DEFAULT_SSH_PORT
lookup_host = host if port == DEFAULT_SSH_PORT else f"[{host}]:{port}"
if err is not None:
unreachable.append((dev["name"], host, err))
continue
existing = hostkeys.lookup(lookup_host)
if existing and existing.get(server_key.get_name()):
if existing[server_key.get_name()] == server_key:
already_known.append((dev["name"], host))
continue
else:
# Nicht blind ueberschreiben -- das waere genau das MITM-
# Szenario, vor dem die App im Terminal auch schuetzt (harte
# Ablehnung statt automatischer Uebernahme eines GEAENDERTEN
# Keys).
changed.append((dev["name"], host))
continue
hostkeys.add(lookup_host, server_key.get_name(), server_key)
accepted.append((dev["name"], host))
hostkeys.save(KNOWN_HOSTS_PATH)
print(f"Neu akzeptiert: {len(accepted)}")
for name, host in accepted:
print(f" + {name} ({host})")
print(f"Bereits bekannt: {len(already_known)}")
if changed:
print(f"ACHTUNG -- Host-Key geaendert, NICHT automatisch uebernommen ({len(changed)}):")
for name, host in changed:
print(f" ! {name} ({host}) -- manuell in der App pruefen (moeglicher MITM oder Geraetetausch)")
print(f"Nicht erreichbar: {len(unreachable)}")
for name, host, err in unreachable:
print(f" - {name} ({host}): {err}")
@@ -0,0 +1,41 @@
#!/usr/bin/env python3
"""Weist allen aktiven Geraeten (Clients) auf einen Schlag dieselben
Zugangsdaten zu -- spart das manuelle Durchklicken jedes einzelnen
Geraets in der Weboberflaeche.
Aufruf auf dem Server:
sudo /srv/tesm/venv/bin/python3 assign_credential_to_all_devices.py [NAME]
NAME ist der Name des Zugangsdaten-Eintrags (Standard: "HAPS")."""
import sqlite3
import sys
from datetime import datetime
DB_PATH = "/srv/tesm/sqlite.db"
CRED_NAME = sys.argv[1] if len(sys.argv) > 1 else "HAPS"
conn = sqlite3.connect(DB_PATH)
cred = conn.execute(
"SELECT id FROM credentials WHERE name = ? AND deleted_at IS NULL", (CRED_NAME,)
).fetchone()
if not cred:
print(f"Fehler: Zugangsdaten '{CRED_NAME}' nicht gefunden (oder im Papierkorb).")
raise SystemExit(1)
credential_id = cred[0]
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
cur = conn.execute(
"UPDATE devices SET credential_id=?, last_modified_by=?, last_modified_at=? WHERE deleted_at IS NULL",
(credential_id, "Admin (Bulk)", now),
)
count = cur.rowcount
conn.execute(
"INSERT INTO audit_log (ts, username, action, target, details) VALUES (?, ?, ?, ?, ?)",
(now, "system", "devices.bulk_credential", CRED_NAME, f"{count} Geräte auf Zugangsdaten '{CRED_NAME}' gesetzt"),
)
conn.commit()
conn.close()
print(f"{count} Geräte auf Zugangsdaten '{CRED_NAME}' (id={credential_id}) gesetzt.")
+1
View File
@@ -0,0 +1 @@
1
+1
View File
@@ -0,0 +1 @@
1.0.0
+41 -4
View File
@@ -509,6 +509,30 @@ def inject_current_year():
return {"current_year": datetime.now().year}
def _read_tesm_version():
"""Liest die Versionsnummer aus VERSION (im Repo neben app.py, wird von
install.sh 1:1 nach /srv/tesm mitkopiert). Fehlt die Datei (z.B. lokale
Entwicklung ohne vollständigen Checkout), lieber "unbekannt" anzeigen
als die App deswegen abstürzen zu lassen."""
try:
with open(os.path.join(BASE_DIR, "VERSION"), "r", encoding="utf-8") as f:
return f.read().strip() or "unbekannt"
except OSError:
return "unbekannt"
TESM_VERSION = _read_tesm_version()
@app.context_processor
def inject_tesm_version():
"""Für die Versionsanzeige im Sidebar-Footer (siehe base.html) — beim
Modulimport einmalig gelesen (Neustart des Dienstes nach einem Update
erforderlich, damit eine neue Versionsnummer sichtbar wird — geschieht
ohnehin bei jedem install.sh/update.sh-Lauf)."""
return {"tesm_version": TESM_VERSION}
@app.context_processor
def inject_nav():
if not current_user.is_authenticated:
@@ -6089,11 +6113,24 @@ def _apt_upgrade_command():
DEBIAN_FRONTEND=noninteractive plus --force-confdef/--force-confold
verhindern, dass ein Paket-Postinst-Skript auf eine Konfig-Datei-
Rückfrage wartet, die nie kommt (das Kommando würde sonst ohne PTY
unbeaufsichtigt hängen bleiben, bis der Timeout zuschlägt)."""
unbeaufsichtigt hängen bleiben, bis der Timeout zuschlägt).
MODULES=most (initramfs-tools) wird nur für diesen einen Aufruf per
Umgebungsvariable gesetzt, nicht dauerhaft in /etc/initramfs-tools/
initramfs.conf geändert -- falls das Update einen neuen Kernel mitbringt
und dabei ein initramfs neu gebaut wird, landen so vorsorglich ALLE
verfügbaren Treiber darin (statt nur die per "dep" erkannten), damit das
Gerät nach einem Kernel-Wechsel zuverlässig bootet, auch wenn sich z.B.
Storage-/Netzwerk-Hardware nicht sauber per Abhängigkeitserkennung
ermitteln lässt. Am Ende noch "apt-get autoremove"/"apt-get clean", damit
nicht mehr benötigte Kernel/Pakete sowie der heruntergeladene .deb-Cache
nicht auf jedem Client unbegrenzt Platz belegen."""
inner = (
"env DEBIAN_FRONTEND=noninteractive apt-get update && "
'env DEBIAN_FRONTEND=noninteractive apt-get -y '
'-o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" upgrade'
"env DEBIAN_FRONTEND=noninteractive MODULES=most apt-get update && "
'env DEBIAN_FRONTEND=noninteractive MODULES=most apt-get -y '
'-o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" upgrade && '
"env DEBIAN_FRONTEND=noninteractive apt-get -y autoremove && "
"apt-get clean"
)
return f"sudo -S -p '' bash -c {shlex.quote(inner)}"
+12 -1
View File
@@ -1,5 +1,16 @@
#!/usr/bin/env python3
"""Initialisiert die SQLite-Datenbank für TESM."""
"""Initialisiert die SQLite-Datenbank für TESM.
WICHTIG: Wer hier eine neue Tabelle oder Spalte hinzufügt, muss auch die
Datei SCHEMA_VERSION (daneben, im selben Verzeichnis) um 1 erhöhen.
update.sh vergleicht diese Zahl zwischen der aktuell installierten und der
neuen Version, um zu entscheiden, ob ein Update in-place laufen darf
(bestehende Datenbank bleibt erhalten) oder eine frische Installation
nötig ist (Datenbank wird beiseitegesichert). Ein vergessenes Hochzählen
würde dazu führen, dass ein Host mit altem Schema faelschlich per
In-Place-Update aktualisiert wird, statt frisch installiert zu werden --
_ensure_schema() faengt die meisten Faelle zwar zur Laufzeit ab, ist aber
kein Ersatz fuer eine korrekte Versionsnummer hier."""
import os
import sqlite3
+1 -1
View File
@@ -90,7 +90,7 @@
</a>
</div>
<div class="sidebar-copyright" style="padding:10px 20px 4px; font-size:10.5px; color:var(--text-faint); text-align:center;">
© {{ current_year }} TESM — TimEShepManager
© {{ current_year }} TESM — TimEShepManager v{{ tesm_version }}
</div>
</div>
</aside>
+1
View File
@@ -35,6 +35,7 @@
</div>
<button type="submit" class="btn btn-primary btn-block" style="margin-top:6px;">Anmelden</button>
</form>
<div style="margin-top:16px; font-size:10.5px; color:var(--text-faint); text-align:center;">TESM v{{ tesm_version }}</div>
</div>
</div>
+128 -70
View File
@@ -15,16 +15,23 @@
# Lädt dafür NUR den Release-Tarball per HTTP herunter (kein "git clone",
# kein Git auf dem Zielsystem nötig).
#
# Alle ALTEN Installationen werden dabei NICHT gelöscht, sondern nur
# gestoppt/deaktiviert und (falls vorhanden) beiseite verschoben (siehe
# OLD_DIRS/BACKED_UP_DIRS) -- als Rückfalloption UND als Datenquelle für
# einen späteren Export/Import.
# /srv/poe_manager (Stufe 2, komplett anderes Schema/App) wird IMMER nur
# gestoppt/deaktiviert und beiseite verschoben, nie in-place übernommen.
#
# WICHTIG: Das Ergebnis ist eine FRISCHE Installation OHNE die Daten der
# alten Instanz (Geräte, Switche, Zugangsdaten, Benutzer, Einstellungen, …).
# Vor dem Ausführen unbedingt unter Einstellungen -> Im-/Export ein Backup
# der alten Instanz exportieren -- danach entweder alles neu einrichten oder
# dieses Backup in die neue Installation importieren.
# /srv/tesm (Stufe 3) dagegen wird per SCHEMA_VERSION-Datei verglichen:
# stimmt die Versionsnummer der bereits installierten mit der des neuen
# Release-Pakets überein, hat sich am Datenbank-Schema nichts geändert --
# dann läuft install.sh IN-PLACE gegen das bestehende /srv/tesm (dessen
# rsync schließt sqlite.db/fernet.key/secret.key ohnehin aus), die
# komplette Datenbank (Geräte, Switche, Zugangsdaten, Benutzer,
# Einstellungen, …) bleibt dabei erhalten. Nur bei einer TATSÄCHLICHEN
# Schema-Änderung (oder wenn die alte Installation noch keine
# SCHEMA_VERSION-Datei kennt, also aus der Zeit vor dieser Prüfung stammt)
# wird wie bisher beiseite verschoben und frisch installiert.
#
# Lädt/entpackt das Release-Paket deshalb VOR der Bestätigungsabfrage --
# nur so lässt sich vorher schon ansagen, ob es sich um ein
# datenerhaltendes Update oder eine frische Installation handelt.
#
# Auszuführen als root auf dem Zielsystem (z.B. POETEST):
# sudo ./update.sh
@@ -45,10 +52,6 @@ PACKAGE_NAME="tesm-${RELEASE_TAG}.tar.gz"
DOWNLOAD_URL="${GITEA_BASE}/${REPO_OWNER}/${REPO_NAME}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
# Alle historischen Installationsorte, die dieser Host je nach Alter tragen
# kann -- siehe Kommentar oben. Jeder, der existiert, wird beiseite
# verschoben statt gelöscht.
OLD_DIRS=("/srv/tesm" "/srv/poe_manager")
WORK_DIR="/tmp/tesm-update-$(date +%s)"
PACKAGE_FILE="$WORK_DIR/${PACKAGE_NAME}"
@@ -66,52 +69,6 @@ echo -e "${YELLOW}==============================================================
echo -e "${YELLOW} TESM Update/Reinstall — Release \"${RELEASE_TAG}\"${NC}"
echo -e "${YELLOW}============================================================================${NC}"
echo
echo -e "${RED}ACHTUNG:${NC} Dies richtet TESM KOMPLETT NEU ein — frische Datenbank, KEINE"
echo "Geräte/Switche/Zugangsdaten/Benutzer/Einstellungen aus der aktuellen Installation."
echo
echo "Alle auf diesem Host ggf. vorhandenen älteren Installationen (/srv/tesm,"
echo "/srv/poe_manager) werden dabei NICHT gelöscht, sondern nach dem Stoppen der"
echo "jeweiligen Dienste nach <Verzeichnis>-backup-${TIMESTAMP} verschoben (bleiben"
echo "vollständig erhalten, inkl. sqlite.db)."
echo
echo "Ebenfalls gestoppt/deaktiviert werden ältere Dienste, falls noch vorhanden:"
echo " - poe_web.service (Flask-App \"PoE Manager\" unter /srv/poe_manager)"
echo " - rpi-check.service / rpi-check-restart.* (das ursprüngliche reine"
echo " Bash-Setup aus dem Repo WiS/Aruba-PoE, ganz ohne Datenbank/Web-UI)"
echo
echo -e "${YELLOW}Falls noch nicht geschehen: JETZT abbrechen und zuerst unter${NC}"
echo -e "${YELLOW}Einstellungen -> Im-/Export ein Backup der aktuellen Daten exportieren!${NC}"
echo "Nach dem Update entweder dieses Backup importieren, oder alles manuell neu anlegen."
echo
read -r -p "Fortfahren? [y/N] " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo "Abgebrochen — nichts wurde verändert."
exit 0
fi
echo -e "${RED}→${NC} Stoppe und deaktiviere bestehende Dienste..."
# Alle drei historischen Generationen -- je nachdem, was auf diesem Host
# tatsächlich (noch) existiert. "|| true" macht nichts kaputt, falls
# einzelne Units gar nicht vorhanden sind.
systemctl stop tesm.service tesm-check.service tesm-check-restart.timer 2>/dev/null || true
systemctl disable tesm.service tesm-check.service tesm-check-restart.timer 2>/dev/null || true
systemctl stop poe_web.service 2>/dev/null || true
systemctl disable poe_web.service 2>/dev/null || true
systemctl stop rpi-check.service rpi-check-restart.service rpi-check-restart.timer 2>/dev/null || true
systemctl disable rpi-check.service rpi-check-restart.service rpi-check-restart.timer 2>/dev/null || true
echo -e "${GREEN}✔${NC} Dienste gestoppt und deaktiviert."
BACKED_UP_DIRS=()
for dir in "${OLD_DIRS[@]}"; do
if [ -d "$dir" ]; then
dest="${dir}-backup-${TIMESTAMP}"
echo -e "${RED}→${NC} Sichere ${dir} nach ${dest}..."
mv "$dir" "$dest"
BACKED_UP_DIRS+=("$dest")
echo -e "${GREEN}✔${NC} Gesichert (nicht gelöscht)."
fi
done
echo -e "${RED}→${NC} Lade Paket von ${DOWNLOAD_URL}..."
mkdir -p "$WORK_DIR"
@@ -127,26 +84,127 @@ mkdir -p "$WORK_DIR/pkg"
tar xzf "$PACKAGE_FILE" -C "$WORK_DIR/pkg" --strip-components=1
echo -e "${GREEN}✔${NC} Paket entpackt."
# Schema-Vergleich: nur wenn BEIDE Seiten eine SCHEMA_VERSION kennen UND
# sie übereinstimmen UND tatsächlich schon eine Datenbank existiert, darf
# in-place aktualisiert werden. Fehlt die Datei auf einer der beiden
# Seiten (ältere Installation/älteres Paket von vor dieser Prüfung), wird
# im Zweifel IMMER der sichere, datenerhaltende-per-Backup Weg gewählt.
CURRENT_SCHEMA_VERSION=""
[ -f /srv/tesm/SCHEMA_VERSION ] && CURRENT_SCHEMA_VERSION="$(tr -d '[:space:]' < /srv/tesm/SCHEMA_VERSION)"
NEW_SCHEMA_VERSION=""
[ -f "$WORK_DIR/pkg/srv/tesm/SCHEMA_VERSION" ] && NEW_SCHEMA_VERSION="$(tr -d '[:space:]' < "$WORK_DIR/pkg/srv/tesm/SCHEMA_VERSION")"
IN_PLACE_UPDATE=0
if [ -f /srv/tesm/sqlite.db ] && [ -n "$CURRENT_SCHEMA_VERSION" ] && [ -n "$NEW_SCHEMA_VERSION" ] \
&& [ "$CURRENT_SCHEMA_VERSION" == "$NEW_SCHEMA_VERSION" ]; then
IN_PLACE_UPDATE=1
fi
if [ "$IN_PLACE_UPDATE" -eq 1 ]; then
echo -e "${GREEN}Datenbank-Schema unverändert (Version ${CURRENT_SCHEMA_VERSION}):${NC} dies wird ein"
echo "IN-PLACE-Update. /srv/tesm bleibt bestehen -- Geräte/Switche/Zugangsdaten/"
echo "Benutzer/Einstellungen (sqlite.db) sowie Host-Keys und Verschlüsselungs-"
echo "Schlüssel (fernet.key/secret.key) bleiben unangetastet. Es werden nur"
echo "Anwendungscode, Templates und Abhängigkeiten aktualisiert."
else
if [ -n "$CURRENT_SCHEMA_VERSION" ] && [ -n "$NEW_SCHEMA_VERSION" ]; then
reason="Datenbank-Schema hat sich geändert (${CURRENT_SCHEMA_VERSION} → ${NEW_SCHEMA_VERSION})"
elif [ -f /srv/tesm/sqlite.db ]; then
reason="Schema-Version der bestehenden Installation oder des neuen Pakets unbekannt (Stand von vor dieser Prüfung)"
else
reason="keine bestehende Installation unter /srv/tesm gefunden"
fi
echo -e "${RED}ACHTUNG:${NC} Dies richtet TESM KOMPLETT NEU ein — frische Datenbank, KEINE"
echo "Geräte/Switche/Zugangsdaten/Benutzer/Einstellungen aus der aktuellen Installation."
echo "Grund: ${reason}."
echo
echo "Alle auf diesem Host ggf. vorhandenen älteren Installationen (/srv/tesm,"
echo "/srv/poe_manager) werden dabei NICHT gelöscht, sondern nach dem Stoppen der"
echo "jeweiligen Dienste nach <Verzeichnis>-backup-${TIMESTAMP} verschoben (bleiben"
echo "vollständig erhalten, inkl. sqlite.db)."
echo
echo -e "${YELLOW}Falls noch nicht geschehen: JETZT abbrechen und zuerst unter${NC}"
echo -e "${YELLOW}Einstellungen -> Im-/Export ein Backup der aktuellen Daten exportieren!${NC}"
echo "Nach dem Update entweder dieses Backup importieren, oder alles manuell neu anlegen."
fi
echo
echo "In jedem Fall gestoppt/deaktiviert werden ältere Dienste, falls noch vorhanden:"
echo " - poe_web.service (Flask-App \"PoE Manager\" unter /srv/poe_manager)"
echo " - rpi-check.service / rpi-check-restart.* (das ursprüngliche reine"
echo " Bash-Setup aus dem Repo WiS/Aruba-PoE, ganz ohne Datenbank/Web-UI)"
echo
read -r -p "Fortfahren? [y/N] " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo "Abgebrochen — nichts wurde verändert."
exit 0
fi
echo -e "${RED}→${NC} Stoppe und deaktiviere bestehende Dienste..."
# Alle drei historischen Generationen -- je nachdem, was auf diesem Host
# tatsächlich (noch) existiert. "|| true" macht nichts kaputt, falls
# einzelne Units gar nicht vorhanden sind. tesm.service wird auch beim
# In-Place-Update gestoppt (sauberer Neustart mit neuem Code danach über
# install.sh "Enabling services"), NUR /srv/tesm selbst bleibt erhalten.
systemctl stop tesm.service tesm-check.service tesm-check-restart.timer 2>/dev/null || true
systemctl disable tesm.service tesm-check.service tesm-check-restart.timer 2>/dev/null || true
systemctl stop poe_web.service 2>/dev/null || true
systemctl disable poe_web.service 2>/dev/null || true
systemctl stop rpi-check.service rpi-check-restart.service rpi-check-restart.timer 2>/dev/null || true
systemctl disable rpi-check.service rpi-check-restart.service rpi-check-restart.timer 2>/dev/null || true
echo -e "${GREEN}✔${NC} Dienste gestoppt und deaktiviert."
BACKED_UP_DIRS=()
# /srv/poe_manager ist eine komplett andere App/Schema -- die wird IMMER
# beiseite verschoben, nie in-place übernommen.
if [ -d /srv/poe_manager ]; then
dest="/srv/poe_manager-backup-${TIMESTAMP}"
echo -e "${RED}→${NC} Sichere /srv/poe_manager nach ${dest}..."
mv /srv/poe_manager "$dest"
BACKED_UP_DIRS+=("$dest")
echo -e "${GREEN}✔${NC} Gesichert (nicht gelöscht)."
fi
# /srv/tesm nur beiseite verschieben, wenn KEIN In-Place-Update möglich ist
# (siehe Schema-Vergleich oben) -- sonst bleibt es unangetastet stehen,
# damit install.sh gleich dagegen rsynct und die Datenbank erhalten bleibt.
if [ "$IN_PLACE_UPDATE" -eq 0 ] && [ -d /srv/tesm ]; then
dest="/srv/tesm-backup-${TIMESTAMP}"
echo -e "${RED}→${NC} Sichere /srv/tesm nach ${dest}..."
mv /srv/tesm "$dest"
BACKED_UP_DIRS+=("$dest")
echo -e "${GREEN}✔${NC} Gesichert (nicht gelöscht)."
fi
echo -e "${RED}→${NC} Starte install.sh aus dem Paket..."
bash "$WORK_DIR/pkg/install.sh"
echo
echo -e "${GREEN}============================================================================${NC}"
echo -e "${GREEN} Frische TESM-Installation (Release \"${RELEASE_TAG}\") abgeschlossen.${NC}"
echo -e "${GREEN}============================================================================${NC}"
echo
echo -e "${YELLOW}WICHTIG — dies ist eine FRISCHE Installation ohne die Daten der alten Instanz.${NC}"
echo "Bitte jetzt eines von beidem tun:"
echo " a) Alle Geräte/Switche/Zugangsdaten/Benutzer/Einstellungen manuell neu anlegen, oder"
echo " b) Ein vor dem Update exportiertes Backup unter Einstellungen -> Im-/Export importieren."
echo
if [ "${#BACKED_UP_DIRS[@]}" -gt 0 ]; then
if [ "$IN_PLACE_UPDATE" -eq 1 ]; then
echo -e "${GREEN} TESM-Update (Release \"${RELEASE_TAG}\") abgeschlossen -- Datenbank erhalten.${NC}"
echo -e "${GREEN}============================================================================${NC}"
echo
echo "Alle Geräte/Switche/Zugangsdaten/Benutzer/Einstellungen sind unverändert"
echo "vorhanden -- kein manuelles Neuanlegen oder Re-Import nötig."
else
echo -e "${GREEN} Frische TESM-Installation (Release \"${RELEASE_TAG}\") abgeschlossen.${NC}"
echo -e "${GREEN}============================================================================${NC}"
echo
echo -e "${YELLOW}WICHTIG — dies ist eine FRISCHE Installation ohne die Daten der alten Instanz.${NC}"
echo "Bitte jetzt eines von beidem tun:"
echo " a) Alle Geräte/Switche/Zugangsdaten/Benutzer/Einstellungen manuell neu anlegen, oder"
echo " b) Ein vor dem Update exportiertes Backup unter Einstellungen -> Im-/Export importieren."
echo
if [ "${#BACKED_UP_DIRS[@]}" -gt 0 ]; then
echo "Falls kein Export gemacht wurde: die alte(n) Datenbank(en) liegen unverändert unter:"
for dest in "${BACKED_UP_DIRS[@]}"; do
echo " ${dest}/sqlite.db"
done
echo "und können bei Bedarf manuell ausgelesen werden (z.B. mit sqlite3)."
echo
fi
echo "Hinweis zum Admin-Konto der neuen Installation: siehe Ausgabe von install.sh oben"
echo "(bei einer frischen Datenbank wird dort auf create_admin.py hingewiesen)."
fi
echo "Hinweis zum Admin-Konto der neuen Installation: siehe Ausgabe von install.sh oben"
echo "(bei einer frischen Datenbank wird dort auf create_admin.py hingewiesen)."