010420262055 first

This commit is contained in:
Manfred Aabye
2026-04-01 20:55:45 +02:00
committed by GitHub
parent ab20d9de73
commit b9113793d2
30 changed files with 13205 additions and 1 deletions
+95 -1
View File
@@ -1,4 +1,4 @@
# **opensimMULTITOOL II V25.5**
# **opensimMULTITOOL II V26.4**
Ein Bash-Skript zum Verwalten von OpenSim-Grids (Starten, Stoppen, Bereinigen, Installation).
@@ -30,6 +30,100 @@ Ein Bash-Skript zum Verwalten von OpenSim-Grids (Starten, Stoppen, Bereinigen, I
---
## **Neu: Modulare Struktur (osmtool_backup_*)**
Fuer die Aufteilung in einzelne Aufgaben gibt es jetzt eine modulare Basis:
- `osmtool_main.sh` (zentraler Starter, CLI + UI)
- `modular/osmtool_install.sh` (Installation)
- `modular/osmtool_startstop.sh` (Start/Stop/Restart)
- `modular/osmtool_cleanup.sh` (Bereinigungen + Reboot)
- `modular/osmtool_data.sh` (DB/OAR Backups)
- `modular/osmtool_core.sh` (gemeinsame Funktionen)
### Empfehlung UI: whiptail oder dialog?
- Empfehlung: **whiptail als Default**, weil es auf Ubuntu meist schon vorhanden ist und weniger Abhaengigkeiten hat.
- Fallback: **dialog**, falls whiptail nicht installiert ist.
- Beide Wege sind im Starter beruecksichtigt: `whiptail -> dialog -> CLI`.
### Beispiele
```bash
# CLI: zentrale Server-Vorbereitung als Pflichtschritt
bash osmtool_main.sh --module install --action bootstrap-server --workdir /opt --profile grid-sim
# CLI: Ubuntu vorbereiten
bash osmtool_main.sh --module install --action prepare-ubuntu
# CLI: OpenSim klonen, Zusatzmodule einbinden und bauen
bash osmtool_main.sh --module install --action install-opensim --workdir /opt --profile grid-sim
# CLI: OpenSim Runtime-INI aus Beispieldateien erzeugen
bash osmtool_main.sh --module install --action configure-opensim --workdir /opt --profile grid-sim
# CLI: MariaDB Datenbanken/Benutzer/Rechte fuer robust/sim* anlegen
bash osmtool_main.sh --module install --action configure-database --workdir /opt --profile grid-sim --db-user opensim --db-pass 'OpenSim#2026'
# CLI: OpenSim bauen ohne Verteilung in Laufzeitordner
bash osmtool_main.sh --module install --action install-opensim --workdir /opt --profile grid-sim --deploy-binaries false
# CLI: komplettes Grid neustarten
bash osmtool_main.sh --module startstop --target grid --action restart --workdir /opt
# CLI: Janus Gateway neustarten
bash osmtool_main.sh --module startstop --target janus --action restart --workdir /opt
# CLI: Janus zuerst kompilieren
bash osmtool_main.sh --module install --action compile-janus --workdir /opt --profile grid-sim
# CLI: Janus danach konfigurieren
bash osmtool_main.sh --module install --action configure-janus --workdir /opt --profile grid-sim --public-host 127.0.0.1
# CLI: Janus kompiliert + konfiguriert in einem Lauf
bash osmtool_main.sh --module install --action install-janus --workdir /opt --profile grid-sim --public-host 127.0.0.1
# CLI: Robust DB sichern
bash osmtool_main.sh --module backup --action db-backup --db-user root --db-pass secret --db-name opensim --workdir /opt
# CLI: OAR Export in laufende sim1 Session senden
bash osmtool_main.sh --module backup --action oar-backup --workdir /opt --region sim1 --session sim1
# CLI: Full Backup (MariaDB + OAR Command) mit gzip-Kompression
bash osmtool_main.sh --module backup --action full-backup --workdir /opt --db-user root --db-pass secret --db-name opensim --region sim1 --session sim1 --compress true
# CLI: OSMTool Cronjobs installieren
bash osmtool_main.sh --module cron --action install --workdir /opt --db-user root --db-name opensim --region sim1 --session sim1
# CLI: OSMTool Cronjobs anzeigen/entfernen
bash osmtool_main.sh --module cron --action list --workdir /opt
bash osmtool_main.sh --module cron --action remove --workdir /opt
# UI Modus
bash osmtool_main.sh --mode ui
```
Hinweis Janus:
- `bootstrap-server` ist Pflicht vor `install-opensim`, `compile-janus`, `configure-janus` und `install-janus`.
- `configure-opensim` erzeugt fehlende Runtime-INI aus den Beispielvorlagen.
- `configure-database` erzeugt MariaDB Datenbanken fuer `robust` und gefundene `sim*` Verzeichnisse sowie Benutzer/Grants.
- Datenbankbasis ist MariaDB; `server-check` prueft `mysqldump` als Pflichttool fuer Backups.
- `install-opensim` klont oder aktualisiert `opensim`, `opensim-tsassets`, `opensimcurrencyserver-dotnet` und `os-data-backup`, fuehrt `runprebuild.sh` aus und baut `OpenSim.sln`.
- Wenn `robust/bin`, `standalone/bin` oder `sim*/bin` existieren, verteilt `install-opensim` die gebauten Binaries dorthin, sofern `--deploy-binaries` nicht auf `false` gesetzt ist.
- Janus muss vor Start/Restart zuerst kompiliert und konfiguriert sein.
- `startstop --target janus` prueft Binary und Konfiguration und blockiert sonst mit klarer Fehlermeldung.
### Sinnvolle weitere Skripte
- `osmtool_backup_health.sh` (Healthcheck fuer screen sessions, ports, DB)
- `osmtool_backup_restore.sh` (SQL Restore + OAR Restore mit Dry-Run)
- `osmtool_backup_update.sh` (OpenSim/Janus Update-Workflow mit Rollback)
- `osmtool_backup_cron.sh` (Cronjobs validieren und automatisch setzen)
- `osmtool_backup_config.sh` (INI/JSON Validierung und Patchen)
---
## **📋 Inhaltsverzeichnis**
1. [OpenSim starten/stoppen](#-opensim-starten-stoppen-und-neustarten)
+60
View File
@@ -0,0 +1,60 @@
APP_TITLE=OSMTool Backup
MENU_CHOOSE_MODULE=Modul waehlen
MENU_INSTALL=Installation
MENU_STARTSTOP=Start/Stop/Neustart
MENU_CLEANUP=Bereinigung und Reboot
MENU_HEALTH=Healthcheck
MENU_BACKUP=Sicherung DB/OAR
MENU_RESTORE=Wiederherstellung DB/OAR
MENU_UPDATE=Aktualisierung mit Ruecksetzpunkt
MENU_CONFIG=Konfigurationspruefung
MENU_REPORT=Tagesreport
MENU_SMOKE=Schnelltest
MENU_CRON=Cronjobs
MENU_CHOOSE_ACTION=Aktion waehlen
MENU_CHOOSE_TARGET=Ziel waehlen
MENU_CHOOSE_PROFILE=Profil waehlen
ACTION_START=Starten
ACTION_STOP=Stoppen
ACTION_RESTART=Neustarten
ACTION_CACHE_CLEAN=Cache-Ordner loeschen
ACTION_LOG_CLEAN=Logs loeschen
ACTION_TMP_CLEAN=Temp-Dateien loeschen
ACTION_REBOOT=Server neu starten
ACTION_HEALTH_RUN=System-Healthcheck ausfuehren
ACTION_DB_BACKUP=Robust Datenbank sichern
ACTION_OAR_BACKUP=Region als OAR sichern
ACTION_FULL_BACKUP=Datenbank und optional OAR sichern
ACTION_RESTORE_LIST=Verfuegbare Backups anzeigen
ACTION_RESTORE_DB=Datenbank wiederherstellen
ACTION_RESTORE_OAR=Region aus OAR wiederherstellen
ACTION_RESTORE_FULL=Datenbank und optional OAR wiederherstellen
ACTION_UPDATE_ROLLBACK=Ruecksetzpunkt erstellen
ACTION_UPDATE_LIST=Ruecksetzpunkte anzeigen
ACTION_UPDATE_OPENSIM=OpenSim aktualisieren
ACTION_UPDATE_JANUS=Janus aktualisieren
ACTION_UPDATE_FULL=OpenSim und Janus aktualisieren
ACTION_CONFIG_FILE=Einzelne Config-Datei pruefen
ACTION_CONFIG_RUNTIME=Runtime-Configs pruefen
ACTION_REPORT_GENERATE=Tagesreport erzeugen
ACTION_SMOKE_RUN=Start/Neustart/Stop Schnelltest ausfuehren
ACTION_CRON_INSTALL=Cron-Jobs installieren/aktualisieren
ACTION_CRON_LIST=Cron-Jobs anzeigen
ACTION_CRON_REMOVE=Cron-Jobs entfernen
TARGET_GRID=Komplettes Grid
TARGET_ROBUST=Nur Robust
TARGET_SIM1=Nur Primaerregion
TARGET_STANDALONE=Standalone Instanz
TARGET_JANUS=Janus Gateway Server
PROFILE_GRID_SIM=Grid Simulatoren
PROFILE_ROBUST=Robust Dienst
PROFILE_STANDALONE=Standalone Betrieb
ERR_UNKNOWN_MODULE=Unbekanntes Modul
ERR_UNKNOWN_UI_MODULE=Unbekanntes UI-Modul
ERR_MISSING_MODULE=Fehlendes --module im CLI Modus
ERR_INVALID_PROFILE=Ungueltiges Profil
ERR_ACTION_NOT_ALLOWED=Aktion fuer dieses Profil nicht erlaubt
ERR_TARGET_NOT_ALLOWED=Ziel fuer dieses Profil nicht erlaubt
ERR_NO_UI_BACKEND=Kein UI Backend gefunden. Installiere whiptail oder dialog oder nutze CLI Parameter.
HELP_LANG=Sprache (de|en|fr|es), Standard: de
HELP_PROFILE=Profil (grid-sim|robust|standalone), Standard: grid-sim
+60
View File
@@ -0,0 +1,60 @@
APP_TITLE=OSMTool Backup
MENU_CHOOSE_MODULE=Choose module
MENU_INSTALL=Installation
MENU_STARTSTOP=Start/stop/restart
MENU_CLEANUP=Cleanup and reboot
MENU_HEALTH=Health check
MENU_BACKUP=Backup DB/OAR
MENU_RESTORE=Restore DB/OAR
MENU_UPDATE=Update with rollback
MENU_CONFIG=Config validation
MENU_REPORT=Daily report
MENU_SMOKE=Smoke test
MENU_CRON=Cron jobs
MENU_CHOOSE_ACTION=Choose action
MENU_CHOOSE_TARGET=Choose target
MENU_CHOOSE_PROFILE=Choose profile
ACTION_START=Start
ACTION_STOP=Stop
ACTION_RESTART=Restart
ACTION_CACHE_CLEAN=Delete cache folders
ACTION_LOG_CLEAN=Delete logs
ACTION_TMP_CLEAN=Delete temp files
ACTION_REBOOT=Reboot server
ACTION_HEALTH_RUN=Run system health check
ACTION_DB_BACKUP=Backup robust database
ACTION_OAR_BACKUP=Backup region as OAR
ACTION_FULL_BACKUP=Backup database and optional OAR
ACTION_RESTORE_LIST=List available backups
ACTION_RESTORE_DB=Restore database
ACTION_RESTORE_OAR=Restore region from OAR
ACTION_RESTORE_FULL=Restore database and optional OAR
ACTION_UPDATE_ROLLBACK=Create rollback point
ACTION_UPDATE_LIST=List rollback points
ACTION_UPDATE_OPENSIM=Update OpenSim
ACTION_UPDATE_JANUS=Update Janus
ACTION_UPDATE_FULL=Update OpenSim and Janus
ACTION_CONFIG_FILE=Validate single config file
ACTION_CONFIG_RUNTIME=Validate runtime configs
ACTION_REPORT_GENERATE=Generate daily report
ACTION_SMOKE_RUN=Run start/restart/stop smoke test
ACTION_CRON_INSTALL=Install/update cron jobs
ACTION_CRON_LIST=List cron jobs
ACTION_CRON_REMOVE=Remove cron jobs
TARGET_GRID=Whole grid
TARGET_ROBUST=Robust only
TARGET_SIM1=Primary region only
TARGET_STANDALONE=Standalone instance
TARGET_JANUS=Janus gateway server
PROFILE_GRID_SIM=Grid simulators
PROFILE_ROBUST=Robust service
PROFILE_STANDALONE=Standalone mode
ERR_UNKNOWN_MODULE=Unknown module
ERR_UNKNOWN_UI_MODULE=Unknown UI module
ERR_MISSING_MODULE=Missing --module in CLI mode
ERR_INVALID_PROFILE=Invalid profile
ERR_ACTION_NOT_ALLOWED=Action not allowed for this profile
ERR_TARGET_NOT_ALLOWED=Target not allowed for this profile
ERR_NO_UI_BACKEND=No UI backend found. Install whiptail or dialog, or use CLI parameters.
HELP_LANG=Language (de|en|fr|es), default: de
HELP_PROFILE=Profile (grid-sim|robust|standalone), default: grid-sim
+60
View File
@@ -0,0 +1,60 @@
APP_TITLE=OSMTool Backup
MENU_CHOOSE_MODULE=Elegir modulo
MENU_INSTALL=Instalacion
MENU_STARTSTOP=Iniciar/detener/reiniciar
MENU_CLEANUP=Limpieza y reinicio
MENU_HEALTH=Comprobacion de salud
MENU_BACKUP=Copia BD/OAR
MENU_RESTORE=Restauracion BD/OAR
MENU_UPDATE=Actualizacion con punto de restauracion
MENU_CONFIG=Validacion de configuracion
MENU_REPORT=Reporte diario
MENU_SMOKE=Prueba rapida
MENU_CRON=Tareas cron
MENU_CHOOSE_ACTION=Elegir accion
MENU_CHOOSE_TARGET=Elegir destino
MENU_CHOOSE_PROFILE=Elegir perfil
ACTION_START=Iniciar
ACTION_STOP=Detener
ACTION_RESTART=Reiniciar
ACTION_CACHE_CLEAN=Eliminar carpetas de cache
ACTION_LOG_CLEAN=Eliminar logs
ACTION_TMP_CLEAN=Eliminar archivos temporales
ACTION_REBOOT=Reiniciar servidor
ACTION_HEALTH_RUN=Ejecutar comprobacion de salud
ACTION_DB_BACKUP=Copia de base Robust
ACTION_OAR_BACKUP=Copia de region en OAR
ACTION_FULL_BACKUP=Copia de base y OAR opcional
ACTION_RESTORE_LIST=Listar copias disponibles
ACTION_RESTORE_DB=Restaurar base de datos
ACTION_RESTORE_OAR=Restaurar region desde OAR
ACTION_RESTORE_FULL=Restaurar base y OAR opcional
ACTION_UPDATE_ROLLBACK=Crear punto de restauracion
ACTION_UPDATE_LIST=Listar puntos de rollback
ACTION_UPDATE_OPENSIM=Actualizar OpenSim
ACTION_UPDATE_JANUS=Actualizar Janus
ACTION_UPDATE_FULL=Actualizar OpenSim y Janus
ACTION_CONFIG_FILE=Validar un archivo de configuracion
ACTION_CONFIG_RUNTIME=Validar configuraciones runtime
ACTION_REPORT_GENERATE=Generar reporte diario
ACTION_SMOKE_RUN=Ejecutar prueba rapida iniciar/reiniciar/detener
ACTION_CRON_INSTALL=Instalar/actualizar tareas cron
ACTION_CRON_LIST=Listar tareas cron
ACTION_CRON_REMOVE=Eliminar tareas cron
TARGET_GRID=Grid completo
TARGET_ROBUST=Solo Robust
TARGET_SIM1=Solo region primaria
TARGET_STANDALONE=Instancia standalone
TARGET_JANUS=Servidor pasarela Janus
PROFILE_GRID_SIM=Simuladores de grid
PROFILE_ROBUST=Servicio Robust
PROFILE_STANDALONE=Modo standalone
ERR_UNKNOWN_MODULE=Modulo desconocido
ERR_UNKNOWN_UI_MODULE=Modulo UI desconocido
ERR_MISSING_MODULE=Falta --module en modo CLI
ERR_INVALID_PROFILE=Perfil invalido
ERR_ACTION_NOT_ALLOWED=Accion no permitida para este perfil
ERR_TARGET_NOT_ALLOWED=Destino no permitido para este perfil
ERR_NO_UI_BACKEND=No se encontro backend UI. Instala whiptail o dialog, o usa parametros CLI.
HELP_LANG=Idioma (de|en|fr|es), por defecto: de
HELP_PROFILE=Perfil (grid-sim|robust|standalone), por defecto: grid-sim
+60
View File
@@ -0,0 +1,60 @@
APP_TITLE=OSMTool Backup
MENU_CHOOSE_MODULE=Choisir un module
MENU_INSTALL=Installation
MENU_STARTSTOP=Demarrer/arreter/redemarrer
MENU_CLEANUP=Nettoyage et redemarrage
MENU_HEALTH=Verification de sante
MENU_BACKUP=Sauvegarde BD/OAR
MENU_RESTORE=Restauration BD/OAR
MENU_UPDATE=Mise a jour avec retour arriere
MENU_CONFIG=Validation de configuration
MENU_REPORT=Rapport quotidien
MENU_SMOKE=Test de fumee
MENU_CRON=Taches cron
MENU_CHOOSE_ACTION=Choisir une action
MENU_CHOOSE_TARGET=Choisir une cible
MENU_CHOOSE_PROFILE=Choisir un profil
ACTION_START=Demarrer
ACTION_STOP=Arreter
ACTION_RESTART=Redemarrer
ACTION_CACHE_CLEAN=Supprimer les dossiers de cache
ACTION_LOG_CLEAN=Supprimer les journaux
ACTION_TMP_CLEAN=Supprimer les fichiers temporaires
ACTION_REBOOT=Redemarrer le serveur
ACTION_HEALTH_RUN=Executer la verification de sante
ACTION_DB_BACKUP=Sauvegarder la base Robust
ACTION_OAR_BACKUP=Sauvegarder la region en OAR
ACTION_FULL_BACKUP=Sauvegarder la base et OAR optionnel
ACTION_RESTORE_LIST=Afficher les sauvegardes disponibles
ACTION_RESTORE_DB=Restaurer la base de donnees
ACTION_RESTORE_OAR=Restaurer la region depuis OAR
ACTION_RESTORE_FULL=Restaurer la base et OAR optionnel
ACTION_UPDATE_ROLLBACK=Creer un point de retour arriere
ACTION_UPDATE_LIST=Afficher les points de rollback
ACTION_UPDATE_OPENSIM=Mettre a jour OpenSim
ACTION_UPDATE_JANUS=Mettre a jour Janus
ACTION_UPDATE_FULL=Mettre a jour OpenSim et Janus
ACTION_CONFIG_FILE=Valider un fichier de configuration
ACTION_CONFIG_RUNTIME=Valider les configurations runtime
ACTION_REPORT_GENERATE=Generer le rapport quotidien
ACTION_SMOKE_RUN=Executer le test de fumee demarrer/redemarrer/arreter
ACTION_CRON_INSTALL=Installer/mettre a jour les taches cron
ACTION_CRON_LIST=Afficher les taches cron
ACTION_CRON_REMOVE=Supprimer les taches cron
TARGET_GRID=Grille complete
TARGET_ROBUST=Robust uniquement
TARGET_SIM1=Region primaire uniquement
TARGET_STANDALONE=Instance standalone
TARGET_JANUS=Serveur passerelle Janus
PROFILE_GRID_SIM=Simulateurs de grille
PROFILE_ROBUST=Service Robust
PROFILE_STANDALONE=Mode standalone
ERR_UNKNOWN_MODULE=Module inconnu
ERR_UNKNOWN_UI_MODULE=Module UI inconnu
ERR_MISSING_MODULE=Option --module manquante en mode CLI
ERR_INVALID_PROFILE=Profil invalide
ERR_ACTION_NOT_ALLOWED=Action non autorisee pour ce profil
ERR_TARGET_NOT_ALLOWED=Cible non autorisee pour ce profil
ERR_NO_UI_BACKEND=Aucun backend UI trouve. Installez whiptail ou dialog, ou utilisez les parametres CLI.
HELP_LANG=Langue (de|en|fr|es), defaut: de
HELP_PROFILE=Profil (grid-sim|robust|standalone), defaut: grid-sim
+125
View File
@@ -0,0 +1,125 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./osmtool_core.sh
source "$SCRIPT_DIR/osmtool_core.sh"
usage() {
cat <<'EOF'
Usage:
osmtool_cleanup.sh [--workdir <path>] --action <cache-clean|log-clean|tmp-clean|reboot> [--dry-run <true|false>]
Examples:
osmtool_cleanup.sh --action log-clean --workdir /opt --dry-run true
osmtool_cleanup.sh --action cache-clean --workdir /opt/opensim
EOF
print_usage_common
}
realpath_safe() {
local p="$1"
python3 -c 'import os,sys; print(os.path.realpath(sys.argv[1]))' "$p" 2>/dev/null || echo "$p"
}
assert_safe_workdir() {
local rp
rp="$(realpath_safe "$WORKDIR")"
[[ -n "$rp" ]] || die "Invalid workdir"
[[ "$rp" != "/" ]] || die "Refusing cleanup on root directory"
[[ "$rp" != "/opt" ]] || die "Refusing cleanup on /opt root; choose a dedicated OpenSim workdir"
[[ -d "$rp" ]] || die "Workdir not found: $rp"
WORKDIR="$rp"
}
run_delete() {
local path="$1"
if [[ "$DRY_RUN" == "true" ]]; then
log INFO "[DRY-RUN] Would remove: $path"
else
rm -rf "$path"
log INFO "Removed: $path"
fi
}
run_delete_file() {
local path="$1"
if [[ "$DRY_RUN" == "true" ]]; then
log INFO "[DRY-RUN] Would delete file: $path"
else
rm -f "$path"
log INFO "Deleted file: $path"
fi
}
cache_clean() {
log INFO "Cleaning map tiles and cache under $WORKDIR"
local d
while IFS= read -r d; do
[[ -n "$d" ]] || continue
run_delete "$d"
done < <(find "$WORKDIR" -mindepth 2 -type d \( -name "assetcache" -o -name "MapTiles" -o -name "cache" \))
}
log_clean() {
log INFO "Cleaning log files under $WORKDIR"
local f
while IFS= read -r f; do
[[ -n "$f" ]] || continue
run_delete_file "$f"
done < <(find "$WORKDIR" -type f \( -name "*.log" -o -name "OpenSim.log" -o -name "ProblemRestarts.log" \))
}
tmp_clean() {
log INFO "Cleaning temporary files under $WORKDIR"
local f
while IFS= read -r f; do
[[ -n "$f" ]] || continue
run_delete_file "$f"
done < <(find "$WORKDIR" -type f \( -name "*.tmp" -o -name "*.temp" \))
}
safe_reboot() {
if confirm "Reboot" "Reboot server now?"; then
log INFO "Reboot requested"
sudo reboot
else
log INFO "Reboot canceled"
fi
}
WORKDIR="$DEFAULT_WORKDIR"
ACTION=""
PROFILE="grid-sim"
DRY_RUN="false"
while [[ $# -gt 0 ]]; do
case "$1" in
--workdir) WORKDIR="$2"; shift 2 ;;
--profile) PROFILE="$2"; shift 2 ;;
--action) ACTION="$2"; shift 2 ;;
--dry-run) DRY_RUN="$2"; shift 2 ;;
--help|-h) usage; exit 0 ;;
*) die "Unknown option: $1" ;;
esac
done
[[ -n "$ACTION" ]] || die "Missing --action"
validate_profile "$PROFILE"
ensure_profile_action_allowed cleanup "$PROFILE" "$ACTION"
case "$DRY_RUN" in true|false) ;; *) die "--dry-run must be true or false" ;; esac
assert_safe_workdir
log INFO "Using profile: $PROFILE"
log INFO "Dry-run mode: $DRY_RUN"
case "$ACTION" in
cache-clean) cache_clean ;;
log-clean) log_clean ;;
tmp-clean) tmp_clean ;;
reboot) safe_reboot ;;
*) die "Unsupported action: $ACTION" ;;
esac
+207
View File
@@ -0,0 +1,207 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./osmtool_core.sh
source "$SCRIPT_DIR/osmtool_core.sh"
usage() {
cat <<'EOF'
Usage:
osmtool_config.sh [--workdir <path>] --action <validate-file|validate-runtime>
[--file <path>] [--strict <true|false>]
Examples:
osmtool_config.sh --action validate-file --file /opt/sim1/bin/OpenSim.ini
osmtool_config.sh --action validate-runtime --profile grid-sim --workdir /opt
EOF
print_usage_common
}
trim() {
local value="$1"
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
printf '%s' "$value"
}
check_runtime_file() {
local file="$1"
local errors_before="$VALIDATION_ERRORS"
if [[ ! -f "$file" ]]; then
log ERROR "Missing config file: $file"
VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1))
return 1
fi
validate_file "$file"
if [[ "$VALIDATION_ERRORS" -gt "$errors_before" ]]; then
return 1
fi
return 0
}
validate_ini_file() {
local file="$1"
local line_number=0
local line trimmed seen_section=false
while IFS= read -r line || [[ -n "$line" ]]; do
line_number=$((line_number + 1))
trimmed="$(trim "$line")"
[[ -z "$trimmed" ]] && continue
case "$trimmed" in
';'*|'#'*) continue ;;
'['*']')
seen_section=true
continue
;;
*=*)
continue
;;
.include*)
continue
;;
*)
log ERROR "Invalid INI syntax in $file:$line_number -> $line"
VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1))
;;
esac
done < "$file"
if [[ "$STRICT" == "true" && "$seen_section" == "false" ]]; then
log ERROR "INI file has no section headers: $file"
VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1))
fi
}
validate_json_file() {
local file="$1"
if command -v jq >/dev/null 2>&1; then
if ! jq empty "$file" >/dev/null 2>&1; then
log ERROR "Invalid JSON syntax: $file"
VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1))
fi
return 0
fi
if command -v python3 >/dev/null 2>&1; then
if ! python3 -m json.tool "$file" >/dev/null 2>&1; then
log ERROR "Invalid JSON syntax: $file"
VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1))
fi
return 0
fi
die "validate-json requires jq or python3"
}
validate_file() {
local file="$1"
[[ -f "$file" ]] || die "Config file not found: $file"
case "$file" in
*.ini|*.INI)
log INFO "Validating INI file: $file"
validate_ini_file "$file"
;;
*.json|*.JSON)
log INFO "Validating JSON file: $file"
validate_json_file "$file"
;;
*)
die "Unsupported config file type: $file"
;;
esac
}
validate_grid_runtime() {
local sim_ini
local sim_count=0
check_runtime_file "$WORKDIR/robust/bin/Robust.ini"
shopt -s nullglob
for sim_ini in "$WORKDIR"/sim*/bin/OpenSim.ini; do
sim_count=$((sim_count + 1))
check_runtime_file "$sim_ini"
done
shopt -u nullglob
if [[ "$sim_count" -eq 0 ]]; then
log ERROR "No region runtime configs found under $WORKDIR/sim*/bin/OpenSim.ini"
VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1))
fi
}
validate_robust_runtime() {
check_runtime_file "$WORKDIR/robust/bin/Robust.ini"
}
validate_standalone_runtime() {
check_runtime_file "$WORKDIR/standalone/bin/OpenSim.ini"
}
validate_runtime() {
case "$PROFILE" in
grid-sim) validate_grid_runtime ;;
robust) validate_robust_runtime ;;
standalone) validate_standalone_runtime ;;
esac
if [[ "$VALIDATION_ERRORS" -gt 0 ]]; then
die "Configuration validation failed with $VALIDATION_ERRORS issue(s)"
fi
log INFO "Configuration validation passed"
}
WORKDIR="$DEFAULT_WORKDIR"
ACTION=""
PROFILE="grid-sim"
FILE=""
STRICT="false"
VALIDATION_ERRORS=0
while [[ $# -gt 0 ]]; do
case "$1" in
--workdir) WORKDIR="$2"; shift 2 ;;
--profile) PROFILE="$2"; shift 2 ;;
--action) ACTION="$2"; shift 2 ;;
--file) FILE="$2"; shift 2 ;;
--strict) STRICT="$2"; shift 2 ;;
--help|-h) usage; exit 0 ;;
*) die "Unknown option: $1" ;;
esac
done
[[ -n "$ACTION" ]] || die "Missing --action"
validate_profile "$PROFILE"
ensure_profile_action_allowed config "$PROFILE" "$ACTION"
case "$STRICT" in
true|false) ;;
*) die "--strict must be true or false" ;;
esac
log INFO "Using profile: $PROFILE"
case "$ACTION" in
validate-file)
[[ -n "$FILE" ]] || die "validate-file requires --file"
validate_file "$FILE"
if [[ "$VALIDATION_ERRORS" -gt 0 ]]; then
die "Configuration validation failed with $VALIDATION_ERRORS issue(s)"
fi
log INFO "Configuration validation passed"
;;
validate-runtime)
validate_runtime
;;
*) die "Unsupported action: $ACTION" ;;
esac
+255
View File
@@ -0,0 +1,255 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
export DEFAULT_WORKDIR="/opt"
LANG_DIR="$ROOT_DIR/lang"
APP_LANG="${OSM_LANG:-de}"
LOG_FILE="${LOG_FILE:-$ROOT_DIR/osmtool_backup.log}"
declare -A I18N_CURRENT=()
declare -A I18N_EN=()
log() {
local level="$1"
shift
printf '[%s] [%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$level" "$*" | tee -a "$LOG_FILE"
}
die() {
log ERROR "$*"
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1"
}
normalize_lang() {
case "$1" in
de|en|fr|es) echo "$1" ;;
*) echo "de" ;;
esac
}
validate_profile() {
case "$1" in
grid-sim|robust|standalone) return 0 ;;
*) die "$(msg ERR_INVALID_PROFILE): $1" ;;
esac
}
profile_action_allowed() {
local module="$1"
local profile="$2"
local action="$3"
case "$module:$profile" in
install:grid-sim|install:robust|install:standalone)
case "$action" in bootstrap-server|server-check|prepare-ubuntu|install-opensim-deps|install-dotnet8|install-opensim|configure-opensim|configure-database|compile-janus|configure-janus|install-janus) return 0 ;; esac
;;
startstop:grid-sim|startstop:robust|startstop:standalone)
case "$action" in start|stop|restart) return 0 ;; esac
;;
cleanup:grid-sim|cleanup:robust|cleanup:standalone)
case "$action" in cache-clean|log-clean|tmp-clean|reboot) return 0 ;; esac
;;
health:grid-sim|health:robust|health:standalone)
case "$action" in run) return 0 ;; esac
;;
backup:grid-sim)
case "$action" in db-backup|oar-backup|full-backup) return 0 ;; esac
;;
backup:robust)
case "$action" in db-backup|full-backup) return 0 ;; esac
;;
backup:standalone)
case "$action" in oar-backup|full-backup) return 0 ;; esac
;;
cron:grid-sim|cron:robust|cron:standalone)
case "$action" in install|list|remove) return 0 ;; esac
;;
restore:grid-sim)
case "$action" in db-restore|oar-restore|full-restore|list-backups) return 0 ;; esac
;;
restore:robust)
case "$action" in db-restore|full-restore|list-backups) return 0 ;; esac
;;
restore:standalone)
case "$action" in oar-restore|full-restore|list-backups) return 0 ;; esac
;;
update:grid-sim|update:robust|update:standalone)
case "$action" in create-rollback|list-rollbacks|update-opensim|update-janus|full-update) return 0 ;; esac
;;
config:grid-sim|config:robust|config:standalone)
case "$action" in validate-file|validate-runtime) return 0 ;; esac
;;
report:grid-sim|report:robust|report:standalone)
case "$action" in generate) return 0 ;; esac
;;
smoke:grid-sim|smoke:robust|smoke:standalone)
case "$action" in run) return 0 ;; esac
;;
esac
return 1
}
ensure_profile_action_allowed() {
local module="$1"
local profile="$2"
local action="$3"
if ! profile_action_allowed "$module" "$profile" "$action"; then
die "$(msg ERR_ACTION_NOT_ALLOWED): module=$module profile=$profile action=$action"
fi
}
profile_target_allowed() {
local profile="$1"
local target="$2"
case "$profile" in
grid-sim)
case "$target" in grid|robust|sim1|region|janus) return 0 ;; esac
;;
robust)
case "$target" in robust|janus) return 0 ;; esac
;;
standalone)
case "$target" in standalone|janus) return 0 ;; esac
;;
esac
return 1
}
ensure_profile_target_allowed() {
local profile="$1"
local target="$2"
if ! profile_target_allowed "$profile" "$target"; then
die "$(msg ERR_TARGET_NOT_ALLOWED): profile=$profile target=$target"
fi
}
load_lang_file() {
local lang_file="$1"
local target_array_name="$2"
local line key value
[[ -f "$lang_file" ]] || return 1
while IFS= read -r line || [[ -n "$line" ]]; do
[[ -z "$line" || "${line:0:1}" == "#" ]] && continue
[[ "$line" == *=* ]] || continue
key="${line%%=*}"
value="${line#*=}"
if [[ "$target_array_name" == "I18N_EN" ]]; then
I18N_EN["$key"]="$value"
else
I18N_CURRENT["$key"]="$value"
fi
done < "$lang_file"
return 0
}
init_i18n() {
APP_LANG="$(normalize_lang "$APP_LANG")"
I18N_CURRENT=()
I18N_EN=()
load_lang_file "$LANG_DIR/en.lang" I18N_EN || true
load_lang_file "$LANG_DIR/${APP_LANG}.lang" I18N_CURRENT || true
}
set_language() {
APP_LANG="$(normalize_lang "$1")"
export OSM_LANG="$APP_LANG"
init_i18n
}
msg() {
local key="$1"
if [[ -n "${I18N_CURRENT[$key]:-}" ]]; then
printf '%s' "${I18N_CURRENT[$key]}"
return
fi
if [[ -n "${I18N_EN[$key]:-}" ]]; then
printf '%s' "${I18N_EN[$key]}"
return
fi
printf '%s' "$key"
}
ui_backend() {
if command -v whiptail >/dev/null 2>&1; then
echo "whiptail"
return
fi
if command -v dialog >/dev/null 2>&1; then
echo "dialog"
return
fi
echo "none"
}
show_menu() {
local title="$1"
local text="$2"
shift 2
local backend
backend="$(ui_backend)"
case "$backend" in
whiptail)
whiptail --title "$title" --menu "$text" 20 80 12 "$@" 3>&1 1>&2 2>&3
;;
dialog)
dialog --clear --stdout --title "$title" --menu "$text" 20 80 12 "$@"
;;
none)
die "$(msg ERR_NO_UI_BACKEND)"
;;
esac
}
confirm() {
local title="$1"
local text="$2"
local backend
backend="$(ui_backend)"
case "$backend" in
whiptail)
whiptail --title "$title" --yesno "$text" 10 70
;;
dialog)
dialog --clear --title "$title" --yesno "$text" 10 70
;;
none)
return 1
;;
esac
}
print_usage_common() {
cat <<'EOF'
Global options:
--workdir <path> Base path (default: /opt)
--mode <cli|ui> Run in CLI mode (default) or menu mode
--lang <de|en|fr|es> Language selection
--profile <name> Runtime profile (grid-sim|robust|standalone)
--help Show help
EOF
}
# Ensure translations are available in every script that sources this core.
init_i18n
+203
View File
@@ -0,0 +1,203 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# shellcheck source=./osmtool_core.sh
source "$SCRIPT_DIR/osmtool_core.sh"
usage() {
cat <<'EOF'
Usage:
osmtool_cron.sh [--workdir <path>] --action <install|list|remove>
[--backup-schedule "<cron>"] [--restart-schedule "<cron>"] [--check-schedule "<cron>"] [--report-schedule "<cron>"] [--smoke-schedule "<cron>"]
[--db-user <user>] [--db-pass <pass>] [--db-name <name>]
[--region <name>] [--session <screen-name>] [--compress <true|false>]
Examples:
osmtool_cron.sh --action list
osmtool_cron.sh --action install --backup-schedule "0 3 * * *" --db-user root --db-pass secret --db-name opensim --region sim1
osmtool_cron.sh --action remove
EOF
print_usage_common
}
need_crontab() {
need_cmd crontab
}
quote_sq() {
local s="$1"
s="${s//\'/\'\"\'\"\'}"
printf "'%s'" "$s"
}
build_backup_cmd() {
local cmd
cmd="bash $ROOT_DIR/osmtool_main.sh --module backup --profile $PROFILE --workdir $WORKDIR --action full-backup --db-user $DB_USER --db-name $DB_NAME --region $REGION --session $SESSION_NAME --compress $COMPRESS"
if [[ -n "$DB_PASS" ]]; then
cmd+=" --db-pass $DB_PASS"
fi
printf '%s' "$cmd"
}
build_restart_cmd() {
printf '%s' "bash $ROOT_DIR/osmtool_main.sh --module startstop --profile $PROFILE --workdir $WORKDIR --target grid --action restart"
}
build_check_cmd() {
printf '%s' "bash $ROOT_DIR/osmtool_main.sh --module install --profile $PROFILE --workdir $WORKDIR --action server-check"
}
build_report_cmd() {
local cmd
cmd="bash $ROOT_DIR/osmtool_main.sh --module report --profile $PROFILE --workdir $WORKDIR --action generate --db-user $DB_USER --db-name $DB_NAME"
if [[ -n "$DB_PASS" ]]; then
cmd+=" --db-pass $DB_PASS"
fi
printf '%s' "$cmd"
}
build_smoke_cmd() {
printf '%s' "bash $ROOT_DIR/osmtool_main.sh --module smoke --profile $PROFILE --workdir $WORKDIR --action run"
}
cron_block_start="# OSMTOOL_CRON_BEGIN"
cron_block_end="# OSMTOOL_CRON_END"
emit_cron_block() {
local backup_cmd restart_cmd check_cmd report_cmd smoke_cmd
backup_cmd="$(build_backup_cmd)"
restart_cmd="$(build_restart_cmd)"
check_cmd="$(build_check_cmd)"
report_cmd="$(build_report_cmd)"
smoke_cmd="$(build_smoke_cmd)"
echo "$cron_block_start"
echo "SHELL=/bin/bash"
echo "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
echo "$BACKUP_SCHEDULE $backup_cmd >> $ROOT_DIR/osmtool_backup.log 2>&1"
echo "$RESTART_SCHEDULE $restart_cmd >> $ROOT_DIR/osmtool_backup.log 2>&1"
echo "$CHECK_SCHEDULE $check_cmd >> $ROOT_DIR/osmtool_backup.log 2>&1"
echo "$REPORT_SCHEDULE $report_cmd >> $ROOT_DIR/osmtool_backup.log 2>&1"
echo "$SMOKE_SCHEDULE $smoke_cmd >> $ROOT_DIR/osmtool_backup.log 2>&1"
echo "$cron_block_end"
}
existing_crontab() {
crontab -l 2>/dev/null || true
}
strip_osmtool_block() {
awk -v start="$cron_block_start" -v end="$cron_block_end" '
$0 == start {inblock=1; next}
$0 == end {inblock=0; next}
!inblock {print}
'
}
install_cron() {
local current tmp
[[ -n "$DB_USER" && -n "$DB_NAME" ]] || die "install cron requires --db-user and --db-name"
current="$(existing_crontab)"
tmp="$(mktemp)"
printf '%s\n' "$current" | strip_osmtool_block > "$tmp"
if [[ -s "$tmp" ]]; then
echo >> "$tmp"
fi
emit_cron_block >> "$tmp"
crontab "$tmp"
rm -f "$tmp"
log INFO "OSMTool cron jobs installed/updated"
}
list_cron() {
local current
current="$(existing_crontab)"
if printf '%s\n' "$current" | grep -q "^$cron_block_start$"; then
printf '%s\n' "$current" | awk -v start="$cron_block_start" -v end="$cron_block_end" '
$0 == start {inblock=1; print; next}
$0 == end {print; inblock=0; next}
inblock {print}
'
else
log INFO "No OSMTool cron block configured"
fi
}
remove_cron() {
local current tmp
current="$(existing_crontab)"
tmp="$(mktemp)"
printf '%s\n' "$current" | strip_osmtool_block > "$tmp"
crontab "$tmp"
rm -f "$tmp"
log INFO "OSMTool cron block removed"
}
WORKDIR="$DEFAULT_WORKDIR"
ACTION=""
PROFILE="grid-sim"
BACKUP_SCHEDULE="0 3 * * *"
RESTART_SCHEDULE="0 5 * * 1"
CHECK_SCHEDULE="*/15 * * * *"
REPORT_SCHEDULE="30 6 * * *"
SMOKE_SCHEDULE="45 6 * * *"
DB_USER="root"
DB_PASS=""
DB_NAME="opensim"
REGION="sim1"
SESSION_NAME="sim1"
COMPRESS="true"
while [[ $# -gt 0 ]]; do
case "$1" in
--workdir) WORKDIR="$2"; shift 2 ;;
--profile) PROFILE="$2"; shift 2 ;;
--action) ACTION="$2"; shift 2 ;;
--backup-schedule) BACKUP_SCHEDULE="$2"; shift 2 ;;
--restart-schedule) RESTART_SCHEDULE="$2"; shift 2 ;;
--check-schedule) CHECK_SCHEDULE="$2"; shift 2 ;;
--report-schedule) REPORT_SCHEDULE="$2"; shift 2 ;;
--smoke-schedule) SMOKE_SCHEDULE="$2"; shift 2 ;;
--db-user) DB_USER="$2"; shift 2 ;;
--db-pass) DB_PASS="$2"; shift 2 ;;
--db-name) DB_NAME="$2"; shift 2 ;;
--region) REGION="$2"; shift 2 ;;
--session) SESSION_NAME="$2"; shift 2 ;;
--compress) COMPRESS="$2"; shift 2 ;;
--help|-h) usage; exit 0 ;;
*) die "Unknown option: $1" ;;
esac
done
[[ -n "$ACTION" ]] || die "Missing --action"
validate_profile "$PROFILE"
need_crontab
case "$ACTION" in
install|list|remove) ;;
*) die "Unsupported action: $ACTION" ;;
esac
case "$COMPRESS" in
true|false) ;;
*) die "--compress must be true or false" ;;
esac
case "$ACTION" in
install) install_cron ;;
list) list_cron ;;
remove) remove_cron ;;
esac
+170
View File
@@ -0,0 +1,170 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./osmtool_core.sh
source "$SCRIPT_DIR/osmtool_core.sh"
usage() {
cat <<'EOF'
Usage:
osmtool_data.sh [--workdir <path>] --action <db-backup|oar-backup|full-backup>
[--db-user <user>] [--db-pass <pass>] [--db-name <name>]
[--region <name>] [--session <screen-name>] [--compress <true|false>]
Examples:
osmtool_data.sh --action db-backup --db-user opensim --db-pass secret --db-name robust
osmtool_data.sh --action oar-backup --region sim1 --session sim1 --workdir /opt
EOF
print_usage_common
}
timestamp() {
date '+%Y%m%d_%H%M%S'
}
backup_dir() {
printf '%s' "$WORKDIR/backup"
}
ensure_backup_dir() {
local dir
dir="$(backup_dir)"
if mkdir -p "$dir" 2>/dev/null; then
return 0
fi
sudo mkdir -p "$dir"
sudo chown "$(id -u):$(id -g)" "$dir"
}
compress_if_enabled() {
local file_path="$1"
if [[ "$COMPRESS" != "true" ]]; then
printf '%s' "$file_path"
return 0
fi
need_cmd gzip
gzip -f "$file_path"
printf '%s.gz' "$file_path"
}
resolve_default_session() {
if [[ -n "$SESSION_NAME" ]]; then
printf '%s' "$SESSION_NAME"
return 0
fi
if [[ -n "$REGION" ]]; then
printf '%s' "$REGION"
return 0
fi
printf 'sim1'
}
screen_session_exists() {
local name="$1"
screen -list 2>/dev/null | grep -qE "\\.${name}[[:space:]]"
}
send_to_screen() {
local session_name="$1"
local command_text="$2"
screen -S "$session_name" -p 0 -X stuff "${command_text}^M"
}
db_backup_mysql() {
local out
need_cmd mysqldump
[[ -n "$DB_USER" && -n "$DB_PASS" && -n "$DB_NAME" ]] || die "db-backup(mysql) requires --db-user --db-pass --db-name"
ensure_backup_dir
out="$(backup_dir)/robust_${DB_NAME}_$(timestamp).sql"
log INFO "Creating MySQL backup: $out"
mysqldump -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" > "$out"
out="$(compress_if_enabled "$out")"
log INFO "DB backup created: $out"
}
db_backup() {
db_backup_mysql
}
oar_backup() {
local session_name out command_text
[[ -n "$REGION" ]] || die "oar-backup requires --region"
session_name="$(resolve_default_session)"
ensure_backup_dir
out="$(backup_dir)/${REGION}_$(timestamp).oar"
if ! screen_session_exists "$session_name"; then
die "Screen session not running: $session_name"
fi
# OpenSim console command to export current region as OAR.
command_text="save oar $out"
log INFO "Sending OAR export command to screen session '$session_name': $command_text"
send_to_screen "$session_name" "$command_text"
log INFO "OAR export command sent. Validate completion in the session log/output."
}
full_backup() {
db_backup
if [[ -n "$REGION" ]]; then
oar_backup
else
log INFO "No --region provided. DB backup only in full-backup mode."
fi
}
WORKDIR="$DEFAULT_WORKDIR"
ACTION=""
DB_USER=""
DB_PASS=""
DB_NAME=""
REGION=""
SESSION_NAME=""
COMPRESS="false"
PROFILE="grid-sim"
while [[ $# -gt 0 ]]; do
case "$1" in
--workdir) WORKDIR="$2"; shift 2 ;;
--profile) PROFILE="$2"; shift 2 ;;
--action) ACTION="$2"; shift 2 ;;
--db-user) DB_USER="$2"; shift 2 ;;
--db-pass) DB_PASS="$2"; shift 2 ;;
--db-name) DB_NAME="$2"; shift 2 ;;
--region) REGION="$2"; shift 2 ;;
--session) SESSION_NAME="$2"; shift 2 ;;
--compress) COMPRESS="$2"; shift 2 ;;
--help|-h) usage; exit 0 ;;
*) die "Unknown option: $1" ;;
esac
done
[[ -n "$ACTION" ]] || die "Missing --action"
validate_profile "$PROFILE"
ensure_profile_action_allowed backup "$PROFILE" "$ACTION"
case "$COMPRESS" in
true|false) ;;
*) die "--compress must be true or false" ;;
esac
log INFO "Using profile: $PROFILE"
case "$ACTION" in
db-backup) db_backup ;;
oar-backup) oar_backup ;;
full-backup) full_backup ;;
*) die "Unsupported action: $ACTION" ;;
esac
+268
View File
@@ -0,0 +1,268 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# shellcheck source=./osmtool_core.sh
source "$SCRIPT_DIR/osmtool_core.sh"
usage() {
cat <<'EOF'
Usage:
osmtool_health.sh [--workdir <path>] --action <run>
[--expected-screens <csv|none>] [--ports <csv|none>] [--db-user <user>] [--db-pass <pass>] [--db-name <name>]
[--log-file <path>] [--warning-threshold <n>]
Examples:
osmtool_health.sh --action run --workdir /opt
osmtool_health.sh --action run --workdir /opt --expected-screens robustserver,sim1 --ports 9000,8002,8088
EOF
print_usage_common
}
split_csv() {
local csv="$1"
local token
local IFS=','
read -r -a _TOKENS <<< "$csv"
for token in "${_TOKENS[@]}"; do
token="${token## }"
token="${token%% }"
[[ -n "$token" ]] && printf '%s\n' "$token"
done
}
report_ok() {
log INFO "[OK] $1"
}
report_warn() {
log INFO "[WARN] $1"
}
report_err() {
log ERROR "[FAIL] $1"
}
check_screens() {
local fail_count=0
local screen_name
if [[ -z "$EXPECTED_SCREENS" ]]; then
report_warn "Screen check skipped (no expected screens configured)"
return 0
fi
while IFS= read -r screen_name; do
[[ -n "$screen_name" ]] || continue
if screen -list 2>/dev/null | grep -qE "\\.${screen_name}[[:space:]]"; then
report_ok "Screen session running: $screen_name"
else
report_err "Screen session missing: $screen_name"
fail_count=$((fail_count + 1))
fi
done < <(split_csv "$EXPECTED_SCREENS")
return "$fail_count"
}
check_ports() {
local fail_count=0
local port
local cmd_ss=0
if [[ -z "$PORTS" ]]; then
report_warn "Port check skipped (no ports configured)"
return 0
fi
if command -v ss >/dev/null 2>&1; then
cmd_ss=1
elif ! command -v netstat >/dev/null 2>&1; then
report_warn "Port check skipped (neither ss nor netstat available)"
return 0
fi
while IFS= read -r port; do
[[ -n "$port" ]] || continue
if [[ "$cmd_ss" -eq 1 ]]; then
if ss -ltnH | awk '{print $4}' | grep -qE "[:.]${port}$"; then
report_ok "Port listening: $port"
else
report_err "Port not listening: $port"
fail_count=$((fail_count + 1))
fi
else
if netstat -ltn 2>/dev/null | awk '{print $4}' | grep -qE "[:.]${port}$"; then
report_ok "Port listening: $port"
else
report_err "Port not listening: $port"
fail_count=$((fail_count + 1))
fi
fi
done < <(split_csv "$PORTS")
return "$fail_count"
}
check_database() {
local fail_count=0
if [[ -z "$DB_USER" || -z "$DB_NAME" ]]; then
report_warn "Database check skipped (missing --db-user/--db-name)"
return 0
fi
if ! command -v mariadb >/dev/null 2>&1; then
report_err "Database check failed: mariadb client missing"
return 1
fi
if [[ -n "$DB_PASS" ]]; then
if mariadb -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT 1" >/dev/null 2>&1; then
report_ok "Database reachable: $DB_NAME"
else
report_err "Database not reachable: $DB_NAME"
fail_count=$((fail_count + 1))
fi
else
if mariadb -u"$DB_USER" "$DB_NAME" -e "SELECT 1" >/dev/null 2>&1; then
report_ok "Database reachable: $DB_NAME"
else
report_err "Database not reachable: $DB_NAME"
fail_count=$((fail_count + 1))
fi
fi
return "$fail_count"
}
count_log_warnings() {
local file="$1"
grep -Eic 'error|warn|exception|fatal' "$file" 2>/dev/null || true
}
check_logs() {
local warn_count total=0
local default_logs=()
if [[ -n "$LOG_FILE_PATH" ]]; then
default_logs+=("$LOG_FILE_PATH")
else
default_logs+=("$ROOT_DIR/osmtool_backup.log")
default_logs+=("$WORKDIR/robust/bin/OpenSim.log")
fi
for logf in "${default_logs[@]}"; do
if [[ -f "$logf" ]]; then
warn_count="$(count_log_warnings "$logf")"
report_ok "Log scan: $logf -> $warn_count matches"
total=$((total + warn_count))
else
report_warn "Log file not found, skipped: $logf"
fi
done
if [[ "$total" -gt "$WARNING_THRESHOLD" ]]; then
report_err "Log warning threshold exceeded: $total > $WARNING_THRESHOLD"
return 1
fi
report_ok "Log warning threshold OK: $total <= $WARNING_THRESHOLD"
return 0
}
default_screens_for_profile() {
case "$1" in
grid-sim) echo "robustserver,sim1" ;;
robust) echo "robustserver" ;;
standalone) echo "standalone" ;;
esac
}
default_ports_for_profile() {
case "$1" in
grid-sim) echo "8002,9000,9001,8088" ;;
robust) echo "8002,9000,9001" ;;
standalone) echo "9000" ;;
esac
}
run_health() {
local fails=0
log INFO "Running health checks for profile: $PROFILE"
log INFO "Using workdir: $WORKDIR"
if ! check_screens; then
fails=$((fails + 1))
fi
if ! check_ports; then
fails=$((fails + 1))
fi
if ! check_database; then
fails=$((fails + 1))
fi
if ! check_logs; then
fails=$((fails + 1))
fi
if [[ "$fails" -eq 0 ]]; then
report_ok "Healthcheck PASSED"
return 0
fi
report_err "Healthcheck FAILED with $fails failing check group(s)"
return 1
}
WORKDIR="$DEFAULT_WORKDIR"
ACTION=""
PROFILE="grid-sim"
EXPECTED_SCREENS=""
PORTS=""
DB_USER=""
DB_PASS=""
DB_NAME=""
LOG_FILE_PATH=""
WARNING_THRESHOLD=50
while [[ $# -gt 0 ]]; do
case "$1" in
--workdir) WORKDIR="$2"; shift 2 ;;
--profile) PROFILE="$2"; shift 2 ;;
--action) ACTION="$2"; shift 2 ;;
--expected-screens) EXPECTED_SCREENS="$2"; shift 2 ;;
--ports) PORTS="$2"; shift 2 ;;
--db-user) DB_USER="$2"; shift 2 ;;
--db-pass) DB_PASS="$2"; shift 2 ;;
--db-name) DB_NAME="$2"; shift 2 ;;
--log-file) LOG_FILE_PATH="$2"; shift 2 ;;
--warning-threshold) WARNING_THRESHOLD="$2"; shift 2 ;;
--help|-h) usage; exit 0 ;;
*) die "Unknown option: $1" ;;
esac
done
[[ -n "$ACTION" ]] || die "Missing --action"
validate_profile "$PROFILE"
ensure_profile_action_allowed health "$PROFILE" "$ACTION"
if [[ -z "$EXPECTED_SCREENS" ]]; then
EXPECTED_SCREENS="$(default_screens_for_profile "$PROFILE")"
elif [[ "$EXPECTED_SCREENS" == "none" ]]; then
EXPECTED_SCREENS=""
fi
if [[ -z "$PORTS" ]]; then
PORTS="$(default_ports_for_profile "$PROFILE")"
elif [[ "$PORTS" == "none" ]]; then
PORTS=""
fi
case "$ACTION" in
run) run_health ;;
*) die "Unsupported action: $ACTION" ;;
esac
File diff suppressed because it is too large Load Diff
+293
View File
@@ -0,0 +1,293 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# shellcheck source=./osmtool_core.sh
source "$SCRIPT_DIR/osmtool_core.sh"
usage() {
cat <<'EOF'
Usage:
osmtool_report.sh [--workdir <path>] --action <generate>
[--output <file>] [--expected-screens <csv|none>] [--ports <csv|none>]
[--db-user <user>] [--db-pass <pass>] [--db-name <name>] [--log-file <path>]
Examples:
osmtool_report.sh --action generate --workdir /opt --db-user opensim --db-pass secret --db-name robust
osmtool_report.sh --action generate --workdir /opt --output /opt/reports/manual_report.txt
EOF
print_usage_common
}
timestamp_compact() {
date '+%Y%m%d_%H%M%S'
}
timestamp_iso() {
date '+%Y-%m-%d %H:%M:%S'
}
split_csv() {
local csv="$1"
local token
local IFS=','
read -r -a _TOKENS <<< "$csv"
for token in "${_TOKENS[@]}"; do
token="${token## }"
token="${token%% }"
[[ -n "$token" ]] && printf '%s\n' "$token"
done
}
reports_dir() {
printf '%s' "$WORKDIR/reports"
}
ensure_reports_dir() {
local dir
dir="$(reports_dir)"
if mkdir -p "$dir" 2>/dev/null; then
return 0
fi
sudo mkdir -p "$dir"
sudo chown "$(id -u):$(id -g)" "$dir"
}
latest_file() {
local dir="$1"
local pattern="$2"
[[ -d "$dir" ]] || return 0
find "$dir" -maxdepth 1 -type f -name "$pattern" -printf '%T@ %p\n' 2>/dev/null | \
sort -rn | head -1 | cut -d' ' -f2-
}
format_file_age_hours() {
local path="$1"
local now ts age
[[ -f "$path" ]] || {
printf 'n/a'
return 0
}
now="$(date +%s)"
ts="$(stat -c %Y "$path" 2>/dev/null || echo 0)"
if [[ "$ts" -le 0 ]]; then
printf 'n/a'
return 0
fi
age=$(( (now - ts) / 3600 ))
printf '%sh' "$age"
}
screen_status_lines() {
local screen_name
if [[ -z "$EXPECTED_SCREENS" ]]; then
printf 'screen: skipped\n'
return 0
fi
while IFS= read -r screen_name; do
[[ -n "$screen_name" ]] || continue
if screen -list 2>/dev/null | grep -qE "\\.${screen_name}[[:space:]]"; then
printf 'screen:%s=up\n' "$screen_name"
else
printf 'screen:%s=down\n' "$screen_name"
fi
done < <(split_csv "$EXPECTED_SCREENS")
}
port_status_lines() {
local port
local use_ss=0
if [[ -z "$PORTS" ]]; then
printf 'port: skipped\n'
return 0
fi
if command -v ss >/dev/null 2>&1; then
use_ss=1
elif ! command -v netstat >/dev/null 2>&1; then
printf 'port: unavailable (no ss/netstat)\n'
return 0
fi
while IFS= read -r port; do
[[ -n "$port" ]] || continue
if [[ "$use_ss" -eq 1 ]]; then
if ss -ltnH | awk '{print $4}' | grep -qE "[:.]${port}$"; then
printf 'port:%s=listen\n' "$port"
else
printf 'port:%s=closed\n' "$port"
fi
else
if netstat -ltn 2>/dev/null | awk '{print $4}' | grep -qE "[:.]${port}$"; then
printf 'port:%s=listen\n' "$port"
else
printf 'port:%s=closed\n' "$port"
fi
fi
done < <(split_csv "$PORTS")
}
database_status() {
if [[ -z "$DB_USER" || -z "$DB_NAME" ]]; then
printf 'db: skipped\n'
return 0
fi
if ! command -v mariadb >/dev/null 2>&1; then
printf 'db:%s=client-missing\n' "$DB_NAME"
return 0
fi
if [[ -n "$DB_PASS" ]]; then
if mariadb -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT 1" >/dev/null 2>&1; then
printf 'db:%s=ok\n' "$DB_NAME"
else
printf 'db:%s=fail\n' "$DB_NAME"
fi
else
if mariadb -u"$DB_USER" "$DB_NAME" -e "SELECT 1" >/dev/null 2>&1; then
printf 'db:%s=ok\n' "$DB_NAME"
else
printf 'db:%s=fail\n' "$DB_NAME"
fi
fi
}
log_warning_count() {
local file="$1"
[[ -f "$file" ]] || {
printf 'n/a'
return 0
}
grep -Eic 'error|warn|exception|fatal' "$file" 2>/dev/null || printf '0'
}
default_screens_for_profile() {
case "$1" in
grid-sim) echo "robustserver,sim1" ;;
robust) echo "robustserver" ;;
standalone) echo "standalone" ;;
esac
}
default_ports_for_profile() {
case "$1" in
grid-sim) echo "8002,9000,9001,8088" ;;
robust) echo "8002,9000,9001" ;;
standalone) echo "9000" ;;
esac
}
generate_report() {
local out report_title log_target
local newest_sql newest_oar newest_rollback
ensure_reports_dir
if [[ -n "$OUTPUT_FILE" ]]; then
out="$OUTPUT_FILE"
else
out="$(reports_dir)/osm_report_$(timestamp_compact).txt"
fi
newest_sql="$(latest_file "$WORKDIR/backup" 'robust_*.sql*')"
newest_oar="$(latest_file "$WORKDIR/backup" '*.oar')"
newest_rollback="$(latest_file "$WORKDIR/rollback" '*.manifest')"
log_target="$LOG_FILE_PATH"
if [[ -z "$log_target" ]]; then
log_target="$ROOT_DIR/osmtool_backup.log"
fi
report_title="OSMTool Daily Status Report"
{
printf '%s\n' "$report_title"
printf 'generated_at: %s\n' "$(timestamp_iso)"
printf 'profile: %s\n' "$PROFILE"
printf 'workdir: %s\n' "$WORKDIR"
printf '\n'
printf '[runtime]\n'
screen_status_lines
port_status_lines
database_status
printf '\n'
printf '[backup]\n'
printf 'sql_latest: %s\n' "${newest_sql:-none}"
printf 'sql_age: %s\n' "$(format_file_age_hours "${newest_sql:-}")"
printf 'oar_latest: %s\n' "${newest_oar:-none}"
printf 'oar_age: %s\n' "$(format_file_age_hours "${newest_oar:-}")"
printf '\n'
printf '[rollback]\n'
printf 'latest_manifest: %s\n' "${newest_rollback:-none}"
printf 'manifest_age: %s\n' "$(format_file_age_hours "${newest_rollback:-}")"
printf '\n'
printf '[logs]\n'
printf 'file: %s\n' "$log_target"
printf 'warning_matches: %s\n' "$(log_warning_count "$log_target")"
} > "$out"
log INFO "Report generated: $out"
cat "$out"
}
WORKDIR="$DEFAULT_WORKDIR"
ACTION=""
PROFILE="grid-sim"
OUTPUT_FILE=""
EXPECTED_SCREENS=""
PORTS=""
DB_USER=""
DB_PASS=""
DB_NAME=""
LOG_FILE_PATH=""
while [[ $# -gt 0 ]]; do
case "$1" in
--workdir) WORKDIR="$2"; shift 2 ;;
--profile) PROFILE="$2"; shift 2 ;;
--action) ACTION="$2"; shift 2 ;;
--output) OUTPUT_FILE="$2"; shift 2 ;;
--expected-screens) EXPECTED_SCREENS="$2"; shift 2 ;;
--ports) PORTS="$2"; shift 2 ;;
--db-user) DB_USER="$2"; shift 2 ;;
--db-pass) DB_PASS="$2"; shift 2 ;;
--db-name) DB_NAME="$2"; shift 2 ;;
--log-file) LOG_FILE_PATH="$2"; shift 2 ;;
--help|-h) usage; exit 0 ;;
*) die "Unknown option: $1" ;;
esac
done
[[ -n "$ACTION" ]] || die "Missing --action"
validate_profile "$PROFILE"
ensure_profile_action_allowed report "$PROFILE" "$ACTION"
if [[ -z "$EXPECTED_SCREENS" ]]; then
EXPECTED_SCREENS="$(default_screens_for_profile "$PROFILE")"
elif [[ "$EXPECTED_SCREENS" == "none" ]]; then
EXPECTED_SCREENS=""
fi
if [[ -z "$PORTS" ]]; then
PORTS="$(default_ports_for_profile "$PROFILE")"
elif [[ "$PORTS" == "none" ]]; then
PORTS=""
fi
log INFO "Using profile: $PROFILE"
case "$ACTION" in
generate) generate_report ;;
*) die "Unsupported action: $ACTION" ;;
esac
+260
View File
@@ -0,0 +1,260 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./osmtool_core.sh
source "$SCRIPT_DIR/osmtool_core.sh"
usage() {
cat <<'EOF'
Usage:
osmtool_restore.sh [--workdir <path>] --action <db-restore|oar-restore|full-restore|list-backups>
[--db-user <user>] [--db-pass <pass>] [--db-name <name>]
[--region <name>] [--session <screen-name>] [--file <backup-file>]
[--dry-run <true|false>]
Examples:
osmtool_restore.sh --action list-backups --workdir /opt
osmtool_restore.sh --action db-restore --db-user opensim --db-pass secret --db-name robust --file /opt/backup/robust_robust_20260401_120000.sql
EOF
print_usage_common
}
backup_dir() {
printf '%s' "$WORKDIR/backup"
}
ensure_backup_dir() {
local dir
dir="$(backup_dir)"
if [[ ! -d "$dir" ]]; then
die "Backup directory does not exist: $dir"
fi
return 0
}
list_backups() {
local dir
dir="$(backup_dir)"
if [[ ! -d "$dir" ]]; then
log INFO "No backup directory found: $dir"
return 0
fi
log INFO "SQL Backups:"
find "$dir" -maxdepth 1 -name "robust_*.sql*" -type f -printf '%T@ %p\n' 2>/dev/null | \
sort -rn | cut -d' ' -f2- | head -5 || true
log INFO "OAR Backups:"
find "$dir" -maxdepth 1 -name "*.oar" -type f -printf '%T@ %p\n' 2>/dev/null | \
sort -rn | cut -d' ' -f2- | head -5 || true
}
resolve_default_session() {
if [[ -n "$SESSION_NAME" ]]; then
printf '%s' "$SESSION_NAME"
return 0
fi
if [[ -n "$REGION" ]]; then
printf '%s' "$REGION"
return 0
fi
printf 'sim1'
}
screen_session_exists() {
local name="$1"
screen -list 2>/dev/null | grep -qE "\\.${name}[[:space:]]"
}
send_to_screen() {
local session_name="$1"
local command_text="$2"
screen -S "$session_name" -p 0 -X stuff "${command_text}^M"
}
validate_sql_file() {
local file="$1"
if [[ ! -f "$file" ]]; then
die "SQL backup file not found: $file"
fi
# Check for gzip compression
if [[ "$file" =~ \.gz$ ]]; then
need_cmd zcat
return 0
fi
return 0
}
validate_oar_file() {
local file="$1"
if [[ ! -f "$file" ]]; then
die "OAR backup file not found: $file"
fi
if [[ ! "$file" =~ \.oar$ ]]; then
die "File does not appear to be an OAR backup: $file"
fi
return 0
}
db_restore_mysql() {
local file sql_cmd
[[ -n "$DB_USER" && -n "$DB_PASS" && -n "$DB_NAME" ]] || die "db-restore requires --db-user --db-pass --db-name"
[[ -n "$FILE" ]] || die "db-restore requires --file"
file="$FILE"
validate_sql_file "$file"
need_cmd mysql
if [[ "$file" =~ \.gz$ ]]; then
sql_cmd="zcat '$file' | mysql -u'$DB_USER' -p'$DB_PASS' '$DB_NAME'"
else
sql_cmd="mysql -u'$DB_USER' -p'$DB_PASS' '$DB_NAME' < '$file'"
fi
log INFO "Database restore command:"
log INFO "$sql_cmd"
if [[ "$DRY_RUN" == "true" ]]; then
log INFO "[DRY-RUN] Not executing database restore"
return 0
fi
log INFO "Restoring database from: $file"
if [[ "$file" =~ \.gz$ ]]; then
zcat "$file" | mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" || die "Database restore failed"
else
mysql -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" < "$file" || die "Database restore failed"
fi
log INFO "Database restore completed successfully from: $file"
}
db_restore() {
db_restore_mysql
}
oar_restore() {
local session_name file cmd
[[ -n "$REGION" ]] || die "oar-restore requires --region"
[[ -n "$FILE" ]] || die "oar-restore requires --file"
file="$FILE"
validate_oar_file "$file"
session_name="$(resolve_default_session)"
if ! screen_session_exists "$session_name"; then
die "Screen session not running: $session_name"
fi
cmd="load oar $file"
log INFO "OAR restore command:"
log INFO "$cmd"
if [[ "$DRY_RUN" == "true" ]]; then
log INFO "[DRY-RUN] Not executing OAR restore"
log INFO "To complete restore, region '$session_name' must be running and responsive"
return 0
fi
log INFO "Sending OAR restore command to screen session '$session_name'"
send_to_screen "$session_name" "$cmd"
log INFO "OAR restore command sent. Region import may take some time. Check session output for completion."
}
full_restore() {
local has_sql has_oar
has_sql=false
has_oar=false
# Try to detect which restores to perform based on parameters
if [[ -n "$FILE" ]] && [[ "$FILE" =~ \.(sql|gz)$ ]]; then
has_sql=true
fi
if [[ -n "$FILE" ]] && [[ "$FILE" =~ \.oar$ ]]; then
has_oar=true
fi
if [[ "$has_sql" == "true" ]]; then
log INFO "Detected SQL backup file, proceeding with database restore..."
db_restore
fi
if [[ "$has_oar" == "true" ]]; then
log INFO "Detected OAR backup file, proceeding with OAR restore..."
if [[ -n "$REGION" ]]; then
oar_restore
else
log WARN "OAR file provided but no --region specified. Skipping OAR restore."
fi
fi
if [[ "$has_sql" == "false" && "$has_oar" == "false" ]]; then
die "full-restore requires --file with .sql/.sql.gz or .oar extension"
fi
}
WORKDIR="$DEFAULT_WORKDIR"
ACTION=""
DB_USER=""
DB_PASS=""
DB_NAME=""
REGION=""
SESSION_NAME=""
FILE=""
DRY_RUN="false"
PROFILE="grid-sim"
while [[ $# -gt 0 ]]; do
case "$1" in
--workdir) WORKDIR="$2"; shift 2 ;;
--profile) PROFILE="$2"; shift 2 ;;
--action) ACTION="$2"; shift 2 ;;
--db-user) DB_USER="$2"; shift 2 ;;
--db-pass) DB_PASS="$2"; shift 2 ;;
--db-name) DB_NAME="$2"; shift 2 ;;
--region) REGION="$2"; shift 2 ;;
--session) SESSION_NAME="$2"; shift 2 ;;
--file) FILE="$2"; shift 2 ;;
--dry-run) DRY_RUN="$2"; shift 2 ;;
--help|-h) usage; exit 0 ;;
*) die "Unknown option: $1" ;;
esac
done
[[ -n "$ACTION" ]] || die "Missing --action"
validate_profile "$PROFILE"
ensure_profile_action_allowed restore "$PROFILE" "$ACTION"
case "$DRY_RUN" in
true|false) ;;
*) die "--dry-run must be true or false" ;;
esac
log INFO "Using profile: $PROFILE"
case "$ACTION" in
db-restore) db_restore ;;
oar-restore) oar_restore ;;
full-restore) full_restore ;;
list-backups) ensure_backup_dir; list_backups ;;
*) die "Unsupported action: $ACTION" ;;
esac
+191
View File
@@ -0,0 +1,191 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# shellcheck source=./osmtool_core.sh
source "$SCRIPT_DIR/osmtool_core.sh"
usage() {
cat <<'EOF'
Usage:
osmtool_smoke.sh [--workdir <path>] --action <run>
[--target <grid|robust|sim1|standalone|janus|region>] [--name <region-screen-name>]
[--expected-screens <csv>] [--wait-after-start <sec>] [--wait-after-stop <sec>]
[--janus-prefix <path>]
Examples:
osmtool_smoke.sh --action run --profile grid-sim --workdir /opt
osmtool_smoke.sh --action run --profile robust --workdir /opt --expected-screens robustserver
EOF
print_usage_common
}
split_csv() {
local csv="$1"
local token
local IFS=','
read -r -a _TOKENS <<< "$csv"
for token in "${_TOKENS[@]}"; do
token="${token## }"
token="${token%% }"
[[ -n "$token" ]] && printf '%s\n' "$token"
done
}
default_target_for_profile() {
case "$1" in
grid-sim) echo "grid" ;;
robust) echo "robust" ;;
standalone) echo "standalone" ;;
esac
}
default_expected_screens_for_target() {
case "$1" in
grid) echo "robustserver,sim1" ;;
robust) echo "robustserver" ;;
sim1) echo "sim1" ;;
standalone) echo "standalone" ;;
janus) echo "janus_gateway" ;;
region) [[ -n "$NAME" ]] && echo "$NAME" || echo "" ;;
*) echo "" ;;
esac
}
screen_session_exists() {
local name="$1"
screen -list 2>/dev/null | grep -qE "\\.${name}[[:space:]]"
}
verify_screens_up() {
local name failed=0
[[ -n "$EXPECTED_SCREENS" ]] || {
log INFO "No expected screens configured; skipping UP verification"
return 0
}
while IFS= read -r name; do
[[ -n "$name" ]] || continue
if screen_session_exists "$name"; then
log INFO "[SMOKE][OK] screen up: $name"
else
log ERROR "[SMOKE][FAIL] screen not running: $name"
failed=$((failed + 1))
fi
done < <(split_csv "$EXPECTED_SCREENS")
return "$failed"
}
verify_screens_down() {
local name failed=0
[[ -n "$EXPECTED_SCREENS" ]] || {
log INFO "No expected screens configured; skipping DOWN verification"
return 0
}
while IFS= read -r name; do
[[ -n "$name" ]] || continue
if screen_session_exists "$name"; then
log ERROR "[SMOKE][FAIL] screen still running: $name"
failed=$((failed + 1))
else
log INFO "[SMOKE][OK] screen down: $name"
fi
done < <(split_csv "$EXPECTED_SCREENS")
return "$failed"
}
run_startstop() {
local action="$1"
local -a cmd
cmd=(bash "$ROOT_DIR/osmtool_main.sh" --module startstop --profile "$PROFILE" --workdir "$WORKDIR" --target "$TARGET" --action "$action")
if [[ "$TARGET" == "region" && -n "$NAME" ]]; then
cmd+=(--name "$NAME")
fi
if [[ -n "$JANUS_PREFIX" ]]; then
cmd+=(--janus-prefix "$JANUS_PREFIX")
fi
"${cmd[@]}"
}
run_smoke() {
local failures=0
log INFO "[SMOKE] profile=$PROFILE target=$TARGET workdir=$WORKDIR"
log INFO "[SMOKE] expected_screens=$EXPECTED_SCREENS"
run_startstop start || failures=$((failures + 1))
sleep "$WAIT_AFTER_START"
verify_screens_up || failures=$((failures + 1))
run_startstop restart || failures=$((failures + 1))
sleep "$WAIT_AFTER_START"
verify_screens_up || failures=$((failures + 1))
run_startstop stop || failures=$((failures + 1))
sleep "$WAIT_AFTER_STOP"
verify_screens_down || failures=$((failures + 1))
if [[ "$failures" -eq 0 ]]; then
log INFO "[SMOKE][PASS] Start/Restart/Stop smoke test successful"
return 0
fi
log ERROR "[SMOKE][FAIL] Smoke test failed with $failures issue group(s)"
return 1
}
WORKDIR="$DEFAULT_WORKDIR"
ACTION=""
PROFILE="grid-sim"
TARGET=""
NAME=""
EXPECTED_SCREENS=""
WAIT_AFTER_START="3"
WAIT_AFTER_STOP="8"
JANUS_PREFIX="${JANUS_PREFIX:-/opt/janus}"
while [[ $# -gt 0 ]]; do
case "$1" in
--workdir) WORKDIR="$2"; shift 2 ;;
--profile) PROFILE="$2"; shift 2 ;;
--action) ACTION="$2"; shift 2 ;;
--target) TARGET="$2"; shift 2 ;;
--name) NAME="$2"; shift 2 ;;
--expected-screens) EXPECTED_SCREENS="$2"; shift 2 ;;
--wait-after-start) WAIT_AFTER_START="$2"; shift 2 ;;
--wait-after-stop) WAIT_AFTER_STOP="$2"; shift 2 ;;
--janus-prefix) JANUS_PREFIX="$2"; shift 2 ;;
--help|-h) usage; exit 0 ;;
*) die "Unknown option: $1" ;;
esac
done
[[ -n "$ACTION" ]] || die "Missing --action"
validate_profile "$PROFILE"
ensure_profile_action_allowed smoke "$PROFILE" "$ACTION"
if [[ -z "$TARGET" ]]; then
TARGET="$(default_target_for_profile "$PROFILE")"
fi
ensure_profile_target_allowed "$PROFILE" "$TARGET"
if [[ "$TARGET" == "region" && -z "$NAME" ]]; then
die "--name is required for --target region"
fi
if [[ -z "$EXPECTED_SCREENS" ]]; then
EXPECTED_SCREENS="$(default_expected_screens_for_target "$TARGET")"
fi
case "$ACTION" in
run) run_smoke ;;
*) die "Unsupported action: $ACTION" ;;
esac
+316
View File
@@ -0,0 +1,316 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./osmtool_core.sh
source "$SCRIPT_DIR/osmtool_core.sh"
usage() {
cat <<'EOF'
Usage:
osmtool_startstop.sh [--workdir <path>] [--profile <grid-sim|robust|standalone>] --target <grid|robust|sim1|standalone|janus|region> --action <start|stop|restart> [--name <region-screen-name>]
[--janus-prefix <path>]
Examples:
osmtool_startstop.sh --profile grid-sim --target grid --action start --workdir /opt
osmtool_startstop.sh --profile robust --target robust --action restart --workdir /opt
EOF
print_usage_common
}
run_screen_cmd() {
local screen_name="$1"
local command_text="$2"
screen -S "$screen_name" -p 0 -X stuff "${command_text}^M"
}
screen_session_exists() {
local screen_name="$1"
screen -list 2>/dev/null | grep -qE "\\.${screen_name}[[:space:]]"
}
stop_screen_session() {
local screen_name="$1"
local grace_seconds="${2:-6}"
if ! screen_session_exists "$screen_name"; then
log INFO "$screen_name is not running"
return 0
fi
run_screen_cmd "$screen_name" "shutdown"
log INFO "Sent shutdown to $screen_name"
sleep "$grace_seconds"
if screen_session_exists "$screen_name"; then
log INFO "Session '$screen_name' still running after shutdown; forcing screen quit"
screen -S "$screen_name" -X quit || true
sleep 1
fi
if screen_session_exists "$screen_name"; then
log ERROR "Failed to stop screen session: $screen_name"
return 1
fi
log INFO "Screen session '$screen_name' stopped"
return 0
}
ensure_janus_ready() {
local marker="$JANUS_PREFIX/.osmtool_janus_ready"
local core_cfg="$JANUS_PREFIX/etc/janus/janus.jcfg"
local core_cfg_legacy="$JANUS_PREFIX/etc/janus/janus.cfg"
local http_cfg="$JANUS_PREFIX/etc/janus/janus.transport.http.jcfg"
local http_cfg_legacy="$JANUS_PREFIX/etc/janus/janus.transport.http.cfg"
[[ -f "$JANUS_PREFIX/bin/janus" ]] || die "Janus binary missing: $JANUS_PREFIX/bin/janus. Run install module action compile-janus/install-janus first."
if [[ ! -f "$core_cfg" && ! -f "$core_cfg_legacy" ]]; then
die "Janus core config missing in $JANUS_PREFIX/etc/janus. Run configure-janus/install-janus first."
fi
if [[ ! -f "$http_cfg" && ! -f "$http_cfg_legacy" ]]; then
die "Janus HTTP config missing in $JANUS_PREFIX/etc/janus. Run configure-janus/install-janus first."
fi
[[ -f "$marker" ]] || log INFO "Janus ready marker not found ($marker). Continuing because binary/config files exist."
}
janus_service_action() {
local action="$1"
local janus_bin janus_cfg
janus_bin="$JANUS_PREFIX/bin/janus"
janus_cfg="$JANUS_PREFIX/etc/janus/janus.jcfg"
[[ -f "$janus_cfg" ]] || janus_cfg="$JANUS_PREFIX/etc/janus/janus.cfg"
if command -v systemctl >/dev/null 2>&1; then
if systemctl list-unit-files 2>/dev/null | grep -q '^janus\.service'; then
sudo systemctl "$action" janus
return 0
fi
fi
if command -v service >/dev/null 2>&1; then
if service --status-all 2>/dev/null | grep -q janus; then
sudo service janus "$action"
return 0
fi
fi
if [[ -x "$WORKDIR/janus.sh" ]]; then
"$WORKDIR/janus.sh" "${action}_janus" || "$WORKDIR/janus.sh" "$action"
return 0
fi
if [[ -x "$WORKDIR/janus/janus.sh" ]]; then
"$WORKDIR/janus/janus.sh" "${action}_janus" || "$WORKDIR/janus/janus.sh" "$action"
return 0
fi
# Fallback: control Janus directly from installed binary.
if [[ -x "$janus_bin" ]]; then
case "$action" in
start)
if pgrep -f "$janus_bin" >/dev/null 2>&1; then
log INFO "Janus already running"
return 0
fi
if command -v screen >/dev/null 2>&1; then
screen -fa -S janus_gateway -d -U -m "$janus_bin" -C "$janus_cfg"
else
nohup "$janus_bin" -C "$janus_cfg" >/tmp/janus.log 2>&1 &
fi
return 0
;;
stop)
pkill -f "$janus_bin" >/dev/null 2>&1 || true
if command -v screen >/dev/null 2>&1; then
if screen -list | grep -q "janus_gateway"; then
screen -S janus_gateway -X quit || true
fi
fi
return 0
;;
restart)
pkill -f "$janus_bin" >/dev/null 2>&1 || true
sleep 1
if command -v screen >/dev/null 2>&1; then
screen -fa -S janus_gateway -d -U -m "$janus_bin" -C "$janus_cfg"
else
nohup "$janus_bin" -C "$janus_cfg" >/tmp/janus.log 2>&1 &
fi
return 0
;;
esac
fi
die "Janus control not configured. Expected system service or janus.sh helper."
}
require_ini() {
local bin_dir="$1"
local ini_file="$2"
local label="$3"
if [[ ! -f "$bin_dir/$ini_file" ]]; then
die "$label cannot start: missing $bin_dir/$ini_file — run the config action first"
fi
}
screen_start() {
local screen_name="$1"
local bin_dir="$2"
local dll="$3"
local pat="\\.${screen_name}[[:space:]]"
if screen -list 2>/dev/null | grep -qE "$pat"; then
log INFO "Screen session '$screen_name' already running — skipping start"
return 0
fi
log INFO "Starting $screen_name from $bin_dir"
(cd "$bin_dir" && screen -fa -S "$screen_name" -d -U -m dotnet "$dll")
sleep 0.5
if screen -list 2>/dev/null | grep -qE "$pat"; then
log INFO "Screen session '$screen_name' started successfully"
else
log ERROR "Screen session '$screen_name' did not stay alive — check logs or missing config"
fi
}
start_grid() {
log INFO "Starting grid from $WORKDIR"
if [[ -d "$WORKDIR/robust/bin" ]]; then
require_ini "$WORKDIR/robust/bin" "Robust.ini" "RobustServer"
screen_start "robustserver" "$WORKDIR/robust/bin" "Robust.dll"
fi
if [[ -f "$WORKDIR/robust/bin/MoneyServer.dll" && -f "$WORKDIR/robust/bin/MoneyServer.ini" ]]; then
screen_start "moneyserver" "$WORKDIR/robust/bin" "MoneyServer.dll"
fi
if [[ -d "$WORKDIR/sim1/bin" ]]; then
require_ini "$WORKDIR/sim1/bin" "OpenSim.ini" "sim1"
screen_start "sim1" "$WORKDIR/sim1/bin" "OpenSim.dll"
fi
}
stop_grid() {
log INFO "Stopping grid"
stop_screen_session "sim1" || true
stop_screen_session "moneyserver" || true
stop_screen_session "robustserver" || true
}
restart_grid() {
stop_grid
sleep 5
start_grid
}
start_single() {
local target="$1"
case "$target" in
robust)
require_ini "$WORKDIR/robust/bin" "Robust.ini" "RobustServer"
screen_start "robustserver" "$WORKDIR/robust/bin" "Robust.dll"
;;
sim1)
require_ini "$WORKDIR/sim1/bin" "OpenSim.ini" "sim1"
screen_start "sim1" "$WORKDIR/sim1/bin" "OpenSim.dll"
;;
standalone)
require_ini "$WORKDIR/standalone/bin" "OpenSim.ini" "standalone"
screen_start "standalone" "$WORKDIR/standalone/bin" "OpenSim.dll"
;;
janus)
ensure_janus_ready
janus_service_action start
;;
region)
[[ -n "$NAME" ]] || die "--name is required for --target region"
require_ini "$WORKDIR/${NAME}/bin" "OpenSim.ini" "$NAME"
screen_start "$NAME" "$WORKDIR/${NAME}/bin" "OpenSim.dll"
;;
*)
die "Unsupported start target: $target"
;;
esac
}
stop_single() {
local target="$1"
case "$target" in
robust)
stop_screen_session "robustserver"
;;
sim1)
stop_screen_session "sim1"
;;
standalone)
stop_screen_session "standalone"
;;
janus) janus_service_action stop ;;
region)
[[ -n "$NAME" ]] || die "--name is required for --target region"
stop_screen_session "$NAME"
;;
*) die "Unsupported stop target: $target" ;;
esac
}
WORKDIR="$DEFAULT_WORKDIR"
TARGET=""
ACTION=""
NAME=""
PROFILE="grid-sim"
JANUS_PREFIX="${JANUS_PREFIX:-/opt/janus}"
while [[ $# -gt 0 ]]; do
case "$1" in
--workdir) WORKDIR="$2"; shift 2 ;;
--profile) PROFILE="$2"; shift 2 ;;
--janus-prefix) JANUS_PREFIX="$2"; shift 2 ;;
--target) TARGET="$2"; shift 2 ;;
--action) ACTION="$2"; shift 2 ;;
--name) NAME="$2"; shift 2 ;;
--help|-h) usage; exit 0 ;;
*) die "Unknown option: $1" ;;
esac
done
[[ -n "$TARGET" ]] || die "Missing --target"
[[ -n "$ACTION" ]] || die "Missing --action"
validate_profile "$PROFILE"
ensure_profile_action_allowed startstop "$PROFILE" "$ACTION"
ensure_profile_target_allowed "$PROFILE" "$TARGET"
if [[ "$TARGET" == "janus" && "$ACTION" != "stop" ]]; then
ensure_janus_ready
fi
log INFO "Using profile: $PROFILE"
if [[ "$TARGET" == "grid" ]]; then
case "$ACTION" in
start) start_grid ;;
stop) stop_grid ;;
restart) restart_grid ;;
*) die "Unsupported action for grid: $ACTION" ;;
esac
else
case "$ACTION" in
start) start_single "$TARGET" ;;
stop) stop_single "$TARGET" ;;
restart)
stop_single "$TARGET"
sleep 3
start_single "$TARGET"
;;
*) die "Unsupported action: $ACTION" ;;
esac
fi
+262
View File
@@ -0,0 +1,262 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./osmtool_core.sh
source "$SCRIPT_DIR/osmtool_core.sh"
usage() {
cat <<'EOF'
Usage:
osmtool_update.sh [--workdir <path>] --action <create-rollback|list-rollbacks|update-opensim|update-janus|full-update>
[--opensim-dir <path>] [--opensim-repo <url>] [--opensim-branch <name>] [--repo-mode <update|fresh>]
[--tsassets-dir <path>] [--currency-dir <path>] [--data-backup-dir <path>]
[--deploy-binaries <true|false>] [--legacy-patch-dir <path>]
[--janus-prefix <path>] [--janus-src <path>] [--public-host <host>] [--http-port <port>]
[--admin-port <port>] [--rtp-range <from-to>] [--enable-admin <true|false>]
[--api-secret <value>] [--admin-secret <value>] [--label <name>] [--dry-run <true|false>]
Examples:
osmtool_update.sh --action create-rollback --workdir /opt --label pre_update
osmtool_update.sh --action update-opensim --workdir /opt --dry-run true
EOF
print_usage_common
}
timestamp() {
date '+%Y%m%d_%H%M%S'
}
rollback_dir() {
printf '%s' "$WORKDIR/rollback"
}
ensure_rollback_dir() {
local dir
dir="$(rollback_dir)"
if mkdir -p "$dir" 2>/dev/null; then
return 0
fi
sudo mkdir -p "$dir"
sudo chown "$(id -u):$(id -g)" "$dir"
}
path_relative_to_root() {
local path="$1"
printf '%s' "${path#/}"
}
append_snapshot_path() {
local path="$1"
[[ -e "$path" ]] || return 0
SNAPSHOT_PATHS+=("$(path_relative_to_root "$path")")
}
build_opensim_snapshot_paths() {
local sim_dir
SNAPSHOT_PATHS=()
append_snapshot_path "$OPENSIM_DIR"
append_snapshot_path "$WORKDIR/robust/bin"
append_snapshot_path "$WORKDIR/standalone/bin"
shopt -s nullglob
for sim_dir in "$WORKDIR"/sim*; do
append_snapshot_path "$sim_dir/bin"
done
shopt -u nullglob
}
build_janus_snapshot_paths() {
SNAPSHOT_PATHS=()
append_snapshot_path "$JANUS_PREFIX"
append_snapshot_path "$JANUS_SRC"
}
create_snapshot_archive() {
local scope="$1"
shift
local archive_name archive_path manifest_path
local -a paths=("$@")
ensure_rollback_dir
if [[ ${#paths[@]} -eq 0 ]]; then
log WARN "No existing paths found for rollback scope: $scope"
return 0
fi
archive_name="${scope}_$(timestamp)"
if [[ -n "$LABEL" ]]; then
archive_name="${archive_name}_${LABEL}"
fi
archive_path="$(rollback_dir)/${archive_name}.tar.gz"
manifest_path="$(rollback_dir)/${archive_name}.manifest"
log INFO "Creating rollback archive: $archive_path"
printf '%s\n' "${paths[@]}" > "$manifest_path"
if [[ "$DRY_RUN" == "true" ]]; then
log INFO "[DRY-RUN] Would archive paths listed in: $manifest_path"
return 0
fi
tar -czf "$archive_path" -C / "${paths[@]}"
log INFO "Rollback archive created: $archive_path"
log INFO "Rollback manifest created: $manifest_path"
}
run_install_action() {
local install_action="$1"
local -a cmd
cmd=("$SCRIPT_DIR/osmtool_install.sh" --profile "$PROFILE" --workdir "$WORKDIR" --action "$install_action")
[[ -n "$OPENSIM_DIR" ]] && cmd+=(--opensim-dir "$OPENSIM_DIR")
[[ -n "$OPENSIM_REPO" ]] && cmd+=(--opensim-repo "$OPENSIM_REPO")
[[ -n "$OPENSIM_BRANCH" ]] && cmd+=(--opensim-branch "$OPENSIM_BRANCH")
[[ -n "$REPO_MODE" ]] && cmd+=(--repo-mode "$REPO_MODE")
[[ -n "$TSASSETS_DIR" ]] && cmd+=(--tsassets-dir "$TSASSETS_DIR")
[[ -n "$CURRENCY_DIR" ]] && cmd+=(--currency-dir "$CURRENCY_DIR")
[[ -n "$DATA_BACKUP_DIR" ]] && cmd+=(--data-backup-dir "$DATA_BACKUP_DIR")
[[ -n "$DEPLOY_BINARIES" ]] && cmd+=(--deploy-binaries "$DEPLOY_BINARIES")
[[ -n "$LEGACY_PATCH_DIR" ]] && cmd+=(--legacy-patch-dir "$LEGACY_PATCH_DIR")
[[ -n "$JANUS_PREFIX" ]] && cmd+=(--janus-prefix "$JANUS_PREFIX")
[[ -n "$JANUS_SRC" ]] && cmd+=(--janus-src "$JANUS_SRC")
[[ -n "$PUBLIC_HOST" ]] && cmd+=(--public-host "$PUBLIC_HOST")
[[ -n "$HTTP_PORT" ]] && cmd+=(--http-port "$HTTP_PORT")
[[ -n "$ADMIN_PORT" ]] && cmd+=(--admin-port "$ADMIN_PORT")
[[ -n "$RTP_RANGE" ]] && cmd+=(--rtp-range "$RTP_RANGE")
[[ -n "$ENABLE_ADMIN" ]] && cmd+=(--enable-admin "$ENABLE_ADMIN")
[[ -n "$API_SECRET" ]] && cmd+=(--api-secret "$API_SECRET")
[[ -n "$ADMIN_SECRET" ]] && cmd+=(--admin-secret "$ADMIN_SECRET")
log INFO "Delegating update to install action: $install_action"
if [[ "$DRY_RUN" == "true" ]]; then
printf -v rendered '%q ' "${cmd[@]}"
log INFO "[DRY-RUN] $rendered"
return 0
fi
"${cmd[@]}"
}
create_rollback() {
build_opensim_snapshot_paths
create_snapshot_archive opensim "${SNAPSHOT_PATHS[@]}"
build_janus_snapshot_paths
create_snapshot_archive janus "${SNAPSHOT_PATHS[@]}"
}
list_rollbacks() {
local dir
dir="$(rollback_dir)"
if [[ ! -d "$dir" ]]; then
log INFO "No rollback directory found: $dir"
return 0
fi
find "$dir" -maxdepth 1 -type f \( -name '*.tar.gz' -o -name '*.manifest' \) -printf '%T@ %p\n' 2>/dev/null | \
sort -rn | cut -d' ' -f2-
}
update_opensim() {
build_opensim_snapshot_paths
create_snapshot_archive opensim "${SNAPSHOT_PATHS[@]}"
run_install_action install-opensim
}
update_janus() {
build_janus_snapshot_paths
create_snapshot_archive janus "${SNAPSHOT_PATHS[@]}"
run_install_action install-janus
}
full_update() {
update_opensim
update_janus
}
WORKDIR="$DEFAULT_WORKDIR"
ACTION=""
PROFILE="grid-sim"
OPENSIM_DIR=""
OPENSIM_REPO=""
OPENSIM_BRANCH=""
REPO_MODE="update"
TSASSETS_DIR=""
CURRENCY_DIR=""
DATA_BACKUP_DIR=""
DEPLOY_BINARIES="true"
LEGACY_PATCH_DIR=""
JANUS_PREFIX=""
JANUS_SRC=""
PUBLIC_HOST=""
HTTP_PORT=""
ADMIN_PORT=""
RTP_RANGE=""
ENABLE_ADMIN=""
API_SECRET=""
ADMIN_SECRET=""
LABEL=""
DRY_RUN="false"
SNAPSHOT_PATHS=()
while [[ $# -gt 0 ]]; do
case "$1" in
--workdir) WORKDIR="$2"; shift 2 ;;
--profile) PROFILE="$2"; shift 2 ;;
--action) ACTION="$2"; shift 2 ;;
--opensim-dir) OPENSIM_DIR="$2"; shift 2 ;;
--opensim-repo) OPENSIM_REPO="$2"; shift 2 ;;
--opensim-branch) OPENSIM_BRANCH="$2"; shift 2 ;;
--repo-mode) REPO_MODE="$2"; shift 2 ;;
--tsassets-dir) TSASSETS_DIR="$2"; shift 2 ;;
--currency-dir) CURRENCY_DIR="$2"; shift 2 ;;
--data-backup-dir) DATA_BACKUP_DIR="$2"; shift 2 ;;
--deploy-binaries) DEPLOY_BINARIES="$2"; shift 2 ;;
--legacy-patch-dir) LEGACY_PATCH_DIR="$2"; shift 2 ;;
--janus-prefix) JANUS_PREFIX="$2"; shift 2 ;;
--janus-src) JANUS_SRC="$2"; shift 2 ;;
--public-host) PUBLIC_HOST="$2"; shift 2 ;;
--http-port) HTTP_PORT="$2"; shift 2 ;;
--admin-port) ADMIN_PORT="$2"; shift 2 ;;
--rtp-range) RTP_RANGE="$2"; shift 2 ;;
--enable-admin) ENABLE_ADMIN="$2"; shift 2 ;;
--api-secret) API_SECRET="$2"; shift 2 ;;
--admin-secret) ADMIN_SECRET="$2"; shift 2 ;;
--label) LABEL="$2"; shift 2 ;;
--dry-run) DRY_RUN="$2"; shift 2 ;;
--help|-h) usage; exit 0 ;;
*) die "Unknown option: $1" ;;
esac
done
[[ -n "$ACTION" ]] || die "Missing --action"
validate_profile "$PROFILE"
ensure_profile_action_allowed update "$PROFILE" "$ACTION"
case "$DRY_RUN" in
true|false) ;;
*) die "--dry-run must be true or false" ;;
esac
OPENSIM_DIR="${OPENSIM_DIR:-$WORKDIR/opensim}"
JANUS_PREFIX="${JANUS_PREFIX:-/opt/janus}"
JANUS_SRC="${JANUS_SRC:-$WORKDIR/janus-gateway}"
log INFO "Using profile: $PROFILE"
case "$ACTION" in
create-rollback) create_rollback ;;
list-rollbacks) list_rollbacks ;;
update-opensim) update_opensim ;;
update-janus) update_janus ;;
full-update) full_update ;;
*) die "Unsupported action: $ACTION" ;;
esac
+6700
View File
File diff suppressed because it is too large Load Diff
+632
View File
@@ -0,0 +1,632 @@
[2026-04-01 10:10:40] [ERROR] ERR_TARGET_NOT_ALLOWED: profile=robust target=sim1
[2026-04-01 10:10:40] [ERROR] ERR_TARGET_NOT_ALLOWED: profile=standalone target=grid
[2026-04-01 10:10:40] [ERROR] ERR_ACTION_NOT_ALLOWED: module=backup profile=robust action=oar-backup
[2026-04-01 10:11:16] [ERROR] Target not allowed for this profile␍: profile=robust target=sim1
[2026-04-01 10:12:25] [ERROR] Janus binary missing: /opt/janus/bin/janus. Run install module action compile-janus/install-janus first.
[2026-04-01 10:13:33] [INFO] Using workdir: /opt
[2026-04-01 10:13:33] [INFO] Using profile: grid-sim
[2026-04-01 10:13:33] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:13:33] [INFO] Installing Janus build dependencies
[2026-04-01 10:14:42] [INFO] Cloning Janus source to /opt/janus-gateway
[2026-04-01 10:15:05] [INFO] Using workdir: /opt
[2026-04-01 10:15:05] [INFO] Using profile: grid-sim
[2026-04-01 10:15:05] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:15:05] [INFO] Installing Janus build dependencies
[2026-04-01 10:15:49] [INFO] Cloning Janus source to /home/manni/janus-gateway
[2026-04-01 10:15:54] [INFO] Building Janus
[2026-04-01 10:16:20] [INFO] Janus compiled and installed to /opt/janus
[2026-04-01 10:17:02] [INFO] Using workdir: /opt
[2026-04-01 10:17:02] [INFO] Using profile: grid-sim
[2026-04-01 10:17:02] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:17:41] [INFO] Using workdir: /home/manni
[2026-04-01 10:17:41] [INFO] Using profile: grid-sim
[2026-04-01 10:17:41] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:18:19] [INFO] Using workdir: /opt
[2026-04-01 10:18:19] [INFO] Using profile: grid-sim
[2026-04-01 10:18:19] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:18:29] [INFO] Janus configured. Marker created: /opt/janus/.osmtool_janus_ready
[2026-04-01 10:18:39] [INFO] Using profile: grid-sim
[2026-04-01 10:18:39] [ERROR] Janus control not configured. Expected system service or janus.sh helper.
[2026-04-01 10:18:40] [INFO] Using profile: grid-sim
[2026-04-01 10:18:40] [ERROR] Janus control not configured. Expected system service or janus.sh helper.
[2026-04-01 10:19:10] [INFO] Using profile: grid-sim
[2026-04-01 10:19:10] [INFO] Janus already running
[2026-04-01 10:19:11] [INFO] Using profile: grid-sim
[2026-04-01 10:19:18] [INFO] Using profile: grid-sim
[2026-04-01 10:19:43] [INFO] Using profile: grid-sim
[2026-04-01 10:19:43] [INFO] Janus already running
[2026-04-01 10:19:43] [INFO] Using profile: grid-sim
[2026-04-01 10:19:55] [INFO] Using profile: grid-sim
[2026-04-01 10:19:55] [INFO] Janus already running
[2026-04-01 10:19:55] [INFO] Using profile: grid-sim
[2026-04-01 10:20:05] [INFO] Using profile: grid-sim
[2026-04-01 10:20:41] [INFO] Using profile: grid-sim
[2026-04-01 10:24:45] [INFO] Using workdir: /opt
[2026-04-01 10:24:45] [INFO] Using profile: grid-sim
[2026-04-01 10:24:45] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:24:45] [INFO] Running server readiness check
[2026-04-01 10:24:45] [INFO] Detected OS: Ubuntu 24.04.4 LTS
[2026-04-01 10:24:45] [INFO] sudo available but password will be required
[2026-04-01 10:24:45] [INFO] apt-get available
[2026-04-01 10:24:45] [INFO] Network check to github.com: OK
[2026-04-01 10:24:45] [INFO] Disk space in /opt is sufficient
[2026-04-01 10:24:45] [INFO] All required Janus build packages are installed
[2026-04-01 10:24:45] [INFO] dotnet not found (required later for OpenSim runtime)
[2026-04-01 10:24:45] [INFO] Server readiness check PASSED
[2026-04-01 10:29:20] [INFO] Using workdir: /opt
[2026-04-01 10:29:20] [INFO] Using profile: grid-sim
[2026-04-01 10:29:20] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:29:20] [INFO] Running server readiness check
[2026-04-01 10:29:20] [INFO] Detected OS: Ubuntu 24.04.4 LTS
[2026-04-01 10:29:20] [INFO] sudo available but password will be required
[2026-04-01 10:29:20] [INFO] apt-get available
[2026-04-01 10:29:20] [INFO] Network check to github.com: OK
[2026-04-01 10:29:20] [INFO] Disk space in /opt is sufficient
[2026-04-01 10:29:20] [INFO] All required Janus build packages are installed
[2026-04-01 10:29:20] [ERROR] dotnet SDK 8.x missing (required for OpenSim build/runtime)
[2026-04-01 10:29:20] [ERROR] Server readiness check FAILED
[2026-04-01 10:38:11] [INFO] Using workdir: /opt
[2026-04-01 10:38:11] [INFO] Using profile: grid-sim
[2026-04-01 10:38:11] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:38:11] [INFO] Running server readiness check
[2026-04-01 10:38:11] [INFO] Detected OS: Ubuntu 24.04.4 LTS
[2026-04-01 10:38:11] [INFO] sudo available but password will be required
[2026-04-01 10:38:11] [INFO] apt-get available
[2026-04-01 10:38:12] [INFO] Network check to github.com: OK
[2026-04-01 10:38:12] [INFO] Disk space in /opt is sufficient
[2026-04-01 10:38:12] [ERROR] Missing packages: zip screen libgdiplus
[2026-04-01 10:38:12] [INFO] Run: sudo apt-get update && sudo apt-get install -y zip screen libgdiplus
[2026-04-01 10:38:12] [ERROR] dotnet SDK 8.x missing (required for OpenSim build/runtime)
[2026-04-01 10:38:12] [ERROR] Server readiness check FAILED
[2026-04-01 10:39:56] [INFO] Using workdir: /opt
[2026-04-01 10:39:56] [INFO] Using profile: grid-sim
[2026-04-01 10:39:56] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:39:56] [ERROR] No installable package found for token: zip
[2026-04-01 10:40:15] [INFO] Using workdir: /opt
[2026-04-01 10:40:15] [INFO] Using profile: grid-sim
[2026-04-01 10:40:15] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:40:15] [INFO] Installing OpenSim dependencies: zip screen libgdiplus libssl3 liblttng-ust1
[2026-04-01 10:40:59] [INFO] Using workdir: /opt
[2026-04-01 10:40:59] [INFO] Using profile: grid-sim
[2026-04-01 10:40:59] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:40:59] [INFO] Installing .NET SDK 8.0
[2026-04-01 10:41:47] [INFO] Adding Microsoft package repository for Ubuntu 24.04
[2026-04-01 10:42:05] [INFO] dotnet SDK 8.x installed successfully
[2026-04-01 10:42:11] [INFO] Using workdir: /opt
[2026-04-01 10:42:11] [INFO] Using profile: grid-sim
[2026-04-01 10:42:11] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:42:11] [INFO] Running server readiness check
[2026-04-01 10:42:11] [INFO] Detected OS: Ubuntu 24.04.4 LTS
[2026-04-01 10:42:11] [INFO] sudo available but password will be required
[2026-04-01 10:42:11] [INFO] apt-get available
[2026-04-01 10:42:11] [INFO] Network check to github.com: OK
[2026-04-01 10:42:11] [INFO] Disk space in /opt is sufficient
[2026-04-01 10:42:11] [INFO] All required Janus/OpenSim dependency packages are installed
[2026-04-01 10:42:11] [INFO] dotnet SDK 8.x found
[2026-04-01 10:42:11] [INFO] Server readiness check PASSED
[2026-04-01 10:50:47] [ERROR] Server preinstallation missing. Run install action bootstrap-server first.
[2026-04-01 10:50:47] [INFO] Using workdir: /opt
[2026-04-01 10:50:47] [INFO] Using profile: grid-sim
[2026-04-01 10:50:47] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:50:47] [INFO] Starting central server bootstrap
[2026-04-01 10:50:47] [INFO] Preparing Ubuntu server
[2026-04-01 10:51:13] [INFO] Ubuntu base packages installed
[2026-04-01 10:51:13] [INFO] Installing OpenSim dependencies: libssl3 liblttng-ust1
[2026-04-01 10:51:14] [INFO] dotnet SDK 8.x already installed
[2026-04-01 10:51:14] [INFO] Running server readiness check
[2026-04-01 10:51:14] [INFO] Detected OS: Ubuntu 24.04.4 LTS
[2026-04-01 10:51:14] [INFO] sudo non-interactive check: OK
[2026-04-01 10:51:14] [INFO] apt-get available
[2026-04-01 10:51:14] [INFO] Network check to github.com: OK
[2026-04-01 10:51:14] [INFO] Disk space in /opt is sufficient
[2026-04-01 10:51:15] [INFO] All required Janus/OpenSim dependency packages are installed
[2026-04-01 10:51:15] [INFO] dotnet SDK 8.x found
[2026-04-01 10:51:15] [INFO] Server readiness check PASSED
[2026-04-01 10:51:15] [INFO] Server bootstrap marker created: /opt/.osmtool_server_prepared
[2026-04-01 10:51:15] [INFO] Central server bootstrap completed
[2026-04-01 10:57:07] [INFO] Using workdir: /opt
[2026-04-01 10:57:07] [INFO] Using profile: grid-sim
[2026-04-01 10:57:07] [INFO] Repo mode: update
[2026-04-01 10:57:07] [INFO] OpenSim dir: /opt/opensim
[2026-04-01 10:57:07] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:57:07] [INFO] Starting OpenSim install/build flow
[2026-04-01 10:57:07] [INFO] OpenSim repository: https://github.com/opensim/opensim.git
[2026-04-01 10:57:07] [INFO] OpenSim branch: master
[2026-04-01 10:57:07] [INFO] OpenSim directory: /opt/opensim
[2026-04-01 10:57:07] [INFO] Cloning repository https://github.com/opensim/opensim.git -> /opt/opensim
[2026-04-01 10:57:30] [INFO] Using workdir: /opt
[2026-04-01 10:57:30] [INFO] Using profile: grid-sim
[2026-04-01 10:57:30] [INFO] Repo mode: update
[2026-04-01 10:57:30] [INFO] OpenSim dir: /opt/opensim
[2026-04-01 10:57:30] [INFO] Janus prefix: /opt/janus
[2026-04-01 10:57:30] [INFO] Starting OpenSim install/build flow
[2026-04-01 10:57:30] [INFO] OpenSim repository: https://github.com/opensim/opensim.git
[2026-04-01 10:57:31] [INFO] OpenSim branch: master
[2026-04-01 10:57:31] [INFO] OpenSim directory: /opt/opensim
[2026-04-01 11:08:23] [INFO] Cloning repository https://github.com/opensim/opensim.git -> /opt/opensim
[2026-04-01 11:09:12] [INFO] Preparing support repository: opensim-tsassets
[2026-04-01 11:09:12] [INFO] Cloning repository https://github.com/ManfredAabye/opensim-tsassets.git -> /opt/opensim-tsassets
[2026-04-01 11:09:44] [INFO] Using workdir: /opt
[2026-04-01 11:09:44] [INFO] Using profile: grid-sim
[2026-04-01 11:09:44] [INFO] Repo mode: update
[2026-04-01 11:09:44] [INFO] OpenSim dir: /opt/opensim
[2026-04-01 11:09:44] [INFO] Janus prefix: /opt/janus
[2026-04-01 11:09:44] [INFO] Starting OpenSim install/build flow
[2026-04-01 11:09:44] [INFO] OpenSim repository: https://github.com/opensim/opensim.git
[2026-04-01 11:09:44] [INFO] OpenSim branch: master
[2026-04-01 11:09:44] [INFO] OpenSim directory: /opt/opensim
[2026-04-01 11:09:44] [INFO] Updating repository: /opt/opensim
[2026-04-01 11:09:46] [INFO] Preparing support repository: opensim-tsassets
[2026-04-01 11:12:10] [INFO] Cloning repository https://github.com/ManfredAabye/opensim-tsassets.git -> /opt/opensim-tsassets
[2026-04-01 11:12:10] [INFO] Preparing support repository: opensimcurrencyserver
[2026-04-01 11:12:10] [INFO] Cloning repository https://github.com/ManfredAabye/opensimcurrencyserver-dotnet.git -> /opt/opensimcurrencyserver
[2026-04-01 11:12:11] [INFO] Preparing support repository: os-data-backup
[2026-04-01 11:12:11] [INFO] Cloning repository https://github.com/ManfredAabye/os-data-backup.git -> /opt/os-data-backup
[2026-04-01 11:12:12] [INFO] Copied tsassets bin content: /opt/opensim-tsassets/bin -> /opt/opensim/bin
[2026-04-01 11:12:12] [INFO] Copied tsassets OpenSim content: /opt/opensim-tsassets/OpenSim -> /opt/opensim/OpenSim
[2026-04-01 11:12:12] [INFO] Copied currency addon modules: /opt/opensimcurrencyserver/addon-modules -> /opt/opensim/addon-modules
[2026-04-01 11:12:12] [INFO] Copied currency bin content: /opt/opensimcurrencyserver/bin -> /opt/opensim/bin
[2026-04-01 11:12:12] [INFO] Copied os-data-backup content: /opt/os-data-backup -> /opt/opensim/addon-modules/os-data-backup
[2026-04-01 11:12:12] [INFO] Running OpenSim prebuild
[2026-04-01 11:12:32] [INFO] Binary deployment skipped by configuration
[2026-04-01 11:12:32] [INFO] OpenSim install/build flow completed
[2026-04-01 11:53:03] [INFO] Using workdir: /opt
[2026-04-01 11:53:03] [INFO] Using profile: grid-sim
[2026-04-01 11:53:03] [INFO] Repo mode: update
[2026-04-01 11:53:03] [INFO] OpenSim dir: /opt/opensim
[2026-04-01 11:53:03] [INFO] Janus prefix: /opt/janus
[2026-04-01 11:53:03] [INFO] Starting OpenSim install/build flow
[2026-04-01 11:53:03] [INFO] OpenSim repository: https://github.com/opensim/opensim.git
[2026-04-01 11:53:03] [INFO] OpenSim branch: master
[2026-04-01 11:53:03] [INFO] OpenSim directory: /opt/opensim
[2026-04-01 11:53:03] [INFO] Updating repository: /opt/opensim
[2026-04-01 11:53:04] [INFO] Preparing support repository: opensim-tsassets
[2026-04-01 11:53:04] [INFO] Updating repository: /opt/opensim-tsassets
[2026-04-01 11:53:05] [INFO] Preparing support repository: opensimcurrencyserver
[2026-04-01 11:53:05] [INFO] Updating repository: /opt/opensimcurrencyserver
[2026-04-01 11:53:06] [INFO] Preparing support repository: os-data-backup
[2026-04-01 11:53:06] [INFO] Updating repository: /opt/os-data-backup
[2026-04-01 11:53:07] [INFO] Copied tsassets bin content: /opt/opensim-tsassets/bin -> /opt/opensim/bin
[2026-04-01 11:53:07] [INFO] Copied tsassets OpenSim content: /opt/opensim-tsassets/OpenSim -> /opt/opensim/OpenSim
[2026-04-01 11:53:07] [INFO] Copied currency addon modules: /opt/opensimcurrencyserver/addon-modules -> /opt/opensim/addon-modules
[2026-04-01 11:53:07] [INFO] Copied currency bin content: /opt/opensimcurrencyserver/bin -> /opt/opensim/bin
[2026-04-01 11:55:36] [INFO] Using workdir: /opt
[2026-04-01 11:55:36] [INFO] Using profile: grid-sim
[2026-04-01 11:55:36] [INFO] Repo mode: update
[2026-04-01 11:55:36] [INFO] OpenSim dir: /opt/opensim
[2026-04-01 11:55:36] [INFO] Janus prefix: /opt/janus
[2026-04-01 11:55:36] [INFO] Starting OpenSim install/build flow
[2026-04-01 11:55:36] [INFO] OpenSim repository: https://github.com/opensim/opensim.git
[2026-04-01 11:55:36] [INFO] OpenSim branch: master
[2026-04-01 11:55:36] [INFO] OpenSim directory: /opt/opensim
[2026-04-01 11:55:36] [INFO] Updating repository: /opt/opensim
[2026-04-01 11:55:37] [INFO] Preparing support repository: opensim-tsassets
[2026-04-01 11:55:37] [INFO] Updating repository: /opt/opensim-tsassets
[2026-04-01 11:55:38] [INFO] Preparing support repository: opensimcurrencyserver
[2026-04-01 11:55:38] [INFO] Updating repository: /opt/opensimcurrencyserver
[2026-04-01 11:55:38] [INFO] Preparing support repository: os-data-backup
[2026-04-01 11:55:38] [INFO] Updating repository: /opt/os-data-backup
[2026-04-01 11:55:39] [INFO] Copied tsassets bin content: /opt/opensim-tsassets/bin -> /opt/opensim/bin
[2026-04-01 11:55:39] [INFO] Copied tsassets OpenSim content: /opt/opensim-tsassets/OpenSim -> /opt/opensim/OpenSim
[2026-04-01 11:55:39] [INFO] Copied currency addon modules: /opt/opensimcurrencyserver/addon-modules -> /opt/opensim/addon-modules
[2026-04-01 11:55:39] [INFO] Copied currency bin content: /opt/opensimcurrencyserver/bin -> /opt/opensim/bin
[2026-04-01 11:55:39] [INFO] Copied os-data-backup content: /opt/os-data-backup -> /opt/opensim/addon-modules/os-data-backup
[2026-04-01 11:55:39] [INFO] Running OpenSim prebuild
[2026-04-01 11:55:59] [INFO] Copied robust binaries: /opt/opensim/bin -> /opt/robust/bin
[2026-04-01 11:55:59] [INFO] Copied region binaries: /opt/opensim/bin -> /opt/sim1/bin
[2026-04-01 11:55:59] [INFO] Copied region binaries: /opt/opensim/bin -> /opt/sim2/bin
[2026-04-01 11:55:59] [INFO] OpenSim binaries deployed to available runtime directories
[2026-04-01 11:55:59] [INFO] OpenSim install/build flow completed
[2026-04-01 12:07:30] [INFO] Using profile: grid-sim
[2026-04-01 12:07:32] [INFO] Using profile: grid-sim
[2026-04-01 12:13:24] [INFO] Using profile: grid-sim
[2026-04-01 12:28:52] [INFO] Using profile: grid-sim
[2026-04-01 12:28:52] [ERROR] RobustServer cannot start: missing /opt/robust/bin/Robust.ini — run the config action first
[2026-04-01 12:28:52] [INFO] Using profile: grid-sim
[2026-04-01 12:28:52] [ERROR] sim1 cannot start: missing /opt/sim1/bin/OpenSim.ini — run the config action first
[2026-04-01 12:29:05] [INFO] Using profile: grid-sim
[2026-04-01 12:29:05] [INFO] Starting robustserver from /opt/robust/bin
[2026-04-01 12:29:06] [INFO] Screen session 'robustserver' started successfully
[2026-04-01 12:29:08] [INFO] Using profile: grid-sim
[2026-04-01 12:30:22] [INFO] Using profile: grid-sim
[2026-04-01 12:30:22] [INFO] Starting robustserver from /opt/robust/bin
[2026-04-01 12:30:22] [INFO] Screen session 'robustserver' started successfully
[2026-04-01 12:30:23] [INFO] Using profile: grid-sim
[2026-04-01 12:30:23] [INFO] Sent shutdown to robustserver
[2026-04-01 12:30:24] [INFO] Using profile: grid-sim
[2026-04-01 12:30:24] [INFO] Sent shutdown to robustserver
[2026-04-01 12:30:24] [INFO] Using profile: grid-sim
[2026-04-01 12:30:24] [ERROR] sim1 cannot start: missing /opt/sim1/bin/OpenSim.ini — run the config action first
[2026-04-01 12:49:19] [INFO] Using profile: grid-sim
[2026-04-01 12:49:19] [ERROR] Missing required command: mysqldump
[2026-04-01 12:49:31] [INFO] Using profile: grid-sim
[2026-04-01 12:49:31] [INFO] No OSMTool cron block configured
[2026-04-01 12:49:32] [INFO] OSMTool cron jobs installed/updated
[2026-04-01 12:49:32] [INFO] OSMTool cron block removed
[2026-04-01 12:49:32] [INFO] No OSMTool cron block configured
[2026-04-01 12:49:58] [INFO] Using profile: grid-sim
[2026-04-01 12:50:34] [INFO] Using profile: grid-sim
[2026-04-01 12:50:34] [INFO] Sending OAR export command to screen session 'sim1': save oar /opt/backup/sim1_20260401_125034.oar
[2026-04-01 12:50:34] [INFO] OAR export command sent. Validate completion in the session log/output.
[2026-04-01 12:52:12] [INFO] OSMTool cron jobs installed/updated
[2026-04-01 12:52:13] [INFO] OSMTool cron block removed
[2026-04-01 12:52:14] [INFO] Using profile: grid-sim
[2026-04-01 12:52:14] [INFO] Sending OAR export command to screen session 'sim1': save oar /opt/backup/sim1_20260401_125214.oar
[2026-04-01 12:52:14] [INFO] OAR export command sent. Validate completion in the session log/output.
[2026-04-01 13:01:08] [INFO] OSMTool cron jobs installed/updated
[2026-04-01 13:01:08] [INFO] OSMTool cron block removed
[2026-04-01 13:01:09] [INFO] Using workdir: /opt
[2026-04-01 13:01:09] [INFO] Using profile: grid-sim
[2026-04-01 13:01:09] [INFO] Repo mode: update
[2026-04-01 13:01:09] [INFO] OpenSim dir: /opt/opensim
[2026-04-01 13:01:09] [INFO] Janus prefix: /opt/janus
[2026-04-01 13:01:09] [INFO] Running server readiness check
[2026-04-01 13:01:09] [INFO] Detected OS: Ubuntu 24.04.4 LTS
[2026-04-01 13:01:09] [INFO] sudo available but password will be required
[2026-04-01 13:01:09] [INFO] apt-get available
[2026-04-01 13:01:09] [INFO] Network check to github.com: OK
[2026-04-01 13:01:09] [INFO] Disk space in /opt is sufficient
[2026-04-01 13:01:09] [INFO] All required Janus/OpenSim dependency packages are installed
[2026-04-01 13:01:09] [INFO] dotnet SDK 8.x found
[2026-04-01 13:01:09] [ERROR] mysqldump missing (MariaDB client tools are required)
[2026-04-01 13:01:09] [ERROR] Server readiness check FAILED
[2026-04-01 13:01:41] [INFO] Using workdir: /opt
[2026-04-01 13:01:41] [INFO] Using profile: grid-sim
[2026-04-01 13:01:41] [INFO] Repo mode: update
[2026-04-01 13:01:41] [INFO] OpenSim dir: /opt/opensim
[2026-04-01 13:01:41] [INFO] Janus prefix: /opt/janus
[2026-04-01 13:01:41] [INFO] Running server readiness check
[2026-04-01 13:01:41] [INFO] Detected OS: Ubuntu 24.04.4 LTS
[2026-04-01 13:01:41] [INFO] sudo available but password will be required
[2026-04-01 13:01:41] [INFO] apt-get available
[2026-04-01 13:01:41] [INFO] Network check to github.com: OK
[2026-04-01 13:01:41] [INFO] Disk space in /opt is sufficient
[2026-04-01 13:01:41] [INFO] All required Janus/OpenSim dependency packages are installed
[2026-04-01 13:01:41] [INFO] dotnet SDK 8.x found
[2026-04-01 13:01:41] [INFO] mysqldump found (MariaDB backup tooling ready)
[2026-04-01 13:01:41] [INFO] Server readiness check PASSED
[2026-04-01 13:04:31] [INFO] Using workdir: /opt
[2026-04-01 13:04:31] [INFO] Using profile: grid-sim
[2026-04-01 13:04:31] [INFO] Repo mode: update
[2026-04-01 13:04:31] [INFO] OpenSim dir: /opt/opensim
[2026-04-01 13:04:31] [INFO] Janus prefix: /opt/janus
[2026-04-01 13:04:31] [INFO] Configuring OpenSim runtime ini files
[2026-04-01 13:04:31] [INFO] Keeping existing config: /opt/robust/bin/Robust.ini
[2026-04-01 13:04:31] [INFO] Keeping existing config: /opt/robust/bin/Robust.ini
[2026-04-01 13:04:31] [INFO] Created config from template: /opt/robust/bin/MoneyServer.ini
[2026-04-01 13:04:31] [INFO] Created config from template: /opt/sim1/bin/OpenSim.ini
[2026-04-01 13:04:31] [INFO] Created config from template: /opt/sim2/bin/OpenSim.ini
[2026-04-01 13:04:31] [INFO] OpenSim runtime ini configuration completed
[2026-04-01 13:04:37] [INFO] Using workdir: /opt
[2026-04-01 13:04:37] [INFO] Using profile: grid-sim
[2026-04-01 13:04:37] [INFO] Repo mode: update
[2026-04-01 13:04:37] [INFO] OpenSim dir: /opt/opensim
[2026-04-01 13:04:37] [INFO] Janus prefix: /opt/janus
[2026-04-01 13:04:37] [INFO] Starting central server bootstrap
[2026-04-01 13:04:37] [INFO] Preparing Ubuntu server
[2026-04-01 13:07:46] [INFO] Using workdir: /opt
[2026-04-01 13:07:46] [INFO] Using profile: grid-sim
[2026-04-01 13:07:46] [INFO] Repo mode: update
[2026-04-01 13:07:46] [INFO] OpenSim dir: /opt/opensim
[2026-04-01 13:07:46] [INFO] Janus prefix: /opt/janus
[2026-04-01 13:07:46] [INFO] Starting central server bootstrap
[2026-04-01 13:07:46] [INFO] Preparing Ubuntu server
[2026-04-01 13:07:50] [INFO] Ubuntu base packages installed
[2026-04-01 13:07:51] [INFO] Installing OpenSim dependencies: mariadb-server libssl3 liblttng-ust1
[2026-04-01 13:07:59] [INFO] dotnet SDK 8.x already installed
[2026-04-01 13:07:59] [INFO] No controllable MariaDB service unit detected; skipping service activation
[2026-04-01 13:07:59] [INFO] Running server readiness check
[2026-04-01 13:07:59] [INFO] Detected OS: Ubuntu 24.04.4 LTS
[2026-04-01 13:07:59] [INFO] sudo non-interactive check: OK
[2026-04-01 13:07:59] [INFO] apt-get available
[2026-04-01 13:08:00] [INFO] Network check to github.com: OK
[2026-04-01 13:08:00] [INFO] Disk space in /opt is sufficient
[2026-04-01 13:08:00] [INFO] All required Janus/OpenSim dependency packages are installed
[2026-04-01 13:08:00] [INFO] dotnet SDK 8.x found
[2026-04-01 13:08:00] [INFO] mysqldump found (MariaDB backup tooling ready)
[2026-04-01 13:08:00] [INFO] Server readiness check PASSED
[2026-04-01 13:08:00] [INFO] Server bootstrap marker created: /opt/.osmtool_server_prepared
[2026-04-01 13:08:00] [INFO] Central server bootstrap completed
[2026-04-01 15:41:57] [INFO] Using workdir: /opt
[2026-04-01 15:41:57] [INFO] Using profile: grid-sim
[2026-04-01 15:41:57] [INFO] Repo mode: update
[2026-04-01 15:41:57] [INFO] OpenSim dir: /opt/opensim
[2026-04-01 15:41:57] [INFO] Janus prefix: /opt/janus
[2026-04-01 15:41:57] [INFO] No controllable MariaDB service unit detected; skipping service activation
[2026-04-01 15:41:57] [INFO] Configuring MariaDB databases and grants
[2026-04-01 15:41:57] [INFO] Ensured database: robust
[2026-04-01 15:41:57] [INFO] Ensured database: sim1
[2026-04-01 15:41:57] [INFO] Ensured database: sim2
[2026-04-01 15:41:57] [INFO] Database credentials written: /opt/UserInfo.ini
[2026-04-01 15:41:57] [INFO] MariaDB database setup completed
[2026-04-01 15:46:48] [INFO] Running health checks for profile: grid-sim
[2026-04-01 15:46:48] [INFO] Using workdir: /opt
[2026-04-01 15:46:48] [ERROR] [FAIL] Screen session missing: robustserver
[2026-04-01 15:46:48] [ERROR] [FAIL] Screen session missing: sim1
[2026-04-01 15:46:48] [ERROR] [FAIL] Port not listening: 8002
[2026-04-01 15:46:48] [ERROR] [FAIL] Port not listening: 9000
[2026-04-01 15:46:48] [ERROR] [FAIL] Port not listening: 9001
[2026-04-01 15:46:48] [ERROR] [FAIL] Port not listening: 8088
[2026-04-01 15:46:48] [INFO] [OK] Database reachable: robust
[2026-04-01 15:46:48] [INFO] [OK] Log scan: /mnt/d/OpenSimBash/opensimMULTITOOLS-II/osmtool_backup.log -> 26 matches
[2026-04-01 15:46:48] [INFO] [WARN] Log file not found, skipped: /opt/robust/bin/OpenSim.log
[2026-04-01 15:46:48] [INFO] [OK] Log warning threshold OK: 26 <= 200
[2026-04-01 15:46:48] [ERROR] [FAIL] Healthcheck FAILED with 2 failing check group(s)
[2026-04-01 15:47:31] [INFO] Running health checks for profile: grid-sim
[2026-04-01 15:47:31] [INFO] Using workdir: /opt
[2026-04-01 15:47:31] [INFO] [WARN] Screen check skipped (no expected screens configured)
[2026-04-01 15:47:31] [INFO] [WARN] Port check skipped (no ports configured)
[2026-04-01 15:47:31] [INFO] [OK] Database reachable: robust
[2026-04-01 15:47:31] [INFO] [OK] Log scan: /mnt/d/OpenSimBash/opensimMULTITOOLS-II/osmtool_backup.log -> 31 matches
[2026-04-01 15:47:31] [INFO] [WARN] Log file not found, skipped: /opt/robust/bin/OpenSim.log
[2026-04-01 15:47:31] [INFO] [OK] Log warning threshold OK: 31 <= 200
[2026-04-01 15:47:31] [INFO] [OK] Healthcheck PASSED
[2026-04-01 15:47:56] [INFO] Using profile: grid-sim
[2026-04-01 15:47:56] [INFO] Dry-run mode: true
[2026-04-01 15:47:56] [INFO] Cleaning log files under /opt/opensim
[2026-04-01 15:48:14] [ERROR] Refusing cleanup on /opt root; choose a dedicated OpenSim workdir
[2026-04-01 16:31:39] [ERROR] Unknown option: --module
[2026-04-01 16:31:45] [INFO] Using profile: grid-sim
[2026-04-01 16:31:45] [INFO] SQL Backups:
[2026-04-01 16:31:45] [INFO] OAR Backups:
[2026-04-01 16:31:50] [INFO] Using profile: grid-sim
[2026-04-01 16:31:50] [INFO] SQL Backups:
[2026-04-01 16:31:50] [INFO] OAR Backups:
[2026-04-01 16:31:55] [INFO] Using profile: grid-sim
[2026-04-01 16:31:55] [ERROR] SQL backup file not found: /tmp/test.sql
[2026-04-01 16:32:02] [INFO] Using profile: grid-sim
[2026-04-01 16:32:02] [INFO] Database restore command:
[2026-04-01 16:32:02] [INFO] mysql -u'root' -p'test' 'robust' < '/tmp/test.sql'
[2026-04-01 16:32:02] [INFO] [DRY-RUN] Not executing database restore
[2026-04-01 16:32:09] [ERROR] Aktion fuer dieses Profil nicht erlaubt␍: module=restore profile=robust action=oar-restore
[2026-04-01 16:57:43] [INFO] Using profile: grid-sim
[2026-04-01 16:57:43] [INFO] Creating rollback archive: /tmp/osm_update_test/rollback/opensim_20260401_165743_smoke.tar.gz
[2026-04-01 16:57:43] [INFO] [DRY-RUN] Would archive paths listed in: /tmp/osm_update_test/rollback/opensim_20260401_165743_smoke.manifest
[2026-04-01 16:57:43] [INFO] Creating rollback archive: /tmp/osm_update_test/rollback/janus_20260401_165743_smoke.tar.gz
[2026-04-01 16:57:43] [INFO] [DRY-RUN] Would archive paths listed in: /tmp/osm_update_test/rollback/janus_20260401_165743_smoke.manifest
[2026-04-01 16:57:43] [INFO] Using profile: grid-sim
[2026-04-01 16:57:43] [INFO] Validating INI file: /tmp/osm_update_test/robust/bin/Robust.ini
[2026-04-01 16:57:43] [INFO] Validating INI file: /tmp/osm_update_test/sim1/bin/OpenSim.ini
[2026-04-01 16:57:43] [INFO] Configuration validation passed
[2026-04-01 16:57:53] [INFO] Using profile: grid-sim
[2026-04-01 16:57:53] [INFO] Creating rollback archive: /tmp/osm_update_test/rollback/opensim_20260401_165753_smoke.tar.gz
[2026-04-01 16:57:53] [INFO] [DRY-RUN] Would archive paths listed in: /tmp/osm_update_test/rollback/opensim_20260401_165753_smoke.manifest
[2026-04-01 16:57:53] [INFO] Delegating update to install action: install-opensim
[2026-04-01 16:57:53] [INFO] [DRY-RUN] /mnt/d/OpenSimBash/opensimMULTITOOLS-II/modular/osmtool_install.sh --profile grid-sim --workdir /tmp/osm_update_test --action install-opensim --opensim-dir /tmp/osm_update_test/opensim --repo-mode update --deploy-binaries true --janus-prefix /opt/janus --janus-src /tmp/osm_update_test/janus-gateway
[2026-04-01 16:57:54] [INFO] Using profile: grid-sim
[2026-04-01 16:57:54] [INFO] Validating INI file: /tmp/test_invalid.ini
[2026-04-01 16:57:54] [ERROR] Invalid INI syntax in /tmp/test_invalid.ini:2 -> this is broken
[2026-04-01 16:57:54] [ERROR] Configuration validation failed with 1 issue(s)
[2026-04-01 16:58:09] [INFO] Using profile: grid-sim
[2026-04-01 16:58:09] [INFO] Validating JSON file: /tmp/test_valid.json
[2026-04-01 16:58:09] [INFO] Configuration validation passed
[2026-04-01 16:58:09] [INFO] Using profile: grid-sim
[2026-04-01 16:58:46] [INFO] Using profile: grid-sim
[2026-04-01 16:58:46] [INFO] Validating INI file: /tmp/osm_config_missing/robust/bin/Robust.ini
[2026-04-01 16:58:46] [ERROR] No region runtime configs found under /tmp/osm_config_missing/sim*/bin/OpenSim.ini
[2026-04-01 16:58:46] [ERROR] Configuration validation failed with 1 issue(s)
[2026-04-01 16:58:47] [INFO] Using profile: grid-sim
[2026-04-01 16:58:47] [ERROR] Missing config file: /tmp/osm_update_test/robust/bin/Robust.ini
[2026-04-01 16:59:00] [INFO] Using profile: grid-sim
[2026-04-01 16:59:00] [INFO] Validating INI file: /tmp/osm_update_test/robust/bin/Robust.ini
[2026-04-01 16:59:00] [INFO] Validating INI file: /tmp/osm_update_test/sim1/bin/OpenSim.ini
[2026-04-01 16:59:00] [INFO] Configuration validation passed
[2026-04-01 17:17:00] [INFO] Using profile: grid-sim
[2026-04-01 17:17:18] [INFO] Using profile: grid-sim
[2026-04-01 17:17:38] [INFO] Using profile: grid-sim
[2026-04-01 17:17:38] [INFO] Validating INI file: /opt/robust/bin/Robust.ini
[2026-04-01 17:17:39] [INFO] Validating INI file: /opt/sim1/bin/OpenSim.ini
[2026-04-01 17:17:39] [INFO] Validating INI file: /opt/sim2/bin/OpenSim.ini
[2026-04-01 17:17:40] [INFO] Configuration validation passed
[2026-04-01 17:18:14] [INFO] Using profile: grid-sim
[2026-04-01 17:18:15] [INFO] Creating rollback archive: /opt/rollback/opensim_20260401_171814_realcheck.tar.gz
[2026-04-01 17:18:15] [INFO] [DRY-RUN] Would archive paths listed in: /opt/rollback/opensim_20260401_171814_realcheck.manifest
[2026-04-01 17:18:15] [INFO] Creating rollback archive: /opt/rollback/janus_20260401_171815_realcheck.tar.gz
[2026-04-01 17:18:15] [INFO] [DRY-RUN] Would archive paths listed in: /opt/rollback/janus_20260401_171815_realcheck.manifest
[2026-04-01 17:18:15] [INFO] Using profile: grid-sim
[2026-04-01 17:18:15] [INFO] Creating rollback archive: /opt/rollback/opensim_20260401_171815_realcheck.tar.gz
[2026-04-01 17:18:15] [INFO] [DRY-RUN] Would archive paths listed in: /opt/rollback/opensim_20260401_171815_realcheck.manifest
[2026-04-01 17:18:15] [INFO] Delegating update to install action: install-opensim
[2026-04-01 17:18:15] [INFO] [DRY-RUN] /mnt/d/OpenSimBash/opensimMULTITOOLS-II/modular/osmtool_install.sh --profile grid-sim --workdir /opt --action install-opensim --opensim-dir /opt/opensim --repo-mode update --deploy-binaries true --janus-prefix /opt/janus --janus-src /opt/janus-gateway
[2026-04-01 17:18:15] [INFO] Using profile: grid-sim
[2026-04-01 17:18:15] [INFO] Creating rollback archive: /opt/rollback/janus_20260401_171815_realcheck.tar.gz
[2026-04-01 17:18:15] [INFO] [DRY-RUN] Would archive paths listed in: /opt/rollback/janus_20260401_171815_realcheck.manifest
[2026-04-01 17:18:15] [INFO] Delegating update to install action: install-janus
[2026-04-01 17:18:15] [INFO] [DRY-RUN] /mnt/d/OpenSimBash/opensimMULTITOOLS-II/modular/osmtool_install.sh --profile grid-sim --workdir /opt --action install-janus --opensim-dir /opt/opensim --repo-mode update --deploy-binaries true --janus-prefix /opt/janus --janus-src /opt/janus-gateway
[2026-04-01 17:18:22] [INFO] Using profile: grid-sim
[2026-04-01 17:22:54] [INFO] Using profile: grid-sim
[2026-04-01 17:22:54] [INFO] Report generated: /opt/reports/osm_report_20260401_172254.txt
[2026-04-01 17:25:10] [INFO] OSMTool cron jobs installed/updated
[2026-04-01 17:25:10] [INFO] OSMTool cron block removed
[2026-04-01 17:25:59] [INFO] OSMTool cron jobs installed/updated
[2026-04-01 17:26:06] [INFO] OSMTool cron block removed
[2026-04-01 17:27:28] [INFO] Using profile: grid-sim
[2026-04-01 17:27:28] [INFO] Starting grid from /opt
[2026-04-01 17:27:28] [INFO] Starting robustserver from /opt/robust/bin
[2026-04-01 17:27:28] [INFO] Screen session 'robustserver' started successfully
[2026-04-01 17:27:28] [INFO] Starting moneyserver from /opt/robust/bin
[2026-04-01 17:27:29] [INFO] Screen session 'moneyserver' started successfully
[2026-04-01 17:27:29] [INFO] Starting sim1 from /opt/sim1/bin
[2026-04-01 17:27:30] [ERROR] Screen session 'sim1' did not stay alive — check logs or missing config
[2026-04-01 17:27:32] [INFO] Using profile: grid-sim
[2026-04-01 17:27:32] [INFO] Stopping grid
[2026-04-01 17:27:32] [INFO] Sent shutdown to robustserver
[2026-04-01 17:27:37] [INFO] Starting grid from /opt
[2026-04-01 17:27:37] [INFO] Starting robustserver from /opt/robust/bin
[2026-04-01 17:27:38] [INFO] Screen session 'robustserver' started successfully
[2026-04-01 17:27:38] [INFO] Starting moneyserver from /opt/robust/bin
[2026-04-01 17:27:38] [ERROR] Screen session 'moneyserver' did not stay alive — check logs or missing config
[2026-04-01 17:27:38] [INFO] Starting sim1 from /opt/sim1/bin
[2026-04-01 17:27:39] [ERROR] Screen session 'sim1' did not stay alive — check logs or missing config
[2026-04-01 17:27:41] [INFO] Using profile: grid-sim
[2026-04-01 17:27:41] [INFO] Stopping grid
[2026-04-01 17:27:41] [INFO] Sent shutdown to robustserver
[2026-04-01 17:27:55] [INFO] Using profile: robust
[2026-04-01 17:27:55] [INFO] Starting robustserver from /opt/robust/bin
[2026-04-01 17:27:56] [INFO] Screen session 'robustserver' started successfully
[2026-04-01 17:27:58] [INFO] Using profile: robust
[2026-04-01 17:27:58] [INFO] Sent shutdown to robustserver
[2026-04-01 17:28:00] [ERROR] Ziel fuer dieses Profil nicht erlaubt␍: profile=robust target=sim1
[2026-04-01 17:28:32] [INFO] Using profile: standalone
[2026-04-01 17:28:32] [ERROR] standalone cannot start: missing /opt/standalone/bin/OpenSim.ini — run the config action first
[2026-04-01 17:28:32] [INFO] Using profile: standalone
[2026-04-01 17:28:32] [INFO] standalone is not running
[2026-04-01 17:28:33] [INFO] Using profile: grid-sim
[2026-04-01 17:28:33] [INFO] Using profile: grid-sim
[2026-04-01 17:29:46] [INFO] Using profile: standalone
[2026-04-01 17:29:46] [ERROR] standalone cannot start: missing /opt/standalone/bin/OpenSim.ini — run the config action first
[2026-04-01 17:29:46] [ERROR] Ziel fuer dieses Profil nicht erlaubt␍: profile=robust target=sim1
[2026-04-01 17:30:35] [INFO] Using profile: grid-sim
[2026-04-01 17:30:35] [INFO] Starting grid from /opt
[2026-04-01 17:30:35] [INFO] Starting robustserver from /opt/robust/bin
[2026-04-01 17:30:35] [INFO] Screen session 'robustserver' started successfully
[2026-04-01 17:30:35] [INFO] Starting moneyserver from /opt/robust/bin
[2026-04-01 17:30:36] [INFO] Screen session 'moneyserver' started successfully
[2026-04-01 17:30:36] [INFO] Starting sim1 from /opt/sim1/bin
[2026-04-01 17:30:36] [ERROR] Screen session 'sim1' did not stay alive — check logs or missing config
[2026-04-01 17:31:16] [INFO] Using profile: grid-sim
[2026-04-01 17:31:16] [INFO] Starting grid from /opt
[2026-04-01 17:31:16] [INFO] Screen session 'robustserver' already running — skipping start
[2026-04-01 17:31:16] [INFO] Screen session 'moneyserver' already running — skipping start
[2026-04-01 17:31:16] [INFO] Starting sim1 from /opt/sim1/bin
[2026-04-01 17:31:16] [INFO] Screen session 'sim1' started successfully
[2026-04-01 17:31:20] [INFO] Using profile: grid-sim
[2026-04-01 17:31:20] [INFO] Stopping grid
[2026-04-01 17:31:20] [INFO] Sent shutdown to sim1
[2026-04-01 17:31:20] [INFO] Sent shutdown to moneyserver
[2026-04-01 17:31:20] [INFO] Sent shutdown to robustserver
[2026-04-01 17:32:50] [INFO] Using profile: grid-sim
[2026-04-01 17:32:50] [INFO] Starting grid from /opt
[2026-04-01 17:32:50] [INFO] Starting robustserver from /opt/robust/bin
[2026-04-01 17:32:50] [INFO] Screen session 'robustserver' started successfully
[2026-04-01 17:32:50] [INFO] Starting moneyserver from /opt/robust/bin
[2026-04-01 17:32:51] [INFO] Screen session 'moneyserver' started successfully
[2026-04-01 17:32:51] [INFO] Screen session 'sim1' already running — skipping start
[2026-04-01 17:32:54] [INFO] Using profile: grid-sim
[2026-04-01 17:32:54] [INFO] Stopping grid
[2026-04-01 17:32:54] [INFO] Sent shutdown to sim1
[2026-04-01 17:33:00] [INFO] Session 'sim1' still running after shutdown; forcing screen quit
[2026-04-01 17:33:01] [INFO] Screen session 'sim1' stopped
[2026-04-01 17:33:01] [INFO] Sent shutdown to moneyserver
[2026-04-01 17:33:07] [INFO] Screen session 'moneyserver' stopped
[2026-04-01 17:33:07] [INFO] Sent shutdown to robustserver
[2026-04-01 17:33:13] [INFO] Screen session 'robustserver' stopped
[2026-04-01 17:33:28] [INFO] Using profile: grid-sim
[2026-04-01 17:33:28] [INFO] Janus already running
[2026-04-01 17:33:30] [INFO] Using profile: grid-sim
[2026-04-01 17:33:40] [INFO] Using profile: grid-sim
[2026-04-01 17:33:40] [INFO] Using profile: grid-sim
[2026-04-01 17:34:46] [INFO] Using workdir: /opt
[2026-04-01 17:34:46] [INFO] Using profile: grid-sim
[2026-04-01 17:34:46] [INFO] Repo mode: update
[2026-04-01 17:34:46] [INFO] OpenSim dir: /opt/opensim
[2026-04-01 17:34:46] [INFO] Janus prefix: /opt/janus
[2026-04-01 17:34:46] [INFO] Configuring OpenSim runtime ini files
[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/robust/bin/Robust.ini
[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/robust/bin/Robust.ini
[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/robust/bin/MoneyServer.ini
[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/robust/bin/config-include/GridCommon.ini
[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/robust/bin/config-include/osslEnable.ini
[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/sim1/bin/OpenSim.ini
[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/sim1/bin/config-include/GridCommon.ini
[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/sim1/bin/config-include/osslEnable.ini
[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/sim2/bin/OpenSim.ini
[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/sim2/bin/config-include/GridCommon.ini
[2026-04-01 17:34:46] [INFO] Keeping existing config: /opt/sim2/bin/config-include/osslEnable.ini
[2026-04-01 17:34:47] [INFO] Updated MoneyServer.ini database credentials from configured values
[2026-04-01 17:34:47] [INFO] OpenSim runtime ini configuration completed
[2026-04-01 17:38:33] [INFO] [SMOKE] profile=grid-sim target=grid workdir=/opt
[2026-04-01 17:38:33] [INFO] [SMOKE] expected_screens=robustserver,sim1
[2026-04-01 17:38:33] [INFO] Using profile: grid-sim
[2026-04-01 17:38:33] [INFO] Starting grid from /opt
[2026-04-01 17:38:33] [INFO] Starting robustserver from /opt/robust/bin
[2026-04-01 17:38:34] [INFO] Screen session 'robustserver' started successfully
[2026-04-01 17:38:34] [INFO] Starting moneyserver from /opt/robust/bin
[2026-04-01 17:38:34] [INFO] Screen session 'moneyserver' started successfully
[2026-04-01 17:38:34] [INFO] Starting sim1 from /opt/sim1/bin
[2026-04-01 17:38:35] [INFO] Screen session 'sim1' started successfully
[2026-04-01 17:38:38] [INFO] [SMOKE][OK] screen up: robustserver
[2026-04-01 17:38:38] [INFO] [SMOKE][OK] screen up: sim1
[2026-04-01 17:38:38] [INFO] Using profile: grid-sim
[2026-04-01 17:38:38] [INFO] Stopping grid
[2026-04-01 17:38:38] [INFO] Sent shutdown to sim1
[2026-04-01 17:38:44] [INFO] Session 'sim1' still running after shutdown; forcing screen quit
[2026-04-01 17:38:45] [INFO] Screen session 'sim1' stopped
[2026-04-01 17:38:45] [INFO] Sent shutdown to moneyserver
[2026-04-01 17:38:51] [INFO] Screen session 'moneyserver' stopped
[2026-04-01 17:38:51] [INFO] Sent shutdown to robustserver
[2026-04-01 17:38:57] [INFO] Screen session 'robustserver' stopped
[2026-04-01 17:39:02] [INFO] Starting grid from /opt
[2026-04-01 17:39:02] [INFO] Starting robustserver from /opt/robust/bin
[2026-04-01 17:39:03] [INFO] Screen session 'robustserver' started successfully
[2026-04-01 17:39:03] [INFO] Starting moneyserver from /opt/robust/bin
[2026-04-01 17:39:03] [INFO] Screen session 'moneyserver' started successfully
[2026-04-01 17:39:03] [INFO] Starting sim1 from /opt/sim1/bin
[2026-04-01 17:39:04] [INFO] Screen session 'sim1' started successfully
[2026-04-01 17:39:07] [INFO] [SMOKE][OK] screen up: robustserver
[2026-04-01 17:39:07] [INFO] [SMOKE][OK] screen up: sim1
[2026-04-01 17:39:07] [INFO] Using profile: grid-sim
[2026-04-01 17:39:07] [INFO] Stopping grid
[2026-04-01 17:39:07] [INFO] Sent shutdown to sim1
[2026-04-01 17:39:13] [INFO] Session 'sim1' still running after shutdown; forcing screen quit
[2026-04-01 17:39:14] [INFO] Screen session 'sim1' stopped
[2026-04-01 17:39:14] [INFO] Sent shutdown to moneyserver
[2026-04-01 17:39:20] [INFO] Screen session 'moneyserver' stopped
[2026-04-01 17:39:20] [INFO] Sent shutdown to robustserver
[2026-04-01 17:39:26] [INFO] Screen session 'robustserver' stopped
[2026-04-01 17:39:34] [INFO] [SMOKE][OK] screen down: robustserver
[2026-04-01 17:39:34] [INFO] [SMOKE][OK] screen down: sim1
[2026-04-01 17:39:34] [INFO] [SMOKE][PASS] Start/Restart/Stop smoke test successful
[2026-04-01 17:39:35] [INFO] [SMOKE] profile=robust target=robust workdir=/opt
[2026-04-01 17:39:35] [INFO] [SMOKE] expected_screens=robustserver
[2026-04-01 17:39:35] [INFO] Using profile: robust
[2026-04-01 17:39:35] [INFO] Starting robustserver from /opt/robust/bin
[2026-04-01 17:39:35] [INFO] Screen session 'robustserver' started successfully
[2026-04-01 17:39:37] [INFO] [SMOKE][OK] screen up: robustserver
[2026-04-01 17:39:37] [INFO] Using profile: robust
[2026-04-01 17:39:37] [INFO] Sent shutdown to robustserver
[2026-04-01 17:39:43] [INFO] Screen session 'robustserver' stopped
[2026-04-01 17:39:46] [INFO] Starting robustserver from /opt/robust/bin
[2026-04-01 17:39:47] [INFO] Screen session 'robustserver' started successfully
[2026-04-01 17:39:49] [INFO] [SMOKE][OK] screen up: robustserver
[2026-04-01 17:39:49] [INFO] Using profile: robust
[2026-04-01 17:39:49] [INFO] Sent shutdown to robustserver
[2026-04-01 17:39:57] [INFO] Screen session 'robustserver' stopped
[2026-04-01 17:40:03] [INFO] [SMOKE][OK] screen down: robustserver
[2026-04-01 17:40:03] [INFO] [SMOKE][PASS] Start/Restart/Stop smoke test successful
[2026-04-01 17:43:37] [INFO] OSMTool cron jobs installed/updated
[2026-04-01 17:43:37] [INFO] OSMTool cron block removed
[2026-04-01 17:47:28] [INFO] Using profile: grid-sim
[2026-04-01 17:47:28] [INFO] [SMOKE] profile=robust target=robust workdir=/opt
[2026-04-01 17:47:28] [INFO] [SMOKE] expected_screens=robustserver
[2026-04-01 17:47:29] [INFO] Using profile: robust
[2026-04-01 17:47:29] [INFO] Starting robustserver from /opt/robust/bin
[2026-04-01 17:47:29] [INFO] Screen session 'robustserver' started successfully
[2026-04-01 17:47:32] [INFO] [SMOKE][OK] screen up: robustserver
[2026-04-01 17:47:32] [INFO] Using profile: robust
[2026-04-01 17:47:32] [INFO] Sent shutdown to robustserver
[2026-04-01 17:47:38] [INFO] Screen session 'robustserver' stopped
[2026-04-01 17:47:41] [INFO] Starting robustserver from /opt/robust/bin
[2026-04-01 17:47:42] [INFO] Screen session 'robustserver' started successfully
[2026-04-01 17:47:45] [INFO] [SMOKE][OK] screen up: robustserver
[2026-04-01 17:47:45] [INFO] Using profile: robust
[2026-04-01 17:47:45] [INFO] Sent shutdown to robustserver
[2026-04-01 17:47:51] [INFO] Screen session 'robustserver' stopped
[2026-04-01 17:47:59] [INFO] [SMOKE][OK] screen down: robustserver
[2026-04-01 17:47:59] [INFO] [SMOKE][PASS] Start/Restart/Stop smoke test successful
[2026-04-01 17:47:59] [INFO] Running health checks for profile: grid-sim
[2026-04-01 17:47:59] [INFO] Using workdir: /opt
[2026-04-01 17:47:59] [INFO] [WARN] Screen check skipped (no expected screens configured)
[2026-04-01 17:47:59] [INFO] [WARN] Port check skipped (no ports configured)
[2026-04-01 17:47:59] [INFO] [OK] Database reachable: robust
[2026-04-01 17:47:59] [INFO] [OK] Log scan: /mnt/d/OpenSimBash/opensimMULTITOOLS-II/osmtool_backup.log -> 52 matches
[2026-04-01 17:47:59] [INFO] [WARN] Log file not found, skipped: /opt/robust/bin/OpenSim.log
[2026-04-01 17:47:59] [ERROR] [FAIL] Log warning threshold exceeded: 52 > 50
[2026-04-01 17:47:59] [ERROR] [FAIL] Healthcheck FAILED with 1 failing check group(s)
[2026-04-01 17:47:59] [INFO] Using profile: grid-sim
[2026-04-01 17:47:59] [INFO] Validating INI file: /opt/robust/bin/Robust.ini
[2026-04-01 17:48:00] [INFO] Validating INI file: /opt/sim1/bin/OpenSim.ini
[2026-04-01 17:48:00] [INFO] Validating INI file: /opt/sim2/bin/OpenSim.ini
[2026-04-01 17:48:01] [INFO] Configuration validation passed
[2026-04-01 17:56:29] [INFO] OSMTool cron jobs installed/updated
[2026-04-01 17:56:37] [INFO] OSMTool cron jobs installed/updated
+718
View File
@@ -0,0 +1,718 @@
#!/bin/bash
# Backup Version 1.0
STARTVERZEICHNIS="/opt" # Startverzeichnis
ROBUSTVERZEICHNIS="Robust" # Robust Verzeichnis
REGIONSDATEI="osmregionlist.ini" # Regionsdatei
SIMDATEI="osmsimlist.ini" # Simulationsdatei
BACKUPWARTEZEIT=60 # Wartezeit in Sekunden zwischen den Backups
KOMMANDO=$1
#──────────────────────────────────────────────────────────────────────────────────────────
#* Informationen
#──────────────────────────────────────────────────────────────────────────────────────────
#
## * makeverzeichnisliste
# Eine Funktion zum Erstellen einer Liste von Verzeichnissen aus einer Datei.
#? Verwendung: makeverzeichnisliste
# Diese Funktion liest Zeilen aus der angegebenen SIMDATEI im STARTVERZEICHNIS und erstellt eine
# Liste von Verzeichnissen. Die Liste wird in der globalen Variable VERZEICHNISSLISTE gespeichert.
# Die Anzahl der Einträge in der Liste wird in der globalen Variable ANZAHLVERZEICHNISSLISTE gespeichert.
#? Argumente:
# STARTVERZEICHNIS - Das Verzeichnis, in dem sich die SIMDATEI befindet.
# SIMDATEI - Die Datei, aus der die Verzeichnisse gelesen werden sollen.
#? Beispielaufruf
# makeverzeichnisliste
##
function makeverzeichnisliste() {
# Letzte Bearbeitung 27.09.2023
# Initialisieren der Verzeichnisliste
VERZEICHNISSLISTE=()
# Schleife zum Lesen der Zeilen aus der SIMDATEI und Hinzufügen zum Array
while IFS= read -r line; do
VERZEICHNISSLISTE+=("$line")
done </$STARTVERZEICHNIS/$SIMDATEI
# Anzahl der Einträge in der Verzeichnisliste
ANZAHLVERZEICHNISSLISTE=${#VERZEICHNISSLISTE[*]}
# Erfolgreiche Ausführung
return 0
}
## * makeregionsliste
# Eine Funktion zum Erstellen einer Liste von Regionen aus einer Datei.
#? Verwendung: makeregionsliste
# Diese Funktion liest Zeilen aus der angegebenen REGIONSDATEI im STARTVERZEICHNIS und erstellt eine
# Liste von Regionen. Die Liste wird in der globalen Variable REGIONSLISTE gespeichert.
# Die Anzahl der Einträge in der Liste wird in der globalen Variable ANZAHLREGIONSLISTE gespeichert.
#? Argumente:
# STARTVERZEICHNIS - Das Verzeichnis, in dem sich die REGIONSDATEI befindet.
# REGIONSDATEI - Die Datei, aus der die Regionen gelesen werden sollen.
#? Beispiel:
# makeregionsliste
##
function makeregionsliste() {
# Letzte Bearbeitung 27.09.2023
# Initialisieren der Regionenliste
REGIONSLISTE=()
# Schleife zum Lesen der Zeilen aus der REGIONSDATEI und Hinzufügen zum Array
while IFS= read -r line; do
REGIONSLISTE+=("$line")
done </$STARTVERZEICHNIS/$REGIONSDATEI
# Anzahl der Einträge in der Regionenliste
ANZAHLREGIONSLISTE=${#REGIONSLISTE[*]} # Anzahl der Eintraege.
# Erfolgreiche Ausführung
return 0
}
## * regionsiniteilen
# Diese Funktion nimmt drei Argumente entgegen: das Verzeichnis, aus dem die Werte gelesen werden sollen ($INIVERZEICHNIS),
# den Namen der Region, für die die Werte gespeichert werden sollen ($RTREGIONSNAME), und den Pfad zur Haupt-Regions.ini-Datei
# ($INI_FILE). Die Funktion überprüft, ob die Haupt-Regions.ini-Datei vorhanden ist, und wenn nicht, werden die Werte für die
# angegebene Region in eine separate INI-Datei geschrieben.
#? Parameter:
# - $1: Das Verzeichnis, aus dem die Werte gelesen werden sollen.
# - $2: Der Name der Region, für die die Werte gespeichert werden sollen.
# - $3: Der Pfad zur Haupt-Regions.ini-Datei.
#? Rückgabewert:
# - 0: Erfolgreiche Ausführung der Funktion
#? Verwendungsbeispiel:
# regionsiniteilen "MeinVerzeichnis" "MeineRegion" "/MeinVerzeichnis/bin/Regions/Regions.ini"
##
function regionsiniteilen() {
# Letzte Bearbeitung 01.10.2023
INIVERZEICHNIS=$1 # Auszulesendes Verzeichnis
RTREGIONSNAME=$2 # Auszulesende Region
INI_FILE="/$STARTVERZEICHNIS/$INIVERZEICHNIS/bin/Regions/Regions.ini" # Auszulesende Datei
if [ ! -d "$INI_FILE" ]; then
echo info "REGIONSINITEILEN: Schreiben der Werte fuer $RTREGIONSNAME"
# Schreiben der einzelnen Punkte nur wenn vorhanden ist.
# shellcheck disable=SC2005
{
echo "[$RTREGIONSNAME]"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "RegionUUID")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "Location")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "SizeX")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "SizeY")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "SizeZ")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "InternalAddress")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "InternalPort")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "AllowAlternatePorts")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "ResolveAddress")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "ExternalHostName")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "MaxPrims")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "MaxAgents")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "DefaultLanding")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "NonPhysicalPrimMax")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "PhysicalPrimMax")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "ClampPrimSize")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "MaxPrimsPerUser")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "ScopeID")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "RegionType")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "MaptileStaticUUID")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "MaptileStaticFile")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "MasterAvatarFirstName")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "MasterAvatarLastName")"
echo "$(get_value_from_Region_key "${INI_FILE}" "$RTREGIONSNAME" "MasterAvatarSandboxPassword")"
} >>"/$STARTVERZEICHNIS/$INIVERZEICHNIS/bin/Regions/$RTREGIONSNAME.ini"
else
echo error "REGIONSINITEILEN: $INI_FILE wurde nicht gefunden"
fi
return 0
}
## * autoregionsiniteilen
# Diese Funktion ruft zuerst die Funktion `makeverzeichnisliste` auf, um eine Liste von Verzeichnissen zu erstellen,
# und iteriert dann über jedes Verzeichnis in der Liste. Für jedes Verzeichnis wird die Regions.ini-Datei in mehrere
# separate INI-Dateien aufgeteilt, eine für jede Region, und die einzelnen Regionsdateien werden umbenannt. Falls die
# Regions.ini-Datei in einem Verzeichnis nicht vorhanden ist, wird sie nicht umbenannt.
#? Parameter:
# - Keine
#? Rückgabewert:
# - 0: Erfolgreiche Ausführung der Funktion
#? Verwendungsbeispiel:
# autoregionsiniteilen
##
function autoregionsiniteilen() {
# Letzte Bearbeitung 01.10.2023
makeverzeichnisliste
sleep 1
for ((i = 0; i < "$ANZAHLVERZEICHNISSLISTE"; i++)); do
echo info "Region.ini ${VERZEICHNISSLISTE[$i]} zerlegen"
echo line
VERZEICHNIS="${VERZEICHNISSLISTE[$i]}"
# Regions.ini teilen:
echo "$VERZEICHNIS" # OK geht
INI_FILE="/$STARTVERZEICHNIS/$VERZEICHNIS/bin/Regions/Regions.ini" # Auszulesende Datei
# shellcheck disable=SC2155
declare -a TARGETS="$(get_regionsarray "${INI_FILE}")"
# shellcheck disable=SC2068
for MeineRegion in ${TARGETS[@]}; do
regionsiniteilen "$VERZEICHNIS" "$MeineRegion"
sleep 1
echo rohtext "regionsiniteilen $VERZEICHNIS $MeineRegion"
done
# Dann umbenennen:
# Pruefung ob Datei vorhanden ist, wenn ja umbenennen.
if [ ! -d "$INI_FILE" ]; then
mv /$STARTVERZEICHNIS/"$INIVERZEICHNIS"/bin/Regions/Regions.ini /$STARTVERZEICHNIS/"$INIVERZEICHNIS"/bin/Regions/"$DATUM"-Regions.ini.old
fi
done
return 0
}
## * createregionlist
# Diese Funktion erstellt eine Liste von Regionsnamen aus den INI-Dateien in den angegebenen Verzeichnissen und speichert
# sie in der Datei osmregionlist.ini. Zuvor wird die vorhandene osmregionlist.ini-Datei (falls vorhanden) gesichert und in
# osmregionlist.ini.old umbenannt.
#? Parameter:
# - Keine
#? Rückgabewert:
# - 0: Erfolgreiche Ausführung der Funktion
#? Verwendungsbeispiel:
# createregionlist
##
function createregionlist() {
# Letzte Bearbeitung 01.10.2023
# Alte osmregionlist.ini sichern und in osmregionlist.ini.old umbenennen.
if [ -f "/$STARTVERZEICHNIS/osmregionlist.ini" ]; then
if [ -f "/$STARTVERZEICHNIS/osmregionlist.ini.old" ]; then
rm -r /$STARTVERZEICHNIS/osmregionlist.ini.old
fi
mv /$STARTVERZEICHNIS/osmregionlist.ini /$STARTVERZEICHNIS/osmregionlist.ini.old
fi
# Die mit regionsconfigdateiliste erstellte Datei osmregionlist.ini nach sim Verzeichnis und Regionsnamen in die osmregionlist.ini speichern.
declare -A Dateien # Array erstellen
makeverzeichnisliste
sleep 1
for ((i = 0; i < "$ANZAHLVERZEICHNISSLISTE"; i++)); do
echo info "Regionnamen ${VERZEICHNISSLISTE[$i]} schreiben"
VERZEICHNIS="${VERZEICHNISSLISTE[$i]}"
# shellcheck disable=SC2178
Dateien=$(find /$STARTVERZEICHNIS/"$VERZEICHNIS"/bin/Regions/ -name "*.ini")
for i2 in "${Dateien[@]}"; do # Array abarbeiten
echo "$i2" >>osmregionlist.ini
done
done
# Ueberfluessige Zeichen entfernen
LOESCHEN=$(sed s/'\/'$STARTVERZEICHNIS'\/'//g /$STARTVERZEICHNIS/osmregionlist.ini)
echo "$LOESCHEN" >/$STARTVERZEICHNIS/osmregionlist.ini # Aenderung /$STARTVERZEICHNIS/ speichern.
LOESCHEN=$(sed s/'\/bin\/Regions\/'/' "'/g /$STARTVERZEICHNIS/osmregionlist.ini) # /bin/Regions/ entfernen.
echo "$LOESCHEN" >/$STARTVERZEICHNIS/osmregionlist.ini # Aenderung /bin/Regions/ speichern.
LOESCHEN=$(sed s/'.ini'/'"'/g /$STARTVERZEICHNIS/osmregionlist.ini) # Endung .ini entfernen.
echo "$LOESCHEN" >/$STARTVERZEICHNIS/osmregionlist.ini # Aenderung .ini entfernen speichern.
LOESCHEN=$(sed s/'\"'/''/g /$STARTVERZEICHNIS/osmregionlist.ini) # " entfernen.
echo "$LOESCHEN" >/$STARTVERZEICHNIS/osmregionlist.ini # Aenderung " entfernen speichern.
# Schauen ob da noch Regions.ini bei sind also Regionen mit dem Namen Regions, diese Zeilen loeschen.
LOESCHEN=$(sed '/Regions/d' /$STARTVERZEICHNIS/osmregionlist.ini) # Alle Zeilen mit dem Eintrag Regions entfernen .
echo "$LOESCHEN" >/$STARTVERZEICHNIS/osmregionlist.ini # Aenderung .ini entfernen speichern.
return 0
}
## * regionbackup
# Diese Funktion erstellt ein Backup für eine OpenSimulator-Region, indem sie OAR- und Terrain-Daten sowie Konfigurationsdateien speichert.
# Die Region wird in den Offline-Modus versetzt, wenn sie nicht bereits offline ist, bevor das Backup erstellt wird.
#? Parameter:
# 1. BACKUPVERZEICHNISSCREENNAME: Der Name des Bildschirms (Screen) für das Backup.
# 2. REGIONSNAME: Der Name der Region, die gesichert werden soll.
#? Rückgabewert:
# - 0: Erfolgreiche Ausführung der Funktion
#? Verwendungsbeispiel:
# regionbackup "BackupScreen" "MyRegion"
##
function regionbackup() {
# Letzte Bearbeitung 01.10.2023
# regionbackup "$BACKUPSCREEN" "$BACKUPREGION"
echo "Erstelle Backup von: $BACKUPSCREEN $BACKUPREGION"
sleep 1
BACKUPVERZEICHNISSCREENNAME=$1
REGIONSNAME=$2
echo
echo "Backup $BACKUPVERZEICHNISSCREENNAME der Region $REGIONSNAME"
cd /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin || return 1 # Test ob Verzeichnis vorhanden.
mkdir -p /$STARTVERZEICHNIS/backup # Backup Verzeichnis anlegen falls nicht vorhanden.
echo "Ich kann nicht pruefen ob die Region im OpenSimulator vorhanden ist."
echo "Sollte sie nicht vorhanden sein wird root also alle Regionen gespeichert"
# Ist die Region Online oder Offline?
if [ -f /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"$REGIONSNAME".ini.offline ]; then
echo "$REGIONSNAME ist heruntergefahren."
else
screen -S "$BACKUPVERZEICHNISSCREENNAME" -p 0 -X eval "stuff 'change region ${REGIONSNAME//\"/}'^M"
echo "$BACKUPVERZEICHNISSCREENNAME $REGIONSNAME"
screen -S "$BACKUPVERZEICHNISSCREENNAME" -p 0 -X eval "stuff 'save oar /$STARTVERZEICHNIS/backup/'$DATUM'-$REGIONSNAME.oar'^M"
echo "$BACKUPVERZEICHNISSCREENNAME $DATUM-$REGIONSNAME.oar"
# Neu xml backup
screen -S "$BACKUPVERZEICHNISSCREENNAME" -p 0 -X eval "stuff 'save xml2 /$STARTVERZEICHNIS/backup/'$DATUM'-$REGIONSNAME.xml2'^M"
echo "$BACKUPVERZEICHNISSCREENNAME $DATUM-$REGIONSNAME.xml2"
# Neu xml backup ende
screen -S "$BACKUPVERZEICHNISSCREENNAME" -p 0 -X eval "stuff 'terrain save /$STARTVERZEICHNIS/backup/'$DATUM'-$REGIONSNAME.png'^M"
echo "$BACKUPVERZEICHNISSCREENNAME $DATUM-$REGIONSNAME.png"
screen -S "$BACKUPVERZEICHNISSCREENNAME" -p 0 -X eval "stuff 'terrain save /$STARTVERZEICHNIS/backup/'$DATUM'-$REGIONSNAME.raw'^M"
echo "$BACKUPVERZEICHNISSCREENNAME $DATUM-$REGIONSNAME.raw"
echo "$BACKUPVERZEICHNISSCREENNAME Region $DATUM-$REGIONSNAME RAW und PNG Terrain werden gespeichert"
fi
sleep 10
if [ -f /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"$REGIONSNAME".ini.offline ]; then
cp /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"$REGIONSNAME".ini.offline /$STARTVERZEICHNIS/backup/"$DATUM"-"$REGIONSNAME".ini.offline
echo "$BACKUPVERZEICHNISSCREENNAME Die Regions $DATUM-$REGIONSNAME.ini.offline wird gespeichert"
fi
if [ -f /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"$REGIONSNAME".ini ]; then
cp /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"$REGIONSNAME".ini /$STARTVERZEICHNIS/backup/"$DATUM"-"$REGIONSNAME".ini
echo "$BACKUPVERZEICHNISSCREENNAME Die Regions $DATUM-$REGIONSNAME.ini wird gespeichert"
fi
if [ -f /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"${REGIONSNAME//\"/}" ]; then
cp /$STARTVERZEICHNIS/"$BACKUPVERZEICHNISSCREENNAME"/bin/Regions/"${REGIONSNAME//\"/}" /$STARTVERZEICHNIS/backup/"$DATUM"-"$REGIONSNAME".ini
echo "$BACKUPVERZEICHNISSCREENNAME Die Regions $DATUM-$REGIONSNAME.ini wird gespeichert"
fi
echo "Backup der Region $REGIONSNAME wird fertiggestellt."
return 0
}
## * autoallclean.
# loescht Log, dll, so, exe, aot Dateien fuer einen saubere neue installation, mit Robust.
# Hierbei werden keine Datenbanken oder Konfigurationen geloescht aber opensim ist anschliessend nicht mehr startbereit.
# Um opensim wieder Funktionsbereit zu machen muss ein Upgrade oder ein oscopy vorgang ausgefuehrt werden.
#? @param keine.
#? @return nichts wird zurueckgegeben.
# todo: nichts.
##
function autoallclean() {
# Letzte Bearbeitung 01.10.2023
makeverzeichnisliste
sleep 1
for ((i = 0; i < "$ANZAHLVERZEICHNISSLISTE"; i++)); do
# Dateien
rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.log
rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.dll
rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.exe
rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.so
rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.xml
rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.dylib
rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.example
rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.sample
rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.txt
rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.config
rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.py
rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.old
rm /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/*.pdb
# Verzeichnisse leeren
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/assetcache/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/maptiles/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/MeshCache/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/j2kDecodeCache/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/ScriptEngines/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/addin-db-002/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/addon-modules/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/assets/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/bakes/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/data/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/Estates/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/inventory/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/lib/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/lib32/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/lib64/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/Library/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/openmetaverse_data/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/robust-include/*
echo "autoallclean: ${VERZEICHNISSLISTE[$i]} geloescht"
sleep 1
done
# nochmal das gleiche mit Robust
echo "autoallclean: $ROBUSTVERZEICHNIS geloescht"
rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.log
rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.dll
rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.exe
rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.so
rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.xml
rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.dylib
rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.example
rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.sample
rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.txt
rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.config
rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.py
rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.old
rm /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/*.pdb
rm -r /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/assetcache/*
rm -r /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/maptiles/*
rm -r /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/MeshCache/*
rm -r /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/j2kDecodeCache/*
rm -r /$STARTVERZEICHNIS/$ROBUSTVERZEICHNIS/bin/ScriptEngines/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/addin-db-002/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/addon-modules/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/assets/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/bakes/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/data/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/Estates/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/inventory/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/lib/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/lib32/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/lib64/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/Library/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/openmetaverse_data/*
rm -r /$STARTVERZEICHNIS/"${VERZEICHNISSLISTE[$i]}"/bin/robust-include/*
return 0
}
## * autoregionbackup
# Diese Funktion durchläuft die Regionen in Ihrem OpenSimulator-Grid und erstellt automatische Backups.
#? Parameter:
# Keine Parameter erforderlich.
#? Rückgabewert:
# - Kein expliziter Rückgabewert
#? Verwendungsbeispiel:
# autoregionbackup
# todo: Uebergabe Parameter Fehler. Bug Modus mit extra ausgaben. Ist keine osmregionlist.ini vorhanden muss sie erstellt werden.
##
function autoregionbackup() {
# Letzte Bearbeitung 01.10.2023
echo "Automatisches Backup wird gestartet."
# Ist die osmregionlist.ini vorhanden?
if [ ! -f "/$STARTVERZEICHNIS/osmregionlist.ini" ]; then
echo "Die osmregionlist.ini Datei ist noch nicht vorhanden und wird erstellt."
createregionlist
else
echo "Die osmregionlist.ini Datei ist bereits vorhanden."
fi
makeregionsliste
sleep 1
for ((i = 0; i < "$ANZAHLREGIONSLISTE"; i++)); do
BACKUPSCREEN=$(echo "${REGIONSLISTE[$i]}" | cut -d ' ' -f 1)
BACKUPREGION=$(echo "${REGIONSLISTE[$i]}" | cut -d ' ' -f 2)
echo "Starte Backup von: $BACKUPSCREEN $BACKUPREGION" # Testausgabe
regionbackup "$BACKUPSCREEN" "$BACKUPREGION"
if [ -f /$STARTVERZEICHNIS/"$BACKUPSCREEN"/bin/Regions/"$BACKUPREGION".ini.offline ]; then
echo "$BACKUPREGION Region ist Offline und wird uebersprungen."
else
sleep $BACKUPWARTEZEIT
echo "BACKUPWARTEZEIT $BACKUPWARTEZEIT Sekunden." # Testausgabe
fi
done
return 0
}
# Zähle die Tabellen in der Datenbank und die Asset-Typen in der Tabelle 'assets'.
# bash osmtool_backup.sh datenbanktabellen "mein_benutzer" "geheim123" "meine_datenbank"
function datenbanktabellen() {
username=$1
password=$2
databasename=$3
logfile="/$STARTVERZEICHNIS/backup/${databasename}/status_report.txt"
mkdir -p "/$STARTVERZEICHNIS/backup/${databasename}" || exit
echo "📊 Statusbericht für Datenbank: $databasename" | tee "$logfile"
echo "📅 Datum: $(date '+%Y-%m-%d %H:%M:%S')" | tee -a "$logfile"
# Tabellen zählen
tabellenanzahl=$(mysql -u"$username" -p"$password" -D"$databasename" -e "SHOW TABLES;" | tail -n +2 | wc -l)
echo "📦 Tabellen gefunden: $tabellenanzahl" | tee -a "$logfile"
# Asset-Typen zählen (wenn Tabelle vorhanden)
if mysql -u"$username" -p"$password" -D"$databasename" -e "SHOW TABLES LIKE 'assets';" | grep -q "assets"; then
assettypen_anzahl=$(mysql -u"$username" -p"$password" -D"$databasename" -e "SELECT COUNT(DISTINCT assetType) FROM assets;" | tail -n 1)
echo "🎨 Asset-Typen in 'assets': $assettypen_anzahl" | tee -a "$logfile"
else
echo "⚠️ Tabelle 'assets' nicht gefunden." | tee -a "$logfile"
fi
echo "✅ Bericht gespeichert unter: $logfile"
}
# beispiel: screen -fa -S BACKUPNOASSETS -d -U -m bash osmtool.sh backuptabelle_noassets "mein_benutzer" "geheim123" "meine_datenbank"
function backuptabelle_noassets() {
username=$1
password=$2
databasename=$3
mkdir -p /$STARTVERZEICHNIS/backup/"$databasename" || exit
logfile="/$STARTVERZEICHNIS/backup/${databasename}/backup_log.txt"
# Tabellennamen holen und speichern
mysql -u"$username" -p"$password" -D"$databasename" -e "SHOW TABLES;" | tail -n +2 > /$STARTVERZEICHNIS/backup/"$databasename"/liste.txt
# Anzahl der Tabellen zählen
tabellenanzahl=$(wc -l < /$STARTVERZEICHNIS/backup/"$databasename"/liste.txt)
echo "📦 Tabellen gefunden: $tabellenanzahl" | tee -a "$logfile"
# Tabellen sichern (außer 'assets')
gesichert=0
while IFS= read -r tabellenname; do
sleep 1
if [[ "$tabellenname" != "assets" ]]; then
mysqldump -u"$username" -p"$password" "$databasename" "$tabellenname" | zip > /$STARTVERZEICHNIS/backup/"$databasename"/"$tabellenname".sql.zip
echo "✅ Gesichert: $tabellenname" | tee -a "$logfile"
((gesichert++))
fi
done < /$STARTVERZEICHNIS/backup/"$databasename"/liste.txt
echo "🔒 Tabellen gesichert (ohne 'assets'): $gesichert" | tee -a "$logfile"
return 0
}
# screen -fa -S ASSETBACKUP -d -U -m bash osmtool.sh asset_backup "mein_benutzer" "geheim123" "meine_datenbank"
function asset_backup() {
username=$1
password=$2
databasename=$3
fromtable="assets"
fromtypes="assetType"
mkdir -p /$STARTVERZEICHNIS/backup/"$databasename"/assets || exit
cd /$STARTVERZEICHNIS/backup/"$databasename"/assets || exit
logfile="./asset_backup_log.txt"
# Assettypen holen
mysql -u"$username" -p"$password" -D"$databasename" -e "SELECT DISTINCT $fromtypes FROM $fromtable;" | tail -n +2 > typen.txt
assettypen_anzahl=$(wc -l < typen.txt)
echo "📦 Assettypen gefunden: $assettypen_anzahl" | tee -a "$logfile"
gesichert=0
while IFS= read -r assettype; do
filename="assetType_${assettype}"
echo "✅ Sichere Asset-Typ: $assettype$filename.sql.zip" | tee -a "$logfile"
mysqldump -u"$username" -p"$password" "$databasename" --tables "$fromtable" --where="$fromtypes = '$assettype'" | zip > "$filename.sql.zip"
((gesichert++))
done < typen.txt
echo "🔒 Assettypen gesichert: $gesichert" | tee -a "$logfile"
}
Unterschiedliche assettypen der Tabelle assets zählen
# screen -fa -S DBRESTORE -d -U -m bash osmtool.sh db_restoretabelle_noassets "mein_benutzer" "geheim123" "meine_datenbank"
function db_restoretabelle_noassets() {
username=$1
password=$2
databasename=$3
restorepath="/$STARTVERZEICHNIS/backup/${databasename}"
logfile="${restorepath}/restore_log.txt"
cd "$restorepath" || exit
if [[ ! -f liste.txt ]]; then
echo "❌ Keine 'liste.txt' gefunden. Backup-Datei fehlt oder ungültig." | tee -a "$logfile"
return 1
fi
echo "📦 Starte Wiederherstellung aus: $restorepath" | tee -a "$logfile"
restored=0
total=$(grep -v "^assets$" liste.txt | wc -l)
echo "🔍 Tabellen zur Wiederherstellung (ohne 'assets'): $total" | tee -a "$logfile"
while IFS= read -r tabellenname; do
if [[ "$tabellenname" != "assets" ]]; then
zipfile="${tabellenname}.sql.zip"
if [[ -f "$zipfile" ]]; then
echo "🔄 Wiederherstellung: $tabellenname" | tee -a "$logfile"
unzip -p "$zipfile" | mysql -u"$username" -p"$password" "$databasename"
((restored++))
else
echo "⚠️ Datei fehlt: $zipfile" | tee -a "$logfile"
fi
fi
done < liste.txt
echo "✅ Tabellen erfolgreich wiederhergestellt: $restored von $total" | tee -a "$logfile"
return 0
}
# Wiederherstellung der Asset-Typen
function asset_restore() {
username=$1
password=$2
databasename=$3
restorepath="/$STARTVERZEICHNIS/backup/${databasename}/assets"
logfile="${restorepath}/asset_restore_log.txt"
cd "$restorepath" || exit
zipfiles=(assetType_*.sql.zip)
anzahl=${#zipfiles[@]}
echo "📦 Asset-Backups gefunden: $anzahl" | tee -a "$logfile"
restored=0
for zipfile in "${zipfiles[@]}"; do
echo "🔄 Wiederherstellung: $zipfile" | tee -a "$logfile"
unzip -p "$zipfile" | mysql -u"$username" -p"$password" "$databasename"
((restored++))
done
echo "✅ Assettypen wiederhergestellt: $restored" | tee -a "$logfile"
}
# Sichere die Datenbanken 'wordweb' und 'money' in ZIP-Dateien.
# bash osmtool_backup.sh sichere_wordweb_und_money "mein_benutzer" "geheim123"
function sichere_wordweb_und_money() {
username=$1
password=$2
logfile="/$STARTVERZEICHNIS/backup/db_sicherung_log.txt"
datum=$(date '+%Y-%m-%d_%H-%M-%S')
mkdir -p /$STARTVERZEICHNIS/backup/wordweb
mkdir -p /$STARTVERZEICHNIS/backup/money
echo "📅 Sicherung gestartet: $datum" | tee -a "$logfile"
# WORDWEB sichern
echo "💾 Sichere Datenbank: wordweb" | tee -a "$logfile"
mysqldump -u"$username" -p"$password" wordweb | zip > /$STARTVERZEICHNIS/backup/wordweb/wordweb_${datum}.sql.zip
size_wordweb=$(du -sh /$STARTVERZEICHNIS/backup/wordweb/wordweb_${datum}.sql.zip | cut -f1)
echo "✅ wordweb gesichert → Größe: $size_wordweb" | tee -a "$logfile"
# MONEY sichern
echo "💾 Sichere Datenbank: money" | tee -a "$logfile"
mysqldump -u"$username" -p"$password" money | zip > /$STARTVERZEICHNIS/backup/money/money_${datum}.sql.zip
size_money=$(du -sh /$STARTVERZEICHNIS/backup/money/money_${datum}.sql.zip | cut -f1)
echo "✅ money gesichert → Größe: $size_money" | tee -a "$logfile"
echo "📦 Sicherung abgeschlossen: $(date '+%Y-%m-%d %H:%M:%S')" | tee -a "$logfile"
}
function restore_wordweb_und_money() {
username=$1
password=$2
for dbname in wordweb money; do
restorepath="/$STARTVERZEICHNIS/backup/${dbname}"
latestfile=$(ls -t "$restorepath"/*.sql.zip | head -n 1)
if [[ -f "$latestfile" ]]; then
echo "🔄 Wiederherstellung: $dbname aus $latestfile"
unzip -p "$latestfile" | mysql -u"$username" -p"$password" "$dbname"
else
echo "⚠️ Keine Backup-Datei gefunden für $dbname"
fi
done
}
# Backup-Konfigurationsdateien aus robust/bin und sim-Verzeichnissen
# bash osmtool_backup.sh backup_config
function backup_config() {
echo "${SYM_WAIT} ${COLOR_START}Sichere Konfigurationsdateien...${COLOR_RESET}"
# Zielverzeichnis für Backups
backup_root="/opt/backup/config"
# Backup aus robust/bin
if [[ -d "robust/bin" ]]; then
src_dir="robust/bin"
dest_dir="${backup_root}/${src_dir}"
echo "${SYM_OK} ${COLOR_START}Sichere ${COLOR_DIR}${src_dir}${COLOR_RESET} nach ${COLOR_DIR}${dest_dir}${COLOR_RESET}"
find "$src_dir" -type f -name "*.ini" | while read -r file; do
rel_path="${file#$src_dir/}" # relativer Pfad
mkdir -p "${dest_dir}/$(dirname "$rel_path")"
cp "$file" "${dest_dir}/$rel_path"
done
else
echo "${SYM_BAD} ${COLOR_DIR}robust/bin${COLOR_RESET} nicht gefunden. Überspringe."
fi
# Backup aus sim-Verzeichnissen
for ((i=1; i<=999; i++)); do
sim_dir="sim$i/bin"
if [[ -d "$sim_dir" ]]; then
dest_dir="${backup_root}/${sim_dir}"
echo "${SYM_OK} ${COLOR_START}Sichere ${COLOR_DIR}${sim_dir}${COLOR_RESET} nach ${COLOR_DIR}${dest_dir}${COLOR_RESET}"
find "$sim_dir" -type f -name "*.ini" | while read -r file; do
rel_path="${file#$sim_dir/}"
mkdir -p "${dest_dir}/$(dirname "$rel_path")"
cp "$file" "${dest_dir}/$rel_path"
done
fi
done
echo "${SYM_OK} ${COLOR_START}Backup abgeschlossen.${COLOR_RESET}"
echo .
}
# Restore-Konfigurationsdateien aus dem backup Verzeichnis nach robust/bin und sim-Verzeichnissen
# bash osmtool_backup.sh restore_config
function restore_config() {
echo "${SYM_WAIT} ${COLOR_START}Stelle Konfigurationsdateien aus Backup wieder her...${COLOR_RESET}"
# Quellverzeichnis für Backups
backup_root="/opt/backup/config"
# Restore für robust/bin
src_dir="${backup_root}/robust/bin"
dest_dir="robust/bin"
if [[ -d "$src_dir" ]]; then
echo "${SYM_OK} ${COLOR_START}Stelle ${COLOR_DIR}${dest_dir}${COLOR_RESET} aus ${COLOR_DIR}${src_dir}${COLOR_RESET} wieder her"
find "$src_dir" -type f -name "*.ini" | while read -r file; do
rel_path="${file#$src_dir/}"
mkdir -p "${dest_dir}/$(dirname "$rel_path")"
cp "$file" "${dest_dir}/$rel_path"
done
else
echo "${SYM_BAD} ${COLOR_DIR}${src_dir}${COLOR_RESET} nicht gefunden. Überspringe."
fi
# Restore für sim-Verzeichnisse
for ((i=1; i<=999; i++)); do
src_dir="${backup_root}/sim$i/bin"
dest_dir="sim$i/bin"
if [[ -d "$src_dir" ]]; then
echo "${SYM_OK} ${COLOR_START}Stelle ${COLOR_DIR}${dest_dir}${COLOR_RESET} aus ${COLOR_DIR}${src_dir}${COLOR_RESET} wieder her"
find "$src_dir" -type f -name "*.ini" | while read -r file; do
rel_path="${file#$src_dir/}"
mkdir -p "${dest_dir}/$(dirname "$rel_path")"
cp "$file" "${dest_dir}/$rel_path"
done
fi
done
echo "${SYM_OK} ${COLOR_START}Wiederherstellung abgeschlossen.${COLOR_RESET}"
echo .
}
case $KOMMANDO in
"regionsiniteilen") regionsiniteilen "$2" "$3" "$4" ;;
"autoregionsiniteilen") autoregionsiniteilen ;;
"createregionlist") createregionlist ;;
"regionbackup") regionbackup "$2" "$3" ;;
"backup_config") backup_config ;;
"restore_config") restore_config ;;
"autoallclean") autoallclean ;;
"autoregionbackup") autoregionbackup ;;
"datenbanktabellen") datenbanktabellen "$2" "$3" "$4" ;;
"backuptabelle_noassets") backuptabelle_noassets "$2" "$3" "$4" ;;
"asset_backup") asset_backup "$2" "$3" "$4" ;;
"db_restoretabelle_noassets") db_restoretabelle_noassets "$2" "$3" "$4" ;;
"asset_restore") asset_restore "$2" "$3" "$4" ;;
"sichere_wordweb_und_money") sichere_wordweb_und_money "$2" "$3" ;;
"restore_wordweb_und_money") restore_wordweb_und_money "$2" "$3" ;;
*) echo "Unbekanntes Kommando: $KOMMANDO" ;;
esac
# Ende des Skripts
exit 0
+258
View File
@@ -0,0 +1,258 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MOD_DIR="$BASE_DIR/modular"
# shellcheck source=./modular/osmtool_core.sh
source "$MOD_DIR/osmtool_core.sh"
init_i18n
usage() {
cat <<'EOF'
Usage:
osmtool_main.sh [--mode <cli|ui>] [--lang <de|en|fr|es>] [--profile <grid-sim|robust|standalone>] --module <install|startstop|cleanup|health|backup|restore|update|config|report|smoke|cron> [module-options]
Examples:
osmtool_main.sh --module install --action prepare-ubuntu
osmtool_main.sh --module startstop --target grid --action restart --workdir /opt
osmtool_main.sh --module cleanup --action log-clean --workdir /opt
osmtool_main.sh --module health --action run --workdir /opt
osmtool_main.sh --module backup --action db-backup --db-user root --db-pass secret --db-name opensim
osmtool_main.sh --module update --action create-rollback --workdir /opt
osmtool_main.sh --module config --action validate-runtime --workdir /opt
osmtool_main.sh --module report --action generate --workdir /opt
osmtool_main.sh --module smoke --action run --profile grid-sim --workdir /opt
osmtool_main.sh --module cron --action list
osmtool_main.sh --mode ui
EOF
print_usage_common
}
dispatch_module() {
local module="$1"
shift
case "$module" in
install)
"$MOD_DIR/osmtool_install.sh" --profile "$PROFILE" "$@"
;;
startstop)
"$MOD_DIR/osmtool_startstop.sh" --profile "$PROFILE" "$@"
;;
cleanup)
"$MOD_DIR/osmtool_cleanup.sh" --profile "$PROFILE" "$@"
;;
health)
"$MOD_DIR/osmtool_health.sh" --profile "$PROFILE" "$@"
;;
backup)
"$MOD_DIR/osmtool_data.sh" --profile "$PROFILE" "$@"
;;
update)
"$MOD_DIR/osmtool_update.sh" --profile "$PROFILE" "$@"
;;
config)
"$MOD_DIR/osmtool_config.sh" --profile "$PROFILE" "$@"
;;
report)
"$MOD_DIR/osmtool_report.sh" --profile "$PROFILE" "$@"
;;
smoke)
"$MOD_DIR/osmtool_smoke.sh" --profile "$PROFILE" "$@"
;;
cron)
bash "$MOD_DIR/osmtool_cron.sh" --profile "$PROFILE" "$@"
;;
restore)
"$MOD_DIR/osmtool_restore.sh" --profile "$PROFILE" "$@"
;;
*)
die "$(msg ERR_UNKNOWN_MODULE): $module"
;;
esac
}
default_target_for_profile() {
case "$1" in
grid-sim) echo "grid" ;;
robust) echo "robust" ;;
standalone) echo "standalone" ;;
esac
}
args_have_target() {
local prev=""
local token
for token in "$@"; do
if [[ "$prev" == "--target" ]]; then
return 0
fi
prev="$token"
done
return 1
}
ui_flow() {
local module action target
PROFILE="$(show_menu "$(msg APP_TITLE)" "$(msg MENU_CHOOSE_PROFILE)" \
"grid-sim" "$(msg PROFILE_GRID_SIM)" \
"robust" "$(msg PROFILE_ROBUST)" \
"standalone" "$(msg PROFILE_STANDALONE)")"
validate_profile "$PROFILE"
module="$(show_menu "$(msg APP_TITLE)" "$(msg MENU_CHOOSE_MODULE)" \
"install" "$(msg MENU_INSTALL)" \
"startstop" "$(msg MENU_STARTSTOP)" \
"cleanup" "$(msg MENU_CLEANUP)" \
"health" "$(msg MENU_HEALTH)" \
"backup" "$(msg MENU_BACKUP)" \
"restore" "$(msg MENU_RESTORE)" \
"update" "$(msg MENU_UPDATE)" \
"config" "$(msg MENU_CONFIG)" \
"report" "$(msg MENU_REPORT)" \
"smoke" "$(msg MENU_SMOKE)" \
"cron" "$(msg MENU_CRON)")"
case "$module" in
install)
action="$(show_menu "$(msg MENU_INSTALL)" "$(msg MENU_CHOOSE_ACTION)" \
"bootstrap-server" "bootstrap-server" \
"server-check" "server-check" \
"prepare-ubuntu" "prepare-ubuntu" \
"install-opensim-deps" "install-opensim-deps" \
"install-dotnet8" "install-dotnet8" \
"install-opensim" "install-opensim" \
"configure-opensim" "configure-opensim" \
"configure-database" "configure-database" \
"compile-janus" "compile-janus" \
"configure-janus" "configure-janus" \
"install-janus" "install-janus")"
dispatch_module install --action "$action"
;;
startstop)
if [[ "$PROFILE" == "grid-sim" ]]; then
target="$(show_menu "$(msg MENU_STARTSTOP)" "$(msg MENU_CHOOSE_TARGET)" \
"grid" "$(msg TARGET_GRID)" \
"robust" "$(msg TARGET_ROBUST)" \
"sim1" "$(msg TARGET_SIM1)" \
"janus" "$(msg TARGET_JANUS)")"
elif [[ "$PROFILE" == "robust" ]]; then
target="$(show_menu "$(msg MENU_STARTSTOP)" "$(msg MENU_CHOOSE_TARGET)" \
"robust" "$(msg TARGET_ROBUST)" \
"janus" "$(msg TARGET_JANUS)")"
else
target="$(show_menu "$(msg MENU_STARTSTOP)" "$(msg MENU_CHOOSE_TARGET)" \
"standalone" "$(msg TARGET_STANDALONE)" \
"janus" "$(msg TARGET_JANUS)")"
fi
action="$(show_menu "$(msg MENU_STARTSTOP)" "$(msg MENU_CHOOSE_ACTION)" \
"start" "$(msg ACTION_START)" \
"stop" "$(msg ACTION_STOP)" \
"restart" "$(msg ACTION_RESTART)")"
dispatch_module startstop --target "$target" --action "$action"
;;
cleanup)
action="$(show_menu "$(msg MENU_CLEANUP)" "$(msg MENU_CHOOSE_ACTION)" \
"cache-clean" "$(msg ACTION_CACHE_CLEAN)" \
"log-clean" "$(msg ACTION_LOG_CLEAN)" \
"tmp-clean" "$(msg ACTION_TMP_CLEAN)" \
"reboot" "$(msg ACTION_REBOOT)")"
dispatch_module cleanup --action "$action"
;;
health)
action="$(show_menu "$(msg MENU_HEALTH)" "$(msg MENU_CHOOSE_ACTION)" \
"run" "$(msg ACTION_HEALTH_RUN)")"
dispatch_module health --action "$action"
;;
backup)
action="$(show_menu "$(msg MENU_BACKUP)" "$(msg MENU_CHOOSE_ACTION)" \
"db-backup" "$(msg ACTION_DB_BACKUP)" \
"oar-backup" "$(msg ACTION_OAR_BACKUP)" \
"full-backup" "$(msg ACTION_FULL_BACKUP)")"
dispatch_module backup --action "$action"
;;
restore)
action="$(show_menu "$(msg MENU_RESTORE)" "$(msg MENU_CHOOSE_ACTION)" \
"list-backups" "$(msg ACTION_RESTORE_LIST)" \
"db-restore" "$(msg ACTION_RESTORE_DB)" \
"oar-restore" "$(msg ACTION_RESTORE_OAR)" \
"full-restore" "$(msg ACTION_RESTORE_FULL)")"
dispatch_module restore --action "$action"
;;
update)
action="$(show_menu "$(msg MENU_UPDATE)" "$(msg MENU_CHOOSE_ACTION)" \
"create-rollback" "$(msg ACTION_UPDATE_ROLLBACK)" \
"list-rollbacks" "$(msg ACTION_UPDATE_LIST)" \
"update-opensim" "$(msg ACTION_UPDATE_OPENSIM)" \
"update-janus" "$(msg ACTION_UPDATE_JANUS)" \
"full-update" "$(msg ACTION_UPDATE_FULL)")"
dispatch_module update --action "$action"
;;
config)
action="$(show_menu "$(msg MENU_CONFIG)" "$(msg MENU_CHOOSE_ACTION)" \
"validate-file" "$(msg ACTION_CONFIG_FILE)" \
"validate-runtime" "$(msg ACTION_CONFIG_RUNTIME)")"
dispatch_module config --action "$action"
;;
report)
action="$(show_menu "$(msg MENU_REPORT)" "$(msg MENU_CHOOSE_ACTION)" \
"generate" "$(msg ACTION_REPORT_GENERATE)")"
dispatch_module report --action "$action"
;;
smoke)
action="$(show_menu "$(msg MENU_SMOKE)" "$(msg MENU_CHOOSE_ACTION)" \
"run" "$(msg ACTION_SMOKE_RUN)")"
dispatch_module smoke --action "$action"
;;
cron)
action="$(show_menu "$(msg MENU_CRON)" "$(msg MENU_CHOOSE_ACTION)" \
"install" "$(msg ACTION_CRON_INSTALL)" \
"list" "$(msg ACTION_CRON_LIST)" \
"remove" "$(msg ACTION_CRON_REMOVE)")"
dispatch_module cron --action "$action"
;;
*)
die "$(msg ERR_UNKNOWN_UI_MODULE): $module"
;;
esac
}
MODE="cli"
MODULE=""
LANG_OVERRIDE="${OSM_LANG:-de}"
PROFILE="grid-sim"
if [[ $# -eq 0 ]]; then
usage
exit 1
fi
while [[ $# -gt 0 ]]; do
case "$1" in
--mode) MODE="$2"; shift 2 ;;
--lang) LANG_OVERRIDE="$2"; shift 2 ;;
--profile) PROFILE="$2"; shift 2 ;;
--module) MODULE="$2"; shift 2 ;;
--help|-h) usage; exit 0 ;;
*) break ;;
esac
done
set_language "$LANG_OVERRIDE"
validate_profile "$PROFILE"
if [[ "$MODE" == "ui" ]]; then
ui_flow
exit 0
fi
[[ -n "$MODULE" ]] || die "$(msg ERR_MISSING_MODULE)"
if [[ "$MODULE" == "startstop" ]] && ! args_have_target "$@"; then
dispatch_module "$MODULE" --target "$(default_target_for_profile "$PROFILE")" "$@"
else
dispatch_module "$MODULE" "$@"
fi
+270
View File
@@ -0,0 +1,270 @@
#!/bin/bash
#* WARTEZEITEN muessen leider sein damit der Server nicht überfordert wird.
Simulator_Start_wait=15 # Sekunden
MoneyServer_Start_wait=30 # Sekunden
RobustServer_Start_wait=30 # Sekunden
Simulator_Stop_wait=15 # Sekunden
MoneyServer_Stop_wait=30 # Sekunden
RobustServer_Stop_wait=30 # Sekunden
#?──────────────────────────────────────────────────────────────────────────────────────────
#* Start Stop Grid
#?──────────────────────────────────────────────────────────────────────────────────────────
function opensimstart() {
echo "${SYM_WAIT} ${COLOR_START}Starte das Grid!${COLOR_RESET}"
# 1. RobustServer (muss zuerst laufen)
if [[ -d "robust/bin" && -f "robust/bin/Robust.dll" ]]; then
echo "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}RobustServer ${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}robust/bin...${COLOR_RESET}"
(cd robust/bin && screen -fa -S robustserver -d -U -m dotnet Robust.dll)
sleep "$RobustServer_Start_wait"
else
echo "${SYM_BAD} ${COLOR_SERVER}RobustServer: ${COLOR_BAD}Robust.dll nicht gefunden.${COLOR_RESET} ${COLOR_START}Überspringe Start.${COLOR_RESET}"
fi
# 2. MoneyServer (optional)
if [[ -f "robust/bin/MoneyServer.dll" ]]; then
echo "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}MoneyServer${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}robust/bin...${COLOR_RESET}"
(cd robust/bin && screen -fa -S moneyserver -d -U -m dotnet MoneyServer.dll)
sleep "$MoneyServer_Start_wait"
fi
# 3. SIM1 ZUERST (sequentiell)
if [[ -d "sim1/bin" && -f "sim1/bin/OpenSim.dll" ]]; then
echo "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}sim1${COLOR_RESET} ${COLOR_START}(primäre Region)...${COLOR_RESET}"
(cd sim1/bin && screen -fa -S sim1 -d -U -m dotnet OpenSim.dll)
sleep "$Simulator_Start_wait"
else
echo "${SYM_BAD} ${COLOR_SERVER}sim1: ${COLOR_BAD}OpenSim.dll nicht gefunden.${COLOR_RESET}"
fi
# 4. SIM2-SIM99 PARALLEL
echo "${SYM_INFO} ${COLOR_START}Starte sekundäre Simulatoren parallel...${COLOR_RESET}"
# Maximal 10 parallele Jobs
MAX_JOBS=10
for ((i=2; i<=99; i++)); do
sim_dir="sim$i/bin"
if [[ -d "$sim_dir" && -f "$sim_dir/OpenSim.dll" ]]; then
# Warten falls zu viele Jobs laufen
while (( $(jobs -p | wc -l) >= MAX_JOBS )); do
sleep 1
done
echo "${SYM_OK} ${COLOR_START}Starte ${COLOR_SERVER}sim$i${COLOR_RESET} ${COLOR_START}aus ${COLOR_DIR}$sim_dir...${COLOR_RESET}"
(cd "$sim_dir" && screen -fa -S "sim$i" -d -U -m dotnet OpenSim.dll) &
fi
done
wait # Warte auf alle Hintergrundjobs
echo "${SYM_OK} ${COLOR_START}Alle Simulatoren gestartet.${COLOR_RESET}"
echo .
}
function opensimstop() {
echo "${SYM_WAIT} ${COLOR_STOP}Stoppe das Grid!${COLOR_RESET}"
local MAX_PARALLEL=10 # Maximale parallele Stopp-Vorgänge
# 1. Paralleles Stoppen von sim999 bis sim2
echo "${SYM_INFO} ${COLOR_STOP}Stoppe sekundäre Simulatoren parallel...${COLOR_RESET}"
for ((i=999; i>=2; i--)); do
sim_name="sim$i"
# Warten falls zu viele parallele Jobs laufen
#while [ $(jobs -p | wc -l) -ge $MAX_PARALLEL ]; do
while [ "$(jobs -p | wc -l)" -ge $MAX_PARALLEL ]; do
sleep 0.5
done
if screen -list | grep -q "$sim_name"; then
echo "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}$sim_name${COLOR_RESET}"
(
screen -S "$sim_name" -p 0 -X stuff "shutdown^M"
sleep $Simulator_Stop_wait
) &
fi
done
wait # Warte auf alle parallelen Stopp-Vorgänge
# 2. Sim1 sequentiell stoppen
if screen -list | grep -q "sim1"; then
echo "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}sim1${COLOR_RESET} ${COLOR_STOP}(primäre Region)...${COLOR_RESET}"
screen -S "sim1" -p 0 -X stuff "shutdown^M"
sleep $Simulator_Stop_wait
fi
# 3. MoneyServer stoppen
if screen -list | grep -q "moneyserver"; then
echo "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}MoneyServer${COLOR_RESET} ${COLOR_STOP}...${COLOR_RESET}"
screen -S moneyserver -p 0 -X stuff "shutdown^M"
sleep $MoneyServer_Stop_wait
else
echo "${SYM_BAD} ${COLOR_SERVER}MoneyServer: ${COLOR_BAD}Läuft nicht.${COLOR_RESET}"
fi
# 4. RobustServer stoppen (zuletzt)
if screen -list | grep -q "robustserver"; then
echo "${SYM_OK} ${COLOR_STOP}Stoppe ${COLOR_SERVER}RobustServer${COLOR_RESET} ${COLOR_STOP}...${COLOR_RESET}"
screen -S robustserver -p 0 -X stuff "shutdown^M"
sleep $RobustServer_Stop_wait
else
echo "${SYM_BAD} ${COLOR_SERVER}RobustServer: ${COLOR_BAD}Läuft nicht.${COLOR_RESET}"
fi
echo "${SYM_OK} ${COLOR_STOP}Alle Dienste gestoppt.${COLOR_RESET}"
echo .
}
# check_screens ist eine Grid Funktion und funktioniert nicht im Standalone.
# bash osmtool_backup.sh check_screens
function check_screens() {
# echo "Überprüfung der laufenden OpenSim-Prozesse..."
restart_all=false
# Überprüfen, ob RobustServer läuft
if [[ -d "robust/bin" && -f "robust/bin/Robust.dll" ]]; then
screenRO=$(screen -ls | grep -w "robustserver")
if [[ -z "$screenRO" ]]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - RobustServer läuft nicht und muss neu gestartet werden." >> ProblemRestarts.log
restart_all=true
fi
fi
# Überprüfen, ob MoneyServer läuft
if [[ -d "robust/bin" && -f "robust/bin/MoneyServer.dll" ]];then
screenMoney=$(screen -ls | grep -w "moneyserver")
if [[ -z "$screenMoney" ]]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - MoneyServer läuft nicht und muss neu gestartet werden." >> ProblemRestarts.log
restart_all=true
fi
fi
# Überprüfen, ob sim1 läuft
if [[ -d "sim1/bin" && -f "sim1/bin/OpenSim.dll" ]]; then
screenSim1=$(screen -ls | grep -w "sim1")
if [[ -z "$screenSim1" ]]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - Sim1 läuft nicht und muss neu gestartet werden." >> ProblemRestarts.log
restart_all=true
fi
fi
# Falls eines der kritischen Systeme ausgefallen ist, alles neu starten
if [[ "$restart_all" == true ]]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - Kritische Prozesse sind ausgefallen! OpenSim wird komplett neu gestartet." >> ProblemRestarts.log
# opensimstart
opensimrestart
return 0
fi
# Überprüfen, ob andere Regionen (sim2 bis sim999) einzeln neu gestartet werden müssen
for ((i=2; i<=999; i++)); do
sim_dir="sim$i/bin"
if [[ -d "$sim_dir" && -f "$sim_dir/OpenSim.dll" ]]; then
screenSim=$(screen -ls | grep -w "sim$i")
if [[ -z "$screenSim" ]]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - Sim$i läuft nicht und wird einzeln neu gestartet." >> ProblemRestarts.log
cd "$sim_dir" || continue
screen -fa -S "sim$i" -d -U -m dotnet OpenSim.dll
cd - >/dev/null 2>&1 || continue
sleep $Simulator_Start_wait
fi
fi
done
echo .
}
# Neu Test
function check_screens_neu() {
echo "${SYM_WAIT} ${COLOR_START}Überprüfe laufende OpenSim-Prozesse...${COLOR_RESET}"
restart_all=false
log_file="ProblemRestarts.log"
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
# Funktion zum Screen-Check
function screen_running() {
screen -ls | grep -qw "$1"
}
# RobustServer prüfen
if [[ -d "robust/bin" && -f "robust/bin/Robust.dll" ]]; then
if ! screen_running "robustserver"; then
echo "$timestamp - RobustServer läuft nicht." >> "$log_file"
restart_all=true
fi
fi
# MoneyServer prüfen
if [[ -f "robust/bin/MoneyServer.dll" ]]; then
if ! screen_running "moneyserver"; then
echo "$timestamp - MoneyServer läuft nicht." >> "$log_file"
restart_all=true
fi
fi
# sim1 prüfen
if [[ -d "sim1/bin" && -f "sim1/bin/OpenSim.dll" ]]; then
if ! screen_running "sim1"; then
echo "$timestamp - sim1 läuft nicht." >> "$log_file"
restart_all=true
fi
fi
# Falls kritische Komponenten fehlen → kompletten Neustart
if [[ "$restart_all" == true ]]; then
echo "$timestamp - Kritische Komponenten fehlen. Starte das gesamte Grid neu." >> "$log_file"
opensimrestart
return 0
fi
# sim2 bis sim999 einzeln prüfen und ggf. neu starten
for ((i=2; i<=999; i++)); do
sim_dir="sim$i/bin"
if [[ -d "$sim_dir" && -f "$sim_dir/OpenSim.dll" ]]; then
if ! screen_running "sim$i"; then
echo "$timestamp - sim$i läuft nicht. Starte Region neu." >> "$log_file"
cd "$sim_dir" || continue
screen -fa -S "sim$i" -d -U -m dotnet OpenSim.dll
cd - >/dev/null 2>&1 || continue
sleep $Simulator_Start_wait
fi
fi
done
echo "${SYM_OK} ${COLOR_START}Überprüfung abgeschlossen.${COLOR_RESET}"
screen -ls || echo "Keine Screen-Sessions gefunden"
}
function opensimrestart() {
opensimstop
sleep $Simulator_Stop_wait # Wartezeit für Dienst-Stopp
opensimstart
screen -ls || echo "Keine Screen-Sessions gefunden"
}
KOMMANDO="$1"
case $KOMMANDO in
"start")
opensimstart
;;
"stop")
opensimstop
;;
"restart")
opensimrestart
;;
"check_screens")
check_screens_neu
;;
*)
opensimrestart
;;
esac
# Ende des Skripts
+77
View File
@@ -0,0 +1,77 @@
# OSMTool Web PHP (DE/EN/FR/ES)
This folder contains a minimal and secure PHP web controller for `osmtool_main.sh`.
## Features
- Login-protected web UI
- Language switch: `de`, `en`, `fr`, `es`
- CSRF protection
- Whitelisted actions only
- Runner script wrapper for safer command execution
## File Layout
- `public/index.php` - web UI and request handling
- `src/bootstrap.php` - session, auth helpers, CSRF
- `src/i18n.php` - translations
- `src/runner.php` - command whitelist and execution
- `src/config.sample.php` - configuration template
- `bin/osmtool_web_runner.sh` - controlled bridge to `osmtool_main.sh`
## Setup
1. Typical Linux deploy path:
```bash
sudo mkdir -p /var/www/html/osmtool-web
sudo cp -a webphp/. /var/www/html/osmtool-web/
sudo chown -R www-data:www-data /var/www/html/osmtool-web
```
1. Edit config values:
```bash
nano /var/www/html/osmtool-web/src/config.php
```
1. Set execute bit for runner and main script:
```bash
chmod +x /var/www/html/osmtool-web/bin/osmtool_web_runner.sh
chmod +x /opt/opensimMULTITOOLS-II/osmtool_main.sh
```
1. Configure web server document root to:
```text
/var/www/html/osmtool-web/public
```
1. Open in browser and log in with `web_password` from config.
## Sudoers Example (recommended)
Use `visudo` and allow only the web runner:
```text
www-data ALL=(manni) NOPASSWD: /var/www/html/osmtool-web/bin/osmtool_web_runner.sh
```
Then keep `use_sudo=true` and `sudo_user=manni` in config.
## Supported Actions
- `start`
- `stop`
- `restart`
- `smoke`
- `report`
- `health`
- `cron-list`
## Notes
- Do not expose this UI publicly without HTTPS and IP restrictions.
- Keep the password strong and unique.
- Extend actions only in `src/runner.php` and `bin/osmtool_web_runner.sh` together.
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
MAIN_SCRIPT="$PROJECT_ROOT/osmtool_main.sh"
if [[ ! -x "$MAIN_SCRIPT" ]]; then
echo "ERROR: osmtool_main.sh not executable: $MAIN_SCRIPT" >&2
exit 1
fi
ACTION="${1:-}"
PROFILE="${2:-grid-sim}"
LANG="${3:-de}"
WORKDIR="${4:-/opt}"
case "$PROFILE" in
grid-sim|robust|standalone) ;;
*) echo "ERROR: invalid profile" >&2; exit 2 ;;
esac
case "$LANG" in
de|en|fr|es) ;;
*) echo "ERROR: invalid language" >&2; exit 2 ;;
esac
run_main() {
bash "$MAIN_SCRIPT" --mode cli --profile "$PROFILE" --lang "$LANG" --workdir "$WORKDIR" "$@"
}
case "$ACTION" in
start)
run_main --module startstop --action start
;;
stop)
run_main --module startstop --action stop
;;
restart)
run_main --module startstop --action restart
;;
smoke)
run_main --module smoke --action run
;;
report)
run_main --module report --action generate
;;
health)
run_main --module health --action run
;;
cron-list)
run_main --module cron --action list
;;
*)
echo "ERROR: unsupported action" >&2
exit 2
;;
esac
+264
View File
@@ -0,0 +1,264 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../src/bootstrap.php';
require_once __DIR__ . '/../src/runner.php';
$lang = detect_lang(supported_languages());
$_SESSION['lang'] = $lang;
$message = '';
$messageType = 'ok';
$resultOutput = '';
$resultCode = null;
if (isset($_GET['logout'])) {
$_SESSION = [];
session_destroy();
header('Location: index.php?lang=' . urlencode($lang));
exit;
}
if (($_POST['form'] ?? '') === 'login') {
$postedLang = detect_lang(supported_languages());
$_SESSION['lang'] = $postedLang;
$lang = $postedLang;
if (!verify_csrf($_POST['csrf'] ?? null)) {
$message = t($lang, 'csrf_error');
$messageType = 'error';
} else {
$password = (string)($_POST['password'] ?? '');
$expected = (string)($config['web_password'] ?? '');
if ($expected !== '' && hash_equals($expected, $password)) {
$_SESSION['auth'] = true;
header('Location: index.php?lang=' . urlencode($lang));
exit;
}
$message = t($lang, 'invalid_login');
$messageType = 'error';
}
}
if (is_logged_in() && (($_POST['form'] ?? '') === 'run')) {
if (!verify_csrf($_POST['csrf'] ?? null)) {
$message = t($lang, 'csrf_error');
$messageType = 'error';
} else {
$action = (string)($_POST['action'] ?? '');
$profile = (string)($_POST['profile'] ?? 'grid-sim');
$workdir = (string)($_POST['workdir'] ?? (string)($config['default_workdir'] ?? '/opt'));
$result = run_whitelisted_action($config, $action, $profile, $lang, $workdir);
$resultOutput = (string)($result['output'] ?? '');
$resultCode = (int)($result['exitCode'] ?? 1);
if (($result['ok'] ?? false) === true) {
$message = t($lang, 'status_ok') . ' (exit=' . $resultCode . ')';
$messageType = 'ok';
} else {
$message = t($lang, 'status_error') . ' (exit=' . $resultCode . ')';
$messageType = 'error';
}
}
}
$actions = [
'start' => t($lang, 'action_start'),
'stop' => t($lang, 'action_stop'),
'restart' => t($lang, 'action_restart'),
'smoke' => t($lang, 'action_smoke'),
'report' => t($lang, 'action_report'),
'health' => t($lang, 'action_health'),
'cron-list' => t($lang, 'action_cron_list'),
];
$profiles = [
'grid-sim' => 'grid-sim',
'robust' => 'robust',
'standalone' => 'standalone',
];
?>
<!doctype html>
<html lang="<?= h($lang) ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= h(t($lang, 'title')) ?></title>
<style>
:root {
--bg1: #0f172a;
--bg2: #1e293b;
--card: #111827;
--line: #334155;
--text: #e5e7eb;
--muted: #94a3b8;
--ok: #16a34a;
--err: #dc2626;
--btn: #0284c7;
--btnHover: #0369a1;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: "Segoe UI", Tahoma, sans-serif;
background: radial-gradient(1000px 600px at 20% 0%, #1d4ed8 0%, transparent 60%),
radial-gradient(900px 500px at 100% 100%, #0ea5e9 0%, transparent 50%),
linear-gradient(160deg, var(--bg1), var(--bg2));
color: var(--text);
min-height: 100vh;
padding: 24px;
}
.wrap { max-width: 980px; margin: 0 auto; }
.card {
background: color-mix(in oklab, var(--card), black 8%);
border: 1px solid var(--line);
border-radius: 14px;
padding: 18px;
box-shadow: 0 12px 40px rgba(0,0,0,.25);
margin-bottom: 18px;
}
h1 { margin: 0 0 8px; font-size: 1.7rem; }
p { margin: 0 0 12px; color: var(--muted); }
.topbar {
display: flex;
justify-content: space-between;
gap: 12px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 14px;
}
label { display: block; margin-bottom: 6px; font-weight: 600; }
input, select, button {
width: 100%;
border-radius: 10px;
border: 1px solid var(--line);
background: #0b1220;
color: var(--text);
padding: 10px 12px;
}
button {
background: var(--btn);
border: none;
font-weight: 700;
cursor: pointer;
}
button:hover { background: var(--btnHover); }
.grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.full { grid-column: 1 / -1; }
.msg {
margin: 10px 0;
padding: 10px 12px;
border-radius: 10px;
font-weight: 700;
}
.ok { background: rgba(22,163,74,.2); border: 1px solid rgba(22,163,74,.5); }
.error { background: rgba(220,38,38,.2); border: 1px solid rgba(220,38,38,.5); }
pre {
background: #020617;
border: 1px solid var(--line);
border-radius: 10px;
padding: 12px;
overflow: auto;
max-height: 420px;
margin: 0;
white-space: pre-wrap;
}
.lang-links a {
color: #bae6fd;
margin-right: 8px;
text-decoration: none;
}
.lang-links a:hover { text-decoration: underline; }
@media (max-width: 800px) {
.grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="wrap">
<div class="card">
<div class="topbar">
<div>
<h1><?= h(t($lang, 'title')) ?></h1>
<p><?= h(t($lang, 'subtitle')) ?></p>
</div>
<div class="lang-links">
<a href="?lang=de">DE</a>
<a href="?lang=en">EN</a>
<a href="?lang=fr">FR</a>
<a href="?lang=es">ES</a>
<?php if (is_logged_in()): ?>
| <a href="?logout=1&amp;lang=<?= h($lang) ?>"><?= h(t($lang, 'logout')) ?></a>
<?php endif; ?>
</div>
</div>
<?php if ($message !== ''): ?>
<div class="msg <?= h($messageType) ?>"><?= h($message) ?></div>
<?php endif; ?>
<?php if (!is_logged_in()): ?>
<form method="post">
<input type="hidden" name="form" value="login">
<input type="hidden" name="csrf" value="<?= h(csrf_token()) ?>">
<input type="hidden" name="lang" value="<?= h($lang) ?>">
<div class="grid">
<div class="full">
<label><?= h(t($lang, 'password')) ?></label>
<input type="password" name="password" autocomplete="current-password" required>
</div>
<div class="full">
<button type="submit"><?= h(t($lang, 'login')) ?></button>
</div>
</div>
</form>
<?php else: ?>
<p><?= h(t($lang, 'hint')) ?></p>
<form method="post">
<input type="hidden" name="form" value="run">
<input type="hidden" name="csrf" value="<?= h(csrf_token()) ?>">
<input type="hidden" name="lang" value="<?= h($lang) ?>">
<div class="grid">
<div>
<label><?= h(t($lang, 'profile')) ?></label>
<select name="profile" required>
<?php foreach ($profiles as $value => $label): ?>
<option value="<?= h($value) ?>"><?= h($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div>
<label><?= h(t($lang, 'action')) ?></label>
<select name="action" required>
<?php foreach ($actions as $value => $label): ?>
<option value="<?= h($value) ?>"><?= h($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="full">
<label><?= h(t($lang, 'workdir')) ?></label>
<input name="workdir" value="<?= h((string)($config['default_workdir'] ?? '/opt')) ?>">
</div>
<div class="full">
<button type="submit"><?= h(t($lang, 'execute')) ?></button>
</div>
</div>
</form>
<?php if ($resultCode !== null): ?>
<div class="card" style="margin-top:14px;">
<label><?= h(t($lang, 'output')) ?></label>
<pre><?= h($resultOutput) ?></pre>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
</body>
</html>
+64
View File
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
$configFile = __DIR__ . '/config.php';
if (!is_file($configFile)) {
$configFile = __DIR__ . '/config.sample.php';
}
$config = require $configFile;
if (!isset($config['session_key']) || !is_string($config['session_key'])) {
throw new RuntimeException('Missing session_key in config.');
}
session_name('osmtool_web');
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'secure' => false,
'httponly' => true,
'samesite' => 'Strict',
]);
session_start();
if (!isset($_SESSION['session_seed'])) {
$_SESSION['session_seed'] = hash('sha256', $config['session_key'] . random_int(1000, 999999));
}
require_once __DIR__ . '/i18n.php';
function is_logged_in(): bool
{
return isset($_SESSION['auth']) && $_SESSION['auth'] === true;
}
function require_login(): void
{
if (!is_logged_in()) {
header('Location: index.php');
exit;
}
}
function csrf_token(): string
{
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
function verify_csrf(?string $token): bool
{
if (!isset($_SESSION['csrf_token']) || !is_string($token)) {
return false;
}
return hash_equals($_SESSION['csrf_token'], $token);
}
function h(string $value): string
{
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
+13
View File
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
return [
'session_key' => 'change-me-super-random-session-key',
'web_password' => 'ChangeMeNow-2026',
// Default assumes deployment under /var/www/html/osmtool-web
'runner_path' => realpath(__DIR__ . '/../bin/osmtool_web_runner.sh') ?: (__DIR__ . '/../bin/osmtool_web_runner.sh'),
'command_timeout_seconds' => 120,
'default_workdir' => '/opt',
'use_sudo' => true,
'sudo_user' => 'manni',
];
+27
View File
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
return [
// Set this to a strong random value in production.
'session_key' => 'change-me-super-random-session-key',
// Default login password. Replace immediately in production.
'web_password' => 'ChangeMeNow-2026',
// Default assumes deployment under /var/www/html/osmtool-web.
// You can keep this dynamic path or set an absolute path manually.
'runner_path' => realpath(__DIR__ . '/../bin/osmtool_web_runner.sh') ?: (__DIR__ . '/../bin/osmtool_web_runner.sh'),
// Web timeout in seconds.
'command_timeout_seconds' => 120,
// Default workdir passed to osmtool_main.sh.
'default_workdir' => '/opt',
// Allow running through sudo if needed.
'use_sudo' => true,
// If use_sudo=true, command becomes: sudo -n <sudo_user> <runner>
// Example: www-data (Linux) or your service account.
'sudo_user' => 'manni',
];
+140
View File
@@ -0,0 +1,140 @@
<?php
declare(strict_types=1);
function supported_languages(): array
{
return ['de', 'en', 'fr', 'es'];
}
function translations(): array
{
return [
'de' => [
'title' => 'OSMTool Web Steuerung',
'subtitle' => 'Sichere Web-Oberflaeche fuer OpenSim Aktionen',
'login_title' => 'Anmeldung',
'password' => 'Passwort',
'login' => 'Einloggen',
'logout' => 'Ausloggen',
'language' => 'Sprache',
'profile' => 'Profil',
'action' => 'Aktion',
'execute' => 'Ausfuehren',
'workdir' => 'Workdir',
'output' => 'Ausgabe',
'status_ok' => 'Erfolgreich',
'status_error' => 'Fehler',
'invalid_login' => 'Anmeldung fehlgeschlagen.',
'csrf_error' => 'Ungueltiges Formular-Token.',
'not_allowed' => 'Aktion nicht erlaubt.',
'run_failed' => 'Ausfuehrung fehlgeschlagen.',
'hint' => 'Es werden nur freigegebene Aktionen ausgefuehrt.',
'action_start' => 'Start',
'action_stop' => 'Stop',
'action_restart' => 'Neustart',
'action_smoke' => 'Smoke-Test',
'action_report' => 'Report erzeugen',
'action_health' => 'Healthcheck',
'action_cron_list' => 'Cronjobs anzeigen',
],
'en' => [
'title' => 'OSMTool Web Control',
'subtitle' => 'Secure web interface for OpenSim actions',
'login_title' => 'Login',
'password' => 'Password',
'login' => 'Sign in',
'logout' => 'Sign out',
'language' => 'Language',
'profile' => 'Profile',
'action' => 'Action',
'execute' => 'Execute',
'workdir' => 'Workdir',
'output' => 'Output',
'status_ok' => 'Success',
'status_error' => 'Error',
'invalid_login' => 'Login failed.',
'csrf_error' => 'Invalid form token.',
'not_allowed' => 'Action not allowed.',
'run_failed' => 'Execution failed.',
'hint' => 'Only whitelisted actions are executed.',
'action_start' => 'Start',
'action_stop' => 'Stop',
'action_restart' => 'Restart',
'action_smoke' => 'Smoke test',
'action_report' => 'Generate report',
'action_health' => 'Health check',
'action_cron_list' => 'List cron jobs',
],
'fr' => [
'title' => 'Controle Web OSMTool',
'subtitle' => 'Interface web securisee pour les actions OpenSim',
'login_title' => 'Connexion',
'password' => 'Mot de passe',
'login' => 'Se connecter',
'logout' => 'Se deconnecter',
'language' => 'Langue',
'profile' => 'Profil',
'action' => 'Action',
'execute' => 'Executer',
'workdir' => 'Repertoire de travail',
'output' => 'Sortie',
'status_ok' => 'Succes',
'status_error' => 'Erreur',
'invalid_login' => 'Echec de connexion.',
'csrf_error' => 'Jeton de formulaire invalide.',
'not_allowed' => 'Action non autorisee.',
'run_failed' => 'Execution echouee.',
'hint' => 'Seules les actions en liste blanche sont executees.',
'action_start' => 'Demarrer',
'action_stop' => 'Arreter',
'action_restart' => 'Redemarrer',
'action_smoke' => 'Test smoke',
'action_report' => 'Generer un rapport',
'action_health' => 'Controle de sante',
'action_cron_list' => 'Lister les cron jobs',
],
'es' => [
'title' => 'Control Web OSMTool',
'subtitle' => 'Interfaz web segura para acciones de OpenSim',
'login_title' => 'Inicio de sesion',
'password' => 'Contrasena',
'login' => 'Entrar',
'logout' => 'Salir',
'language' => 'Idioma',
'profile' => 'Perfil',
'action' => 'Accion',
'execute' => 'Ejecutar',
'workdir' => 'Directorio de trabajo',
'output' => 'Salida',
'status_ok' => 'Correcto',
'status_error' => 'Error',
'invalid_login' => 'Error de inicio de sesion.',
'csrf_error' => 'Token de formulario invalido.',
'not_allowed' => 'Accion no permitida.',
'run_failed' => 'La ejecucion fallo.',
'hint' => 'Solo se ejecutan acciones permitidas.',
'action_start' => 'Iniciar',
'action_stop' => 'Detener',
'action_restart' => 'Reiniciar',
'action_smoke' => 'Prueba smoke',
'action_report' => 'Generar informe',
'action_health' => 'Chequeo de salud',
'action_cron_list' => 'Listar cron jobs',
],
];
}
function detect_lang(array $supported): string
{
$lang = $_GET['lang'] ?? $_POST['lang'] ?? $_SESSION['lang'] ?? 'de';
if (!is_string($lang) || !in_array($lang, $supported, true)) {
return 'de';
}
return $lang;
}
function t(string $lang, string $key): string
{
$all = translations();
return $all[$lang][$key] ?? $all['en'][$key] ?? $key;
}
+77
View File
@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
function allowed_profiles(): array
{
return ['grid-sim', 'robust', 'standalone'];
}
function allowed_actions(): array
{
return ['start', 'stop', 'restart', 'smoke', 'report', 'health', 'cron-list'];
}
function build_command(array $config, string $action, string $profile, string $lang, string $workdir): array
{
$runnerPath = (string)($config['runner_path'] ?? '');
if ($runnerPath === '') {
throw new RuntimeException('runner_path missing in config.');
}
$base = [];
$useSudo = (bool)($config['use_sudo'] ?? false);
if ($useSudo) {
$sudoUser = (string)($config['sudo_user'] ?? '');
if ($sudoUser === '') {
throw new RuntimeException('sudo_user missing in config.');
}
$base = ['sudo', '-n', '-u', $sudoUser];
}
return array_merge($base, [$runnerPath, $action, $profile, $lang, $workdir]);
}
function run_whitelisted_action(array $config, string $action, string $profile, string $lang, string $workdir): array
{
if (!in_array($action, allowed_actions(), true)) {
return ['ok' => false, 'exitCode' => 2, 'output' => "ERROR: action not allowed\n"];
}
if (!in_array($profile, allowed_profiles(), true)) {
return ['ok' => false, 'exitCode' => 2, 'output' => "ERROR: invalid profile\n"];
}
if (!in_array($lang, supported_languages(), true)) {
return ['ok' => false, 'exitCode' => 2, 'output' => "ERROR: invalid language\n"];
}
$workdirClean = trim($workdir);
if ($workdirClean === '') {
$workdirClean = (string)($config['default_workdir'] ?? '/opt');
}
$cmd = build_command($config, $action, $profile, $lang, $workdirClean);
$descriptorSpec = [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
];
$process = proc_open($cmd, $descriptorSpec, $pipes);
if (!is_resource($process)) {
return ['ok' => false, 'exitCode' => 1, 'output' => "ERROR: unable to start process\n"];
}
fclose($pipes[0]);
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
$exitCode = proc_close($process);
$output = (string)$stdout . (string)$stderr;
return ['ok' => $exitCode === 0, 'exitCode' => $exitCode, 'output' => $output];
}