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
+6 -1
View File
@@ -144,8 +144,13 @@ CMK_DATA_DIR=./data/checkmk/sites
NPM_DATA_DIR=./data/npm/data NPM_DATA_DIR=./data/npm/data
NPM_LE_DIR=./data/npm/letsencrypt NPM_LE_DIR=./data/npm/letsencrypt
# Ablage fuer Backups / Migrations-Bundles und heruntergeladene Images # Ablage fuer MANUELLE Backups und Migrations-Bundles.
# Geplante Jobs bringen ihr eigenes Zielverzeichnis mit (backup-jobs.conf).
BACKUP_DIR=./backups BACKUP_DIR=./backups
# Sollen die Backup-Jobs automatisch per cron laufen?
# Umschalten ueber das Menue oder: ./scripts/cmk-manage.sh job auto on
BACKUP_AUTO=off
IMAGE_DIR=./images IMAGE_DIR=./images
# ===================================================================== # =====================================================================
+1
View File
@@ -13,3 +13,4 @@ backups/*
*.log *.log
images/* images/*
!images/.gitkeep !images/.gitkeep
backup-jobs.conf
+105 -6
View File
@@ -553,7 +553,8 @@ Die wichtigsten Schalter:
| `NPM_ENABLED` | `on`/`off` – soll der Proxy-Manager-Container überhaupt laufen? Siehe [12.4](#124-proxy-manager-ganz-abschalten-externer-nginx) | | `NPM_ENABLED` | `on`/`off` – soll der Proxy-Manager-Container überhaupt laufen? Siehe [12.4](#124-proxy-manager-ganz-abschalten-externer-nginx) |
| `NPM_ADMIN_PORT` | Port der Proxy-Manager-Oberfläche (Vorgabe 81) – nur über `npm-port` bzw. das Menü ändern | | `NPM_ADMIN_PORT` | Port der Proxy-Manager-Oberfläche (Vorgabe 81) – nur über `npm-port` bzw. das Menü ändern |
| `NPM_ADMIN_URL` | Startseite des Terminal-Browsers; leer = automatisch aus `NPM_ADMIN_PORT` | | `NPM_ADMIN_URL` | Startseite des Terminal-Browsers; leer = automatisch aus `NPM_ADMIN_PORT` |
| `BACKUP_DIR` | Ablage für Backups/Bundles (Vorgabe `./backups`) | | `BACKUP_DIR` | Ablage für **manuelle** Backups (Vorgabe `./backups`); geplante Jobs haben eigene Pfade |
| `BACKUP_AUTO` | `on`/`off` – sollen die Backup-Jobs per cron laufen? |
| `CERT_DOMAIN`, `CERT_DAYS` | Vorgaben für selbstsignierte Zertifikate | | `CERT_DOMAIN`, `CERT_DAYS` | Vorgaben für selbstsignierte Zertifikate |
### 7.1 Wer liest die `.env` – und warum gibt es sie zusätzlich zum Skript? ### 7.1 Wer liest die `.env` – und warum gibt es sie zusätzlich zum Skript?
@@ -1324,6 +1325,7 @@ Eine Dateiauswahl mit Verzeichnisnavigation ist eingebaut.
| `start-all-sites` | alle Sites starten (wird von systemd aufgerufen) | | `start-all-sites` | alle Sites starten (wird von systemd aufgerufen) |
| `backup …` | Backup als `.tar.gz`-Bundle | | `backup …` | Backup als `.tar.gz`-Bundle |
| `list` / `verify` / `prune --keep N` | Backups verwalten | | `list` / `verify` / `prune --keep N` | Backups verwalten |
| `job list\|add\|set\|del\|run\|auto` | geplante Backups (eigene Zielpfade je Job) |
| `restore …` | Backup zurückspielen | | `restore …` | Backup zurückspielen |
| `import …` | Migrations-Bundle einspielen | | `import …` | Migrations-Bundle einspielen |
| `image status\|list\|pull\|download\|load\|use` | Checkmk-Image beschaffen und auswaehlen | | `image status\|list\|pull\|download\|load\|use` | Checkmk-Image beschaffen und auswaehlen |
@@ -1435,17 +1437,112 @@ Auflisten und prüfen:
./scripts/cmk-manage.sh prune --keep 7 ./scripts/cmk-manage.sh prune --keep 7
``` ```
### Nächtliches Backup per Cron ### 15.1 Geplante Backups (Backup-Jobs)
Manuelle Backups gehen immer nach `BACKUP_DIR` aus der `.env`. Geplante Jobs
bringen dagegen **ihr eigenes Zielverzeichnis** mit – so lässt sich täglich auf
eine NAS-Freigabe und wöchentlich woanders hin sichern.
Im Menü: **backup → „Backup erstellen / planen"**
```
┌──────────── Backup erstellen / planen ─────────────────┐
│ Manuelle Backups gehen immer nach: │
│ /opt/checkmk-stack/backups (Einstellung BACKUP_DIR)│
│ │
│ Geplante Jobs haben ihr eigenes Zielverzeichnis. │
│ Automatische Ausfuehrung: aus │
│ │
│ manuell Backup jetzt erstellen │
│ jobs Backup-Jobs verwalten │
│ auto Automatische Ausfuehrung ein/aus │
│ status Zeitplan und Jobs anzeigen │
│ jetzt Einen Job sofort ausfuehren │
│ back zurueck │
└────────────────────────────────────────────────────────┘
```
Dieselben Jobs liegen auch in den **Einstellungen → „Backup-Jobs"**.
#### Job anlegen
Im Menü führt ein Dialog durch Sites, Zeitplan (täglich / wöchentlich /
monatlich / freier Cron-Ausdruck), Zielverzeichnis, Aufbewahrung und Optionen.
Dasselbe per Parameter:
```bash ```bash
sudo crontab -e # täglich 02:15, alle Sites, ohne Logs, 14 Backups aufbewahren
sudo ./scripts/cmk-manage.sh job add --name nachts \
--cron "15 2 * * *" --no-logs --keep 14
# sonntags 03:30, nur zwei Sites, auf die NAS, ohne RRDs, 8 aufbewahren
sudo ./scripts/cmk-manage.sh job add --name nas-woche \
--cron "30 3 * * 0" --sites prod,test \
--dir /mnt/nas/checkmk --no-rrds --no-logs --keep 8
``` ```
```cron Alle Kommandos:
15 2 * * * /opt/checkmk-stack/scripts/cmk-manage.sh -y backup --all --no-logs >>/var/log/cmk-backup.log 2>&1
30 3 * * 0 /opt/checkmk-stack/scripts/cmk-manage.sh -y prune --keep 14 >>/var/log/cmk-backup.log 2>&1 | Kommando | Wirkung |
|---|---|
| `job list` | angelegte Jobs anzeigen |
| `job status` | Zeitplan, Cron-Datei und Schalter |
| `job add --name N --cron "…"` | Job anlegen |
| `job set NAME …` | Job ändern (nicht genannte Felder bleiben) |
| `job del NAME` | Job löschen (die Backups bleiben) |
| `job enable\|disable NAME` | Job ein-/ausschalten |
| `job run NAME` | Job sofort ausführen |
| `job run-all` | alle aktiven Jobs ausführen |
| `job auto on\|off` | automatische Ausführung ein/aus |
Optionen von `job add` / `job set`:
| Option | Bedeutung |
|---|---|
| `--sites ALL\|a,b` | zu sichernde Sites (Vorgabe `ALL`) |
| `--cron "15 2 * * *"` | Zeitplan, fünf Cron-Felder |
| `--dir VERZ` | Zielverzeichnis dieses Jobs (Vorgabe `BACKUP_DIR`) |
| `--keep N` | wie viele Backups im Ziel bleiben (`0` = alle) |
| `--no-rrds`, `--no-logs`, `--stop` | wie bei `backup` |
| `--disabled` | anlegen, aber noch nicht einplanen |
#### Automatik ein- und ausschalten
```bash
sudo ./scripts/cmk-manage.sh job auto on # einplanen
sudo ./scripts/cmk-manage.sh job auto off # Zeitplan entfernen
``` ```
`on` schreibt aus allen **aktiven** Jobs die Datei `/etc/cron.d/checkmk-backup`
– je Job eine Zeile mit seinem eigenen Zeitplan:
```cron
15 2 * * * root /opt/checkmk-stack/scripts/cmk-manage.sh -y job run nachts >>/var/log/cmk-backup.log 2>&1
30 3 * * 0 root /opt/checkmk-stack/scripts/cmk-manage.sh -y job run nas-woche >>/var/log/cmk-backup.log 2>&1
```
Dafür werden root-Rechte gebraucht. Ohne root gibt das Kommando die Zeilen
aus, damit man sie selbst übernehmen kann. `off` entfernt die Datei wieder; die
Jobs selbst bleiben erhalten. Der Schalter steht als `BACKUP_AUTO` in der
`.env`.
Nach jedem Lauf räumt der Job sein eigenes Zielverzeichnis auf `--keep` auf –
ein separater `prune`-Eintrag ist nicht nötig.
#### Wo die Jobs liegen
In `backup-jobs.conf` im Projektverzeichnis, eine Zeile je Job:
```
name|sites|cron|verzeichnis|optionen|aufbewahren|aktiv
nachts|ALL|15 2 * * *|/opt/checkmk-stack/backups|--no-logs|14|on
nas-woche|prod,test|30 3 * * 0|/mnt/nas/checkmk|--no-rrds --no-logs|8|on
```
Die Datei ist bewusst lesbar und versionierbar, gehört aber nicht ins
Repository (sie steht in `.gitignore`, weil sie systemspezifische Pfade
enthält).
--- ---
## 16. Restore ## 16. Restore
@@ -1739,6 +1836,8 @@ Checkmk-Werksnotizen (Werks/Release Notes) lesen.
| Proxy-Manager-Oberfläche im Browser leer / nur Textfragmente | Textbrowser ohne JavaScript verwendet. `cmk-manage.sh browser --engine browsh` (bzw. `carbonyl`) nehmen | | Proxy-Manager-Oberfläche im Browser leer / nur Textfragmente | Textbrowser ohne JavaScript verwendet. `cmk-manage.sh browser --engine browsh` (bzw. `carbonyl`) nehmen |
| Selbstsigniertes Zertifikat lässt sich im Terminal-Browser nicht hochladen | Datei-Dialoge gibt es dort nicht – Upload über die API, siehe [Kapitel 11.3](#113-grenze-datei-upload) | | Selbstsigniertes Zertifikat lässt sich im Terminal-Browser nicht hochladen | Datei-Dialoge gibt es dort nicht – Upload über die API, siehe [Kapitel 11.3](#113-grenze-datei-upload) |
| `browser` meldet „braucht ein echtes Terminal" | Aufruf aus Pipe/Skript. Direkt auf der Konsole oder per SSH aufrufen; nur der Bau geht headless mit `--build-only` | | `browser` meldet „braucht ein echtes Terminal" | Aufruf aus Pipe/Skript. Direkt auf der Konsole oder per SSH aufrufen; nur der Bau geht headless mit `--build-only` |
| Menüpunkt bricht mit einer Fehlermeldung in die Shell ab | behoben: fehlende Container, Sites oder Backups werden jetzt als Dialog gemeldet, die Oberfläche bleibt offen |
| `job auto on` meldet fehlende Rechte | `/etc/cron.d/` braucht root: mit `sudo` aufrufen, oder die ausgegebenen Zeilen selbst übernehmen |
| TUI startet nicht | `dialog` installieren: `apt-get install -y dialog` bzw. `dnf install -y dialog` | | TUI startet nicht | `dialog` installieren: `apt-get install -y dialog` bzw. `dnf install -y dialog` |
| Site startet nicht, `tmp`-Fehler | `./scripts/cmk-manage.sh omd config <site> set TMPFS off`, danach Site starten | | Site startet nicht, `tmp`-Fehler | `./scripts/cmk-manage.sh omd config <site> set TMPFS off`, danach Site starten |
+105 -6
View File
@@ -556,7 +556,8 @@ The most important settings:
| `NPM_ENABLED` | `on`/`off` – should the Proxy Manager container run at all? See [12.4](#124-turning-the-proxy-manager-off-entirely-external-nginx) | | `NPM_ENABLED` | `on`/`off` – should the Proxy Manager container run at all? See [12.4](#124-turning-the-proxy-manager-off-entirely-external-nginx) |
| `NPM_ADMIN_PORT` | port of the Proxy Manager interface (default 81) – change only via `npm-port` or the menu | | `NPM_ADMIN_PORT` | port of the Proxy Manager interface (default 81) – change only via `npm-port` or the menu |
| `NPM_ADMIN_URL` | start page of the terminal browser; empty = derived from `NPM_ADMIN_PORT` | | `NPM_ADMIN_URL` | start page of the terminal browser; empty = derived from `NPM_ADMIN_PORT` |
| `BACKUP_DIR` | location for backups/bundles (default `./backups`) | | `BACKUP_DIR` | location for **manual** backups (default `./backups`); scheduled jobs have their own paths |
| `BACKUP_AUTO` | `on`/`off` – should the backup jobs run via cron? |
| `CERT_DOMAIN`, `CERT_DAYS` | defaults for self-signed certificates | | `CERT_DOMAIN`, `CERT_DAYS` | defaults for self-signed certificates |
### 7.1 Who reads `.env` – and why does it exist alongside the script? ### 7.1 Who reads `.env` – and why does it exist alongside the script?
@@ -1331,6 +1332,7 @@ A file chooser with directory navigation is built in.
| `start-all-sites` | start all sites (invoked by systemd) | | `start-all-sites` | start all sites (invoked by systemd) |
| `backup …` | backup as a `.tar.gz` bundle | | `backup …` | backup as a `.tar.gz` bundle |
| `list` / `verify` / `prune --keep N` | manage backups | | `list` / `verify` / `prune --keep N` | manage backups |
| `job list\|add\|set\|del\|run\|auto` | scheduled backups (own target path per job) |
| `restore …` | restore a backup | | `restore …` | restore a backup |
| `import …` | import a migration bundle | | `import …` | import a migration bundle |
| `image status\|list\|pull\|download\|load\|use` | obtain and select the Checkmk image | | `image status\|list\|pull\|download\|load\|use` | obtain and select the Checkmk image |
@@ -1445,17 +1447,112 @@ Listing and verifying:
./scripts/cmk-manage.sh prune --keep 7 ./scripts/cmk-manage.sh prune --keep 7
``` ```
### Nightly backup via cron ### 15.1 Scheduled backups (backup jobs)
Manual backups always go to `BACKUP_DIR` from `.env`. Scheduled jobs, by
contrast, bring **their own target directory** – so you can back up to a NAS
share daily and somewhere else weekly.
In the menu: **backup → "Backup erstellen / planen"** (create / schedule)
```
┌──────────── Backup erstellen / planen ─────────────────┐
│ Manuelle Backups gehen immer nach: │
│ /opt/checkmk-stack/backups (Einstellung BACKUP_DIR)│
│ │
│ Geplante Jobs haben ihr eigenes Zielverzeichnis. │
│ Automatische Ausfuehrung: aus │
│ │
│ manuell Backup jetzt erstellen │
│ jobs Backup-Jobs verwalten │
│ auto Automatische Ausfuehrung ein/aus │
│ status Zeitplan und Jobs anzeigen │
│ jetzt Einen Job sofort ausfuehren │
│ back zurueck │
└────────────────────────────────────────────────────────┘
```
(create now / manage jobs / automatic execution on-off / show schedule / run a
job now / back.) The same jobs are also reachable from
**Einstellungen → "Backup-Jobs"**.
#### Creating a job
In the menu a dialog walks through sites, schedule (daily / weekly / monthly /
free cron expression), target directory, retention and options. The same with
parameters:
```bash ```bash
sudo crontab -e # daily at 02:15, all sites, without logs, keep 14 backups
sudo ./scripts/cmk-manage.sh job add --name nachts \
--cron "15 2 * * *" --no-logs --keep 14
# Sundays at 03:30, two sites only, to the NAS, without RRDs, keep 8
sudo ./scripts/cmk-manage.sh job add --name nas-woche \
--cron "30 3 * * 0" --sites prod,test \
--dir /mnt/nas/checkmk --no-rrds --no-logs --keep 8
``` ```
```cron All commands:
15 2 * * * /opt/checkmk-stack/scripts/cmk-manage.sh -y backup --all --no-logs >>/var/log/cmk-backup.log 2>&1
30 3 * * 0 /opt/checkmk-stack/scripts/cmk-manage.sh -y prune --keep 14 >>/var/log/cmk-backup.log 2>&1 | Command | Effect |
|---|---|
| `job list` | show the configured jobs |
| `job status` | schedule, cron file and switch |
| `job add --name N --cron "…"` | create a job |
| `job set NAME …` | modify a job (fields not given stay as they are) |
| `job del NAME` | delete a job (the backups remain) |
| `job enable\|disable NAME` | turn a job on/off |
| `job run NAME` | run a job right now |
| `job run-all` | run every active job |
| `job auto on\|off` | turn automatic execution on/off |
Options for `job add` / `job set`:
| Option | Meaning |
|---|---|
| `--sites ALL\|a,b` | sites to back up (default `ALL`) |
| `--cron "15 2 * * *"` | schedule, five cron fields |
| `--dir DIR` | this job's target directory (default `BACKUP_DIR`) |
| `--keep N` | how many backups stay in the target (`0` = all) |
| `--no-rrds`, `--no-logs`, `--stop` | as for `backup` |
| `--disabled` | create it but do not schedule it yet |
#### Turning automation on and off
```bash
sudo ./scripts/cmk-manage.sh job auto on # schedule
sudo ./scripts/cmk-manage.sh job auto off # remove the schedule
``` ```
`on` writes `/etc/cron.d/checkmk-backup` from all **active** jobs – one line
per job with its own schedule:
```cron
15 2 * * * root /opt/checkmk-stack/scripts/cmk-manage.sh -y job run nachts >>/var/log/cmk-backup.log 2>&1
30 3 * * 0 root /opt/checkmk-stack/scripts/cmk-manage.sh -y job run nas-woche >>/var/log/cmk-backup.log 2>&1
```
This needs root. Without root the command prints the lines so you can add them
yourself. `off` removes the file again; the jobs themselves are kept. The
switch is stored as `BACKUP_AUTO` in `.env`.
After each run a job prunes its own target directory down to `--keep` – a
separate `prune` entry is not needed.
#### Where the jobs live
In `backup-jobs.conf` in the project directory, one line per job:
```
name|sites|cron|directory|options|keep|enabled
nachts|ALL|15 2 * * *|/opt/checkmk-stack/backups|--no-logs|14|on
nas-woche|prod,test|30 3 * * 0|/mnt/nas/checkmk|--no-rrds --no-logs|8|on
```
The file is deliberately readable and diffable, but does not belong in the
repository (it is in `.gitignore` because it contains system-specific paths).
--- ---
## 16. Restore ## 16. Restore
@@ -1749,6 +1846,8 @@ release notes beforehand.
| Proxy Manager interface blank / only text fragments | a text browser without JavaScript was used. Use `cmk-manage.sh browser --engine browsh` (or `carbonyl`) | | Proxy Manager interface blank / only text fragments | a text browser without JavaScript was used. Use `cmk-manage.sh browser --engine browsh` (or `carbonyl`) |
| Self-signed certificate cannot be uploaded in the terminal browser | there are no file dialogs there – upload via the API, see [chapter 11.3](#113-limitation-file-upload) | | Self-signed certificate cannot be uploaded in the terminal browser | there are no file dialogs there – upload via the API, see [chapter 11.3](#113-limitation-file-upload) |
| `browser` reports "braucht ein echtes Terminal" (needs a real terminal) | called from a pipe/script. Call it on the console or over SSH; only the build runs headless, with `--build-only` | | `browser` reports "braucht ein echtes Terminal" (needs a real terminal) | called from a pipe/script. Call it on the console or over SSH; only the build runs headless, with `--build-only` |
| A menu entry drops out to the shell with an error | fixed: missing containers, sites or backups are now reported in a dialog and the interface stays open |
| `job auto on` reports missing permissions | `/etc/cron.d/` needs root: call it with `sudo`, or add the printed lines yourself |
| The TUI does not start | install `dialog`: `apt-get install -y dialog` or `dnf install -y dialog` | | The TUI does not start | install `dialog`: `apt-get install -y dialog` or `dnf install -y dialog` |
| Site does not start, `tmp` errors | `./scripts/cmk-manage.sh omd config <site> set TMPFS off`, then start the site | | Site does not start, `tmp` errors | `./scripts/cmk-manage.sh omd config <site> set TMPFS off`, then start the site |
+686 -72
View File
@@ -89,6 +89,21 @@ KOMMANDOS - Backup / Restore / Migration
prune --keep N nur die N neuesten Backups behalten 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 KOMMANDOS - Checkmk-Image beschaffen
image status konfiguriertes und lokal vorhandene Images anzeigen image status konfiguriertes und lokal vorhandene Images anzeigen
image list lokal vorhandene Checkmk-Images image list lokal vorhandene Checkmk-Images
@@ -1217,6 +1232,334 @@ EOF
LAST_BACKUP_FILE="$outfile" 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 # 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|CMK_DATA_DIR|path|Checkmk-Sites (Bind-Mount)||
paths|NPM_DATA_DIR|path|Proxy-Manager-Daten|| paths|NPM_DATA_DIR|path|Proxy-Manager-Daten||
paths|NPM_LE_DIR|path|Let's-Encrypt-Zertifikate|| 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|| paths|IMAGE_DIR|path|Heruntergeladene Image-Tarballs||
cert|CERT_DOMAIN|text|FQDN fuer selbstsignierte Zertifikate|| cert|CERT_DOMAIN|text|FQDN fuer selbstsignierte Zertifikate||
cert|CERT_DAYS|int|Gueltigkeit in Tagen|| cert|CERT_DAYS|int|Gueltigkeit in Tagen||
@@ -1825,6 +2169,7 @@ npm|NGINX Proxy Manager
browser|Terminal-Browser browser|Terminal-Browser
paths|Verzeichnisse paths|Verzeichnisse
cert|Selbstsignierte Zertifikate cert|Selbstsignierte Zertifikate
backup|Backup-Automatik
EOF EOF
} }
@@ -1893,6 +2238,8 @@ settings_post_set() {
NPM_ENABLED) NPM_ENABLED)
# Container entsprechend entfernen bzw. starten # Container entsprechend entfernen bzw. starten
cmd_npm_enable "$val" ;; 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) 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)" ;; log "Wirksam nach: $SELF up (Container werden neu erstellt)" ;;
esac esac
@@ -1934,6 +2281,8 @@ cmd_config() {
cmd_npm_port "$val" cmd_npm_port "$val"
elif [ "$key" = NPM_ENABLED ]; then elif [ "$key" = NPM_ENABLED ]; then
cmd_npm_enable "$val" cmd_npm_enable "$val"
elif [ "$key" = BACKUP_AUTO ]; then
env_set "$key" "$val"; BACKUP_AUTO="$val"; cmd_job auto "$val"
else else
env_set "$key" "$val" "$opt" env_set "$key" "$val" "$opt"
ok "$key=$val" ok "$key=$val"
@@ -2157,7 +2506,61 @@ EOF
# ===================================================================== # =====================================================================
# ncurses-Oberflaeche # 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_pick_site() { # -> Site auf stdout
gui_need_container || return 1
local -a items=() s local -a items=() s
while IFS= read -r s; do while IFS= read -r s; do
[ -n "$s" ] || continue [ -n "$s" ] || continue
@@ -2220,7 +2623,7 @@ gui_stack_menu() {
} }
gui_backup() { gui_backup() {
require_cmk_running gui_need_sites || return 0
local -a items=() s local -a items=() s
while IFS= read -r s; do while IFS= read -r s; do
[ -n "$s" ] || continue [ -n "$s" ] || continue
@@ -2229,14 +2632,14 @@ gui_backup() {
[ ${#items[@]} -gt 0 ] || { ui_msg "Backup" "Keine Sites vorhanden."; return; } [ ${#items[@]} -gt 0 ] || { ui_msg "Backup" "Keine Sites vorhanden."; return; }
local chosen 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; } [ -n "$chosen" ] || { ui_msg "Backup" "Keine Site ausgewaehlt."; return; }
local optsel local optsel
optsel="$(ui_checklist "Backup-Optionen" "Optionen waehlen:" \ optsel="$(ui_checklist "Backup-Optionen" "Optionen waehlen:" \
no-rrds "Performance-Daten (RRD) auslassen - kleineres Archiv" off \ no-rrds "Performance-Daten (RRD) auslassen - kleineres Archiv" off \
no-logs "Logdateien auslassen" 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 local label
label="$(ui_input "Kennzeichnung" "Optionaler Text fuer den Dateinamen (leer = keiner):" "")" || 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[*]}" 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() { gui_restore() {
require_cmk_running gui_need_container || return 0
local file; file="$(gui_pick_backup)" || return local file; file="$(gui_pick_backup)" || return 0
[ -n "$file" ] || return [ -n "$file" ] || return 0
local f; f="$(mktemp)" local f; f="$(mktemp)"
bundle_show "$file" >"$f" 2>&1 || true 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; } bundle_read "$file" || { ui_msg "Fehler" "Diese Datei ist kein Checkmk-Backup."; return; }
local reuse="" local reuse=""
if cmk_site_exists "$B_SITE"; then 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" reuse="--reuse --kill"
fi 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" ui_run "Restore laeuft" bash -c "cd '$PROJECT_DIR' && '$SELF' -y restore --file '$file' $reuse"
} }
gui_import() { gui_import() {
require_cmk_running gui_need_container || return 0
ui_msg "Site importieren" \ ui_msg "Site importieren" \
"Import einer Site von einer Direktinstallation (Bare-Metal/VM). "Import einer Site von einer Direktinstallation (Bare-Metal/VM).
@@ -2288,8 +2898,8 @@ Wichtig fuer die Agents:
- Port 8000 (Agent-Receiver) ist erreichbar - Port 8000 (Agent-Receiver) ist erreichbar
Dann muessen die Agents NICHT neu eingerichtet werden." Dann muessen die Agents NICHT neu eingerichtet werden."
local file; file="$(gui_pick_backup)" || return local file; file="$(gui_pick_backup)" || return 0
[ -n "$file" ] || return [ -n "$file" ] || return 0
local f; f="$(mktemp)" local f; f="$(mktemp)"
bundle_show "$file" >"$f" 2>&1 || true 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; } bundle_read "$file" || { ui_msg "Fehler" "Diese Datei ist kein Migrations-Bundle."; return; }
local reuse="" local reuse=""
if cmk_site_exists "$B_SITE"; then 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" reuse="--reuse --kill"
fi fi
local setdef="" local setdef=""
@@ -2334,7 +2944,7 @@ gui_backups_menu() {
} }
gui_sites_menu() { gui_sites_menu() {
require_cmk_running gui_need_container || return 0
while :; do while :; do
local sel local sel
sel="$(ui_menu "Sites" "Sites im Container '$CMK_CONTAINER_NAME'" \ sel="$(ui_menu "Sites" "Sites im Container '$CMK_CONTAINER_NAME'" \
@@ -2371,14 +2981,14 @@ Edition/Version: ${CMK_EDITION:-raw} / ${CMK_VERSION:-?}" \
case "$sel" in case "$sel" in
status) local f; f="$(mktemp)"; NO_COLOR=1 cmd_image status >"$f" 2>&1 || true status) local f; f="$(mktemp)"; NO_COLOR=1 cmd_image status >"$f" 2>&1 || true
ui_text "Image-Status" "$f"; rm -f "$f" ;; ui_text "Image-Status" "$f"; rm -f "$f" ;;
test) gui_image_test ;; test) gui_image_test || true ;;
pull) gui_image_pull ;; pull) gui_image_pull || true ;;
download) gui_image_download ;; download) gui_image_download || true ;;
load) local lf; lf="$(ui_pick_file "${IMAGE_DIR:-$PROJECT_DIR/images}" '*.tar.gz')" || continue load) local lf; lf="$(ui_pick_file "${IMAGE_DIR:-$PROJECT_DIR/images}" '*.tar.gz')" || continue
[ -n "$lf" ] || continue [ -n "$lf" ] || continue
ui_run "Image einspielen" bash -c "cd '$PROJECT_DIR' && '$SELF' -y image load --file '$lf'" ui_run "Image einspielen" bash -c "cd '$PROJECT_DIR' && '$SELF' -y image load --file '$lf'"
load_env "${ENV_FILE:-$PROJECT_DIR/.env}" ;; load_env "${ENV_FILE:-$PROJECT_DIR/.env}" ;;
use) gui_image_use ;; use) gui_image_use || true ;;
back) return 0 ;; back) return 0 ;;
esac esac
done done
@@ -2398,14 +3008,14 @@ gui_image_pick_edition() {
gui_image_test() { gui_image_test() {
local ed ver user pass local ed ver user pass
ed="$(gui_image_pick_edition)" || return ed="$(gui_image_pick_edition)" || return 0
ver="$(ui_input "Version" "Version (fuer die Pruefung des Downloadportals):" "${CMK_VERSION:-}")" || return ver="$(ui_input "Version" "Version (fuer die Pruefung des Downloadportals):" "${CMK_VERSION:-}")" || return 0
local -a extra=() 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 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 else
user="$(ui_input "Zugangsdaten" "Benutzer (Checkmk-Kundenportal):" "${CMK_REGISTRY_USER:-}")" || return user="$(ui_input "Zugangsdaten" "Benutzer (Checkmk-Kundenportal):" "${CMK_REGISTRY_USER:-}")" || return 0
pass="$(ui_password "Zugangsdaten" "Kennwort:")" || return pass="$(ui_password "Zugangsdaten" "Kennwort:")" || return 0
[ -n "$user" ] && extra+=(--user "$user" --password "$pass") [ -n "$user" ] && extra+=(--user "$user" --password "$pass")
fi fi
ui_run "Zugangsdaten pruefen" bash -c \ ui_run "Zugangsdaten pruefen" bash -c \
@@ -2414,13 +3024,13 @@ gui_image_test() {
gui_image_pull() { gui_image_pull() {
local ed ver user pass local ed ver user pass
ed="$(gui_image_pick_edition)" || 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 ver="$(ui_input "Version" "Version bzw. Tag (z.B. 2.3.0p23 oder 2.3.0-latest):" "${CMK_VERSION:-}")" || return 0
[ -n "$ver" ] || return [ -n "$ver" ] || return 0
local -a extra=() local -a extra=()
if [ "$ed" = enterprise ] || [ -n "${CMK_REGISTRY_USER:-}" ]; then if [ "$ed" = enterprise ] || [ -n "${CMK_REGISTRY_USER:-}" ]; then
user="$(ui_input "Registry-Anmeldung" "Benutzer (Checkmk-Kundenportal):" "${CMK_REGISTRY_USER:-}")" || return user="$(ui_input "Registry-Anmeldung" "Benutzer (Checkmk-Kundenportal):" "${CMK_REGISTRY_USER:-}")" || return 0
pass="$(ui_password "Registry-Anmeldung" "Kennwort:")" || return pass="$(ui_password "Registry-Anmeldung" "Kennwort:")" || return 0
[ -n "$user" ] && extra+=(--user "$user" --password "$pass") [ -n "$user" ] && extra+=(--user "$user" --password "$pass")
fi fi
ui_run "Image holen" bash -c "cd '$PROJECT_DIR' && '$SELF' -y image pull --edition '$ed' --version '$ver' ${extra[*]+${extra[*]}}" 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() { gui_image_download() {
local ed ver user pass local ed ver user pass
ed="$(gui_image_pick_edition)" || return ed="$(gui_image_pick_edition)" || return 0
ver="$(ui_input "Version" "EXAKTE Version (z.B. 2.3.0p23 - kein '-latest'):" "${CMK_VERSION:-}")" || return ver="$(ui_input "Version" "EXAKTE Version (z.B. 2.3.0p23 - kein '-latest'):" "${CMK_VERSION:-}")" || return 0
[ -n "$ver" ] || return [ -n "$ver" ] || return 0
user="$(ui_input "Downloadportal" "Benutzer:" "${CMK_DOWNLOAD_USER:-}")" || return user="$(ui_input "Downloadportal" "Benutzer:" "${CMK_DOWNLOAD_USER:-}")" || return 0
pass="$(ui_password "Downloadportal" "Kennwort:")" || return 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'" 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}" load_env "${ENV_FILE:-$PROJECT_DIR/.env}"
} }
@@ -2446,7 +3056,7 @@ gui_image_use() {
done < <(image_local_list) 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; } [ ${#items[@]} -gt 0 ] || { ui_msg "Images" "Es ist kein Checkmk-Image lokal vorhanden.\n\nZuerst 'pull', 'download' oder 'load' verwenden."; return; }
local sel 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'" ui_run "Image setzen" bash -c "cd '$PROJECT_DIR' && '$SELF' -y image use '$sel'"
load_env "${ENV_FILE:-$PROJECT_DIR/.env}" load_env "${ENV_FILE:-$PROJECT_DIR/.env}"
} }
@@ -2485,15 +3095,15 @@ gui_settings_edit() {
IFS=':' IFS=':'
done done
IFS="$IFS_BAK" 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="" [ "$new" = auto ] && new=""
;; ;;
password) 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 [ -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 esac
@@ -2541,6 +3151,7 @@ gui_settings_menu() {
while IFS='|' read -r c l; do while IFS='|' read -r c l; do
[ -n "$c" ] && items+=("$c" "$l") [ -n "$c" ] && items+=("$c" "$l")
done < <(settings_categories) done < <(settings_categories)
items+=("jobs" "Backup-Jobs (Zeitplan, Zielpfade, Aufbewahrung)")
items+=("show" "Alle Werte auflisten") items+=("show" "Alle Werte auflisten")
items+=("editor" "Datei .env im Editor oeffnen") items+=("editor" "Datei .env im Editor oeffnen")
items+=("back" "zurueck") items+=("back" "zurueck")
@@ -2550,12 +3161,14 @@ gui_settings_menu() {
Datei: ${ENV_FILE:-$PROJECT_DIR/.env}" "${items[@]}")" || return 0 Datei: ${ENV_FILE:-$PROJECT_DIR/.env}" "${items[@]}")" || return 0
case "$sel" in case "$sel" in
back) return 0 ;; back) return 0 ;;
jobs) gui_jobs_menu || true ;;
show) local f; f="$(mktemp)" show) local f; f="$(mktemp)"
{ printf '%-24s %-10s %s\n' "SCHLUESSEL" "BEREICH" "WERT" { printf '%-24s %-10s %s\n' "SCHLUESSEL" "BEREICH" "WERT"
NO_COLOR=1 cmd_config list; } >"$f" 2>&1 NO_COLOR=1 cmd_config list; } >"$f" 2>&1
ui_text "Alle Einstellungen" "$f"; rm -f "$f" ;; ui_text "Alle Einstellungen" "$f"; rm -f "$f" ;;
editor) local ef="${ENV_FILE:-$PROJECT_DIR/.env}" 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." ;; load_env "$ef"; ui_msg "Konfiguration" "Die .env wurde neu eingelesen." ;;
*) local title *) local title
title="$(settings_categories | awk -F'|' -v c="$sel" '$1==c{print $2}')" 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" ;; [ -n "$u" ] && NPM_ADMIN_URL="$u" ;;
build) ui_run "Werkzeug-Image bauen" bash -c "cd '$PROJECT_DIR' && '$SELF' browser --build-only" || true ;; build) ui_run "Werkzeug-Image bauen" bash -c "cd '$PROJECT_DIR' && '$SELF' browser --build-only" || true ;;
back) return 0 ;; back) return 0 ;;
*) clear *) gui_run_interactive cmd_browser --engine "$sel" \
cmd_browser --engine "$sel" --url "${NPM_ADMIN_URL:-http://127.0.0.1:81}" || true --url "${NPM_ADMIN_URL:-http://127.0.0.1:81}" ;;
;;
esac esac
done done
} }
gui_cert() { gui_cert() {
local domain alt days local domain alt days
domain="$(ui_input "Zertifikat" "FQDN / Common Name:" "${CERT_DOMAIN:-checkmk.example.local}")" || return domain="$(ui_input "Zertifikat" "FQDN / Common Name:" "${CERT_DOMAIN:-checkmk.example.local}")" || return 0
[ -n "$domain" ] || return [ -n "$domain" ] || return 0
alt="$(ui_input "Zertifikat" "Weitere Namen/IPs (durch Leerzeichen getrennt, optional):" "$(primary_ip)")" || alt="" 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 days="$(ui_input "Zertifikat" "Gueltigkeit in Tagen:" "${CERT_DAYS:-3650}")" || days=3650
local -a a=(); local x local -a a=(); local x
@@ -2616,7 +3228,7 @@ Runtime : $RUNTIME Standard-Site: $CMK_SITE_ID
Backups : $BACKUP_DIR" \ Backups : $BACKUP_DIR" \
status "Status von Containern und Sites" \ status "Status von Containern und Sites" \
stack "Stack steuern (Start/Stop/Logs/Update)" \ stack "Stack steuern (Start/Stop/Logs/Update)" \
backup "Backup erstellen" \ backup "Backup erstellen / planen" \
restore "Backup wiederherstellen" \ restore "Backup wiederherstellen" \
import "Site von Direktinstallation importieren" \ import "Site von Direktinstallation importieren" \
manage "Backups verwalten (Liste/Details/Loeschen)" \ manage "Backups verwalten (Liste/Details/Loeschen)" \
@@ -2628,22 +3240,23 @@ Backups : $BACKUP_DIR" \
shell "Root-Shell im Checkmk-Container" \ shell "Root-Shell im Checkmk-Container" \
quit "Beenden")" || break quit "Beenden")" || break
case "$sel" in case "$sel" in
status) gui_status ;; status) gui_status || true ;;
stack) gui_stack_menu ;; stack) gui_stack_menu || true ;;
backup) gui_backup ;; backup) gui_backup_menu || true ;;
restore) gui_restore ;; restore) gui_restore || true ;;
import) gui_import ;; import) gui_import || true ;;
manage) gui_backups_menu ;; manage) gui_backups_menu || true ;;
sites) gui_sites_menu ;; sites) gui_sites_menu || true ;;
image) gui_image_menu ;; image) gui_image_menu || true ;;
browser) gui_browser_menu ;; browser) gui_browser_menu || true ;;
cert) gui_cert ;; cert) gui_cert || true ;;
config) gui_settings_menu ;; config) gui_settings_menu || true ;;
shell) clear; cmd_shell || true ;; shell) gui_need_container || continue
gui_run_interactive cmd_shell ;;
quit) break ;; quit) break ;;
esac esac
done done
clear clear 2>/dev/null || true
ok "Beendet." ok "Beendet."
} }
@@ -2670,29 +3283,30 @@ main() {
gui|menu) gui_main ;; gui|menu) gui_main ;;
up|start) cmd_up ;; up|start) cmd_up ;;
down|stop) cmd_down ;; down|stop) cmd_down ;;
restart) cmd_restart ;; restart) cmd_restart || true ;;
pull) cmd_pull ;; pull) cmd_pull || true ;;
status) cmd_status ;; status) cmd_status || true ;;
logs) cmd_logs "$@" ;; logs) cmd_logs "$@" || true ;;
shell) cmd_shell ;; shell) cmd_shell || true ;;
omd) cmd_omd "$@" ;; omd) cmd_omd "$@" || true ;;
sites) cmd_sites ;; sites) cmd_sites || true ;;
site-start) cmd_site_start "$@" ;; site-start) cmd_site_start "$@" ;;
site-stop) cmd_site_stop "$@" ;; site-stop) cmd_site_stop "$@" ;;
start-all-sites) cmd_start_all_sites ;; start-all-sites) cmd_start_all_sites ;;
backup) cmd_backup "$@" ;; backup) cmd_backup "$@" || true ;;
list) cmd_list ;; list) cmd_list || true ;;
prune) cmd_prune "$@" ;; prune) cmd_prune "$@" || true ;;
job|jobs) cmd_job "$@" ;;
verify) local vf=""; [ "${1:-}" = "--file" ] && vf="$2"; [ -n "$vf" ] || vf="${1:-}" verify) local vf=""; [ "${1:-}" = "--file" ] && vf="$2"; [ -n "$vf" ] || vf="${1:-}"
[ -n "$vf" ] || die "--file fehlt."; bundle_verify "$vf" ;; [ -n "$vf" ] || die "--file fehlt."; bundle_verify "$vf" ;;
restore) cmd_restore "$@" ;; restore) cmd_restore "$@" || true ;;
import) cmd_import "$@" ;; import) cmd_import "$@" || true ;;
cert) cmd_cert "$@" ;; cert) cmd_cert "$@" || true ;;
image) cmd_image "$@" ;; image) cmd_image "$@" || true ;;
browser) cmd_browser "$@" ;; browser) cmd_browser "$@" || true ;;
npm-port) cmd_npm_port "$@" ;; npm-port) cmd_npm_port "$@" ;;
npm-enable) cmd_npm_enable "$@" ;; npm-enable) cmd_npm_enable "$@" ;;
config) cmd_config "$@" ;; config) cmd_config "$@" || true ;;
help|-h|--help) usage ;; help|-h|--help) usage ;;
*) err "Unbekanntes Kommando: $cmd"; echo; usage; exit 1 ;; *) err "Unbekanntes Kommando: $cmd"; echo; usage; exit 1 ;;
esac esac
+1
View File
@@ -44,6 +44,7 @@ load_env() {
CMK_SITE_ID="${CMK_SITE_ID:-cmk}" CMK_SITE_ID="${CMK_SITE_ID:-cmk}"
CMK_IMAGE="${CMK_IMAGE:-docker.io/checkmk/check-mk-raw:2.3.0-latest}" CMK_IMAGE="${CMK_IMAGE:-docker.io/checkmk/check-mk-raw:2.3.0-latest}"
NPM_ENABLED="${NPM_ENABLED:-on}" NPM_ENABLED="${NPM_ENABLED:-on}"
BACKUP_AUTO="${BACKUP_AUTO:-off}"
NPM_ADMIN_PORT="${NPM_ADMIN_PORT:-81}" NPM_ADMIN_PORT="${NPM_ADMIN_PORT:-81}"
# Leer = automatisch aus dem Admin-Port ableiten # Leer = automatisch aus dem Admin-Port ableiten
NPM_ADMIN_URL="${NPM_ADMIN_URL:-http://127.0.0.1:$NPM_ADMIN_PORT}" NPM_ADMIN_URL="${NPM_ADMIN_URL:-http://127.0.0.1:$NPM_ADMIN_PORT}"