#!/bin/bash # ============================================================================ # TESM — Update/Reinstall # Ersetzt eine bestehende Installation -- ganz gleich welche der drei # historischen Stufen dieses Hosts gerade aktiv ist: # 1. Das ursprüngliche, reine Bash-Setup aus dem separaten Repo # https://gitea.int.eertmoed.net/WiS/Aruba-PoE (rpi-check.service + # statische ips.list, keine Datenbank/Web-UI). # 2. Die spätere Flask-App "PoE Manager" unter /srv/poe_manager # (poe_web.service, Vorgänger von TESM in diesem Repo vor der # Umbenennung). # 3. Eine bereits während der Entwicklung ad-hoc gepflegte TESM-Instanz # unter /srv/tesm. # -- durch eine frische Installation aus einem gepackten Gitea-Release. # Lädt dafür NUR den Release-Tarball per HTTP herunter (kein "git clone", # kein Git auf dem Zielsystem nötig). # # /srv/poe_manager (Stufe 2, komplett anderes Schema/App) wird IMMER nur # gestoppt/deaktiviert und beiseite verschoben, nie in-place übernommen. # # /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 # Optional übersteuerbar (z.B. für eine bestimmte Version statt "latest"): # TESM_RELEASE_TAG=v1.0.0 sudo -E ./update.sh # Für ein privates Repo wird ein Zugriffstoken benötigt: # TESM_GITEA_USER=alientim TESM_GITEA_TOKEN= sudo -E ./update.sh # ============================================================================ set -e GITEA_BASE="${TESM_GITEA_BASE:-https://gitea.int.eertmoed.net}" REPO_OWNER="${TESM_REPO_OWNER:-alientim}" REPO_NAME="${TESM_REPO_NAME:-tesm}" RELEASE_TAG="${TESM_RELEASE_TAG:-latest}" GITEA_USER="${TESM_GITEA_USER:-}" GITEA_TOKEN="${TESM_GITEA_TOKEN:-}" 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)" WORK_DIR="/tmp/tesm-update-$(date +%s)" PACKAGE_FILE="$WORK_DIR/${PACKAGE_NAME}" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' if [ "$(id -u)" -ne 0 ]; then echo "Bitte als root ausführen (sudo ./update.sh)." >&2 exit 1 fi echo -e "${YELLOW}============================================================================${NC}" echo -e "${YELLOW} TESM Update/Reinstall — Release \"${RELEASE_TAG}\"${NC}" echo -e "${YELLOW}============================================================================${NC}" echo echo -e "${RED}→${NC} Lade Paket von ${DOWNLOAD_URL}..." mkdir -p "$WORK_DIR" CURL_AUTH=() if [ -n "$GITEA_USER" ] && [ -n "$GITEA_TOKEN" ]; then CURL_AUTH=(-u "${GITEA_USER}:${GITEA_TOKEN}") fi curl -fsSL "${CURL_AUTH[@]}" -o "$PACKAGE_FILE" "$DOWNLOAD_URL" echo -e "${GREEN}✔${NC} Paket heruntergeladen ($(du -h "$PACKAGE_FILE" | cut -f1))." echo -e "${RED}→${NC} Entpacke Paket..." 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 -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}" 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