Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d050cbedf | ||
|
|
14019487b3 | ||
|
|
0141cdc4c7 | ||
|
|
cbef884db8 | ||
|
|
d83c3113f8 | ||
|
|
1619f22f18 |
No files matched your search
@@ -0,0 +1,195 @@
|
|||||||
|
# v3 – Dev Log
|
||||||
|
|
||||||
|
## 2026-06-19 00:56 – 00:58 (1m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** miyagi-backup
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- config.example | 2 +-
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-06-19 01:01 – 01:05 (4m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- config.example | 2 +-
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-06-19 01:06 – 01:07 (0m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- config.example | 2 +-
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-06-19 01:26 – 01:28 (2m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- config.example | 2 +-
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-06-19 01:32 – 01:33 (0m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- config.example | 2 +-
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-06-19 01:40 – 01:42 (2m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- config.example | 2 +-
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-06-19 01:44 – 01:47 (2m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- config.example | 2 +-
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-06-19 01:48 – 01:48 (0m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- config.example | 2 +-
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-06-19 01:49 – 02:08 (18m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- config.example | 2 +-
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-06-19 02:08 – 02:08 (0m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- config.example | 2 +-
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-06-19 02:09 – 02:10 (0m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- config.example | 2 +-
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-06-19 02:11 – 02:15 (3m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- config.example | 2 +-
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-06-19 02:16 – 02:16 (0m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- config.example | 2 +-
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-07-07 – Pre-Shutdown-Hooks + Installpfade
|
||||||
|
|
||||||
|
**Feature:** `run_pre_shutdown_hooks()` eingebaut, aufgerufen in `shutdown_now()` nach Piggyback/CheckZFS, vor 60s-Sleep.
|
||||||
|
|
||||||
|
**Hook-Verzeichnis:** `/etc/miyagi-backup/hooks.d/pre-shutdown/`
|
||||||
|
- Verzeichnis fehlt oder leer → still übersprungen
|
||||||
|
- Nur `*.sh` mit `chmod +x` werden ausgeführt
|
||||||
|
- Alphabetisch sortiert
|
||||||
|
- Hook-Fehler → WARNUNG im Log, Shutdown läuft weiter
|
||||||
|
|
||||||
|
**Installpfade festgelegt:**
|
||||||
|
- Skripte: `/opt/miyagi-backup/`
|
||||||
|
- Konfigs: `/etc/miyagi-backup/`
|
||||||
|
- Hooks: `/etc/miyagi-backup/hooks.d/pre-shutdown/`
|
||||||
|
- Symlink: `/usr/local/bin/miyagi-backup` → `/opt/miyagi-backup/miyagi-backup.sh`
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-07-16 14:13 – 14:13 (0m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- miyagi-check.sh | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-07-16 14:16 – 14:48 (32m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- miyagi-check.sh | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-07-16 14:49 – 14:51 (1m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** v3
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- miyagi-check.sh | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
|
||||||
|
|
||||||
|
---
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Beispielhafte Aufrufe
|
|
||||||
|
|
||||||
Befehl Wirkung
|
|
||||||
```
|
|
||||||
./miyagi-backup.sh -c /etc/miyagi.conf Führt den vollständigen Backup-Prozess aus
|
|
||||||
./miyagi-backup.sh help Listet alle verfügbaren Funktionen
|
|
||||||
./miyagi-backup.sh run_updates Führt nur Updates lokal aus
|
|
||||||
./miyagi-backup.sh run_pbs_backup Führt nur das vzdump-PBS-Backup aus
|
|
||||||
./miyagi-backup.sh run_maintenance Nur Wartung (Prune + GC)
|
|
||||||
./miyagi-backup.sh shutdown_if_requested Prüft ob Shutdown nötig ist
|
|
||||||
```
|
|
||||||
|
|
||||||
####
|
|
||||||
|
|
||||||
# miyagi-check.sh
|
|
||||||
|
|
||||||
Dieses Skript überprüft eine Bash-basierte Konfigurationsdatei für ein Backup-System:
|
|
||||||
|
|
||||||
## Funktionen:
|
|
||||||
- Validiert alle benötigten Variablen
|
|
||||||
- Testet SSH-Verbindungen zu `SOURCEHOST` und optional `PBSHOST`
|
|
||||||
- Prüft und installiert bei Bedarf den lokalen SSH-Key (`~/.ssh/id_rsa.pub`)
|
|
||||||
- Erkennt unsichere `PermitRootLogin yes` Einstellungen
|
|
||||||
- Bietet automatische Umstellung auf `prohibit-password` an
|
|
||||||
|
|
||||||
## Nutzung:
|
|
||||||
```bash
|
|
||||||
./miyagi-check.sh /pfad/zur/config
|
|
||||||
```
|
|
||||||
+19
-15
@@ -2,46 +2,50 @@
|
|||||||
|
|
||||||
UPDATES='yes' # Proxmox VE und PBS Updates nach dem Lauf
|
UPDATES='yes' # Proxmox VE und PBS Updates nach dem Lauf
|
||||||
SHUTDOWN='no' # System nach Ausführung herunterfahren?
|
SHUTDOWN='no' # System nach Ausführung herunterfahren?
|
||||||
DYNROUTE='no' #Dynroute setzen?
|
DYNROUTE='yes' # DYNroute ja oder nein?
|
||||||
DDNS_GATEWAY='192.168.66.1' # Gateway
|
DDNS_GATEWAY='192.168.66.1' # DYNDNS Gateway
|
||||||
DDNS_SERVER='1.1.1.1' # Externer DNS Server
|
DDNS_SERVER='1.1.1.1' #DNS Server extern
|
||||||
REBOOT='no' # Reboot nach Update?
|
REBOOT='no' # Reboot nach Update?
|
||||||
|
|
||||||
# Quelle (Proxmox VE System, das gesichert wird)
|
# Quelle (Proxmox VE System, das gesichert wird)
|
||||||
SOURCEPORT='22' # SSH-Port, normalerweise 22
|
SOURCEPORT='22' # SSH-Port, normalerweise 22
|
||||||
SOURCEHOST='192.168.50.200' # IP des Quell-Proxmox-Servers
|
SOURCEHOST='192.168.50.200' # IP des Quell-Proxmox-Servers
|
||||||
|
SOURCEUSER='root' # SSH-User für SOURCEHOST
|
||||||
|
|
||||||
# Replikation (ZFS)
|
# Replikation (ZFS)
|
||||||
ZFSROOT='rpool/data' # Erstes Dataset vom Quellsystem
|
ZFSTRGT='rpool-ssd1/repl/pve200' # Zielpfad für Replikation
|
||||||
ZFSSECOND='rpool-hdd/data' # Optional zweites Dataset
|
|
||||||
ZFSTRGT='rpool/repl/pve200' # Zielpfad für Replikation
|
|
||||||
|
|
||||||
# ZFS Zsync Replikation
|
# ZFS Zsync Replikation
|
||||||
ZSYNC='yes' # ZSYNC aktivieren (ja/nein)
|
ZSYNC='yes' # ZSYNC aktivieren (ja/nein)
|
||||||
ZPUSHTAG='bashclub:zsync' # Benutzer-Tag für ZFS
|
ZPUSHTAG='bashclub:zsync-198-ssd' # Benutzer-Tag für ZFS
|
||||||
ZPUSHMINKEEP='3' # Mindestens zu behaltende Snapshots
|
ZPUSHMINKEEP='3' # Mindestens zu behaltende Snapshots
|
||||||
ZPUSHKEEP='14' # Snapshots mit dem Tag, die behalten werden
|
ZPUSHKEEP='14' # Snapshots mit dem Tag, die behalten werden
|
||||||
ZPUSHLABEL='zsync' # Suffix für Snapshot-Autoengine
|
ZPUSHLABEL='zsync-rz' # Suffix für Snapshot-Autoengine
|
||||||
ZPUSHFILTER='hourly|daily|weekly|monthly' # Weitere Filter (leer lassen oder Muster wie daily| weekly etc.)
|
ZPUSHFILTER='hourly|daily|weekly|monthly' # Weitere Filter (leer lassen oder Muster wie daily| weekly etc.)
|
||||||
|
|
||||||
# Backup mit Proxmox Backup Server
|
# Backup mit Proxmox Backup Server
|
||||||
BACKUPSERVER='no' # Backup via PBS aktivieren?
|
BACKUPSERVER='no' # Backup via PBS aktivieren?
|
||||||
MAINTDAY='7' # Wartungstag (1=Mo, 7=So)
|
MAINTDAY='7' # Wartungstag (1=Mo, 7=So)
|
||||||
PBSHOST='192.168.50.199' # IP des Proxmox Backup Servers
|
PBSHOST='192.168.50.199' # IP des Proxmox Backup Servers
|
||||||
PBSPORT='22'
|
PBSPORT='22' # SSH-Port PBS
|
||||||
|
PBSUSER='root' # SSH-User für PBSHOST
|
||||||
|
PBSBACKUP_STATUS='86400' # Warnung ab 24h (86400s)
|
||||||
|
PBS_REPO_USER='root@pam' # PBS-User oder API-Token-ID (z.B. 'user@pbs!tokenname') für write_pbs_status
|
||||||
|
PBS_PASSWORD='' # Passwort oder API-Token-Secret zu PBS_REPO_USER (write_pbs_status)
|
||||||
BACKUPSTORE='backup' # Datastore auf Quell-Proxmox
|
BACKUPSTORE='backup' # Datastore auf Quell-Proxmox
|
||||||
BACKUPSTOREPBS='backup' # Datastore auf PBS
|
BACKUPSTOREPBS='backup' # Datastore auf PBS
|
||||||
PBS_NAMESPACE='none' # Namespace auf PBS, setzen wenn verfügbar sonst none!
|
BACKUPEXCLUDE='10,3252,3253,3254' # VM/CT-IDs, die vom Backup ausgeschlossen sind
|
||||||
BACKUPEXCLUDE='9999,99988' # VM/CT-IDs, die vom Backup ausgeschlossen sind
|
REPLEXCLUDE="$BACKUPEXCLUDE" # Diese auch von Replikation ausschließen
|
||||||
REPLEXCLUDE=$BACKUPEXCLUDE # Diese auch von Replikation ausschließen
|
|
||||||
|
|
||||||
# Zusätzliche Monitoring-Ziele
|
# Zusätzliche Monitoring-Ziele
|
||||||
# External Piggyback Host
|
# External Piggyback Host
|
||||||
EPIGGYBACK='no' # Piggyback-Daten an Monitoring-Ziel senden?
|
EPIGGYBACK='no' # Piggyback-Daten an Monitoring-Ziel senden?
|
||||||
EPIGGYBACK_PORT='222' # SSH-Port für EPIGGYBACK_HOST
|
EPIGGYBACK_PORT='222' # SSH-Port für EPIGGYBACK_HOST
|
||||||
EPIGGYBACK_HOST='192.168.66.10' # Monitoring-Zielhost für Piggyback
|
EPIGGYBACK_HOST='192.168.66.10' # Monitoring-Zielhost für Piggyback
|
||||||
|
EPIGGYBACK_USER='root' # SSH-User für EPIGGYBACK_HOST
|
||||||
|
|
||||||
# External Checkzfs Host
|
# External Checkzfs Host
|
||||||
ECHECKZFS='no' # check_zfs-Output an Monitoring-Ziel senden?
|
ECHECKZFS='no' # CheckZFS-Daten an externen Monitoring-Host senden?
|
||||||
ECHECKZFS_PORT='2222' # SSH-Port für ECHECKZFS_HOST
|
ECHECKZFS_PORT='2222' # SSH-Port für ECHECKZFS_HOST
|
||||||
ECHECKZFS_HOST='192.168.50.210' # Monitoring-Zielhost für check_zfs
|
ECHECKZFS_HOST='192.168.50.210' # Monitoring-Zielhost für check_zfs
|
||||||
|
ECHECKZFS_USER='root' # SSH-User für ECHECKZFS_HOST
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Beispiel Pre-Shutdown-Hook für miyagi-backup
|
||||||
|
#
|
||||||
|
# Ablage: /etc/miyagi-backup/hooks.d/pre-shutdown/
|
||||||
|
# Ausführung: chmod +x <hookname>.sh
|
||||||
|
# Reihenfolge: alphabetisch (01-foo.sh, 02-bar.sh, ...)
|
||||||
|
# Fehlerverhalten: Exit != 0 erzeugt WARNUNG im Log, Shutdown läuft weiter
|
||||||
|
#
|
||||||
|
# Dieser Hook wird vor dem 60s-Countdown in shutdown_now() ausgeführt.
|
||||||
|
# Hinweis: eigene sleep-Aufrufe addieren sich zum 60s-Countdown dazu.
|
||||||
|
|
||||||
|
# ── Beispiel 1: Monitoring-Agent vor Shutdown reporten ───────────────────────
|
||||||
|
#
|
||||||
|
# if [ -x /usr/local/bin/patchmon-agent ]; then
|
||||||
|
# /usr/local/bin/patchmon-agent report
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# ── Beispiel 2: CheckMK-Agent aktualisieren ───────────────────────────────────
|
||||||
|
#
|
||||||
|
# if [ -x /usr/bin/cmk-agent-update ]; then
|
||||||
|
# /usr/bin/cmk-agent-update
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# ── Beispiel 3: Dienst neu starten (nur wenn installiert) ────────────────────
|
||||||
|
#
|
||||||
|
# if systemctl list-unit-files wazuh-agent.service &>/dev/null; then
|
||||||
|
# systemctl restart wazuh-agent.service
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# ── Beispiel 4: Eigenes Skript aufrufen ───────────────────────────────────────
|
||||||
|
#
|
||||||
|
# /opt/myscript/notify-shutdown.sh
|
||||||
|
|
||||||
|
exit 0
|
||||||
+328
-247
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
IFS=$'\n\t'
|
IFS=$'\n\t'
|
||||||
|
|
||||||
@@ -6,6 +6,24 @@ PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
|
|||||||
SCRIPT_NAME=$(basename "$0")
|
SCRIPT_NAME=$(basename "$0")
|
||||||
LOGFILE="/var/log/${SCRIPT_NAME%.sh}.log"
|
LOGFILE="/var/log/${SCRIPT_NAME%.sh}.log"
|
||||||
|
|
||||||
|
detect_os() {
|
||||||
|
case "$(uname -s)" in
|
||||||
|
Linux) echo "linux" ;;
|
||||||
|
FreeBSD) echo "freebsd" ;;
|
||||||
|
*) echo "unknown" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
OS_TYPE="$(detect_os)"
|
||||||
|
|
||||||
|
epoch_to_str() {
|
||||||
|
local epoch="$1"
|
||||||
|
if [[ "$OS_TYPE" == "freebsd" ]]; then
|
||||||
|
date -r "$epoch" "+%Y-%m-%d %H:%M:%S"
|
||||||
|
else
|
||||||
|
date -d "@$epoch" "+%Y-%m-%d %H:%M:%S"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
log() {
|
log() {
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"
|
||||||
}
|
}
|
||||||
@@ -40,29 +58,15 @@ if [[ -n "${CONFIG_FILE:-}" ]]; then
|
|||||||
|
|
||||||
source "$CONFIG_FILE"
|
source "$CONFIG_FILE"
|
||||||
|
|
||||||
: "${CHECKZFS_DISABLED:=0}"
|
|
||||||
: "${CHECKZFS_LOCAL:=0}"
|
|
||||||
: "${CHECKZFS_MAX_AGE:=1500,2000}"
|
|
||||||
: "${CHECKZFS_MAX_SNAPSHOT_COUNT:=180,200}"
|
|
||||||
: "${CHECKZFS_SPOOL:=1}"
|
|
||||||
: "${CHECKZFS_SPOOL_MAXAGE:=90000}"
|
|
||||||
|
|
||||||
: "${SPOOL_DIR:=/var/lib/check_mk_agent/spool}"
|
|
||||||
: "${SPOOL_PREFIX_PBS:=120000}"
|
|
||||||
: "${SPOOL_PREFIX_PIGGYBACK:=100000}"
|
|
||||||
: "${SPOOL_PREFIX_PIGGYBACK_EXTERNAL:=100000}"
|
|
||||||
: "${SPOOL_PREFIX_CHECKZFS_EXTERNAL:=120000}"
|
|
||||||
: "${SPOOL_CLEANUP_DAYS:=7}"
|
|
||||||
|
|
||||||
REQUIRED_VARS=(
|
REQUIRED_VARS=(
|
||||||
SOURCEPORT BACKUPSERVER ZSYNC MAINTDAY SHUTDOWN UPDATES
|
SOURCEPORT BACKUPSERVER ZSYNC MAINTDAY SHUTDOWN UPDATES
|
||||||
SOURCEHOST ZFSROOT ZFSSECOND ZFSTRGT ZPUSHTAG ZPUSHMINKEEP ZPUSHKEEP ZPUSHLABEL
|
SOURCEHOST ZFSTRGT ZPUSHTAG ZPUSHMINKEEP ZPUSHKEEP ZPUSHLABEL
|
||||||
PBSHOST BACKUPSTORE BACKUPSTOREPBS BACKUPEXCLUDE REPLEXCLUDE DYNROUTE REBOOT DDNS_GATEWAY
|
PBSHOST PBSPORT BACKUPSTORE BACKUPSTOREPBS BACKUPEXCLUDE REPLEXCLUDE DYNROUTE REBOOT
|
||||||
)
|
)
|
||||||
|
|
||||||
if [[ "${DYNROUTE,,}" == "yes" ]]; then
|
if [[ "${DYNROUTE,,}" == "yes" ]]; then
|
||||||
REQUIRED_VARS+=(DDNS_GATEWAY)
|
REQUIRED_VARS+=(DDNS_GATEWAY DDNS_SERVER)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
MISSING_VARS=()
|
MISSING_VARS=()
|
||||||
for var in "${REQUIRED_VARS[@]}"; do
|
for var in "${REQUIRED_VARS[@]}"; do
|
||||||
@@ -83,13 +87,29 @@ fi
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rssh() { ssh -p "$SOURCEPORT" "$@"; }
|
SOURCEUSER="${SOURCEUSER:-root}"
|
||||||
rscp() { scp -P "$SOURCEPORT" "$@"; }
|
PBSUSER="${PBSUSER:-root}"
|
||||||
|
EPIGGYBACK_USER="${EPIGGYBACK_USER:-root}"
|
||||||
|
ECHECKZFS_USER="${ECHECKZFS_USER:-root}"
|
||||||
|
|
||||||
|
rssh() { ssh -o BatchMode=yes -o ConnectTimeout=10 -p "$SOURCEPORT" "${SOURCEUSER}@${SOURCEHOST}" "$@"; }
|
||||||
|
rscp() { scp -P "$SOURCEPORT" "$@"; }
|
||||||
|
rpbssh() { ssh -o BatchMode=yes -o ConnectTimeout=10 -p "$PBSPORT" "${PBSUSER}@${PBSHOST}" "$@"; }
|
||||||
|
|
||||||
|
detect_host_type() {
|
||||||
|
if command -v pveversion >/dev/null 2>&1; then
|
||||||
|
echo "pve"
|
||||||
|
elif command -v proxmox-backup-server >/dev/null 2>&1; then
|
||||||
|
echo "pbs"
|
||||||
|
else
|
||||||
|
echo "unknown"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
get_sourcehostname() {
|
get_sourcehostname() {
|
||||||
if [[ -z "${SOURCEHOSTNAME:-}" ]]; then
|
if [[ -z "${SOURCEHOSTNAME:-}" ]]; then
|
||||||
log "SOURCEHOSTNAME is empty, retrieving via SSH from $SOURCEHOST..."
|
log "SOURCEHOSTNAME is empty, retrieving via SSH from $SOURCEHOST..."
|
||||||
SOURCEHOSTNAME=$(rssh "$SOURCEHOST" hostname)
|
SOURCEHOSTNAME=$(rssh hostname)
|
||||||
log "Detected SOURCEHOSTNAME: $SOURCEHOSTNAME"
|
log "Detected SOURCEHOSTNAME: $SOURCEHOSTNAME"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -127,6 +147,11 @@ wait_replace() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set_wol_g_enabled() {
|
set_wol_g_enabled() {
|
||||||
|
if [[ "$OS_TYPE" == "freebsd" ]]; then
|
||||||
|
log "FreeBSD erkannt – set_wol_g_enabled() wird übersprungen (ethtool/apt nicht verfügbar)."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
log "Checking if ethtool is installed..."
|
log "Checking if ethtool is installed..."
|
||||||
|
|
||||||
if ! command -v ethtool >/dev/null 2>&1; then
|
if ! command -v ethtool >/dev/null 2>&1; then
|
||||||
@@ -173,21 +198,21 @@ write_zsync_config() {
|
|||||||
log "Writing zsync config to $conf_file"
|
log "Writing zsync config to $conf_file"
|
||||||
{
|
{
|
||||||
echo "target=$ZFSTRGT"
|
echo "target=$ZFSTRGT"
|
||||||
echo "source=root@$SOURCEHOST"
|
echo "source=${SOURCEUSER}@${SOURCEHOST}"
|
||||||
echo "sshport=$SOURCEPORT"
|
echo "sshport=$SOURCEPORT"
|
||||||
echo "tag=$ZPUSHTAG"
|
echo "tag=$ZPUSHTAG"
|
||||||
echo "snapshot_filter=\"$ZPUSHFILTER\""
|
echo "snapshot_filter=\"$ZPUSHFILTER\""
|
||||||
echo "min_keep=$ZPUSHMINKEEP"
|
echo "min_keep=$ZPUSHMINKEEP"
|
||||||
echo "zfs_auto_snapshot_keep=$ZPUSHKEEP"
|
echo "zfs_auto_snapshot_keep=$ZPUSHKEEP"
|
||||||
echo "zfs_auto_snapshot_label=\"$ZPUSHLABEL\""
|
echo "zfs_auto_snapshot_label=zfs-auto-snap_$ZPUSHLABEL"
|
||||||
echo "zfs_auto_snapshot_engine=\"internal\""
|
echo "zfs_auto_snapshot_engine=internal"
|
||||||
echo "checkzfs_disabled=$CHECKZFS_DISABLED"
|
echo "checkzfs_disabled=0"
|
||||||
echo "checkzfs_local=$CHECKZFS_LOCAL"
|
echo "checkzfs_local=0"
|
||||||
echo "checkzfs_prefix=miyagi-$SOURCEHOSTNAME-$(hostname)-$ZPUSHTAG"
|
echo "checkzfs_prefix=miyagi-$SOURCEHOSTNAME-$(hostname)-$ZPUSHTAG"
|
||||||
echo "checkzfs_max_age=$CHECKZFS_MAX_AGE"
|
echo "checkzfs_max_age=1500,2000"
|
||||||
echo "checkzfs_max_snapshot_count=$CHECKZFS_MAX_SNAPSHOT_COUNT"
|
echo "checkzfs_max_snapshot_count=180,200"
|
||||||
echo "checkzfs_spool=$CHECKZFS_SPOOL"
|
echo "checkzfs_spool=1"
|
||||||
echo "checkzfs_spool_maxage=$CHECKZFS_SPOOL_MAXAGE"
|
echo "checkzfs_spool_maxage=100000"
|
||||||
} > "$conf_file"
|
} > "$conf_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,83 +225,132 @@ run_zsync() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disable_pve_notifications() {
|
||||||
|
if [[ "$(hostname -I 2>/dev/null)" == *"$SOURCEHOST"* || "$(hostname)" == "$SOURCEHOST" ]]; then
|
||||||
|
log "Lokaler Host ist SOURCEHOST – Notification-Deaktivierung wird hier übersprungen."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$(detect_host_type)" == "pve" ]]; then
|
||||||
|
log "PVE erkannt – deaktiviere Notification-Target mail-to-root und default-matcher (keine lokalen Mails an root)."
|
||||||
|
pvesh set /cluster/notifications/endpoints/sendmail/mail-to-root --disable 1 >/dev/null 2>&1 \
|
||||||
|
|| log "WARN: Notification-Target mail-to-root konnte nicht deaktiviert werden."
|
||||||
|
pvesh set /cluster/notifications/matchers/default-matcher --disable 1 >/dev/null 2>&1 \
|
||||||
|
|| log "WARN: Notification-Matcher default-matcher konnte nicht deaktiviert werden."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_local_apt_updates() {
|
||||||
|
log "Running updates on local system..."
|
||||||
|
|
||||||
|
local TIMESTAMP
|
||||||
|
TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
|
||||||
|
local SNAPSHOT_TAG="pve-update-via-miyagi"
|
||||||
|
local MAX_SNAPSHOTS=5
|
||||||
|
local REBOOT_FLAG="${REBOOT^^}"
|
||||||
|
local UPDATE_LOGFILE="/var/log/proxmox_update.log"
|
||||||
|
|
||||||
|
log_msg() {
|
||||||
|
local level="$1"
|
||||||
|
local message="$2"
|
||||||
|
local timestamp
|
||||||
|
timestamp=$(date +"%Y-%m-%d %H:%M:%S")
|
||||||
|
echo "[$timestamp] $message"
|
||||||
|
echo "[$timestamp] [$level] $message" >> "$UPDATE_LOGFILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
local HOST_TYPE
|
||||||
|
HOST_TYPE=$(detect_host_type)
|
||||||
|
local ZFS_DATASETS=()
|
||||||
|
case "$HOST_TYPE" in
|
||||||
|
pve)
|
||||||
|
log_msg "INFO" "Systemtyp erkannt: PVE"
|
||||||
|
ZFS_DATASETS=("rpool/ROOT/pve-1" "rpool/pveconf")
|
||||||
|
;;
|
||||||
|
pbs)
|
||||||
|
log_msg "INFO" "Systemtyp erkannt: PBS"
|
||||||
|
ZFS_DATASETS=("rpool/ROOT/pbs-1")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_msg "WARN" "Systemtyp (PVE/PBS) konnte nicht erkannt werden – ueberspringe ZFS-Snapshots."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Prüfen, ob Updates verfügbar sind
|
||||||
|
if ! apt update | tee -a "$UPDATE_LOGFILE" | grep -qi "upgradable"; then
|
||||||
|
log_msg "INFO" "Keine Updates verfügbar."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_msg "INFO" "Updates verfügbar. Erstelle Snapshots."
|
||||||
|
|
||||||
|
for dataset in "${ZFS_DATASETS[@]}"; do
|
||||||
|
if ! zfs list -H -o name "$dataset" >/dev/null 2>&1; then
|
||||||
|
log_msg "WARN" "Dataset $dataset existiert nicht – übersprungen."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
snapshot="${dataset}@${SNAPSHOT_TAG}-${TIMESTAMP}"
|
||||||
|
log_msg "INFO" "Erstelle Snapshot: $snapshot"
|
||||||
|
zfs snapshot "$snapshot"
|
||||||
|
|
||||||
|
# Alte Snapshots bereinigen
|
||||||
|
log_msg "INFO" "Bereinige alte Snapshots in $dataset"
|
||||||
|
old_snaps=$(zfs list -t snapshot -o name -s creation | grep "^${dataset}@${SNAPSHOT_TAG}-")
|
||||||
|
snap_count=$(grep -c . <<< "$old_snaps")
|
||||||
|
if (( snap_count > MAX_SNAPSHOTS )); then
|
||||||
|
snaps_to_delete=$(echo "$old_snaps" | head -n $((snap_count - MAX_SNAPSHOTS)))
|
||||||
|
while IFS= read -r snap; do
|
||||||
|
log_msg "INFO" "Lösche alten Snapshot: $snap"
|
||||||
|
zfs destroy "$snap"
|
||||||
|
done <<< "$snaps_to_delete"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# System-Upgrade
|
||||||
|
log_msg "INFO" "Starte dist-upgrade"
|
||||||
|
if ! apt dist-upgrade -y | tee -a "$UPDATE_LOGFILE"; then
|
||||||
|
log_msg "ERROR" "Fehler während dist-upgrade"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_msg "INFO" "Starte autoremove"
|
||||||
|
apt autoremove -y | tee -a "$UPDATE_LOGFILE"
|
||||||
|
|
||||||
|
# Kernel-Update prüfen
|
||||||
|
if apt list --upgradable 2>/dev/null | grep -q "linux-image-"; then
|
||||||
|
log_msg "WARN" "Kernel-Update erkannt. Neustart empfohlen."
|
||||||
|
if [[ "$REBOOT_FLAG" == "YES" ]]; then
|
||||||
|
log_msg "INFO" "REBOOT=YES erkannt. System wird neugestartet."
|
||||||
|
reboot
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_msg "INFO" "Update abgeschlossen. Kein Neustart erforderlich."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_pbs_apt_updates() {
|
||||||
|
if [[ "${BACKUPSERVER,,}" == "yes" ]]; then
|
||||||
|
log "Running updates on PBS host ($PBSHOST)..."
|
||||||
|
rpbssh apt update && rpbssh apt dist-upgrade -y || {
|
||||||
|
log "Error during updates on $PBSHOST"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log "PBS updates skipped (BACKUPSERVER=$BACKUPSERVER)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
run_remote_updates() {
|
run_remote_updates() {
|
||||||
if [[ "${UPDATES,,}" == "yes" ]]; then
|
if [[ "${UPDATES,,}" == "yes" ]]; then
|
||||||
log "Running updates on local system..."
|
if [[ "$OS_TYPE" == "freebsd" ]]; then
|
||||||
|
log "FreeBSD erkannt – lokales apt-Update wird übersprungen (apt nicht verfügbar)."
|
||||||
TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
|
|
||||||
SNAPSHOT_TAG="pve-update-via-miyagi"
|
|
||||||
MAX_SNAPSHOTS=5
|
|
||||||
ZFS_DATASETS=("rpool/ROOT" "rpool/pveconf")
|
|
||||||
REBOOT_FLAG="${REBOOT^^}"
|
|
||||||
LOGFILE="/var/log/proxmox_update.log"
|
|
||||||
|
|
||||||
log_msg() {
|
|
||||||
local level="$1"
|
|
||||||
local message="$2"
|
|
||||||
local timestamp
|
|
||||||
timestamp=$(date +"%Y-%m-%d %H:%M:%S")
|
|
||||||
echo "[$timestamp] $message"
|
|
||||||
echo "[$timestamp] [$level] $message" >> "$LOGFILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Prüfen, ob Updates verfügbar sind
|
|
||||||
if ! apt update | tee -a "$LOGFILE" | grep -qi "upgradable"; then
|
|
||||||
log_msg "INFO" "Keine Updates verfügbar."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_msg "INFO" "Updates verfügbar. Erstelle Snapshots."
|
|
||||||
|
|
||||||
for dataset in "${ZFS_DATASETS[@]}"; do
|
|
||||||
snapshot="${dataset}@${SNAPSHOT_TAG}-${TIMESTAMP}"
|
|
||||||
log_msg "INFO" "Erstelle Snapshot: $snapshot"
|
|
||||||
zfs snapshot "$snapshot"
|
|
||||||
|
|
||||||
# Alte Snapshots bereinigen
|
|
||||||
log_msg "INFO" "Bereinige alte Snapshots in $dataset"
|
|
||||||
old_snaps=$(zfs list -t snapshot -o name -s creation | grep "^${dataset}@${SNAPSHOT_TAG}-")
|
|
||||||
snap_count=$(echo "$old_snaps" | wc -l)
|
|
||||||
if (( snap_count > MAX_SNAPSHOTS )); then
|
|
||||||
snaps_to_delete=$(echo "$old_snaps" | head -n $((snap_count - MAX_SNAPSHOTS)))
|
|
||||||
while IFS= read -r snap; do
|
|
||||||
log_msg "INFO" "Lösche alten Snapshot: $snap"
|
|
||||||
zfs destroy "$snap"
|
|
||||||
done <<< "$snaps_to_delete"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# System-Upgrade
|
|
||||||
log_msg "INFO" "Starte dist-upgrade"
|
|
||||||
if ! apt dist-upgrade -y | tee -a "$LOGFILE"; then
|
|
||||||
log_msg "ERROR" "Fehler während dist-upgrade"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_msg "INFO" "Starte autoremove"
|
|
||||||
apt autoremove -y | tee -a "$LOGFILE"
|
|
||||||
|
|
||||||
# Kernel-Update prüfen
|
|
||||||
if apt list --upgradable 2>/dev/null | grep -q "linux-image-"; then
|
|
||||||
log_msg "WARN" "Kernel-Update erkannt. Neustart empfohlen."
|
|
||||||
if [[ "$REBOOT_FLAG" == "YES" ]]; then
|
|
||||||
log_msg "INFO" "REBOOT=YES erkannt. System wird neugestartet."
|
|
||||||
reboot
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
log_msg "INFO" "Update abgeschlossen. Kein Neustart erforderlich."
|
run_local_apt_updates
|
||||||
fi
|
disable_pve_notifications
|
||||||
|
|
||||||
# PBS-Host-Update
|
|
||||||
if [[ "${BACKUPSERVER,,}" == "yes" ]]; then
|
|
||||||
log "Running updates on PBS host ($PBSHOST)..."
|
|
||||||
ssh -p $PBSPORT root@"$PBSHOST" apt update && ssh -p $PBSPORT root@"$PBSHOST" apt dist-upgrade -y || {
|
|
||||||
log "Error during updates on $PBSHOST"
|
|
||||||
}
|
|
||||||
else
|
|
||||||
log "PBS updates skipped (BACKUPSERVER=$BACKUPSERVER)"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# PBS-Host-Update läuft unabhängig vom lokalen Update-Ergebnis/Skript-Host-OS
|
||||||
|
run_pbs_apt_updates
|
||||||
else
|
else
|
||||||
log "Updates disabled (UPDATES=$UPDATES)"
|
log "Updates disabled (UPDATES=$UPDATES)"
|
||||||
fi
|
fi
|
||||||
@@ -285,7 +359,7 @@ run_remote_updates() {
|
|||||||
send_piggyback() {
|
send_piggyback() {
|
||||||
get_sourcehostname
|
get_sourcehostname
|
||||||
local combined_host="miyagi-${SOURCEHOSTNAME}-$(hostname)"
|
local combined_host="miyagi-${SOURCEHOSTNAME}-$(hostname)"
|
||||||
local filename="${SPOOL_PREFIX_PIGGYBACK}_${combined_host}"
|
local filename="100000_${combined_host}"
|
||||||
|
|
||||||
log "Reminder: Add a host named ${combined_host} in CMK (without Agent, Piggyback enabled)!"
|
log "Reminder: Add a host named ${combined_host} in CMK (without Agent, Piggyback enabled)!"
|
||||||
log "Creating piggyback file: $filename"
|
log "Creating piggyback file: $filename"
|
||||||
@@ -296,10 +370,8 @@ send_piggyback() {
|
|||||||
echo "<<<<>>>>"
|
echo "<<<<>>>>"
|
||||||
} > "$filename"
|
} > "$filename"
|
||||||
|
|
||||||
if rscp "$filename" "$SOURCEHOST:${SPOOL_DIR}/"; then
|
if rscp "$filename" "${SOURCEUSER}@${SOURCEHOST}:/var/lib/check_mk_agent/spool/"; then
|
||||||
log "Piggyback data successfully sent to $SOURCEHOST"
|
log "Piggyback data successfully sent to $SOURCEHOST"
|
||||||
rssh root@"$SOURCEHOST" "find '${SPOOL_DIR}' -maxdepth 1 -name '*_${combined_host}' -not -name '$(basename "$filename")' -mtime +${SPOOL_CLEANUP_DAYS} -delete" \
|
|
||||||
|| log "WARNUNG: Aufräumen alter Piggyback-Spool-Dateien auf $SOURCEHOST fehlgeschlagen"
|
|
||||||
else
|
else
|
||||||
log "ERROR: Failed to send piggyback data to $SOURCEHOST"
|
log "ERROR: Failed to send piggyback data to $SOURCEHOST"
|
||||||
fi
|
fi
|
||||||
@@ -310,14 +382,18 @@ send_piggyback() {
|
|||||||
run_pbs_log_clear() {
|
run_pbs_log_clear() {
|
||||||
log "Running clear LOG on PBS host ($PBSHOST)..."
|
log "Running clear LOG on PBS host ($PBSHOST)..."
|
||||||
|
|
||||||
if ! ssh -p "$PBSPORT" root@"$PBSHOST" \
|
if ! rpbssh find /var/log/proxmox-backup/tasks/ -type f -mtime +60 -delete; then
|
||||||
find /var/log/proxmox-backup/tasks/ -type f -mtime +60 -delete; then
|
|
||||||
log "Error clearing PBS LOG on $PBSHOST"
|
log "Error clearing PBS LOG on $PBSHOST"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
vzdump_success=false
|
run_pve_log_clear() {
|
||||||
pbs_enabled_by_script=false
|
log "Running clear LOG on PVE host ($PBSHOST)..."
|
||||||
|
|
||||||
|
if ! rssh find /var/log/vzdump -type f -mtime +30 -delete; then
|
||||||
|
log "Error clearing PVE LOG on $SOURCEHOST"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
run_pbs_backup() {
|
run_pbs_backup() {
|
||||||
if [[ "${BACKUPSERVER,,}" != "yes" ]]; then
|
if [[ "${BACKUPSERVER,,}" != "yes" ]]; then
|
||||||
@@ -328,152 +404,67 @@ run_pbs_backup() {
|
|||||||
get_sourcehostname
|
get_sourcehostname
|
||||||
log "Starte PBS Backup auf Host: $SOURCEHOST"
|
log "Starte PBS Backup auf Host: $SOURCEHOST"
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
# PBS-Storage prüfen und ggf. aktivieren
|
||||||
# PBS-Storage prüfen und ggf. temporär aktivieren
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
pbs_enabled_by_script=false
|
|
||||||
|
|
||||||
log "Prüfe, ob PBS-Storage '$BACKUPSTORE' auf $SOURCEHOST aktiviert ist..."
|
log "Prüfe, ob PBS-Storage '$BACKUPSTORE' auf $SOURCEHOST aktiviert ist..."
|
||||||
|
if rssh "pvesm status | grep -w '$BACKUPSTORE' | grep -q 'disabled'"; then
|
||||||
if rssh root@"$SOURCEHOST" \
|
|
||||||
"pvesm status | grep -w '$BACKUPSTORE' | grep -q 'disabled'"; then
|
|
||||||
|
|
||||||
log "PBS-Storage '$BACKUPSTORE' ist deaktiviert – aktiviere temporär und warte 10 Sekunden..."
|
log "PBS-Storage '$BACKUPSTORE' ist deaktiviert – aktiviere temporär und warte 10 Sekunden..."
|
||||||
|
rssh "pvesm set '$BACKUPSTORE' --disable 0 && sleep 10"
|
||||||
if rssh root@"$SOURCEHOST" \
|
pbs_enabled_by_script=true
|
||||||
"pvesm set '$BACKUPSTORE' --disable 0 && sleep 10"; then
|
|
||||||
|
|
||||||
pbs_enabled_by_script=true
|
|
||||||
log "PBS-Storage '$BACKUPSTORE' wurde erfolgreich aktiviert."
|
|
||||||
else
|
|
||||||
log "FEHLER: PBS-Storage '$BACKUPSTORE' konnte nicht aktiviert werden."
|
|
||||||
vzdump_success=false
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
else
|
else
|
||||||
log "PBS-Storage '$BACKUPSTORE' ist bereits aktiviert."
|
log "PBS-Storage '$BACKUPSTORE' ist bereits aktiviert."
|
||||||
|
pbs_enabled_by_script=false
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# PBS Namespace setzen
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
PBS_NS_ARG=()
|
|
||||||
|
|
||||||
if [[ -n "$PBS_NAMESPACE" && "${PBS_NAMESPACE,,}" != "none" ]]; then
|
|
||||||
PBS_NS_ARG=(--pbs-namespace "$PBS_NAMESPACE")
|
|
||||||
log "PBS Namespace aktiviert: $PBS_NAMESPACE"
|
|
||||||
else
|
|
||||||
log "PBS Namespace nicht gesetzt – Backup läuft im Standard-Datastore"
|
|
||||||
fi
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# Backup-Status zurücksetzen
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
vzdump_success=false
|
vzdump_success=false
|
||||||
# ------------------------------------------------------------
|
|
||||||
# vzdump mit Change Detection
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
log "Starte vzdump mit Change Detection..."
|
|
||||||
|
|
||||||
if rssh root@"$SOURCEHOST" \
|
# Versuch: vzdump mit Change Detection
|
||||||
vzdump --pbs-change-detection-mode metadata \
|
## Eintrag muss in /etc/vzdump.conf entries-max 100000000
|
||||||
"${PBS_NS_ARG[@]}" \
|
log "Starte vzdump mit Change Detection..."
|
||||||
--node "$SOURCEHOSTNAME" \
|
if rssh vzdump --pbs-change-detection-mode metadata \
|
||||||
--storage "$BACKUPSTORE" \
|
--node "$SOURCEHOSTNAME" --storage "$BACKUPSTORE" \
|
||||||
--exclude "$BACKUPEXCLUDE" \
|
--exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \
|
||||||
--mode snapshot \
|
|
||||||
--all 1 \
|
|
||||||
--notes-template '{{guestname}}'; then
|
--notes-template '{{guestname}}'; then
|
||||||
|
|
||||||
log "vzdump (Change Detection) erfolgreich."
|
log "vzdump (Change Detection) erfolgreich."
|
||||||
vzdump_success=true
|
vzdump_success=true
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
log "vzdump (Change Detection) fehlgeschlagen – versuche Fallback ohne Change Detection..."
|
log "vzdump (Change Detection) fehlgeschlagen – versuche Fallback ohne Change Detection..."
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
if rssh vzdump \
|
||||||
# Fallback ohne Change Detection
|
--node "$SOURCEHOSTNAME" --storage "$BACKUPSTORE" \
|
||||||
# ----------------------------------------------------------
|
--exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \
|
||||||
if rssh root@"$SOURCEHOST" \
|
|
||||||
vzdump \
|
|
||||||
"${PBS_NS_ARG[@]}" \
|
|
||||||
--node "$SOURCEHOSTNAME" \
|
|
||||||
--storage "$BACKUPSTORE" \
|
|
||||||
--exclude "$BACKUPEXCLUDE" \
|
|
||||||
--mode snapshot \
|
|
||||||
--all 1 \
|
|
||||||
--notes-template '{{guestname}}'; then
|
--notes-template '{{guestname}}'; then
|
||||||
|
|
||||||
log "Fallback-vzdump erfolgreich."
|
log "Fallback-vzdump erfolgreich."
|
||||||
vzdump_success=true
|
vzdump_success=true
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
log "FEHLER: vzdump fehlgeschlagen – auch im Fallback."
|
log "FEHLER: vzdump fehlgeschlagen – auch im Fallback."
|
||||||
vzdump_success=false
|
|
||||||
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
# ------------------------------------------------------------
|
|
||||||
# PBS-Storage nach erfolgreichem Backup wieder deaktivieren
|
# PBS-Storage ggf. wieder deaktivieren
|
||||||
#
|
|
||||||
# WICHTIG:
|
|
||||||
# Nur deaktivieren, wenn dieses Script den Storage
|
|
||||||
# vorher selbst aktiviert hat.
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
if [[ "$vzdump_success" == true && "$pbs_enabled_by_script" == true ]]; then
|
if [[ "$vzdump_success" == true && "$pbs_enabled_by_script" == true ]]; then
|
||||||
|
|
||||||
log "Deaktiviere temporär aktiviertes PBS-Storage '$BACKUPSTORE' auf $SOURCEHOST..."
|
log "Deaktiviere temporär aktiviertes PBS-Storage '$BACKUPSTORE' auf $SOURCEHOST..."
|
||||||
|
rssh "pvesm set '$BACKUPSTORE' --disable 1"
|
||||||
if rssh root@"$SOURCEHOST" \
|
|
||||||
"pvesm set '$BACKUPSTORE' --disable 1"; then
|
|
||||||
|
|
||||||
log "PBS-Storage '$BACKUPSTORE' wurde wieder deaktiviert."
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
log "WARNUNG: PBS-Storage '$BACKUPSTORE' konnte nicht wieder deaktiviert werden."
|
|
||||||
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# Monitoring-Ausgabe
|
# Monitoring-Ausgabe
|
||||||
# ------------------------------------------------------------
|
|
||||||
if [[ "$vzdump_success" == true ]]; then
|
if [[ "$vzdump_success" == true ]]; then
|
||||||
echo "0 DailyPBS - Daily Backup erfolgreich" > /tmp/cmk_tmp.out
|
echo "0 DailyPBS - Daily Backup erfolgreich" > /tmp/cmk_tmp.out
|
||||||
else
|
else
|
||||||
echo "2 DailyPBS - Daily Backup FEHLGESCHLAGEN" > /tmp/cmk_tmp.out
|
echo "2 DailyPBS - Daily Backup FEHLGESCHLAGEN" > /tmp/cmk_tmp.out
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local pbs_spool_file="${SPOOL_PREFIX_PBS}_checkpbs"
|
|
||||||
|
|
||||||
{
|
{
|
||||||
echo "<<<local>>>"
|
echo "<<<local>>>"
|
||||||
cat /tmp/cmk_tmp.out
|
cat /tmp/cmk_tmp.out
|
||||||
} > "/tmp/${pbs_spool_file}"
|
} > /tmp/1200000_checkpbs
|
||||||
|
|
||||||
if rscp "/tmp/${pbs_spool_file}" root@"$SOURCEHOST":"${SPOOL_DIR}"; then
|
rscp /tmp/1200000_checkpbs "${SOURCEUSER}@${SOURCEHOST}":/var/lib/check_mk_agent/spool \
|
||||||
rssh root@"$SOURCEHOST" "find '${SPOOL_DIR}' -maxdepth 1 -name '*_checkpbs' -not -name '${pbs_spool_file}' -mtime +${SPOOL_CLEANUP_DAYS} -delete" \
|
|| log "Fehler beim Übertragen des Monitoring-Outputs via SCP"
|
||||||
|| log "WARNUNG: Aufräumen alter PBS-Spool-Dateien auf $SOURCEHOST fehlgeschlagen"
|
|
||||||
else
|
|
||||||
log "Fehler beim Übertragen des Monitoring-Outputs via SCP"
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -f /tmp/cmk_tmp.out "/tmp/${pbs_spool_file}"
|
|
||||||
|
|
||||||
|
rm -f /tmp/cmk_tmp.out /tmp/1200000_checkpbs
|
||||||
#write_pbs_status
|
#write_pbs_status
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# Rückgabestatus
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
if [[ "$vzdump_success" == true ]]; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
run_maintenance() {
|
run_maintenance() {
|
||||||
@@ -484,10 +475,10 @@ run_maintenance() {
|
|||||||
|
|
||||||
if [[ "$(date +%u)" == "$MAINTDAY" ]]; then
|
if [[ "$(date +%u)" == "$MAINTDAY" ]]; then
|
||||||
log "Running maintenance..."
|
log "Running maintenance..."
|
||||||
PRUNEJOB=$(ssh -p $PBSPORT "$PBSHOST" proxmox-backup-manager prune-job list --output-format json-pretty | grep -m 1 "id" | cut -d'"' -f4)
|
PRUNEJOB=$(rpbssh proxmox-backup-manager prune-job list --output-format json-pretty | grep -m 1 "id" | cut -d'"' -f4)
|
||||||
ssh -p $PBSPORT root@"$PBSHOST" proxmox-backup-manager prune-job run "$PRUNEJOB"
|
rpbssh proxmox-backup-manager prune-job run "$PRUNEJOB"
|
||||||
ssh -p $PBSPORT root@"$PBSHOST" proxmox-backup-manager garbage-collection start "$BACKUPSTOREPBS"
|
rpbssh proxmox-backup-manager garbage-collection start "$BACKUPSTOREPBS"
|
||||||
ssh -p $PBSPORT root@"$PBSHOST" proxmox-backup-manager verify backup
|
rpbssh proxmox-backup-manager verify backup
|
||||||
else
|
else
|
||||||
log "No maintenance scheduled for today."
|
log "No maintenance scheduled for today."
|
||||||
fi
|
fi
|
||||||
@@ -498,7 +489,7 @@ run_scrub_stop() {
|
|||||||
local ssh_cmd=()
|
local ssh_cmd=()
|
||||||
|
|
||||||
if [[ "$mode" == "remote" ]]; then
|
if [[ "$mode" == "remote" ]]; then
|
||||||
ssh_cmd=(ssh -p "$SOURCEPORT" root@"$SOURCEHOST")
|
ssh_cmd=(ssh -o BatchMode=yes -o ConnectTimeout=10 -p "$SOURCEPORT" "${SOURCEUSER}@${SOURCEHOST}")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
"${ssh_cmd[@]}" bash -c '
|
"${ssh_cmd[@]}" bash -c '
|
||||||
@@ -518,8 +509,8 @@ run_scrub_stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
write_pbs_status() {
|
write_pbs_status() {
|
||||||
local spool_file="${SPOOL_DIR}/${SPOOL_PREFIX_PBS}_checkpbs_local"
|
local spool_file="/var/lib/check_mk_agent/spool/90000_checkpbs_local"
|
||||||
local repo="$BACKUPSTOREPBS"
|
local repo="${PBS_REPO_USER}@${PBSHOST}:${BACKUPSTOREPBS}"
|
||||||
local tmpfile
|
local tmpfile
|
||||||
tmpfile=$(mktemp)
|
tmpfile=$(mktemp)
|
||||||
|
|
||||||
@@ -528,6 +519,11 @@ write_pbs_status() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${PBS_REPO_USER:-}" || -z "${PBS_PASSWORD:-}" ]]; then
|
||||||
|
log "PBS_REPO_USER oder PBS_PASSWORD nicht gesetzt – PBS-Status-Ausgabe übersprungen."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "<<<local>>>" > "$tmpfile"
|
echo "<<<local>>>" > "$tmpfile"
|
||||||
|
|
||||||
local now
|
local now
|
||||||
@@ -552,8 +548,8 @@ write_pbs_status() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Backup-Daten extrahieren
|
# Backup-Daten extrahieren (direkt vom Skript-Host gegen PBSHOST, mit PBS_PASSWORD authentifiziert)
|
||||||
rssh root@"$SOURCEHOST" proxmox-backup-client list --repository "$repo" --output-format json 2>/dev/null | \
|
PBS_PASSWORD="$PBS_PASSWORD" proxmox-backup-client list --repository "$repo" --output-format json 2>/dev/null | \
|
||||||
grep -E '"backup-id"|"backup-type"|"backup-time"' | \
|
grep -E '"backup-id"|"backup-type"|"backup-time"' | \
|
||||||
sed -E 's/[",]//g; s/^ *//' > "$tmpfile.json"
|
sed -E 's/[",]//g; s/^ *//' > "$tmpfile.json"
|
||||||
|
|
||||||
@@ -572,7 +568,7 @@ write_pbs_status() {
|
|||||||
if [[ -n "$backup_type" && -n "$backup_id" && -n "$backup_time" ]]; then
|
if [[ -n "$backup_type" && -n "$backup_id" && -n "$backup_time" ]]; then
|
||||||
local age status timestamp msg
|
local age status timestamp msg
|
||||||
age=$((now - backup_time))
|
age=$((now - backup_time))
|
||||||
timestamp=$(date -d "@$backup_time" "+%Y-%m-%d %H:%M:%S")
|
timestamp=$(epoch_to_str "$backup_time")
|
||||||
|
|
||||||
if (( age < warn_threshold )); then
|
if (( age < warn_threshold )); then
|
||||||
status=0
|
status=0
|
||||||
@@ -597,6 +593,27 @@ write_pbs_status() {
|
|||||||
log "PBS Backup Status lokal geschrieben: $spool_file"
|
log "PBS Backup Status lokal geschrieben: $spool_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_pre_shutdown_hooks() {
|
||||||
|
local hook_dir="/etc/miyagi-backup/hooks.d/pre-shutdown"
|
||||||
|
if [[ ! -d "$hook_dir" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
local hooks=()
|
||||||
|
while IFS= read -r -d '' f; do
|
||||||
|
hooks+=("$f")
|
||||||
|
done < <(find "$hook_dir" -maxdepth 1 -name "*.sh" -executable -print0 | sort -z)
|
||||||
|
if [[ ${#hooks[@]} -eq 0 ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
log "Führe Pre-Shutdown-Hooks aus ($hook_dir)..."
|
||||||
|
for hook in "${hooks[@]}"; do
|
||||||
|
log "Hook: $hook"
|
||||||
|
if ! bash "$hook"; then
|
||||||
|
log "WARNUNG: Hook $hook fehlgeschlagen (Exit $?) – fahre fort"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
shutdown_now() {
|
shutdown_now() {
|
||||||
if [[ "${SHUTDOWN,,}" == "yes" ]]; then
|
if [[ "${SHUTDOWN,,}" == "yes" ]]; then
|
||||||
if zfs_replace; then
|
if zfs_replace; then
|
||||||
@@ -609,6 +626,7 @@ shutdown_now() {
|
|||||||
send_piggyback
|
send_piggyback
|
||||||
send_piggyback_external
|
send_piggyback_external
|
||||||
send_checkzfs_external
|
send_checkzfs_external
|
||||||
|
run_pre_shutdown_hooks
|
||||||
log "Shutting down now...in 60sec"
|
log "Shutting down now...in 60sec"
|
||||||
sleep 60
|
sleep 60
|
||||||
shutdown now
|
shutdown now
|
||||||
@@ -622,12 +640,11 @@ send_piggyback_external() {
|
|||||||
log "Externer Piggyback-Export deaktiviert."
|
log "Externer Piggyback-Export deaktiviert."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
write_zsync_config
|
|
||||||
get_sourcehostname
|
get_sourcehostname
|
||||||
log "Ermittelter SOURCEHOSTNAME: $SOURCEHOSTNAME"
|
log "Ermittelter SOURCEHOSTNAME: $SOURCEHOSTNAME"
|
||||||
|
|
||||||
local combined_host="miyagi-${SOURCEHOSTNAME}-$(hostname)"
|
local combined_host="miyagi-${SOURCEHOSTNAME}-$(hostname)"
|
||||||
local spool_file="${SPOOL_PREFIX_PIGGYBACK_EXTERNAL}_${combined_host}_external"
|
local spool_file="100000_${combined_host}_piggyback_external"
|
||||||
local temp_dir
|
local temp_dir
|
||||||
temp_dir=$(mktemp -d)
|
temp_dir=$(mktemp -d)
|
||||||
|
|
||||||
@@ -639,10 +656,8 @@ send_piggyback_external() {
|
|||||||
echo "<<<<>>>>"
|
echo "<<<<>>>>"
|
||||||
} > "$temp_dir/$spool_file"
|
} > "$temp_dir/$spool_file"
|
||||||
|
|
||||||
if scp -P "$EPIGGYBACK_PORT" "$temp_dir/$spool_file" "$EPIGGYBACK_HOST:${SPOOL_DIR}/"; then
|
if scp -P "$EPIGGYBACK_PORT" "$temp_dir/$spool_file" "${EPIGGYBACK_USER}@${EPIGGYBACK_HOST}:/var/lib/check_mk_agent/spool/"; then
|
||||||
log "Piggyback-Daten erfolgreich an $EPIGGYBACK_HOST gesendet: $spool_file"
|
log "Piggyback-Daten erfolgreich an $EPIGGYBACK_HOST gesendet: $spool_file"
|
||||||
ssh -p "$EPIGGYBACK_PORT" "$EPIGGYBACK_HOST" "find '${SPOOL_DIR}' -maxdepth 1 -name '*_${combined_host}_external' -not -name '${spool_file}' -mtime +${SPOOL_CLEANUP_DAYS} -delete" \
|
|
||||||
|| log "WARNUNG: Aufräumen alter Piggyback-Spool-Dateien auf $EPIGGYBACK_HOST fehlgeschlagen"
|
|
||||||
else
|
else
|
||||||
log "Fehler beim Übertragen der Piggyback-Daten an $EPIGGYBACK_HOST"
|
log "Fehler beim Übertragen der Piggyback-Daten an $EPIGGYBACK_HOST"
|
||||||
rm -rf "$temp_dir"
|
rm -rf "$temp_dir"
|
||||||
@@ -653,9 +668,70 @@ send_piggyback_external() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
send_checkzfs() {
|
||||||
|
if [[ "${ZSYNC,,}" != "yes" ]]; then
|
||||||
|
log "send_checkzfs: ZSYNC nicht aktiv – übersprungen"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
get_sourcehostname
|
||||||
|
|
||||||
|
local checkzfs_cmd
|
||||||
|
checkzfs_cmd="$(command -v checkzfs || true)"
|
||||||
|
if [[ -z "$checkzfs_cmd" || ! -x "$checkzfs_cmd" ]]; then
|
||||||
|
log "send_checkzfs: checkzfs nicht gefunden – übersprungen"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Defaults für checkzfs-Parameter (können via zsync-Config überschrieben werden)
|
||||||
|
local checkzfs_prefix="${checkzfs_prefix:-miyagi-${SOURCEHOSTNAME}-$(hostname)-${ZPUSHTAG}}"
|
||||||
|
local checkzfs_max_age="${checkzfs_max_age:-1500,2000}"
|
||||||
|
local checkzfs_max_snapshot_count="${checkzfs_max_snapshot_count:-180,200}"
|
||||||
|
|
||||||
|
# Optional zsync-Config laden falls vorhanden (überschreibt Defaults)
|
||||||
|
local zsync_config="/etc/bashclub/${SOURCEHOST}.conf"
|
||||||
|
[[ -f "$zsync_config" ]] && source "$zsync_config"
|
||||||
|
|
||||||
|
local filter=""
|
||||||
|
while IFS=$'\t' read -r name value src; do
|
||||||
|
if [[ "$src" == "local" && "$value" == "subvols" ]]; then
|
||||||
|
filter+="${name}/|"
|
||||||
|
elif [[ "$src" == "local" && "$value" == "all" ]]; then
|
||||||
|
filter+="${name}|"
|
||||||
|
fi
|
||||||
|
done < <(rssh "zfs get -H -o name,value,source -t filesystem,volume $ZPUSHTAG")
|
||||||
|
filter="#${filter%|}"
|
||||||
|
log "Generierter ZFS-Filter: $filter"
|
||||||
|
|
||||||
|
local combined="miyagi-${SOURCEHOSTNAME}-$(hostname)-${ZPUSHTAG}"
|
||||||
|
local spoolfile="/tmp/${combined}_checkzfs"
|
||||||
|
local spooldest="100000_${combined}_checkzfs"
|
||||||
|
|
||||||
|
log "Führe checkzfs aus..."
|
||||||
|
{
|
||||||
|
echo "<<<local>>>"
|
||||||
|
"$checkzfs_cmd" \
|
||||||
|
--source "${SOURCEUSER}@${SOURCEHOST}:${SOURCEPORT}" \
|
||||||
|
--filter "$filter" \
|
||||||
|
--replicafilter "^${ZFSTRGT}" \
|
||||||
|
--prefix "$checkzfs_prefix" \
|
||||||
|
--threshold "$checkzfs_max_age" \
|
||||||
|
--maxsnapshots "$checkzfs_max_snapshot_count" \
|
||||||
|
--output checkmk
|
||||||
|
} > "$spoolfile"
|
||||||
|
|
||||||
|
if rscp "$spoolfile" "${SOURCEUSER}@${SOURCEHOST}:/var/lib/check_mk_agent/spool/${spooldest}"; then
|
||||||
|
log "CheckZFS-Daten an $SOURCEHOST gesendet: $spooldest"
|
||||||
|
else
|
||||||
|
log "FEHLER: CheckZFS-Daten konnten nicht übertragen werden"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$spoolfile"
|
||||||
|
}
|
||||||
|
|
||||||
send_checkzfs_external() {
|
send_checkzfs_external() {
|
||||||
if [[ "${ECHECKZFS,,}" != "yes" ]]; then
|
if [[ "${ECHECKZFS,,}" != "yes" ]]; then
|
||||||
log "Externer Piggyback-Export deaktiviert."
|
log "Externer CheckZFS deaktiviert."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
local config="/etc/bashclub/${SOURCEHOST}.conf"
|
local config="/etc/bashclub/${SOURCEHOST}.conf"
|
||||||
@@ -665,11 +741,6 @@ send_checkzfs_external() {
|
|||||||
fi
|
fi
|
||||||
source "$config"
|
source "$config"
|
||||||
|
|
||||||
if [[ "${ECHECKZFS,,}" != "yes" ]]; then
|
|
||||||
log "Externer Check-ZFS deaktiviert (ECHECKZFS=$ECHECKZFS)"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
write_zsync_config
|
|
||||||
get_sourcehostname # Setzt SOURCEHOSTNAME, wenn nicht vorhanden
|
get_sourcehostname # Setzt SOURCEHOSTNAME, wenn nicht vorhanden
|
||||||
|
|
||||||
local checkzfs_cmd="${checkzfs_cmd:-$(command -v checkzfs)}"
|
local checkzfs_cmd="${checkzfs_cmd:-$(command -v checkzfs)}"
|
||||||
@@ -687,20 +758,20 @@ send_checkzfs_external() {
|
|||||||
elif [[ "$source" == "local" && "$value" == "all" ]]; then
|
elif [[ "$source" == "local" && "$value" == "all" ]]; then
|
||||||
filter+="${name}|"
|
filter+="${name}|"
|
||||||
fi
|
fi
|
||||||
done < <(ssh -p "$SOURCEPORT" "$SOURCEHOST" "zfs get -H -o name,value,source -t filesystem,volume $ZPUSHTAG")
|
done < <(rssh "zfs get -H -o name,value,source -t filesystem,volume $ZPUSHTAG")
|
||||||
|
|
||||||
filter="#${filter%|}"
|
filter="#${filter%|}"
|
||||||
log "Generierter ZFS-Filter: $filter"
|
log "Generierter ZFS-Filter: $filter"
|
||||||
|
|
||||||
local combined="miyagi-${SOURCEHOSTNAME}-$(hostname)-${ZPUSHTAG}"
|
local combined="miyagi-${SOURCEHOSTNAME}-$(hostname)-${ZPUSHTAG}"
|
||||||
local spoolfile="/tmp/${combined}_checkzfs_external"
|
local spoolfile="/tmp/${combined}_checkzfs_external"
|
||||||
local spooldest="${SPOOL_PREFIX_CHECKZFS_EXTERNAL}_${combined}_checkzfs_external"
|
local spooldest="100000_${combined}_checkzfs_external"
|
||||||
|
|
||||||
log "Führe checkzfs aus..."
|
log "Führe checkzfs aus..."
|
||||||
{
|
{
|
||||||
echo "<<<local>>>"
|
echo "<<<local>>>"
|
||||||
"$checkzfs_cmd" \
|
"$checkzfs_cmd" \
|
||||||
--source root@"${SOURCEHOST}:${SOURCEPORT}" \
|
--source ${SOURCEUSER}@"${SOURCEHOST}:${SOURCEPORT}" \
|
||||||
--filter "$filter" \
|
--filter "$filter" \
|
||||||
--replicafilter "^${ZFSTRGT}" \
|
--replicafilter "^${ZFSTRGT}" \
|
||||||
--prefix "$checkzfs_prefix" \
|
--prefix "$checkzfs_prefix" \
|
||||||
@@ -709,10 +780,8 @@ send_checkzfs_external() {
|
|||||||
--output checkmk
|
--output checkmk
|
||||||
} > "$spoolfile"
|
} > "$spoolfile"
|
||||||
|
|
||||||
if scp -P "$ECHECKZFS_PORT" "$spoolfile" root@"$ECHECKZFS_HOST":"${SPOOL_DIR}/${spooldest}"; then
|
if scp -P "$ECHECKZFS_PORT" "$spoolfile" ${ECHECKZFS_USER}@"$ECHECKZFS_HOST":/var/lib/check_mk_agent/spool/"$spooldest"; then
|
||||||
log "Spool-Datei erfolgreich übertragen an $ECHECKZFS_HOST:$spooldest"
|
log "Spool-Datei erfolgreich übertragen an $ECHECKZFS_HOST:$spooldest"
|
||||||
ssh -p "$ECHECKZFS_PORT" root@"$ECHECKZFS_HOST" "find '${SPOOL_DIR}' -maxdepth 1 -name 'miyagi-${SOURCEHOSTNAME}-$(hostname)-*_checkzfs_external' -not -name '${spooldest}' -mtime +${SPOOL_CLEANUP_DAYS} -delete" \
|
|
||||||
|| log "WARNUNG: Aufräumen alter checkzfs-Spool-Dateien auf $ECHECKZFS_HOST fehlgeschlagen"
|
|
||||||
else
|
else
|
||||||
log "Fehler beim Übertragen der Spool-Datei an $ECHECKZFS_HOST"
|
log "Fehler beim Übertragen der Spool-Datei an $ECHECKZFS_HOST"
|
||||||
fi
|
fi
|
||||||
@@ -812,7 +881,8 @@ if [[ $# -eq 0 ]]; then
|
|||||||
run_pbs_backup
|
run_pbs_backup
|
||||||
run_maintenance
|
run_maintenance
|
||||||
run_pbs_log_clear
|
run_pbs_log_clear
|
||||||
else
|
run_pve_log_clear
|
||||||
|
else
|
||||||
log "BACKUPSERVER ist nicht aktiviert (BACKUPSERVER=$BACKUPSERVER), überspringe Backup."
|
log "BACKUPSERVER ist nicht aktiviert (BACKUPSERVER=$BACKUPSERVER), überspringe Backup."
|
||||||
fi
|
fi
|
||||||
run_remote_updates
|
run_remote_updates
|
||||||
@@ -828,12 +898,23 @@ if [[ $# -eq 0 ]]; then
|
|||||||
usage
|
usage
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
# Funktionen die keine Config brauchen
|
||||||
|
CONFIG_FREE_FUNCTIONS=(help detect_os detect_host_type zfs_replace wait_replace set_wol_g_enabled run_scrub_stop)
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
help)
|
help)
|
||||||
declare -F | awk '{print $3}' | grep -v "^_" | grep -v "^main$"
|
declare -F | awk '{print $3}' | grep -v "^_" | grep -v "^main$"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
if declare -F "$1" >/dev/null 2>&1; then
|
if declare -F "$1" >/dev/null 2>&1; then
|
||||||
|
config_required=true
|
||||||
|
for fn in "${CONFIG_FREE_FUNCTIONS[@]}"; do
|
||||||
|
[[ "$1" == "$fn" ]] && config_required=false && break
|
||||||
|
done
|
||||||
|
if [[ "$config_required" == true && -z "${SOURCEHOST:-}" ]]; then
|
||||||
|
log "Funktion '$1' benötigt eine Config. Aufruf: $0 -c /etc/miyagi-backup/backup.conf $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
"$@"
|
"$@"
|
||||||
else
|
else
|
||||||
log "Function $1 not found."
|
log "Function $1 not found."
|
||||||
|
|||||||
-220
@@ -1,220 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
IFS=$'\n\t'
|
|
||||||
|
|
||||||
PERMITROOT_YES_HOSTS=()
|
|
||||||
LOG() {
|
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
|
||||||
}
|
|
||||||
|
|
||||||
CONFIG_FILE="${1:-}"
|
|
||||||
|
|
||||||
if [[ -z "$CONFIG_FILE" ]]; then
|
|
||||||
LOG "Keine Konfigurationsdatei übergeben."
|
|
||||||
echo "Usage: $0 /pfad/zur/config"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
||||||
LOG "Konfigurationsdatei nicht gefunden: $CONFIG_FILE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! bash -n "$CONFIG_FILE"; then
|
|
||||||
LOG "Syntaxfehler in der Konfigurationsdatei!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
source "$CONFIG_FILE"
|
|
||||||
|
|
||||||
REQUIRED_VARS=(
|
|
||||||
SOURCEPORT
|
|
||||||
BACKUPSERVER
|
|
||||||
ZSYNC
|
|
||||||
MAINTDAY
|
|
||||||
SHUTDOWN
|
|
||||||
UPDATES
|
|
||||||
SOURCEHOST
|
|
||||||
PBSHOST
|
|
||||||
BACKUPSTORE
|
|
||||||
BACKUPSTOREPBS
|
|
||||||
BACKUPEXCLUDE
|
|
||||||
REPLEXCLUDE
|
|
||||||
)
|
|
||||||
|
|
||||||
MISSING=()
|
|
||||||
for var in "${REQUIRED_VARS[@]}"; do
|
|
||||||
if ! declare -p "$var" &>/dev/null || [[ -z "${!var}" ]]; then
|
|
||||||
MISSING+=("$var")
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ ${#MISSING[@]} -gt 0 ]]; then
|
|
||||||
LOG " Fehlende Variablen in Konfiguration:"
|
|
||||||
for v in "${MISSING[@]}"; do echo " - $v"; done
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
LOG " Alle Variablen gesetzt."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ${#MISSING[@]} -gt 0 ]]; then
|
|
||||||
LOG " Fehlende Variablen in Konfiguration:"
|
|
||||||
for v in "${MISSING[@]}"; do echo " - $v"; done
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
LOG " Alle Variablen gesetzt."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Prüfung, ob SHUTDOWN auch tatsächlich in der Datei definiert wurde
|
|
||||||
if ! grep -qE '^\s*SHUTDOWN\s*=' "$CONFIG_FILE"; then
|
|
||||||
LOG "Hinweis: Die Variable 'SHUTDOWN' ist zwar gesetzt, aber nicht direkt in der Konfigurationsdatei '$CONFIG_FILE' definiert."
|
|
||||||
LOG " → Bitte prüfen, ob dies gewollt ist oder von einer übergeordneten Quelle stammt."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Anzeigen, ob SHUTDOWN aktiv ist
|
|
||||||
LOG "SHUTDOWN-Status: ${SHUTDOWN:-nicht gesetzt}"
|
|
||||||
|
|
||||||
case "${SHUTDOWN,,}" in
|
|
||||||
yes)
|
|
||||||
LOG "Nach dem Backup wird das System heruntergefahren."
|
|
||||||
;;
|
|
||||||
no)
|
|
||||||
LOG "Kein automatischer Shutdown nach dem Backup."
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
LOG "Unbekannter SHUTDOWN-Wert: '${SHUTDOWN}' , erwartet: 'yes' oder 'no'"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
check_ssh_connection() {
|
|
||||||
local host=$1
|
|
||||||
LOG "Prüfe SSH-Verbindung zu $host ..."
|
|
||||||
if ssh -p "$SOURCEPORT" -o BatchMode=yes -o ConnectTimeout=5 "$host" "echo OK" 2>/dev/null | grep -q OK; then
|
|
||||||
LOG " SSH-Verbindung zu $host erfolgreich."
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
LOG " SSH-Verbindung zu $host fehlgeschlagen!"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_and_copy_ssh_key() {
|
|
||||||
local host=$1
|
|
||||||
local keyfile="$HOME/.ssh/id_rsa.pub"
|
|
||||||
|
|
||||||
if [[ ! -f "$keyfile" ]]; then
|
|
||||||
LOG " Lokaler SSH-Public-Key ($keyfile) nicht gefunden!"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
local pubkey
|
|
||||||
pubkey=$(<"$keyfile")
|
|
||||||
|
|
||||||
LOG " Prüfe, ob SSH-Key auf $host autorisiert ist ..."
|
|
||||||
|
|
||||||
if ssh -p "$SOURCEPORT" "$host" "grep -qF '$pubkey' ~/.ssh/authorized_keys" 2>/dev/null; then
|
|
||||||
LOG " SSH-Key ist bereits auf $host hinterlegt."
|
|
||||||
else
|
|
||||||
LOG " SSH-Key nicht auf $host vorhanden."
|
|
||||||
|
|
||||||
read -rp " Möchtest du den SSH-Key jetzt via ssh-copy-id übertragen? [j/N] " ans
|
|
||||||
if [[ "$ans" =~ ^[JjYy]$ ]]; then
|
|
||||||
ssh-copy-id -p "$SOURCEPORT" "$host"
|
|
||||||
else
|
|
||||||
LOG " SSH-Key nicht übertragen."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_sshd_config_recommendation() {
|
|
||||||
local host=$1
|
|
||||||
LOG " Prüfe sshd_config auf $host bzgl. 'PermitRootLogin'..."
|
|
||||||
|
|
||||||
local current_setting
|
|
||||||
current_setting=$(ssh -p "$SOURCEPORT" "$host" "grep -i '^PermitRootLogin' /etc/ssh/sshd_config" 2>/dev/null || echo "")
|
|
||||||
|
|
||||||
if [[ -z "$current_setting" ]]; then
|
|
||||||
LOG " Keine explizite 'PermitRootLogin'-Einstellung gefunden."
|
|
||||||
elif echo "$current_setting" | grep -qi "PermitRootLogin yes"; then
|
|
||||||
LOG " Aktuell ist PermitRootLogin=YES erlaubt auf $host"
|
|
||||||
LOG " Empfehlung: auf 'prohibit-password' umstellen."
|
|
||||||
PERMITROOT_YES_HOSTS+=("$host")
|
|
||||||
else
|
|
||||||
LOG " PermitRootLogin-Einstellung ist: $current_setting"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_pveversion() {
|
|
||||||
local host=$1
|
|
||||||
LOG "Prüfe PVE-Version auf $host ..."
|
|
||||||
if ssh -p "$SOURCEPORT" "$host" "command -v pveversion >/dev/null"; then
|
|
||||||
ssh -p "$SOURCEPORT" "$host" "pveversion" | while read -r line; do
|
|
||||||
LOG " $host: $line"
|
|
||||||
done
|
|
||||||
else
|
|
||||||
LOG " 'pveversion' ist auf $host nicht verfügbar – kein Proxmox?"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
check_pbs_version() {
|
|
||||||
local host=$1
|
|
||||||
local port=$2
|
|
||||||
LOG "Prüfe PBS-Version auf $host ..."
|
|
||||||
if ssh -p "$port" "$host" "command -v proxmox-backup-manager >/dev/null"; then
|
|
||||||
ssh -p "$port" "$host" "proxmox-backup-manager version" | while read -r line; do
|
|
||||||
LOG " $host: $line"
|
|
||||||
done
|
|
||||||
else
|
|
||||||
LOG " 'proxmox-backup-manager' ist auf $host nicht verfügbar – kein PBS?"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
run_host_check() {
|
|
||||||
local host=$1
|
|
||||||
local type=${2:-pve}
|
|
||||||
local port=$SOURCEPORT
|
|
||||||
local pbsport=$PBSPORT
|
|
||||||
|
|
||||||
LOG ""
|
|
||||||
LOG "=== Prüfung für Host: $host (Typ: $type) ==="
|
|
||||||
|
|
||||||
if check_ssh_connection "$host" "$port"; then
|
|
||||||
check_and_copy_ssh_key "$host"
|
|
||||||
check_sshd_config_recommendation "$host"
|
|
||||||
|
|
||||||
if [[ "$type" == "pve" ]]; then
|
|
||||||
check_pveversion "$host" "$port"
|
|
||||||
elif [[ "$type" == "pbs" ]]; then
|
|
||||||
check_pbs_version "$host" "$pbsport"
|
|
||||||
else
|
|
||||||
LOG " Unbekannter Host-Typ: $type"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
run_host_check "$SOURCEHOST" pve
|
|
||||||
|
|
||||||
if [[ "$BACKUPSERVER" == "yes" ]]; then
|
|
||||||
run_host_check "$PBSHOST" pbs
|
|
||||||
else
|
|
||||||
LOG " BACKUPSERVER ist deaktiviert PBSHOST wird übersprungen."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# NACHLAUF: Frage zur automatischen Änderung von PermitRootLogin
|
|
||||||
if [[ ${#PERMITROOT_YES_HOSTS[@]} -gt 0 ]]; then
|
|
||||||
echo ""
|
|
||||||
echo " Die folgenden Hosts erlauben derzeit root-Login per Passwort:"
|
|
||||||
for h in "${PERMITROOT_YES_HOSTS[@]}"; do
|
|
||||||
echo " - $h"
|
|
||||||
done
|
|
||||||
|
|
||||||
read -rp " Möchtest du PermitRootLogin auf diesen Hosts auf 'prohibit-password' setzen und sshd neustarten? [j/N] " change_ans
|
|
||||||
if [[ "$change_ans" =~ ^[JjYy]$ ]]; then
|
|
||||||
for h in "${PERMITROOT_YES_HOSTS[@]}"; do
|
|
||||||
echo "Ändere sshd_config auf $h ..."
|
|
||||||
ssh -p "$SOURCEPORT" "$h" "sed -i 's/^PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config && systemctl reload sshd && echo '✅ sshd auf $h neu geladen.' || echo '❌ Fehler bei $h'"
|
|
||||||
done
|
|
||||||
else
|
|
||||||
echo " Änderung von sshd_config übersprungen."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
IFS=$'\n\t'
|
|
||||||
|
|
||||||
log() {
|
|
||||||
echo "[INFO] $*"
|
|
||||||
}
|
|
||||||
|
|
||||||
error_exit() {
|
|
||||||
echo "[ERROR] $*" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
sanitize_value() {
|
|
||||||
echo "$1" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
|
|
||||||
}
|
|
||||||
|
|
||||||
load_config() {
|
|
||||||
local config_file="$1"
|
|
||||||
if [[ ! -f "$config_file" ]]; then
|
|
||||||
error_exit "Konfigurationsdatei nicht gefunden: $config_file"
|
|
||||||
fi
|
|
||||||
|
|
||||||
log "Lade und bereinige Konfigurationsdatei: $config_file"
|
|
||||||
while IFS='=' read -r key value; do
|
|
||||||
# nur gültige Variablennamen parsen
|
|
||||||
if [[ "$key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
|
|
||||||
# Kommentar nach Wert entfernen
|
|
||||||
value="${value%%#*}"
|
|
||||||
value="$(sanitize_value "$value")"
|
|
||||||
# Variable setzen (für den write_new_config Zugriff)
|
|
||||||
eval "$key=\"$value\""
|
|
||||||
fi
|
|
||||||
done < "$config_file"
|
|
||||||
}
|
|
||||||
|
|
||||||
write_new_config() {
|
|
||||||
local out_file="$1"
|
|
||||||
|
|
||||||
cat > "$out_file" <<EOF
|
|
||||||
#Edit all Variables for best Experience
|
|
||||||
|
|
||||||
UPDATES='${UPDATES}' # Proxmox VE und PBS Updates nach dem Lauf
|
|
||||||
SHUTDOWN='${SHUTDOWN}' # System nach Ausführung herunterfahren?
|
|
||||||
|
|
||||||
# Quelle (Proxmox VE System, das gesichert wird)
|
|
||||||
SOURCEPORT='${SOURCEPORT}' # SSH-Port, normalerweise 22
|
|
||||||
SOURCEHOST='${SOURCEHOST}' # IP des Quell-Proxmox-Servers
|
|
||||||
|
|
||||||
# Replikation (ZFS)
|
|
||||||
ZFSROOT='${ZFSROOT}' # Erstes Dataset vom Quellsystem
|
|
||||||
ZFSSECOND='${ZFSSECOND}' # Optional zweites Dataset
|
|
||||||
ZFSTRGT='${ZFSTRGT}' # Zielpfad für Replikation
|
|
||||||
|
|
||||||
# ZFS Zsync Replikation
|
|
||||||
ZSYNC='${ZSYNC}' # ZSYNC aktivieren (ja/nein)
|
|
||||||
ZPUSHTAG='${ZPUSHTAG}' # Benutzer-Tag für ZFS
|
|
||||||
ZPUSHMINKEEP='${ZPUSHMINKEEP}' # Mindestens zu behaltende Snapshots
|
|
||||||
ZPUSHKEEP='${ZPUSHKEEP}' # Snapshots mit dem Tag, die behalten werden
|
|
||||||
ZPUSHLABEL='${ZPUSHLABEL}' # Suffix für Snapshot-Autoengine
|
|
||||||
ZPUSHFILTER='${ZPUSHFILTER}' # Weitere Filter (leer lassen oder Muster wie daily| weekly etc.)
|
|
||||||
|
|
||||||
# Backup mit Proxmox Backup Server
|
|
||||||
BACKUPSERVER='${BACKUPSERVER}' # Backup via PBS aktivieren?
|
|
||||||
MAINTDAY='${MAINTDAY}' # Wartungstag (1=Mo, 7=So)
|
|
||||||
PBSHOST='${PBSHOST}' # IP des Proxmox Backup Servers
|
|
||||||
BACKUPSTORE='${BACKUPSTORE}' # Datastore auf Quell-Proxmox
|
|
||||||
BACKUPSTOREPBS='${BACKUPSTOREPBS}' # Datastore auf PBS
|
|
||||||
BACKUPEXCLUDE='${BACKUPEXCLUDE}' # VM/CT-IDs, die vom Backup ausgeschlossen sind
|
|
||||||
REPLEXCLUDE=\$BACKUPEXCLUDE # Diese auch von Replikation ausschließen
|
|
||||||
|
|
||||||
# Zusätzliche Monitoring-Ziele
|
|
||||||
# External Piggyback Host
|
|
||||||
EPIGGYBACK='${EPIGGYBACK}' # Piggyback-Daten an Monitoring-Ziel senden?
|
|
||||||
EPIGGYBACK_PORT='${EPIGGYBACK_PORT}' # SSH-Port für EPIGGYBACK_HOST
|
|
||||||
EPIGGYBACK_HOST='${EPIGGYBACK_HOST}' # Monitoring-Zielhost für Piggyback
|
|
||||||
|
|
||||||
# External Checkzfs Host
|
|
||||||
ECHECKZFS='${ECHECKZFS}' # check_zfs-Output an Monitoring-Ziel senden?
|
|
||||||
ECHECKZFS_PORT='${ECHECKZFS_PORT}' # SSH-Port für ECHECKZFS_HOST
|
|
||||||
ECHECKZFS_HOST='${ECHECKZFS_HOST}' # Monitoring-Zielhost für check_zfs
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if [[ $# -ne 1 ]]; then
|
|
||||||
echo "Usage: $0 <configfile>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
ORIGINAL="$1"
|
|
||||||
|
|
||||||
read -rp "Soll die aktuelle Datei als Backup gesichert werden (umbenennen)? (ja/nein): " RESPONSE
|
|
||||||
RESPONSE="${RESPONSE,,}"
|
|
||||||
|
|
||||||
if [[ "$RESPONSE" == "ja" || "$RESPONSE" == "j" ]]; then
|
|
||||||
BACKUPFILE="${ORIGINAL}.bak"
|
|
||||||
mv "$ORIGINAL" "$BACKUPFILE"
|
|
||||||
log "Originaldatei wurde umbenannt in: $BACKUPFILE"
|
|
||||||
CONFIG_TO_READ="$BACKUPFILE"
|
|
||||||
else
|
|
||||||
log "Keine Sicherung der Originaldatei durchgeführt. Original bleibt unverändert."
|
|
||||||
CONFIG_TO_READ="$ORIGINAL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
NEWFILE="${ORIGINAL}.convert"
|
|
||||||
load_config "$CONFIG_TO_READ"
|
|
||||||
write_new_config "$NEWFILE"
|
|
||||||
log "Neue Konfiguration geschrieben in: $NEWFILE"
|
|
||||||
+198
-160
@@ -1,29 +1,41 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
# Author: (C) 2025 Patrick Perlbach <patrick@perlbach24.de>
|
# miyagi-backup Installer v3
|
||||||
# --- Konfiguration und Initiale Variablen ---
|
set -uo pipefail
|
||||||
|
|
||||||
FORCE_YES=0
|
FORCE_YES=0
|
||||||
DRY_RUN=0
|
DRY_RUN=0
|
||||||
|
DEV_MODE=0
|
||||||
CONFIG_FILE=""
|
CONFIG_FILE=""
|
||||||
|
INSTALL_DIR="/opt/miyagi-backup"
|
||||||
|
CONFIG_DIR="/etc/miyagi-backup"
|
||||||
|
HOOKS_DIR="${CONFIG_DIR}/hooks.d/pre-shutdown"
|
||||||
|
SYMLINK="/usr/local/bin/miyagi-backup"
|
||||||
|
|
||||||
|
GITHUB_BASE="https://raw.githubusercontent.com/bashclub/miyagi-backup/main"
|
||||||
|
GITEA_BASE="https://gitea.perlbach24.de/scripte/miyagi-backup/raw/branch/main"
|
||||||
|
|
||||||
|
CHECKZFS_URL="https://gitea.perlbach24.de/scripte/check-zfs-replication/raw/branch/main/checkzfs.py"
|
||||||
|
CHECK_SNAPSHOT_AGE_URL="https://gitea.perlbach24.de/scripte/check-zfs-replication/raw/branch/main/check-snapshot-age"
|
||||||
|
CLEANSNAPS_URL="https://raw.githubusercontent.com/bashclub/zfs-housekeeping/refs/heads/main/cleansnaps.sh"
|
||||||
|
DISK_SMART_INFO_URL="https://raw.githubusercontent.com/bashclub/checkmk-smart/main/disk_smart_info.py"
|
||||||
|
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
|
||||||
|
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||||
|
info() { echo -e "${BLUE}[..]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[!!]${NC} $*"; }
|
||||||
|
die() { echo -e "${RED}[ERR]${NC} $*" >&2; exit 1; }
|
||||||
|
|
||||||
# --- Hilfsfunktionen ---
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: $0 -c <config> [--yes] [--dry-run]
|
Usage: $0 [OPTIONS]
|
||||||
|
|
||||||
-c <file> Pfad zur Konfigurationsdatei (Pflicht)
|
-c <file> Konfigurationsdatei laden (für SSH-Check + ZFS-Tagging)
|
||||||
--yes Automatische Ausführung ohne Rückfrage
|
--yes Keine Rückfragen
|
||||||
--dry-run Zeigt nur, was gemacht würde
|
--dry-run Zeigt nur was gemacht würde
|
||||||
--help Diese Hilfe anzeigen
|
--dev Installiere von Gitea (dev) statt GitHub (stable)
|
||||||
|
--help Diese Hilfe
|
||||||
EOF
|
EOF
|
||||||
exit 1
|
exit 0
|
||||||
}
|
|
||||||
|
|
||||||
run_or_echo() {
|
|
||||||
if [[ "$DRY_RUN" == "1" ]]; then
|
|
||||||
echo "[DRY-RUN] $*"
|
|
||||||
else
|
|
||||||
eval "$@"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_args() {
|
parse_args() {
|
||||||
@@ -32,224 +44,250 @@ parse_args() {
|
|||||||
-c) CONFIG_FILE="$2"; shift 2 ;;
|
-c) CONFIG_FILE="$2"; shift 2 ;;
|
||||||
--yes) FORCE_YES=1; shift ;;
|
--yes) FORCE_YES=1; shift ;;
|
||||||
--dry-run) DRY_RUN=1; shift ;;
|
--dry-run) DRY_RUN=1; shift ;;
|
||||||
|
--dev) DEV_MODE=1; shift ;;
|
||||||
--help) usage ;;
|
--help) usage ;;
|
||||||
*) echo "[ERROR] Unbekannter Parameter: $1"; usage ;;
|
*) die "Unbekannter Parameter: $1" ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
[[ -z "$CONFIG_FILE" || ! -f "$CONFIG_FILE" ]] && {
|
if [[ -n "$CONFIG_FILE" ]]; then
|
||||||
echo "[ERROR] Konfigurationsdatei fehlt oder ungültig!"
|
[[ ! -f "$CONFIG_FILE" ]] && die "Konfigurationsdatei nicht gefunden: $CONFIG_FILE"
|
||||||
exit 1
|
bash -n "$CONFIG_FILE" || die "Syntaxfehler in $CONFIG_FILE"
|
||||||
}
|
source "$CONFIG_FILE"
|
||||||
|
SOURCEUSER="${SOURCEUSER:-root}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
source "$CONFIG_FILE"
|
run() {
|
||||||
|
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||||
|
echo "[DRY-RUN] $*"
|
||||||
|
else
|
||||||
|
"$@"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
confirm() {
|
confirm() {
|
||||||
if [[ "$FORCE_YES" -eq 1 ]]; then
|
[[ "$FORCE_YES" -eq 1 ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
read -rp "$1 [j/N]: " yn
|
read -rp "$1 [j/N]: " yn
|
||||||
case $yn in
|
case "$yn" in
|
||||||
[JjYy]*) return 0 ;;
|
[JjYy]*) return 0 ;;
|
||||||
[Nn]*|"") return 1 ;;
|
[Nn]*|"") return 1 ;;
|
||||||
*) echo "Bitte j (ja) oder n (nein) eingeben." ;;
|
*) echo "Bitte j oder n eingeben." ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ensure_dialog() {
|
||||||
install_cmk() {
|
if ! command -v dialog &>/dev/null; then
|
||||||
echo "[INFO] Lade cmk.deb herunter..."
|
info "Installiere dialog..."
|
||||||
run_or_echo "curl -fsSL \"$CMK_URL\" -o \"$CMK_FILE\""
|
run apt-get install -y -qq dialog || die "dialog konnte nicht installiert werden"
|
||||||
|
fi
|
||||||
echo "[INFO] Installiere cmk.deb..."
|
|
||||||
run_or_echo "sudo apt install -y \"$CMK_FILE\""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
install_fix_interface_names() {
|
get_base_url() {
|
||||||
echo "[INFO] Installiere Fix für Interface-Namen..."
|
[[ "$DEV_MODE" -eq 1 ]] && echo "$GITEA_BASE" || echo "$GITHUB_BASE"
|
||||||
run_or_echo "wget -O /tmp/debian-fix-interface-names.sh \"$FIX_INTERFACE_NAMES_URL\""
|
|
||||||
run_or_echo "chmod +x /tmp/debian-fix-interface-names.sh"
|
|
||||||
run_or_echo "bash /tmp/debian-fix-interface-names.sh"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
install_miyagi_scripts() {
|
# ── Installationsschritte ─────────────────────────────────────────────────────
|
||||||
echo "[INFO] Lade Miyagi-Backup-Skripte..."
|
|
||||||
run_or_echo "wget -O miyagi-backup.sh \"$MIYAGI_BACKUP_URL\""
|
|
||||||
run_or_echo "wget -O miyagi-check.sh \"$MIYAGI_CHECK_URL\""
|
|
||||||
run_or_echo "wget -O miyagi-convert.sh \"$MIYAGI_CONVERT_URL\""
|
|
||||||
run_or_echo "mkdir -p /root/miyagi-backup"
|
|
||||||
run_or_echo "mv miyagi-backup.sh miyagi-check.sh miyagi-convert.sh /root/miyagi-backup/"
|
|
||||||
run_or_echo "chmod +x /root/miyagi-backup/*.sh"
|
|
||||||
}
|
|
||||||
|
|
||||||
install_checkmk_plugins() {
|
install_miyagi() {
|
||||||
echo "[INFO] Installiere disk_smart_info Plugin..."
|
local base
|
||||||
run_or_echo "curl -fsSL \"$DISK_SMART_INFO_URL\" -o /usr/lib/check_mk_agent/plugins/disk_smart_info"
|
base="$(get_base_url)"
|
||||||
run_or_echo "chmod +x /usr/lib/check_mk_agent/plugins/disk_smart_info"
|
local source_label
|
||||||
}
|
[[ "$DEV_MODE" -eq 1 ]] && source_label="Gitea (dev)" || source_label="GitHub (stable)"
|
||||||
|
|
||||||
install_cleansnaps() {
|
info "Installiere miyagi-backup von $source_label..."
|
||||||
echo "[INFO] Installiere cleansnaps Skript..."
|
|
||||||
run_or_echo "wget -O /usr/local/bin/cleansnaps \"$CLEANSNAPS_URL\""
|
|
||||||
run_or_echo "chmod +x /usr/local/bin/cleansnaps"
|
|
||||||
}
|
|
||||||
|
|
||||||
install_check_snapshot_age() {
|
run mkdir -p "$INSTALL_DIR"
|
||||||
echo "[INFO] Installiere check-snapshot-age..."
|
run mkdir -p "$CONFIG_DIR"
|
||||||
run_or_echo "wget -O /usr/local/bin/check-snapshot-age \"$CHECK_SNAPSHOT_AGE_URL\""
|
run mkdir -p "$HOOKS_DIR"
|
||||||
run_or_echo "chmod +x /usr/local/bin/check-snapshot-age"
|
|
||||||
|
run curl -fsSL "${base}/miyagi-backup.sh" -o "${INSTALL_DIR}/miyagi-backup.sh"
|
||||||
|
run chmod +x "${INSTALL_DIR}/miyagi-backup.sh"
|
||||||
|
|
||||||
|
# config.example nur holen, nie bestehende Config überschreiben
|
||||||
|
run curl -fsSL "${base}/config.example" -o "${CONFIG_DIR}/config.example"
|
||||||
|
|
||||||
|
# Example-Hook kopieren (nicht ausführbar = inaktiv bis User chmod +x setzt)
|
||||||
|
run curl -fsSL "${base}/hooks.d/pre-shutdown/example-hook.sh" \
|
||||||
|
-o "${HOOKS_DIR}/example-hook.sh"
|
||||||
|
|
||||||
|
# Symlink
|
||||||
|
run ln -sf "${INSTALL_DIR}/miyagi-backup.sh" "$SYMLINK"
|
||||||
|
|
||||||
|
ok "miyagi-backup installiert: ${INSTALL_DIR}/miyagi-backup.sh"
|
||||||
|
ok "Symlink: $SYMLINK"
|
||||||
|
ok "Config-Verzeichnis: $CONFIG_DIR"
|
||||||
|
ok "Hook-Verzeichnis: $HOOKS_DIR"
|
||||||
}
|
}
|
||||||
|
|
||||||
install_checkzfs() {
|
install_checkzfs() {
|
||||||
echo "[INFO] Installiere checkzfs..."
|
info "Installiere checkzfs..."
|
||||||
run_or_echo "wget -O /usr/local/bin/checkzfs \"$CHECKZFS_URL\""
|
run curl -fsSL "$CHECKZFS_URL" -o /usr/local/bin/checkzfs
|
||||||
run_or_echo "chmod +x /usr/local/bin/checkzfs"
|
run chmod +x /usr/local/bin/checkzfs
|
||||||
|
ok "checkzfs installiert"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_check_snapshot_age() {
|
||||||
|
info "Installiere check-snapshot-age..."
|
||||||
|
run curl -fsSL "$CHECK_SNAPSHOT_AGE_URL" -o /usr/local/bin/check-snapshot-age
|
||||||
|
run chmod +x /usr/local/bin/check-snapshot-age
|
||||||
|
ok "check-snapshot-age installiert"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_cleansnaps() {
|
||||||
|
info "Installiere cleansnaps..."
|
||||||
|
run curl -fsSL "$CLEANSNAPS_URL" -o /usr/local/bin/cleansnaps
|
||||||
|
run chmod +x /usr/local/bin/cleansnaps
|
||||||
|
ok "cleansnaps installiert"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_disk_smart_info() {
|
||||||
|
info "Installiere disk_smart_info..."
|
||||||
|
run mkdir -p /usr/lib/check_mk_agent/plugins
|
||||||
|
run curl -fsSL "$DISK_SMART_INFO_URL" -o /usr/lib/check_mk_agent/plugins/disk_smart_info
|
||||||
|
run chmod +x /usr/lib/check_mk_agent/plugins/disk_smart_info
|
||||||
|
ok "disk_smart_info installiert"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_ethtool() {
|
||||||
|
info "Installiere ethtool..."
|
||||||
|
run apt-get install -y -qq ethtool
|
||||||
|
ok "ethtool installiert"
|
||||||
}
|
}
|
||||||
|
|
||||||
check_ssh() {
|
check_ssh() {
|
||||||
echo "[INFO] Teste SSH-Verbindung zu $SOURCEHOST ..."
|
[[ -z "${SOURCEHOST:-}" ]] && warn "Kein SOURCEHOST in Config – SSH-Check übersprungen" && return
|
||||||
|
info "Teste SSH-Verbindung zu ${SOURCEUSER}@${SOURCEHOST}:${SOURCEPORT}..."
|
||||||
|
|
||||||
if ssh -p "$SOURCEPORT" -o BatchMode=yes -o ConnectTimeout=5 root@"$SOURCEHOST" "echo Verbindung erfolgreich" 2>/dev/null; then
|
if ssh -p "${SOURCEPORT:-22}" -o BatchMode=yes -o ConnectTimeout=5 \
|
||||||
echo "[OK] SSH-Verbindung über Schlüssel funktioniert."
|
"${SOURCEUSER}@${SOURCEHOST}" "echo ok" &>/dev/null; then
|
||||||
|
ok "SSH-Verbindung via Schlüssel funktioniert"
|
||||||
else
|
else
|
||||||
echo "[WARN] Keine Schlüsselbasierte SSH-Verbindung möglich oder abgelehnt."
|
warn "Keine schlüsselbasierte SSH-Verbindung möglich"
|
||||||
|
if confirm "SSH-Schlüssel jetzt auf ${SOURCEHOST} kopieren (ssh-copy-id)?"; then
|
||||||
echo "[HINWEIS] Eine Passwortabfrage erfolgt jetzt – dies ist normal."
|
run ssh-copy-id -p "${SOURCEPORT:-22}" "${SOURCEUSER}@${SOURCEHOST}"
|
||||||
if ssh -p "$SOURCEPORT" root@"$SOURCEHOST" "echo Verbindung erfolgreich"; then
|
ok "Schlüssel kopiert – bitte Installer erneut starten zum Verifizieren"
|
||||||
echo "[OK] SSH-Verbindung per Passwort erfolgreich."
|
exit 0
|
||||||
|
|
||||||
if confirm "Möchtest du den lokalen SSH-Schlüssel auf $SOURCEHOST kopieren (per ssh-copy-id)?"; then
|
|
||||||
run_or_echo "ssh-copy-id -p \"$SOURCEPORT\" root@\"$SOURCEHOST\""
|
|
||||||
echo "[INFO] Bitte erneut starten, um die Schlüsselverbindung zu nutzen."
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
echo "[HINWEIS] Es wird weiterhin Passwortabfrage benötigt."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "[FEHLER] SSH-Verbindung nicht möglich – bitte Zugang prüfen!"
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
select_zfs_pools() {
|
select_zfs_pools() {
|
||||||
echo "[INFO] Lade ZFS-Datasets vom Remote-Host..."
|
[[ -z "${SOURCEHOST:-}" ]] && warn "Kein SOURCEHOST – ZFS-Tagging übersprungen" && return
|
||||||
|
|
||||||
LOCAL_HOSTNAME=$(hostname -s)
|
info "Lade ZFS-Datasets von ${SOURCEHOST}..."
|
||||||
REMOTE_HOSTNAME=$(ssh -p "$SOURCEPORT" root@"$SOURCEHOST" "hostname -s")
|
local local_hostname remote_hostname tag_key tag_value
|
||||||
TAG_KEY="bashclub:miyagi-${REMOTE_HOSTNAME}-${LOCAL_HOSTNAME}"
|
local_hostname="$(hostname -s)"
|
||||||
TAG_VALUE="subvols"
|
remote_hostname="$(ssh -p "${SOURCEPORT:-22}" "${SOURCEUSER}@${SOURCEHOST}" "hostname -s" 2>/dev/null)" \
|
||||||
|
|| { warn "SSH-Verbindung fehlgeschlagen – ZFS-Tagging übersprungen"; return; }
|
||||||
|
|
||||||
# Hole alle relevanten Pool-Namen
|
tag_key="bashclub:miyagi-${remote_hostname}-${local_hostname}"
|
||||||
mapfile -t pools < <(ssh -p "$SOURCEPORT" root@"$SOURCEHOST" \
|
tag_value="subvols"
|
||||||
"zfs list -H -o name" | \
|
|
||||||
|
mapfile -t pools < <(ssh -p "${SOURCEPORT:-22}" "${SOURCEUSER}@${SOURCEHOST}" \
|
||||||
|
"zfs list -H -o name" 2>/dev/null | \
|
||||||
grep -E '/(vm|subvol)-' | \
|
grep -E '/(vm|subvol)-' | \
|
||||||
grep -viE 'repl|replica' | \
|
grep -viE 'repl|replica' | \
|
||||||
sed 's:/[^/]*$::' | \
|
sed 's:/[^/]*$::' | \
|
||||||
sort -u)
|
sort -u)
|
||||||
|
|
||||||
if [[ ${#pools[@]} -eq 0 ]]; then
|
if [[ ${#pools[@]} -eq 0 ]]; then
|
||||||
echo "[WARN] Keine geeigneten Datasets gefunden!"
|
warn "Keine geeigneten ZFS-Datasets gefunden"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local options=()
|
local options=()
|
||||||
local tagged_pools=()
|
|
||||||
|
|
||||||
for pool in "${pools[@]}"; do
|
for pool in "${pools[@]}"; do
|
||||||
local tag status
|
local tag status
|
||||||
tag=$(ssh -p "$SOURCEPORT" root@"$SOURCEHOST" zfs get -H -o value "$TAG_KEY" "$pool" 2>/dev/null)
|
tag="$(ssh -p "${SOURCEPORT:-22}" "${SOURCEUSER}@${SOURCEHOST}" \
|
||||||
if [[ "$tag" == "$TAG_VALUE" ]]; then
|
zfs get -H -o value "$tag_key" "$pool" 2>/dev/null)"
|
||||||
status="on"
|
[[ "$tag" == "$tag_value" ]] && status="on" || status="off"
|
||||||
tagged_pools+=("$pool")
|
options+=("$pool" "" "$status")
|
||||||
else
|
|
||||||
status="off"
|
|
||||||
fi
|
|
||||||
options+=("$pool" "$([[ "$status" == "on" ]] && echo 'TAGGED' || echo 'untagged')" "$status")
|
|
||||||
done
|
done
|
||||||
|
|
||||||
local selected
|
local selected
|
||||||
selected=$(whiptail --title "ZFS Pools auswählen" \
|
selected=$(dialog --clear \
|
||||||
--checklist "Wähle Datasets zum Taggen aus:" 20 78 12 \
|
--backtitle "miyagi-backup Installer" \
|
||||||
|
--title "ZFS Pools taggen" \
|
||||||
|
--checklist "Datasets für Replikation auswählen:" 20 78 12 \
|
||||||
"${options[@]}" \
|
"${options[@]}" \
|
||||||
3>&1 1>&2 2>&3)
|
3>&1 1>&2 2>&3)
|
||||||
|
clear
|
||||||
if [[ $? -ne 0 ]]; then
|
[[ $? -ne 0 ]] && info "ZFS-Tagging abgebrochen" && return
|
||||||
echo "[INFO] Auswahl abgebrochen."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
local selected_array=()
|
local selected_array=()
|
||||||
eval "selected_array=($selected)"
|
read -ra selected_array <<< "$selected"
|
||||||
|
|
||||||
echo "[INFO] Verarbeite Tagging für ausgewählte Pools:"
|
for pool in "${pools[@]}"; do
|
||||||
|
|
||||||
for ((i = 0; i < ${#options[@]}; i += 3)); do
|
|
||||||
local pool="${options[i]}"
|
|
||||||
local prev_status="${options[i+2]}"
|
|
||||||
local is_selected=0
|
local is_selected=0
|
||||||
|
local prev_status="off"
|
||||||
|
for ((i=0; i<${#options[@]}; i+=3)); do
|
||||||
|
[[ "${options[i]}" == "$pool" ]] && prev_status="${options[i+2]}" && break
|
||||||
|
done
|
||||||
for sel in "${selected_array[@]}"; do
|
for sel in "${selected_array[@]}"; do
|
||||||
if [[ "$sel" == "$pool" ]]; then
|
[[ "$sel" == "\"$pool\"" || "$sel" == "$pool" ]] && is_selected=1 && break
|
||||||
is_selected=1
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ "$is_selected" -eq 1 && "$prev_status" == "off" ]]; then
|
if [[ "$is_selected" -eq 1 && "$prev_status" == "off" ]]; then
|
||||||
echo " → Tagge $pool"
|
info "Tagge $pool"
|
||||||
run_or_echo ssh -p "$SOURCEPORT" root@"$SOURCEHOST" zfs set "$TAG_KEY=$TAG_VALUE" "$pool"
|
run ssh -p "${SOURCEPORT:-22}" "${SOURCEUSER}@${SOURCEHOST}" \
|
||||||
|
zfs set "${tag_key}=${tag_value}" "$pool"
|
||||||
elif [[ "$is_selected" -eq 0 && "$prev_status" == "on" ]]; then
|
elif [[ "$is_selected" -eq 0 && "$prev_status" == "on" ]]; then
|
||||||
echo " → Entferne Tag von $pool"
|
info "Entferne Tag von $pool"
|
||||||
run_or_echo ssh -p "$SOURCEPORT" root@"$SOURCEHOST" zfs inherit "$TAG_KEY" "$pool"
|
run ssh -p "${SOURCEPORT:-22}" "${SOURCEUSER}@${SOURCEHOST}" \
|
||||||
|
zfs inherit "$tag_key" "$pool"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
ok "ZFS-Tagging abgeschlossen"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── Hauptmenü ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
# URLs und Dateipfade als Variablen, ggf. in Kopfbereich auslagern
|
[[ $EUID -ne 0 ]] && die "Bitte als root ausführen"
|
||||||
CMK_URL="https://nc.sysops.de/index.php/s/YofRT5LBfX5ZDQs/download/cmk.deb"
|
|
||||||
CMK_FILE="/tmp/cmk.deb"
|
|
||||||
|
|
||||||
FIX_INTERFACE_NAMES_URL="https://raw.githubusercontent.com/bashclub/trmm-scripts/refs/heads/main/debian-fix-interface-names.sh"
|
ensure_dialog
|
||||||
|
|
||||||
MIYAGI_BACKUP_URL="https://gitea.perlbach24.de/scripte/miyagi-backup/raw/branch/main/miyagi-backup.sh"
|
echo ""
|
||||||
MIYAGI_CHECK_URL="https://gitea.perlbach24.de/scripte/miyagi-backup/raw/branch/main/miyagi-check.sh"
|
echo " ╔══════════════════════════════════════╗"
|
||||||
MIYAGI_CONVERT_URL="https://gitea.perlbach24.de/scripte/miyagi-backup/raw/branch/main/miyagi-convert.sh"
|
echo " ║ miyagi-backup Installer v3 ║"
|
||||||
|
echo " ╚══════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
DISK_SMART_INFO_URL="https://raw.githubusercontent.com/bashclub/checkmk-smart/main/disk_smart_info.py"
|
local choices
|
||||||
|
choices=$(dialog --clear \
|
||||||
|
--backtitle "miyagi-backup Installer v3" \
|
||||||
|
--title "Komponenten auswählen" \
|
||||||
|
--checklist "Was soll installiert werden?" 22 65 12 \
|
||||||
|
"miyagi" "miyagi-backup.sh + Verzeichnisse + Symlink" on \
|
||||||
|
"checkzfs" "checkzfs (ZFS-Replikationsprüfung)" on \
|
||||||
|
"snapage" "check-snapshot-age" on \
|
||||||
|
"cleansnaps" "cleansnaps (ZFS-Snapshot-Housekeeping)" on \
|
||||||
|
"smartinfo" "disk_smart_info (CheckMK Plugin)" on \
|
||||||
|
"ethtool" "ethtool (WOL-Unterstützung)" off \
|
||||||
|
"ssh" "SSH-Verbindung prüfen / Schlüssel kopieren" on \
|
||||||
|
"zfstag" "ZFS-Pools taggen (benötigt Config -c)" off \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
|
clear
|
||||||
|
|
||||||
CLEANSNAPS_URL="https://raw.githubusercontent.com/bashclub/zfs-housekeeping/refs/heads/main/cleansnaps.sh"
|
[[ $? -ne 0 ]] && echo "Abgebrochen." && exit 0
|
||||||
|
[[ -z "$choices" ]] && echo "Nichts ausgewählt." && exit 0
|
||||||
|
|
||||||
CHECK_SNAPSHOT_AGE_URL="https://gitea.perlbach24.de/scripte/check-zfs-replication/raw/branch/main/check-snapshot-age"
|
[[ "$choices" == *'"miyagi"'* || "$choices" == *'miyagi'* ]] && install_miyagi
|
||||||
|
[[ "$choices" == *'"checkzfs"'* || "$choices" == *'checkzfs'* ]] && install_checkzfs
|
||||||
|
[[ "$choices" == *'"snapage"'* || "$choices" == *'snapage'* ]] && install_check_snapshot_age
|
||||||
|
[[ "$choices" == *'"cleansnaps"'* || "$choices" == *'cleansnaps'* ]] && install_cleansnaps
|
||||||
|
[[ "$choices" == *'"smartinfo"'* || "$choices" == *'smartinfo'* ]] && install_disk_smart_info
|
||||||
|
[[ "$choices" == *'"ethtool"'* || "$choices" == *'ethtool'* ]] && install_ethtool
|
||||||
|
[[ "$choices" == *'"ssh"'* || "$choices" == *'ssh'* ]] && check_ssh
|
||||||
|
[[ "$choices" == *'"zfstag"'* || "$choices" == *'zfstag'* ]] && select_zfs_pools
|
||||||
|
|
||||||
CHECKZFS_URL="https://gitea.perlbach24.de/scripte/check-zfs-replication/raw/branch/main/checkzfs.py"
|
echo ""
|
||||||
|
ok "Installation abgeschlossen."
|
||||||
echo "[INFO] Starte Installation mit Konfig: $CONFIG_FILE"
|
echo ""
|
||||||
|
|
||||||
install_cmk
|
|
||||||
|
|
||||||
install_fix_interface_names
|
|
||||||
|
|
||||||
install_miyagi_scripts
|
|
||||||
|
|
||||||
install_checkmk_plugins
|
|
||||||
|
|
||||||
install_cleansnaps
|
|
||||||
|
|
||||||
install_check_snapshot_age
|
|
||||||
|
|
||||||
install_checkzfs
|
|
||||||
check_ssh
|
|
||||||
select_zfs_pools
|
|
||||||
|
|
||||||
echo "[OK] Installation abgeschlossen."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Start
|
|
||||||
parse_args "$@"
|
parse_args "$@"
|
||||||
main
|
main
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# miyagi-backup Updater v3
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
DEV_MODE=0
|
||||||
|
INSTALL_DIR="/opt/miyagi-backup"
|
||||||
|
CONFIG_DIR="/etc/miyagi-backup"
|
||||||
|
HOOKS_DIR="${CONFIG_DIR}/hooks.d/pre-shutdown"
|
||||||
|
SYMLINK="/usr/local/bin/miyagi-backup"
|
||||||
|
|
||||||
|
GITHUB_BASE="https://raw.githubusercontent.com/bashclub/miyagi-backup/main"
|
||||||
|
GITEA_BASE="https://gitea.perlbach24.de/scripte/miyagi-backup/raw/branch/main"
|
||||||
|
|
||||||
|
CHECKZFS_URL="https://gitea.perlbach24.de/scripte/check-zfs-replication/raw/branch/main/checkzfs.py"
|
||||||
|
CHECK_SNAPSHOT_AGE_URL="https://gitea.perlbach24.de/scripte/check-zfs-replication/raw/branch/main/check-snapshot-age"
|
||||||
|
CLEANSNAPS_URL="https://raw.githubusercontent.com/bashclub/zfs-housekeeping/refs/heads/main/cleansnaps.sh"
|
||||||
|
DISK_SMART_INFO_URL="https://raw.githubusercontent.com/bashclub/checkmk-smart/main/disk_smart_info.py"
|
||||||
|
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
|
||||||
|
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||||
|
info() { echo -e "${BLUE}[..]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[!!]${NC} $*"; }
|
||||||
|
die() { echo -e "${RED}[ERR]${NC} $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $0 [--dev]
|
||||||
|
|
||||||
|
--dev Update von Gitea (dev) statt GitHub (stable)
|
||||||
|
--help Diese Hilfe
|
||||||
|
EOF
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args() {
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--dev) DEV_MODE=1; shift ;;
|
||||||
|
--help) usage ;;
|
||||||
|
*) die "Unbekannter Parameter: $1" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
get_base_url() {
|
||||||
|
[[ "$DEV_MODE" -eq 1 ]] && echo "$GITEA_BASE" || echo "$GITHUB_BASE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Self-Update ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
self_update() {
|
||||||
|
local base
|
||||||
|
base="$(get_base_url)"
|
||||||
|
local self="${INSTALL_DIR}/miyagi-update.sh"
|
||||||
|
|
||||||
|
if [[ -f "$self" ]] && command -v curl >/dev/null; then
|
||||||
|
curl -fsSL "${base}/miyagi-update.sh" -o "${self}.new" 2>/dev/null \
|
||||||
|
&& mv "${self}.new" "$self" \
|
||||||
|
&& chmod +x "$self" \
|
||||||
|
&& info "Updater aktualisiert" \
|
||||||
|
|| rm -f "${self}.new"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── miyagi-backup.sh updaten ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
update_miyagi() {
|
||||||
|
local base
|
||||||
|
base="$(get_base_url)"
|
||||||
|
local source_label
|
||||||
|
[[ "$DEV_MODE" -eq 1 ]] && source_label="Gitea (dev)" || source_label="GitHub (stable)"
|
||||||
|
|
||||||
|
info "Aktualisiere miyagi-backup.sh von $source_label..."
|
||||||
|
|
||||||
|
if [[ ! -d "$INSTALL_DIR" ]]; then
|
||||||
|
warn "Installverzeichnis $INSTALL_DIR fehlt – bitte zuerst miyagi-install.sh ausführen"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl -fsSL "${base}/miyagi-backup.sh" -o "${INSTALL_DIR}/miyagi-backup.sh.new" || {
|
||||||
|
warn "Download fehlgeschlagen – miyagi-backup.sh bleibt unverändert"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
mv "${INSTALL_DIR}/miyagi-backup.sh.new" "${INSTALL_DIR}/miyagi-backup.sh"
|
||||||
|
chmod +x "${INSTALL_DIR}/miyagi-backup.sh"
|
||||||
|
|
||||||
|
# Symlink sicherstellen
|
||||||
|
ln -sf "${INSTALL_DIR}/miyagi-backup.sh" "$SYMLINK"
|
||||||
|
|
||||||
|
# config.example aktualisieren (nie .conf überschreiben)
|
||||||
|
curl -fsSL "${base}/config.example" -o "${CONFIG_DIR}/config.example.new" 2>/dev/null \
|
||||||
|
&& mv "${CONFIG_DIR}/config.example.new" "${CONFIG_DIR}/config.example" \
|
||||||
|
|| warn "config.example konnte nicht aktualisiert werden"
|
||||||
|
|
||||||
|
# Hooks-Verzeichnis sicherstellen
|
||||||
|
mkdir -p "$HOOKS_DIR"
|
||||||
|
|
||||||
|
# Example-Hook aktualisieren (nicht ausführbar = bleibt inaktiv)
|
||||||
|
curl -fsSL "${base}/hooks.d/pre-shutdown/example-hook.sh" \
|
||||||
|
-o "${HOOKS_DIR}/example-hook.sh" 2>/dev/null \
|
||||||
|
&& info "example-hook.sh aktualisiert" \
|
||||||
|
|| warn "example-hook.sh konnte nicht aktualisiert werden"
|
||||||
|
|
||||||
|
ok "miyagi-backup.sh aktualisiert"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Abhängigkeiten updaten (nur wenn bereits installiert) ─────────────────────
|
||||||
|
|
||||||
|
update_dep() {
|
||||||
|
local name="$1"
|
||||||
|
local url="$2"
|
||||||
|
local target="$3"
|
||||||
|
|
||||||
|
if [[ ! -f "$target" ]]; then
|
||||||
|
info "$name nicht installiert – übersprungen (install mit miyagi-install.sh)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Aktualisiere $name..."
|
||||||
|
curl -fsSL "$url" -o "${target}.new" 2>/dev/null || {
|
||||||
|
warn "$name Download fehlgeschlagen – bleibt unverändert"
|
||||||
|
rm -f "${target}.new"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
mv "${target}.new" "$target"
|
||||||
|
chmod +x "$target"
|
||||||
|
ok "$name aktualisiert"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Status-Zusammenfassung ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
print_status() {
|
||||||
|
echo ""
|
||||||
|
echo " ┌──────────────────────────────────────────┐"
|
||||||
|
printf " │ %-40s│\n" "miyagi-backup $([ -f "${INSTALL_DIR}/miyagi-backup.sh" ] && echo "✓ installiert" || echo "✗ fehlt")"
|
||||||
|
printf " │ %-40s│\n" "checkzfs $(command -v checkzfs &>/dev/null && echo "✓" || echo "✗ nicht installiert")"
|
||||||
|
printf " │ %-40s│\n" "check-snap-age $(command -v check-snapshot-age &>/dev/null && echo "✓" || echo "✗ nicht installiert")"
|
||||||
|
printf " │ %-40s│\n" "cleansnaps $(command -v cleansnaps &>/dev/null && echo "✓" || echo "✗ nicht installiert")"
|
||||||
|
printf " │ %-40s│\n" "ethtool $(command -v ethtool &>/dev/null && echo "✓" || echo "✗ nicht installiert")"
|
||||||
|
echo " └──────────────────────────────────────────┘"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Main ──────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
main() {
|
||||||
|
[[ $EUID -ne 0 ]] && die "Bitte als root ausführen"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo " ╔══════════════════════════════════════╗"
|
||||||
|
echo " ║ miyagi-backup Updater v3 ║"
|
||||||
|
echo " ╚══════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
self_update
|
||||||
|
update_miyagi
|
||||||
|
|
||||||
|
update_dep "checkzfs" "$CHECKZFS_URL" "/usr/local/bin/checkzfs"
|
||||||
|
update_dep "check-snapshot-age" "$CHECK_SNAPSHOT_AGE_URL" "/usr/local/bin/check-snapshot-age"
|
||||||
|
update_dep "cleansnaps" "$CLEANSNAPS_URL" "/usr/local/bin/cleansnaps"
|
||||||
|
update_dep "disk_smart_info" "$DISK_SMART_INFO_URL" "/usr/lib/check_mk_agent/plugins/disk_smart_info"
|
||||||
|
|
||||||
|
print_status
|
||||||
|
ok "Update abgeschlossen."
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args "$@"
|
||||||
|
main
|
||||||
Reference in new issue
Block a user