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.
This commit is contained in:
2026-08-14 00:20:52 +02:00
parent d77327cee1
commit 7f79534af4
3 changed files with 146 additions and 76 deletions
+1
View File
@@ -0,0 +1 @@
1
+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
+133 -75
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
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)."
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)."