Fehler in der Oberflaeche abfangen, Backups planbar machen

Der Anlass: "Backup erstellen" bei fehlendem Container beendete die
ganze TUI mit einer Fehlerzeile in der Shell.

Ursachen waren zwei, beide behoben:
  1. die() in GUI-Pfaden. Neue Helfer gui_need_container/gui_need_sites
     melden das Problem im Dialog und kehren zurueck. Interaktives wie
     Shell und Terminal-Browser laeuft jetzt ueber gui_run_interactive
     in einer Subshell mit Weiter-mit-ENTER.
  2. "set -e". Schon ein Rueckgabewert != 0 aus einer Menuefunktion
     beendete das Skript, weil Menuezweige keine Bedingung sind. 47
     "|| return" auf "|| return 0" gestellt und 37 Menuezweige mit
     "|| true" abgesichert. Ausserdem schlug ein "clear" ohne TERM fehl
     und riss alles mit - drei Aufrufe abgesichert.
Jeder Hauptmenuepunkt wurde einzeln durchlaufen und kehrt sauber zurueck.

Neu: Backup-Jobs
  Der Menuepunkt heisst jetzt "Backup erstellen / planen" mit Untermenue
  (manuell erstellen, Jobs verwalten, Automatik ein/aus, Zeitplan
  anzeigen, Job sofort ausfuehren). Dieselben Jobs liegen auch in den
  Einstellungen unter "Backup-Jobs".

  Manuelle Backups nehmen weiterhin immer BACKUP_DIR aus der .env.
  Jeder Job hat dagegen sein eigenes Zielverzeichnis, seinen eigenen
  Zeitplan, eigene Optionen und eine eigene Aufbewahrung, nach der er
  sein Zielverzeichnis selbst aufraeumt.

  Vollstaendig auch per Parameter:
    job list | status | add | set | del | enable | disable
        | run | run-all | auto [on|off]
  mit --name --sites --cron --dir --keep --no-rrds --no-logs --stop
  --disabled.

  "job auto on" erzeugt aus den aktiven Jobs /etc/cron.d/checkmk-backup,
  eine Zeile je Job mit dessen eigenem Zeitplan. Ohne root werden die
  Zeilen ausgegeben statt kommentarlos zu scheitern. Der Schalter steht
  als BACKUP_AUTO in der .env, die Jobs in backup-jobs.conf.

Nebenbei behoben: "local f; f=... tmp" fuehrte tmp als Kommando aus,
wodurch job_put und job_remove nichts schrieben.

Dokumentiert als Abschnitt 15.1 in beiden Sprachfassungen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-20 15:23:51 +02:00
co-authored by Claude Opus 5
parent 4bbd1de4f6
commit 2d645bcc0b
6 changed files with 904 additions and 85 deletions
+686 -72
View File
@@ -89,6 +89,21 @@ KOMMANDOS - Backup / Restore / Migration
prune --keep N nur die N neuesten Backups behalten
job list angelegte Backup-Jobs anzeigen
job status Zeitplan, Cron-Datei und Schalter anzeigen
job add --name N --cron "15 2 * * *" [Optionen] Job anlegen
--sites ALL|a,b zu sichernde Sites (Vorgabe ALL)
--dir VERZ Zielverzeichnis des Jobs (Vorgabe: $BACKUP_DIR)
--keep N Anzahl Backups, die im Ziel bleiben (0 = alle)
--no-rrds --no-logs --stop wie bei "backup"
--disabled angelegt, aber noch nicht eingeplant
job set NAME [Optionen] vorhandenen Job aendern
job del NAME Job loeschen
job enable|disable NAME Job ein-/ausschalten
job run NAME Job sofort ausfuehren
job run-all alle aktiven Jobs ausfuehren
job auto [on|off] automatische Ausfuehrung per cron ein/aus
KOMMANDOS - Checkmk-Image beschaffen
image status konfiguriertes und lokal vorhandene Images anzeigen
image list lokal vorhandene Checkmk-Images
@@ -1217,6 +1232,334 @@ EOF
LAST_BACKUP_FILE="$outfile"
}
# =====================================================================
# Backup-Jobs (geplante Backups)
#
# Manuelle Backups nehmen immer BACKUP_DIR aus der .env. Geplante Jobs
# bringen ihr eigenes Zielverzeichnis mit - so kann z.B. taeglich auf
# eine NAS-Freigabe und woechentlich woanders hin gesichert werden.
#
# Ablage: backup-jobs.conf im Projektverzeichnis, eine Zeile je Job:
# name|sites|cron|verzeichnis|optionen|aufbewahren|aktiv
# Ausgefuehrt werden sie ueber /etc/cron.d/checkmk-backup, das aus
# dieser Datei erzeugt wird ("job auto on").
# =====================================================================
JOBS_FILE_NAME="backup-jobs.conf"
CRON_FILE="/etc/cron.d/checkmk-backup"
jobs_file() { printf '%s/%s\n' "$PROJECT_DIR" "$JOBS_FILE_NAME"; }
jobs_init() {
local f; f="$(jobs_file)"
[ -f "$f" ] && return 0
cat >"$f" <<'EOF'
# Backup-Jobs fuer cmk-manage.sh - eine Zeile je Job:
# name|sites|cron|verzeichnis|optionen|aufbewahren|aktiv
#
# name nur Buchstaben, Ziffern, _ und -
# sites ALL oder Site-Namen durch Komma getrennt
# cron fuenf Cron-Felder, z.B. "15 2 * * *"
# verzeichnis Zielverzeichnis (absolut oder relativ zum Projekt)
# optionen leer oder --no-rrds / --no-logs / --stop (mit Leerzeichen)
# aufbewahren Anzahl der Backups, die im Zielverzeichnis bleiben (0 = alle)
# aktiv on oder off
#
# Bearbeiten moeglichst ueber: cmk-manage.sh job ... bzw. das Menue.
EOF
ok "Neue Jobdatei angelegt: $f"
}
jobs_read() { # nur Datenzeilen
local f; f="$(jobs_file)"
[ -f "$f" ] || return 0
grep -vE '^[[:space:]]*(#|$)' "$f" || true
}
job_line() { jobs_read | awk -F'|' -v n="$1" '$1==n {print; exit}'; }
job_exists() { [ -n "$(job_line "$1")" ]; }
job_f() { printf '%s' "$1" | cut -d'|' -f"$2"; }
job_dir_abs() { # Verzeichnis eines Jobs absolut
local d="$1"
case "$d" in /*) printf '%s\n' "$d" ;; *) printf '%s\n' "$PROJECT_DIR/${d#./}" ;; esac
}
job_validate_name() {
case "$1" in
''|*[!A-Za-z0-9_-]*) echo "Der Name darf nur Buchstaben, Ziffern, _ und - enthalten."; return 1 ;;
esac
return 0
}
job_validate_cron() {
local n; n="$(printf '%s' "$1" | awk '{print NF}')"
[ "$n" = 5 ] || { echo "Ein Cron-Ausdruck besteht aus genau fuenf Feldern, z.B. '15 2 * * *'."; return 1; }
return 0
}
job_validate_field() { # keine Trennzeichen in Feldern
case "$1" in *'|'*) echo "Das Zeichen | ist in diesem Feld nicht erlaubt."; return 1 ;; esac
return 0
}
# Job schreiben (anlegen oder ersetzen)
job_put() { # name sites cron dir opts keep enabled
jobs_init
local f tmp; f="$(jobs_file)"
tmp="$(mktemp)"
awk -F'|' -v n="$1" '$1!=n' "$f" >"$tmp"
printf '%s|%s|%s|%s|%s|%s|%s\n' "$1" "$2" "$3" "$4" "$5" "$6" "$7" >>"$tmp"
# Kommentarkopf erhalten
{ grep -E '^[[:space:]]*#' "$f" 2>/dev/null || true; grep -vE '^[[:space:]]*(#|$)' "$tmp" ; } >"$f"
rm -f "$tmp"
}
job_remove() {
local f tmp; f="$(jobs_file)"
[ -f "$f" ] || return 0
tmp="$(mktemp)"
awk -F'|' -v n="$1" '$1!=n' "$f" >"$tmp" && mv "$tmp" "$f"
}
# --- Aufraeumen im Zielverzeichnis eines Jobs -------------------------
prune_in_dir() { # verzeichnis anzahl
local dir="$1" keep="$2" i=0
[ -n "$keep" ] && [ "$keep" -gt 0 ] 2>/dev/null || return 0
local -a files=()
mapfile -t files < <(find "$dir" -maxdepth 1 -type f -name '*.tar.gz' -printf '%T@ %p\n' 2>/dev/null \
| sort -rn | cut -d' ' -f2-)
[ ${#files[@]} -le "$keep" ] && return 0
for ((i=keep; i<${#files[@]}; i++)); do
log "Aufbewahrung: entferne $(basename "${files[$i]}")"
rm -f "${files[$i]}"
done
}
# --- Job ausfuehren ---------------------------------------------------
job_run_one() {
local name="$1" line
line="$(job_line "$name")"
[ -n "$line" ] || die "Job '$name' ist nicht vorhanden. Uebersicht: $SELF job list"
local sites cron dir opts keep enabled
sites="$(job_f "$line" 2)"; cron="$(job_f "$line" 3)"; dir="$(job_f "$line" 4)"
opts="$(job_f "$line" 5)"; keep="$(job_f "$line" 6)"; enabled="$(job_f "$line" 7)"
dir="$(job_dir_abs "$dir")"
mkdir -p "$dir" || die "Zielverzeichnis nicht anlegbar: $dir"
log "Job '$name' - Ziel $dir, Sites ${sites}${opts:+, Optionen $opts}"
require_cmk_running
local -a args=(--dir "$dir" --label "$name")
local o
for o in $opts; do
case "$o" in
--no-rrds|--no-logs|--stop) args+=("$o") ;;
*) warn "Unbekannte Option '$o' im Job '$name' - wird uebergangen." ;;
esac
done
if [ "$sites" = ALL ]; then
args+=(--all)
else
local sname
for sname in $(printf '%s' "$sites" | tr ',' ' '); do args+=(--site "$sname"); done
fi
ASSUME_YES=1 cmd_backup "${args[@]}"
prune_in_dir "$dir" "$keep"
ok "Job '$name' abgeschlossen."
}
# --- Cron ------------------------------------------------------------
cron_write() {
jobs_init
local n=0 line name cron
local tmp; tmp="$(mktemp)"
{
printf '# Automatische Checkmk-Backups - erzeugt von cmk-manage.sh\n'
printf '# Nicht von Hand aendern, Quelle ist %s\n' "$(jobs_file)"
printf 'SHELL=/bin/sh\n'
printf 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin\n\n'
while IFS= read -r line; do
[ -n "$line" ] || continue
[ "$(job_f "$line" 7)" = on ] || continue
name="$(job_f "$line" 1)"; cron="$(job_f "$line" 3)"
printf '%s root %s -y job run %s >>/var/log/cmk-backup.log 2>&1\n' \
"$cron" "$SELF" "$name"
n=$((n+1))
done < <(jobs_read)
} >"$tmp"
if [ "$n" -eq 0 ]; then
rm -f "$tmp"
warn "Kein aktiver Job vorhanden - es wird nichts eingeplant."
[ -f "$CRON_FILE" ] && { rm -f "$CRON_FILE"; log "Vorhandene Cron-Datei entfernt."; }
return 0
fi
if [ "$(id -u)" -ne 0 ]; then
err "Zum Schreiben von $CRON_FILE werden root-Rechte benoetigt."
echo
cat "$tmp"
echo
log "Die Zeilen oben von Hand nach $CRON_FILE uebernehmen, oder erneut mit sudo aufrufen."
rm -f "$tmp"
return 1
fi
install -m 0644 "$tmp" "$CRON_FILE"
rm -f "$tmp"
ok "$n Job(s) eingeplant in $CRON_FILE"
}
cron_remove_file() {
if [ -f "$CRON_FILE" ]; then
[ "$(id -u)" -eq 0 ] || die "Zum Entfernen von $CRON_FILE werden root-Rechte benoetigt."
rm -f "$CRON_FILE"
ok "Automatische Backups abgeschaltet ($CRON_FILE entfernt)."
else
ok "Es war nichts eingeplant."
fi
}
backup_auto_enabled() {
case "$(printf '%s' "${BACKUP_AUTO:-off}" | tr 'A-Z' 'a-z')" in
on|true|1|yes|ja) return 0 ;; *) return 1 ;;
esac
}
# --- Anzeige ----------------------------------------------------------
jobs_list_table() {
printf '%-14s %-12s %-14s %-28s %-18s %5s %s\n' \
"NAME" "SITES" "ZEITPLAN" "ZIEL" "OPTIONEN" "HALT" "AKTIV"
local line n=0
while IFS= read -r line; do
[ -n "$line" ] || continue
printf '%-14s %-12s %-14s %-28s %-18s %5s %s\n' \
"$(job_f "$line" 1)" "$(job_f "$line" 2)" "$(job_f "$line" 3)" \
"$(job_f "$line" 4)" "$(job_f "$line" 5)" "$(job_f "$line" 6)" "$(job_f "$line" 7)"
n=$((n+1))
done < <(jobs_read)
[ "$n" -eq 0 ] && echo "(noch keine Jobs angelegt)"
return 0
}
cmd_job() {
local sub="${1:-list}"; [ $# -gt 0 ] && shift || true
case "$sub" in
list|ls) jobs_list_table ;;
status)
printf '\n%sAutomatische Backups%s\n' "$C_BLD" "$C_RST"
printf ' Schalter : %s\n' "$(backup_auto_enabled && echo 'ein (BACKUP_AUTO=on)' || echo 'aus (BACKUP_AUTO=off)')"
printf ' Cron-Datei : %s\n' "$([ -f "$CRON_FILE" ] && echo "$CRON_FILE" || echo 'nicht vorhanden')"
printf ' Jobdatei : %s\n' "$(jobs_file)"
printf ' Manuell nach: %s (BACKUP_DIR)\n' "$BACKUP_DIR"
printf '\n%sJobs%s\n' "$C_BLD" "$C_RST"
jobs_list_table
if [ -f "$CRON_FILE" ]; then
printf '\n%sEingeplant%s\n' "$C_BLD" "$C_RST"
grep -vE '^[[:space:]]*(#|SHELL|PATH|$)' "$CRON_FILE" | sed 's/^/ /'
fi
echo ;;
add|set)
local name="" sites="ALL" cron="" dir="" keep="7" enabled="on" opts=""
local is_set=""; [ "$sub" = set ] && is_set=1
while [ $# -gt 0 ]; do
case "$1" in
--name) name="$2"; shift 2 ;;
--sites) sites="$2"; shift 2 ;;
--cron) cron="$2"; shift 2 ;;
--dir) dir="$2"; shift 2 ;;
--keep) keep="$2"; shift 2 ;;
--no-rrds) opts="$opts --no-rrds"; shift ;;
--no-logs) opts="$opts --no-logs"; shift ;;
--stop) opts="$opts --stop"; shift ;;
--enabled) enabled=on; shift ;;
--disabled) enabled=off; shift ;;
*) [ -z "$name" ] && { name="$1"; shift; } || die "Unbekannte Option fuer 'job $sub': $1" ;;
esac
done
[ -n "$name" ] || die "--name fehlt."
local msg
msg="$(job_validate_name "$name")" || die "$msg"
local old=""; old="$(job_line "$name")"
if [ -n "$is_set" ]; then
[ -n "$old" ] || die "Job '$name' ist nicht vorhanden."
# nicht angegebene Felder aus dem Bestand uebernehmen
[ "$sites" = ALL ] && sites="$(job_f "$old" 2)"
[ -z "$cron" ] && cron="$(job_f "$old" 3)"
[ -z "$dir" ] && dir="$(job_f "$old" 4)"
[ -z "$opts" ] && opts="$(job_f "$old" 5)"
[ "$keep" = 7 ] && keep="$(job_f "$old" 6)"
enabled="$(job_f "$old" 7)"
else
[ -z "$old" ] || die "Job '$name' existiert bereits. Aendern mit: $SELF job set $name ..."
fi
dir="${dir:-$BACKUP_DIR}"
[ -n "$cron" ] || die "--cron fehlt, z.B. --cron '15 2 * * *'."
msg="$(job_validate_cron "$cron")" || die "$msg"
msg="$(job_validate_field "$dir")" || die "$msg"
case "$keep" in ''|*[!0-9]*) die "--keep braucht eine Zahl (0 = alles behalten)." ;; esac
opts="$(printf '%s' "$opts" | sed 's/^ *//')"
job_put "$name" "$sites" "$cron" "$dir" "$opts" "$keep" "$enabled"
ok "Job '$name' gespeichert."
jobs_list_table | sed -n "1p;/^$name /p"
backup_auto_enabled && cron_write || log "Einplanen mit: $SELF job auto on"
;;
del|rm|delete)
[ $# -ge 1 ] || die "Name fehlt: $SELF job del NAME"
job_exists "$1" || die "Job '$1' ist nicht vorhanden."
confirm "Job '$1' wirklich loeschen?" || die "Abgebrochen."
job_remove "$1"; ok "Job '$1' geloescht."
backup_auto_enabled && cron_write || true
;;
enable|disable)
[ $# -ge 1 ] || die "Name fehlt: $SELF job $sub NAME"
local l; l="$(job_line "$1")"
[ -n "$l" ] || die "Job '$1' ist nicht vorhanden."
local st=on; [ "$sub" = disable ] && st=off
job_put "$1" "$(job_f "$l" 2)" "$(job_f "$l" 3)" "$(job_f "$l" 4)" \
"$(job_f "$l" 5)" "$(job_f "$l" 6)" "$st"
ok "Job '$1' ist jetzt $st."
backup_auto_enabled && cron_write || true
;;
run)
[ $# -ge 1 ] || die "Name fehlt: $SELF job run NAME"
job_run_one "$1" ;;
run-all)
local line n=0
while IFS= read -r line; do
[ -n "$line" ] || continue
[ "$(job_f "$line" 7)" = on ] || continue
job_run_one "$(job_f "$line" 1)" || warn "Job fehlgeschlagen: $(job_f "$line" 1)"
n=$((n+1))
done < <(jobs_read)
[ "$n" -eq 0 ] && warn "Kein aktiver Job vorhanden."
return 0 ;;
auto)
case "${1:-}" in
on) env_set BACKUP_AUTO on; BACKUP_AUTO=on; cron_write ;;
off) env_set BACKUP_AUTO off; BACKUP_AUTO=off; cron_remove_file ;;
'') printf ' Automatische Backups: %s\n' \
"$(backup_auto_enabled && echo ein || echo aus)" ;;
*) die "Aufruf: $SELF job auto [on|off]" ;;
esac ;;
*) die "Unbekanntes Unterkommando 'job $sub'.
Moeglich: list | status | add | set | del | enable | disable | run | run-all | auto" ;;
esac
}
# =====================================================================
# Auflisten / Aufraeumen
# =====================================================================
@@ -1807,7 +2150,8 @@ browser|BROWSER_TOOLS_IMAGE|text|Werkzeug-Image (leer = automatisch)||1
paths|CMK_DATA_DIR|path|Checkmk-Sites (Bind-Mount)||
paths|NPM_DATA_DIR|path|Proxy-Manager-Daten||
paths|NPM_LE_DIR|path|Let's-Encrypt-Zertifikate||
paths|BACKUP_DIR|path|Backups und Bundles||
paths|BACKUP_DIR|path|Backups und Bundles (manuell)||
backup|BACKUP_AUTO|choice|Automatische Backups per cron|off:on|
paths|IMAGE_DIR|path|Heruntergeladene Image-Tarballs||
cert|CERT_DOMAIN|text|FQDN fuer selbstsignierte Zertifikate||
cert|CERT_DAYS|int|Gueltigkeit in Tagen||
@@ -1825,6 +2169,7 @@ npm|NGINX Proxy Manager
browser|Terminal-Browser
paths|Verzeichnisse
cert|Selbstsignierte Zertifikate
backup|Backup-Automatik
EOF
}
@@ -1893,6 +2238,8 @@ settings_post_set() {
NPM_ENABLED)
# Container entsprechend entfernen bzw. starten
cmd_npm_enable "$val" ;;
BACKUP_AUTO)
cmd_job auto "$val" ;;
CMK_IMAGE|CMK_SITE_ID|CMK_CONTAINER_NAME|NPM_IMAGE|NPM_CONTAINER_NAME|TZ|CMK_LIVESTATUS_TCP|NPM_DISABLE_IPV6|CMK_DATA_DIR|NPM_DATA_DIR|NPM_LE_DIR)
log "Wirksam nach: $SELF up (Container werden neu erstellt)" ;;
esac
@@ -1934,6 +2281,8 @@ cmd_config() {
cmd_npm_port "$val"
elif [ "$key" = NPM_ENABLED ]; then
cmd_npm_enable "$val"
elif [ "$key" = BACKUP_AUTO ]; then
env_set "$key" "$val"; BACKUP_AUTO="$val"; cmd_job auto "$val"
else
env_set "$key" "$val" "$opt"
ok "$key=$val"
@@ -2157,7 +2506,61 @@ EOF
# =====================================================================
# ncurses-Oberflaeche
# =====================================================================
# ---------------------------------------------------------------------
# GUI-sichere Vorbedingungen
#
# In der Oberflaeche darf nichts "die" aufrufen - das wuerde die ganze TUI
# beenden. Diese Helfer melden das Problem im Dialog und geben 1 zurueck.
# ---------------------------------------------------------------------
gui_need_container() {
if ! container_exists "$CMK_CONTAINER_NAME"; then
ui_msg "Checkmk-Container fehlt" \
"Der Container '$CMK_CONTAINER_NAME' existiert nicht.
Dafuer wird er zuerst gebraucht. Im Hauptmenue:
stack -> Stack starten
Auf der Kommandozeile:
$SELF up"
return 1
fi
if ! container_running "$CMK_CONTAINER_NAME"; then
ui_msg "Checkmk-Container gestoppt" \
"Der Container '$CMK_CONTAINER_NAME' existiert, laeuft aber nicht.
Im Hauptmenue: stack -> Stack starten
Kommandozeile: $SELF up"
return 1
fi
return 0
}
gui_need_sites() {
gui_need_container || return 1
local n; n="$(cmk_sites | sed '/^$/d' | wc -l)"
if [ "$n" -eq 0 ]; then
ui_msg "Keine Sites" \
"Im Container ist noch keine Site vorhanden.
Der Container legt die Site aus CMK_SITE_ID beim ersten Start selbst an.
Alternativ eine Site importieren: Hauptmenue -> import"
return 1
fi
return 0
}
# Interaktives Kommando ausfuehren, ohne dass ein Fehler die TUI beendet
gui_run_interactive() {
clear 2>/dev/null || true
if ! ( "$@" ); then
printf '\n%sDas Kommando wurde mit einem Fehler beendet.%s\n' "$C_YEL" "$C_RST"
fi
printf '\nWeiter mit ENTER ...'
read -r _ </dev/tty || true
}
gui_pick_site() { # -> Site auf stdout
gui_need_container || return 1
local -a items=() s
while IFS= read -r s; do
[ -n "$s" ] || continue
@@ -2220,7 +2623,7 @@ gui_stack_menu() {
}
gui_backup() {
require_cmk_running
gui_need_sites || return 0
local -a items=() s
while IFS= read -r s; do
[ -n "$s" ] || continue
@@ -2229,14 +2632,14 @@ gui_backup() {
[ ${#items[@]} -gt 0 ] || { ui_msg "Backup" "Keine Sites vorhanden."; return; }
local chosen
chosen="$(ui_checklist "Backup" "Zu sichernde Site(s) mit LEERTASTE markieren:" "${items[@]}")" || return
chosen="$(ui_checklist "Backup" "Zu sichernde Site(s) mit LEERTASTE markieren:" "${items[@]}")" || return 0
[ -n "$chosen" ] || { ui_msg "Backup" "Keine Site ausgewaehlt."; return; }
local optsel
optsel="$(ui_checklist "Backup-Optionen" "Optionen waehlen:" \
no-rrds "Performance-Daten (RRD) auslassen - kleineres Archiv" off \
no-logs "Logdateien auslassen" off \
stop "Site waehrend des Backups stoppen (konsistenter)" off)" || return
stop "Site waehrend des Backups stoppen (konsistenter)" off)" || return 0
local label
label="$(ui_input "Kennzeichnung" "Optionaler Text fuer den Dateinamen (leer = keiner):" "")" || label=""
@@ -2255,10 +2658,217 @@ gui_backup() {
ui_run "Backup laeuft" bash -c "cd '$PROJECT_DIR' && '$SELF' -y ${args[*]}"
}
# ---------------------------------------------------------------------
# Backup: erstellen und planen
# ---------------------------------------------------------------------
gui_backup_menu() {
while :; do
local auto sel
auto="$(backup_auto_enabled && echo 'ein' || echo 'aus')"
sel="$(ui_menu "Backup erstellen / planen" \
"Manuelle Backups gehen immer nach:
$BACKUP_DIR (Einstellung BACKUP_DIR)
Geplante Jobs haben ihr eigenes Zielverzeichnis.
Automatische Ausfuehrung: $auto" \
manuell "Backup jetzt erstellen" \
jobs "Backup-Jobs verwalten (anlegen/aendern/loeschen)" \
auto "Automatische Ausfuehrung ein/aus (derzeit: $auto)" \
status "Zeitplan und Jobs anzeigen" \
jetzt "Einen Job sofort ausfuehren" \
back "zurueck")" || return 0
case "$sel" in
manuell) gui_backup || true ;;
jobs) gui_jobs_menu || true ;;
auto) gui_backup_auto || true ;;
status) local f; f="$(mktemp)"; NO_COLOR=1 cmd_job status >"$f" 2>&1 || true
ui_text "Backup-Zeitplan" "$f"; rm -f "$f" ;;
jetzt) gui_job_run_now || true ;;
back) return 0 ;;
esac
done
}
gui_backup_auto() {
if backup_auto_enabled; then
ui_yesno "Automatische Backups" \
"Automatische Backups sind eingeschaltet.
Abschalten? Die Jobs bleiben erhalten, es wird nur
nichts mehr automatisch ausgefuehrt." || return 0
ui_run "Automatik abschalten" bash -c "cd '$PROJECT_DIR' && '$SELF' -y job auto off"
else
local n; n="$(jobs_read | awk -F'|' '$7=="on"' | wc -l)"
if [ "$n" -eq 0 ]; then
ui_msg "Keine aktiven Jobs" \
"Es ist noch kein aktiver Backup-Job angelegt.
Zuerst unter 'Backup-Jobs verwalten' einen Job anlegen -
dort wird auch der Zeitplan festgelegt."
return
fi
ui_yesno "Automatische Backups" \
"$n aktive(r) Job(s) werden in die System-Cron eingetragen
($CRON_FILE).
Dafuer werden root-Rechte benoetigt. Einschalten?" || return 0
ui_run "Automatik einschalten" bash -c "cd '$PROJECT_DIR' && '$SELF' -y job auto on"
fi
load_env "${ENV_FILE:-$PROJECT_DIR/.env}"
}
gui_pick_job() { # -> Jobname auf stdout
local -a items=() line
while IFS= read -r line; do
[ -n "$line" ] || continue
items+=("$(job_f "$line" 1)" \
"$(job_f "$line" 3) -> $(job_f "$line" 4) [$(job_f "$line" 7)]")
done < <(jobs_read)
if [ ${#items[@]} -eq 0 ]; then
ui_msg "Keine Jobs" "Es ist noch kein Backup-Job angelegt."
return 1
fi
ui_menu "Job waehlen" "Vorhandene Backup-Jobs:" "${items[@]}"
}
gui_job_run_now() {
gui_need_sites || return 0
local j; j="$(gui_pick_job)" || return 0
[ -n "$j" ] || return 0
ui_yesno "Job ausfuehren" "Job '$j' jetzt ausfuehren?" || return 0
ui_run "Job '$j'" bash -c "cd '$PROJECT_DIR' && '$SELF' -y job run '$j'"
}
gui_jobs_menu() {
while :; do
local sel
sel="$(ui_menu "Backup-Jobs" \
"Jeder Job hat eigenen Zeitplan, eigenes Zielverzeichnis
und eigene Aufbewahrung.
Jobdatei: $(jobs_file)" \
list "Jobs anzeigen" \
neu "Neuen Job anlegen" \
edit "Job aendern" \
toggle "Job ein-/ausschalten" \
del "Job loeschen" \
back "zurueck")" || return 0
case "$sel" in
list) local f; f="$(mktemp)"; NO_COLOR=1 jobs_list_table >"$f" 2>&1 || true
ui_text "Backup-Jobs" "$f"; rm -f "$f" ;;
neu) gui_job_edit "" || true ;;
edit) local j; j="$(gui_pick_job)" || continue; [ -n "$j" ] && gui_job_edit "$j" ;;
toggle) local j2 l st
j2="$(gui_pick_job)" || continue; [ -n "$j2" ] || continue
l="$(job_line "$j2")"; st=disable
[ "$(job_f "$l" 7)" = off ] && st=enable
ui_run "Job $st" bash -c "cd '$PROJECT_DIR' && '$SELF' -y job $st '$j2'" ;;
del) local j3; j3="$(gui_pick_job)" || continue; [ -n "$j3" ] || continue
ui_yesno "Loeschen" "Job '$j3' wirklich loeschen?\n\n(Bereits erstellte Backups bleiben erhalten.)" \
&& ui_run "Job loeschen" bash -c "cd '$PROJECT_DIR' && '$SELF' -y job del '$j3'" ;;
back) return 0 ;;
esac
done
}
# Zeitplan bequem zusammenklicken
gui_pick_cron() { # vorgabe -> cron-ausdruck auf stdout
local cur="${1:-15 2 * * *}" kind time hh mm dow dom
kind="$(ui_menu "Zeitplan" "Wie oft soll gesichert werden?" \
taeglich "jeden Tag" \
woechentlich "einmal pro Woche" \
monatlich "einmal pro Monat" \
frei "Cron-Ausdruck selbst eingeben")" || return 1
if [ "$kind" = frei ]; then
ui_input "Cron-Ausdruck" "Fuenf Felder: Minute Stunde Tag Monat Wochentag" "$cur"
return
fi
time="$(ui_input "Uhrzeit" "Uhrzeit im Format HH:MM" "02:15")" || return 1
case "$time" in
[0-9][0-9]:[0-9][0-9]) : ;;
*) ui_msg "Ungueltig" "Bitte im Format HH:MM angeben, z.B. 02:15."; return 1 ;;
esac
hh="${time%%:*}"; mm="${time##*:}"
hh="$((10#$hh))"; mm="$((10#$mm))"
[ "$hh" -le 23 ] && [ "$mm" -le 59 ] || { ui_msg "Ungueltig" "Stunde 0-23, Minute 0-59."; return 1; }
case "$kind" in
taeglich) printf '%s %s * * *\n' "$mm" "$hh" ;;
woechentlich) dow="$(ui_menu "Wochentag" "An welchem Tag?" \
1 Montag 2 Dienstag 3 Mittwoch 4 Donnerstag \
5 Freitag 6 Samstag 0 Sonntag)" || return 1
printf '%s %s * * %s\n' "$mm" "$hh" "$dow" ;;
monatlich) dom="$(ui_input "Tag im Monat" "Welcher Tag (1-28)?" "1")" || return 1
case "$dom" in ''|*[!0-9]*) ui_msg "Ungueltig" "Bitte eine Zahl."; return 1 ;; esac
printf '%s %s %s * *\n' "$mm" "$hh" "$dom" ;;
esac
}
gui_job_edit() { # name (leer = neu)
local name="$1" line="" sites="ALL" cron="15 2 * * *" dir="$BACKUP_DIR" opts="" keep="7"
if [ -n "$name" ]; then
line="$(job_line "$name")"
sites="$(job_f "$line" 2)"; cron="$(job_f "$line" 3)"; dir="$(job_f "$line" 4)"
opts="$(job_f "$line" 5)"; keep="$(job_f "$line" 6)"
else
name="$(ui_input "Neuer Job" "Name des Jobs (Buchstaben, Ziffern, _ und -):" "nachts")" || return 0
local msg
if ! msg="$(job_validate_name "$name")"; then ui_msg "Ungueltiger Name" "$msg"; return; fi
if job_exists "$name"; then ui_msg "Schon vorhanden" "Ein Job namens '$name' existiert bereits."; return; fi
fi
# Sites auswaehlen
if gui_need_sites; then
local -a items=() sname
items+=("ALL" "alle Sites (auch spaeter neu angelegte)" "$([ "$sites" = ALL ] && echo on || echo off)")
while IFS= read -r sname; do
[ -n "$sname" ] || continue
items+=("$sname" "einzelne Site" \
"$(printf '%s' ",$sites," | grep -q ",$sname," && echo on || echo off)")
done < <(cmk_sites)
local chosen
chosen="$(ui_checklist "Sites" "Was soll dieser Job sichern?\n(ALL schliesst alle anderen ein)" "${items[@]}")" || return 0
chosen="$(printf '%s' "$chosen" | tr -d '"')"
[ -n "$chosen" ] || { ui_msg "Keine Auswahl" "Es wurde keine Site gewaehlt."; return; }
case " $chosen " in
*" ALL "*) sites=ALL ;;
*) sites="$(printf '%s' "$chosen" | tr ' ' ',')" ;;
esac
fi
cron="$(gui_pick_cron "$cron")" || return 0
[ -n "$cron" ] || return 0
dir="$(ui_input "Zielverzeichnis" \
"Wohin soll dieser Job sichern?
(absolut oder relativ zum Projekt; wird bei Bedarf angelegt)" "$dir")" || return 0
[ -n "$dir" ] || return 0
keep="$(ui_input "Aufbewahrung" \
"Wie viele Backups sollen in diesem Verzeichnis bleiben?
0 = alle behalten" "$keep")" || return 0
case "$keep" in ''|*[!0-9]*) ui_msg "Ungueltig" "Bitte eine Zahl angeben."; return ;; esac
local optsel
optsel="$(ui_checklist "Optionen" "Optionen fuer diesen Job:" \
no-rrds "Performance-Daten (RRD) auslassen" "$(case "$opts" in *no-rrds*) echo on ;; *) echo off ;; esac)" \
no-logs "Logdateien auslassen" "$(case "$opts" in *no-logs*) echo on ;; *) echo off ;; esac)" \
stop "Site waehrend des Backups stoppen" "$(case "$opts" in *stop*) echo on ;; *) echo off ;; esac)")" || return 0
opts=""
case "$optsel" in *no-rrds*) opts="$opts --no-rrds" ;; esac
case "$optsel" in *no-logs*) opts="$opts --no-logs" ;; esac
case "$optsel" in *stop*) opts="$opts --stop" ;; esac
opts="$(printf '%s' "$opts" | sed 's/^ *//')"
local action=add; job_exists "$name" && action=set
ui_run "Job speichern" bash -c \
"cd '$PROJECT_DIR' && '$SELF' -y job $action --name '$name' --sites '$sites' \
--cron '$cron' --dir '$dir' --keep '$keep' $opts"
}
gui_restore() {
require_cmk_running
local file; file="$(gui_pick_backup)" || return
[ -n "$file" ] || return
gui_need_container || return 0
local file; file="$(gui_pick_backup)" || return 0
[ -n "$file" ] || return 0
local f; f="$(mktemp)"
bundle_show "$file" >"$f" 2>&1 || true
@@ -2267,15 +2877,15 @@ gui_restore() {
bundle_read "$file" || { ui_msg "Fehler" "Diese Datei ist kein Checkmk-Backup."; return; }
local reuse=""
if cmk_site_exists "$B_SITE"; then
ui_yesno "Site existiert" "Die Site '$B_SITE' ist bereits vorhanden.\n\nSoll sie UEBERSCHRIEBEN werden?\nAlle aktuellen Daten dieser Site gehen verloren!" || return
ui_yesno "Site existiert" "Die Site '$B_SITE' ist bereits vorhanden.\n\nSoll sie UEBERSCHRIEBEN werden?\nAlle aktuellen Daten dieser Site gehen verloren!" || return 0
reuse="--reuse --kill"
fi
ui_yesno "Restore starten" "Site '$B_SITE' aus\n$(basename "$file")\nwiederherstellen?" || return
ui_yesno "Restore starten" "Site '$B_SITE' aus\n$(basename "$file")\nwiederherstellen?" || return 0
ui_run "Restore laeuft" bash -c "cd '$PROJECT_DIR' && '$SELF' -y restore --file '$file' $reuse"
}
gui_import() {
require_cmk_running
gui_need_container || return 0
ui_msg "Site importieren" \
"Import einer Site von einer Direktinstallation (Bare-Metal/VM).
@@ -2288,8 +2898,8 @@ Wichtig fuer die Agents:
- Port 8000 (Agent-Receiver) ist erreichbar
Dann muessen die Agents NICHT neu eingerichtet werden."
local file; file="$(gui_pick_backup)" || return
[ -n "$file" ] || return
local file; file="$(gui_pick_backup)" || return 0
[ -n "$file" ] || return 0
local f; f="$(mktemp)"
bundle_show "$file" >"$f" 2>&1 || true
@@ -2298,7 +2908,7 @@ Dann muessen die Agents NICHT neu eingerichtet werden."
bundle_read "$file" || { ui_msg "Fehler" "Diese Datei ist kein Migrations-Bundle."; return; }
local reuse=""
if cmk_site_exists "$B_SITE"; then
ui_yesno "Site existiert" "Die Site '$B_SITE' existiert bereits im Container.\n\nUEBERSCHREIBEN?" || return
ui_yesno "Site existiert" "Die Site '$B_SITE' existiert bereits im Container.\n\nUEBERSCHREIBEN?" || return 0
reuse="--reuse --kill"
fi
local setdef=""
@@ -2334,7 +2944,7 @@ gui_backups_menu() {
}
gui_sites_menu() {
require_cmk_running
gui_need_container || return 0
while :; do
local sel
sel="$(ui_menu "Sites" "Sites im Container '$CMK_CONTAINER_NAME'" \
@@ -2371,14 +2981,14 @@ Edition/Version: ${CMK_EDITION:-raw} / ${CMK_VERSION:-?}" \
case "$sel" in
status) local f; f="$(mktemp)"; NO_COLOR=1 cmd_image status >"$f" 2>&1 || true
ui_text "Image-Status" "$f"; rm -f "$f" ;;
test) gui_image_test ;;
pull) gui_image_pull ;;
download) gui_image_download ;;
test) gui_image_test || true ;;
pull) gui_image_pull || true ;;
download) gui_image_download || true ;;
load) local lf; lf="$(ui_pick_file "${IMAGE_DIR:-$PROJECT_DIR/images}" '*.tar.gz')" || continue
[ -n "$lf" ] || continue
ui_run "Image einspielen" bash -c "cd '$PROJECT_DIR' && '$SELF' -y image load --file '$lf'"
load_env "${ENV_FILE:-$PROJECT_DIR/.env}" ;;
use) gui_image_use ;;
use) gui_image_use || true ;;
back) return 0 ;;
esac
done
@@ -2398,14 +3008,14 @@ gui_image_pick_edition() {
gui_image_test() {
local ed ver user pass
ed="$(gui_image_pick_edition)" || return
ver="$(ui_input "Version" "Version (fuer die Pruefung des Downloadportals):" "${CMK_VERSION:-}")" || return
ed="$(gui_image_pick_edition)" || return 0
ver="$(ui_input "Version" "Version (fuer die Pruefung des Downloadportals):" "${CMK_VERSION:-}")" || return 0
local -a extra=()
if ui_yesno "Zugangsdaten" "Zugangsdaten aus der .env verwenden?\n\nRegistry : ${CMK_REGISTRY_USER:-<leer>}\nDownload : ${CMK_DOWNLOAD_USER:-<leer>}\n\n<Nein> = jetzt eingeben"; then
:
else
user="$(ui_input "Zugangsdaten" "Benutzer (Checkmk-Kundenportal):" "${CMK_REGISTRY_USER:-}")" || return
pass="$(ui_password "Zugangsdaten" "Kennwort:")" || return
user="$(ui_input "Zugangsdaten" "Benutzer (Checkmk-Kundenportal):" "${CMK_REGISTRY_USER:-}")" || return 0
pass="$(ui_password "Zugangsdaten" "Kennwort:")" || return 0
[ -n "$user" ] && extra+=(--user "$user" --password "$pass")
fi
ui_run "Zugangsdaten pruefen" bash -c \
@@ -2414,13 +3024,13 @@ gui_image_test() {
gui_image_pull() {
local ed ver user pass
ed="$(gui_image_pick_edition)" || return
ver="$(ui_input "Version" "Version bzw. Tag (z.B. 2.3.0p23 oder 2.3.0-latest):" "${CMK_VERSION:-}")" || return
[ -n "$ver" ] || return
ed="$(gui_image_pick_edition)" || return 0
ver="$(ui_input "Version" "Version bzw. Tag (z.B. 2.3.0p23 oder 2.3.0-latest):" "${CMK_VERSION:-}")" || return 0
[ -n "$ver" ] || return 0
local -a extra=()
if [ "$ed" = enterprise ] || [ -n "${CMK_REGISTRY_USER:-}" ]; then
user="$(ui_input "Registry-Anmeldung" "Benutzer (Checkmk-Kundenportal):" "${CMK_REGISTRY_USER:-}")" || return
pass="$(ui_password "Registry-Anmeldung" "Kennwort:")" || return
user="$(ui_input "Registry-Anmeldung" "Benutzer (Checkmk-Kundenportal):" "${CMK_REGISTRY_USER:-}")" || return 0
pass="$(ui_password "Registry-Anmeldung" "Kennwort:")" || return 0
[ -n "$user" ] && extra+=(--user "$user" --password "$pass")
fi
ui_run "Image holen" bash -c "cd '$PROJECT_DIR' && '$SELF' -y image pull --edition '$ed' --version '$ver' ${extra[*]+${extra[*]}}"
@@ -2429,11 +3039,11 @@ gui_image_pull() {
gui_image_download() {
local ed ver user pass
ed="$(gui_image_pick_edition)" || return
ver="$(ui_input "Version" "EXAKTE Version (z.B. 2.3.0p23 - kein '-latest'):" "${CMK_VERSION:-}")" || return
[ -n "$ver" ] || return
user="$(ui_input "Downloadportal" "Benutzer:" "${CMK_DOWNLOAD_USER:-}")" || return
pass="$(ui_password "Downloadportal" "Kennwort:")" || return
ed="$(gui_image_pick_edition)" || return 0
ver="$(ui_input "Version" "EXAKTE Version (z.B. 2.3.0p23 - kein '-latest'):" "${CMK_VERSION:-}")" || return 0
[ -n "$ver" ] || return 0
user="$(ui_input "Downloadportal" "Benutzer:" "${CMK_DOWNLOAD_USER:-}")" || return 0
pass="$(ui_password "Downloadportal" "Kennwort:")" || return 0
ui_run "Image herunterladen" bash -c "cd '$PROJECT_DIR' && '$SELF' -y image download --edition '$ed' --version '$ver' --user '$user' --password '$pass'"
load_env "${ENV_FILE:-$PROJECT_DIR/.env}"
}
@@ -2446,7 +3056,7 @@ gui_image_use() {
done < <(image_local_list)
[ ${#items[@]} -gt 0 ] || { ui_msg "Images" "Es ist kein Checkmk-Image lokal vorhanden.\n\nZuerst 'pull', 'download' oder 'load' verwenden."; return; }
local sel
sel="$(ui_menu "Image auswaehlen" "Lokal vorhandene Checkmk-Images:" "${items[@]}")" || return
sel="$(ui_menu "Image auswaehlen" "Lokal vorhandene Checkmk-Images:" "${items[@]}")" || return 0
ui_run "Image setzen" bash -c "cd '$PROJECT_DIR' && '$SELF' -y image use '$sel'"
load_env "${ENV_FILE:-$PROJECT_DIR/.env}"
}
@@ -2485,15 +3095,15 @@ gui_settings_edit() {
IFS=':'
done
IFS="$IFS_BAK"
new="$(ui_radiolist "$key" "$label\n\nAktuell: ${cur:-<leer>}" "${ropts[@]}")" || return
new="$(ui_radiolist "$key" "$label\n\nAktuell: ${cur:-<leer>}" "${ropts[@]}")" || return 0
[ "$new" = auto ] && new=""
;;
password)
new="$(ui_password "$key" "$label\n\n(leer lassen = unveraendert)")" || return
new="$(ui_password "$key" "$label\n\n(leer lassen = unveraendert)")" || return 0
[ -z "$new" ] && return
;;
*)
new="$(ui_input "$key" "$label\n\nTyp: $typ$([ -n "$opt" ] && echo ' (darf leer bleiben)')" "$cur")" || return
new="$(ui_input "$key" "$label\n\nTyp: $typ$([ -n "$opt" ] && echo ' (darf leer bleiben)')" "$cur")" || return 0
;;
esac
@@ -2541,6 +3151,7 @@ gui_settings_menu() {
while IFS='|' read -r c l; do
[ -n "$c" ] && items+=("$c" "$l")
done < <(settings_categories)
items+=("jobs" "Backup-Jobs (Zeitplan, Zielpfade, Aufbewahrung)")
items+=("show" "Alle Werte auflisten")
items+=("editor" "Datei .env im Editor oeffnen")
items+=("back" "zurueck")
@@ -2550,12 +3161,14 @@ gui_settings_menu() {
Datei: ${ENV_FILE:-$PROJECT_DIR/.env}" "${items[@]}")" || return 0
case "$sel" in
back) return 0 ;;
jobs) gui_jobs_menu || true ;;
show) local f; f="$(mktemp)"
{ printf '%-24s %-10s %s\n' "SCHLUESSEL" "BEREICH" "WERT"
NO_COLOR=1 cmd_config list; } >"$f" 2>&1
ui_text "Alle Einstellungen" "$f"; rm -f "$f" ;;
editor) local ef="${ENV_FILE:-$PROJECT_DIR/.env}"
clear; "${EDITOR:-nano}" "$ef"
if [ ! -f "$ef" ]; then ui_msg "Keine .env" "Die Datei $ef existiert nicht.\n\nAnlegen mit: cp .env.example .env"; continue; fi
clear 2>/dev/null || true; "${EDITOR:-nano}" "$ef"
load_env "$ef"; ui_msg "Konfiguration" "Die .env wurde neu eingelesen." ;;
*) local title
title="$(settings_categories | awk -F'|' -v c="$sel" '$1==c{print $2}')"
@@ -2588,17 +3201,16 @@ Oberflaeche des Proxy Managers darstellen." \
[ -n "$u" ] && NPM_ADMIN_URL="$u" ;;
build) ui_run "Werkzeug-Image bauen" bash -c "cd '$PROJECT_DIR' && '$SELF' browser --build-only" || true ;;
back) return 0 ;;
*) clear
cmd_browser --engine "$sel" --url "${NPM_ADMIN_URL:-http://127.0.0.1:81}" || true
;;
*) gui_run_interactive cmd_browser --engine "$sel" \
--url "${NPM_ADMIN_URL:-http://127.0.0.1:81}" ;;
esac
done
}
gui_cert() {
local domain alt days
domain="$(ui_input "Zertifikat" "FQDN / Common Name:" "${CERT_DOMAIN:-checkmk.example.local}")" || return
[ -n "$domain" ] || return
domain="$(ui_input "Zertifikat" "FQDN / Common Name:" "${CERT_DOMAIN:-checkmk.example.local}")" || return 0
[ -n "$domain" ] || return 0
alt="$(ui_input "Zertifikat" "Weitere Namen/IPs (durch Leerzeichen getrennt, optional):" "$(primary_ip)")" || alt=""
days="$(ui_input "Zertifikat" "Gueltigkeit in Tagen:" "${CERT_DAYS:-3650}")" || days=3650
local -a a=(); local x
@@ -2616,7 +3228,7 @@ Runtime : $RUNTIME Standard-Site: $CMK_SITE_ID
Backups : $BACKUP_DIR" \
status "Status von Containern und Sites" \
stack "Stack steuern (Start/Stop/Logs/Update)" \
backup "Backup erstellen" \
backup "Backup erstellen / planen" \
restore "Backup wiederherstellen" \
import "Site von Direktinstallation importieren" \
manage "Backups verwalten (Liste/Details/Loeschen)" \
@@ -2628,22 +3240,23 @@ Backups : $BACKUP_DIR" \
shell "Root-Shell im Checkmk-Container" \
quit "Beenden")" || break
case "$sel" in
status) gui_status ;;
stack) gui_stack_menu ;;
backup) gui_backup ;;
restore) gui_restore ;;
import) gui_import ;;
manage) gui_backups_menu ;;
sites) gui_sites_menu ;;
image) gui_image_menu ;;
browser) gui_browser_menu ;;
cert) gui_cert ;;
config) gui_settings_menu ;;
shell) clear; cmd_shell || true ;;
status) gui_status || true ;;
stack) gui_stack_menu || true ;;
backup) gui_backup_menu || true ;;
restore) gui_restore || true ;;
import) gui_import || true ;;
manage) gui_backups_menu || true ;;
sites) gui_sites_menu || true ;;
image) gui_image_menu || true ;;
browser) gui_browser_menu || true ;;
cert) gui_cert || true ;;
config) gui_settings_menu || true ;;
shell) gui_need_container || continue
gui_run_interactive cmd_shell ;;
quit) break ;;
esac
done
clear
clear 2>/dev/null || true
ok "Beendet."
}
@@ -2670,29 +3283,30 @@ main() {
gui|menu) gui_main ;;
up|start) cmd_up ;;
down|stop) cmd_down ;;
restart) cmd_restart ;;
pull) cmd_pull ;;
status) cmd_status ;;
logs) cmd_logs "$@" ;;
shell) cmd_shell ;;
omd) cmd_omd "$@" ;;
sites) cmd_sites ;;
restart) cmd_restart || true ;;
pull) cmd_pull || true ;;
status) cmd_status || true ;;
logs) cmd_logs "$@" || true ;;
shell) cmd_shell || true ;;
omd) cmd_omd "$@" || true ;;
sites) cmd_sites || true ;;
site-start) cmd_site_start "$@" ;;
site-stop) cmd_site_stop "$@" ;;
start-all-sites) cmd_start_all_sites ;;
backup) cmd_backup "$@" ;;
list) cmd_list ;;
prune) cmd_prune "$@" ;;
backup) cmd_backup "$@" || true ;;
list) cmd_list || true ;;
prune) cmd_prune "$@" || true ;;
job|jobs) cmd_job "$@" ;;
verify) local vf=""; [ "${1:-}" = "--file" ] && vf="$2"; [ -n "$vf" ] || vf="${1:-}"
[ -n "$vf" ] || die "--file fehlt."; bundle_verify "$vf" ;;
restore) cmd_restore "$@" ;;
import) cmd_import "$@" ;;
cert) cmd_cert "$@" ;;
image) cmd_image "$@" ;;
browser) cmd_browser "$@" ;;
restore) cmd_restore "$@" || true ;;
import) cmd_import "$@" || true ;;
cert) cmd_cert "$@" || true ;;
image) cmd_image "$@" || true ;;
browser) cmd_browser "$@" || true ;;
npm-port) cmd_npm_port "$@" ;;
npm-enable) cmd_npm_enable "$@" ;;
config) cmd_config "$@" ;;
config) cmd_config "$@" || true ;;
help|-h|--help) usage ;;
*) err "Unbekanntes Kommando: $cmd"; echo; usage; exit 1 ;;
esac
+1
View File
@@ -44,6 +44,7 @@ load_env() {
CMK_SITE_ID="${CMK_SITE_ID:-cmk}"
CMK_IMAGE="${CMK_IMAGE:-docker.io/checkmk/check-mk-raw:2.3.0-latest}"
NPM_ENABLED="${NPM_ENABLED:-on}"
BACKUP_AUTO="${BACKUP_AUTO:-off}"
NPM_ADMIN_PORT="${NPM_ADMIN_PORT:-81}"
# Leer = automatisch aus dem Admin-Port ableiten
NPM_ADMIN_URL="${NPM_ADMIN_URL:-http://127.0.0.1:$NPM_ADMIN_PORT}"