Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2fa79afd66 | |||
| 15e86400f0 | |||
| 1166a9c03e | |||
| c519aac1d9 | |||
| effc9646be | |||
| e2960c19b4 | |||
| 7d42984bfa | |||
| f0890c61ff | |||
| eab882aa44 | |||
| 87eb3307cb | |||
| 067fe8f1af | |||
| e2f64a4491 | |||
| ca8c2854a9 | |||
| 3fbb50e2bb | |||
| f7b9acd73b | |||
| 76a081468d | |||
| 75534137ff | |||
| beb53968a4 | |||
| dcae51da9f | |||
| 87c2d13069 | |||
| 602cfe21b0 | |||
| 7d3e53b42a | |||
| a15a7bad54 | |||
| 849b5e9570 | |||
| 1221a6cf23 | |||
| 0db8fee019 | |||
| 564939053a | |||
| 127431689a | |||
| f999ff6a87 | |||
| 8e121cb75c | |||
| 2329aa7377 | |||
| 7eb712fae4 | |||
| 93780dad1b | |||
| 802b7b8c63 | |||
| e6842f1de8 | |||
| 25220beae2 | |||
| 2954647d87 | |||
| 0efa02e0c2 |
+3
-1
@@ -1,13 +1,14 @@
|
|||||||
# Beispielhafte Aufrufe
|
# Beispielhafte Aufrufe
|
||||||
|
|
||||||
Befehl Wirkung
|
Befehl Wirkung
|
||||||
|
```
|
||||||
./miyagi-backup.sh -c /etc/miyagi.conf Führt den vollständigen Backup-Prozess aus
|
./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 help Listet alle verfügbaren Funktionen
|
||||||
./miyagi-backup.sh run_updates Führt nur Updates lokal aus
|
./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_pbs_backup Führt nur das vzdump-PBS-Backup aus
|
||||||
./miyagi-backup.sh run_maintenance Nur Wartung (Prune + GC)
|
./miyagi-backup.sh run_maintenance Nur Wartung (Prune + GC)
|
||||||
./miyagi-backup.sh shutdown_if_requested Prüft ob Shutdown nötig ist
|
./miyagi-backup.sh shutdown_if_requested Prüft ob Shutdown nötig ist
|
||||||
|
```
|
||||||
|
|
||||||
####
|
####
|
||||||
|
|
||||||
@@ -25,3 +26,4 @@ Dieses Skript überprüft eine Bash-basierte Konfigurationsdatei für ein Backup
|
|||||||
## Nutzung:
|
## Nutzung:
|
||||||
```bash
|
```bash
|
||||||
./miyagi-check.sh /pfad/zur/config
|
./miyagi-check.sh /pfad/zur/config
|
||||||
|
```
|
||||||
+22
-6
@@ -1,30 +1,46 @@
|
|||||||
#Edit all Variables for best Experience
|
#Edit all Variables for best Experience
|
||||||
|
|
||||||
SSHPORT='22' # SSH-Port, normalerweise 22
|
|
||||||
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?
|
||||||
|
DDNS_GATEWAY='192.168.66.1' # Gateway
|
||||||
|
DDNS_SERVER='1.1.1.1' # Externer DNS Server
|
||||||
|
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
|
||||||
SOURCEHOST='192.168.50.200' # IP des Quell-Proxmox-Servers
|
SOURCEHOST='192.168.50.200' # IP des Quell-Proxmox-Servers
|
||||||
|
|
||||||
# Replikation (ZFS)
|
# Replikation (ZFS)
|
||||||
ZFSROOT='rpool/data' # Erstes Dataset vom Quellsystem
|
ZFSROOT='rpool/data' # Erstes Dataset vom Quellsystem
|
||||||
ZFSSECOND='rpool-hdd/data' # Optional zweites Dataset
|
ZFSSECOND='rpool-hdd/data' # Optional zweites Dataset
|
||||||
ZFSTRGT='rpool-ssd1/repl/pve200' # Zielpfad für Replikation
|
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-198-ssd' # Benutzer-Tag für ZFS
|
ZPUSHTAG='bashclub:zsync' # 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-rz' # Suffix für Snapshot-Autoengine
|
ZPUSHLABEL='zsync' # 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'
|
||||||
BACKUPSTORE='backup' # Datastore auf Quell-Proxmox
|
BACKUPSTORE='backup' # Datastore auf Quell-Proxmox
|
||||||
BACKUPSTOREPBS='backup' # Datastore auf PBS
|
BACKUPSTOREPBS='backup' # Datastore auf PBS
|
||||||
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
|
||||||
|
# External Piggyback Host
|
||||||
|
EPIGGYBACK='no' # Piggyback-Daten an Monitoring-Ziel senden?
|
||||||
|
EPIGGYBACK_PORT='222' # SSH-Port für EPIGGYBACK_HOST
|
||||||
|
EPIGGYBACK_HOST='192.168.66.10' # Monitoring-Zielhost für Piggyback
|
||||||
|
|
||||||
|
# External Checkzfs Host
|
||||||
|
ECHECKZFS='no' # check_zfs-Output an Monitoring-Ziel senden?
|
||||||
|
ECHECKZFS_PORT='2222' # SSH-Port für ECHECKZFS_HOST
|
||||||
|
ECHECKZFS_HOST='192.168.50.210' # Monitoring-Zielhost für check_zfs
|
||||||
+613
-270
@@ -7,367 +7,710 @@ SCRIPT_NAME=$(basename "$0")
|
|||||||
LOGFILE="/var/log/${SCRIPT_NAME%.sh}.log"
|
LOGFILE="/var/log/${SCRIPT_NAME%.sh}.log"
|
||||||
|
|
||||||
log() {
|
log() {
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo "Usage:"
|
echo "Usage:"
|
||||||
echo " $0 -c /path/to/config # Full backup run"
|
echo " $0 -c /path/to/config # Full backup run"
|
||||||
echo " $0 [function] # Run individual function"
|
echo " $0 [function] # Run individual function"
|
||||||
echo " $0 help # Show available functions"
|
echo " $0 help # Show available functions"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_FILE=""
|
CONFIG_FILE=""
|
||||||
while getopts "c:" opt; do
|
while getopts "c:" opt; do
|
||||||
case "$opt" in
|
case "$opt" in
|
||||||
c) CONFIG_FILE="$OPTARG" ;;
|
c) CONFIG_FILE="$OPTARG" ;;
|
||||||
*) usage ;;
|
*) usage ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Bei Einzelaufruf muss Konfiguration geladen sein
|
|
||||||
shift $((OPTIND - 1))
|
shift $((OPTIND - 1))
|
||||||
|
|
||||||
if [[ -n "${CONFIG_FILE:-}" ]]; then
|
if [[ -n "${CONFIG_FILE:-}" ]]; then
|
||||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||||
log "ERROR: Configuration file not found: $CONFIG_FILE"
|
log "ERROR: Configuration file not found: $CONFIG_FILE"
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! bash -n "$CONFIG_FILE"; then
|
|
||||||
log "Syntaxfehler in Konfigurationsdatei $CONFIG_FILE"
|
|
||||||
exit 1
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! bash -n "$CONFIG_FILE"; then
|
||||||
|
log "Syntax error in configuration file $CONFIG_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
source "$CONFIG_FILE"
|
||||||
|
|
||||||
|
REQUIRED_VARS=(
|
||||||
|
SOURCEPORT BACKUPSERVER ZSYNC MAINTDAY SHUTDOWN UPDATES
|
||||||
|
SOURCEHOST ZFSROOT ZFSSECOND ZFSTRGT ZPUSHTAG ZPUSHMINKEEP ZPUSHKEEP ZPUSHLABEL
|
||||||
|
PBSHOST BACKUPSTORE BACKUPSTOREPBS BACKUPEXCLUDE REPLEXCLUDE DYNROUTE REBOOT DDNS_GATEWAY
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ "${DYNROUTE,,}" == "yes" ]]; then
|
||||||
|
REQUIRED_VARS+=(DDNS_GATEWAY)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
source "$CONFIG_FILE"
|
MISSING_VARS=()
|
||||||
|
for var in "${REQUIRED_VARS[@]}"; do
|
||||||
# ==========================
|
if [[ -z "${!var:-}" ]]; then
|
||||||
# Konfigurationsprüfung
|
MISSING_VARS+=("$var")
|
||||||
# ==========================
|
|
||||||
REQUIRED_VARS=(
|
|
||||||
SSHPORT
|
|
||||||
BACKUPSERVER
|
|
||||||
ZSYNC
|
|
||||||
MAINTDAY
|
|
||||||
SHUTDOWN
|
|
||||||
UPDATES
|
|
||||||
SOURCEHOST
|
|
||||||
ZFSROOT
|
|
||||||
ZFSSECOND
|
|
||||||
ZFSTRGT
|
|
||||||
ZPUSHTAG
|
|
||||||
ZPUSHMINKEEP
|
|
||||||
ZPUSHKEEP
|
|
||||||
ZPUSHLABEL
|
|
||||||
ZPUSHFILTER
|
|
||||||
PBSHOST
|
|
||||||
BACKUPSTORE
|
|
||||||
BACKUPSTOREPBS
|
|
||||||
BACKUPEXCLUDE
|
|
||||||
REPLEXCLUDE
|
|
||||||
)
|
|
||||||
|
|
||||||
MISSING_VARS=()
|
|
||||||
for var in "${REQUIRED_VARS[@]}"; do
|
|
||||||
#if [[ -z "${!var:-}" ]]; then
|
|
||||||
if ! declare -p "$var" &>/dev/null || [[ -z "${!var}" ]]; then
|
|
||||||
MISSING_VARS+=("$var")
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ "${#MISSING_VARS[@]}" -ne 0 ]]; then
|
|
||||||
log "Fehlende Konfigurationsvariablen:"
|
|
||||||
for var in "${MISSING_VARS[@]}"; do
|
|
||||||
log " - $var"
|
|
||||||
done
|
|
||||||
log "Breche ab – bitte Konfigurationsdatei prüfen."
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
log "Alle erforderlichen Konfigurationsvariablen sind gesetzt."
|
|
||||||
fi
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "${#MISSING_VARS[@]}" -gt 0 ]]; then
|
||||||
|
log "Missing configuration variables:"
|
||||||
|
for var in "${MISSING_VARS[@]}"; do
|
||||||
|
log " - $var"
|
||||||
|
done
|
||||||
|
log "Aborting — please check your configuration file."
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
log "All required configuration variables are set."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Funktionen
|
rssh() { ssh -p "$SOURCEPORT" "$@"; }
|
||||||
remote_ssh() {
|
rscp() { scp -P "$SOURCEPORT" "$@"; }
|
||||||
ssh -p "$SSHPORT" "$@"
|
|
||||||
|
get_sourcehostname() {
|
||||||
|
if [[ -z "${SOURCEHOSTNAME:-}" ]]; then
|
||||||
|
log "SOURCEHOSTNAME is empty, retrieving via SSH from $SOURCEHOST..."
|
||||||
|
SOURCEHOSTNAME=$(rssh "$SOURCEHOST" hostname)
|
||||||
|
log "Detected SOURCEHOSTNAME: $SOURCEHOSTNAME"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
remote_scp() {
|
zfs_replace() {
|
||||||
scp -P "$SSHPORT" "$@"
|
local status
|
||||||
|
status=$(zpool status)
|
||||||
|
|
||||||
|
if echo "$status" | grep -q 'scan: resilver in progress'; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$status" | grep -qE 'replacing-[0-9]'; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_replace() {
|
||||||
|
local interval_seconds=300 # Alle 5 Minuten prüfen
|
||||||
|
local waited_minutes=0
|
||||||
|
|
||||||
|
log "ZFS Replace-Vorgang erkannt – warte unbegrenzt auf Abschluss..."
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
if ! zfs_replace; then
|
||||||
|
log "Replace abgeschlossen nach $waited_minutes Minuten – fahre jetzt herunter."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
log "Replace läuft noch – erneut prüfen in $((interval_seconds/60)) Minuten..."
|
||||||
|
sleep "$interval_seconds"
|
||||||
|
((waited_minutes+=interval_seconds/60))
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
set_wol_g_enabled() {
|
set_wol_g_enabled() {
|
||||||
log "Pruefe, ob ethtool installiert ist..."
|
log "Checking if ethtool is installed..."
|
||||||
|
|
||||||
if ! command -v ethtool >/dev/null 2>&1; then
|
if ! command -v ethtool >/dev/null 2>&1; then
|
||||||
log "ethtool ist nicht installiert, versuche Installation..."
|
log "ethtool is not installed, attempting installation..."
|
||||||
apt update && apt install -y ethtool || {
|
apt update && apt install -y ethtool || {
|
||||||
log "Fehler: ethtool konnte nicht installiert werden."
|
log "Error: ethtool could not be installed."
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
log "ethtool ist bereits installiert."
|
log "ethtool is already installed."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Pruefe und setze Wake-on-LAN (WOL) auf 'g' nur bei Interfaces mit statischer IP..."
|
log "Setting Wake-on-LAN (WOL) to 'g' on interfaces with static IP..."
|
||||||
|
|
||||||
for iface in $(ls /sys/class/net | grep -vE '^(lo|tap|vmbr|veth|br|docker|bond|wl)'); do
|
for iface in $(ls /sys/class/net | grep -vE '^(lo|tap|vmbr|veth|br|docker|bond|wl)'); do
|
||||||
if [[ -e "/sys/class/net/$iface/device" ]]; then
|
if [[ -e "/sys/class/net/$iface/device" ]]; then
|
||||||
log "Bearbeite physisches Interface: $iface"
|
log "Processing physical interface: $iface"
|
||||||
|
|
||||||
# Aktuellen WOL-Status pruefen
|
current_wol=$(ethtool "$iface" 2>/dev/null | awk '/Wake-on:/ {print $2}')
|
||||||
current_wol=$(ethtool "$iface" 2>/dev/null | awk '/Wake-on:/ {print $2}')
|
if [[ "$current_wol" != "g" ]]; then
|
||||||
if [[ "$current_wol" != "g" ]]; then
|
log "Setting WOL to 'g' for $iface..."
|
||||||
log "Setze WOL auf 'g' fuer $iface..."
|
ethtool -s "$iface" wol g || log "Error setting WOL on $iface"
|
||||||
ethtool -s "$iface" wol g || log "Fehler beim Setzen von WOL auf $iface"
|
else
|
||||||
else
|
log "WOL already set to 'g' for $iface"
|
||||||
log "WOL ist bereits korrekt auf 'g' fuer $iface"
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
# Pruefen, ob ein 'iface $iface inet static' Eintrag existiert
|
if grep -qE "^\s*iface\s+$iface\s+inet\s+static" /etc/network/interfaces; then
|
||||||
if grep -qE "^\s*iface\s+$iface\s+inet\s+static" /etc/network/interfaces; then
|
if ! grep -A 5 -E "^\s*iface\s+$iface\s+inet\s+static" /etc/network/interfaces | grep -q "post-up ethtool -s $iface wol g"; then
|
||||||
if ! grep -A 5 -E "^\s*iface\s+$iface\s+inet\s+static" /etc/network/interfaces | grep -q "post-up ethtool -s $iface wol g"; then
|
log "Adding WOL command in static block for $iface..."
|
||||||
log "Ergaenze WOL-Befehl im statischen Block fuer $iface..."
|
sed -i "/^\s*iface\s\+$iface\s\+inet\s\+static/a \ post-up ethtool -s $iface wol g" /etc/network/interfaces
|
||||||
sed -i "/^\s*iface\s\+$iface\s\+inet\s\+static/a \ post-up ethtool -s $iface wol g" /etc/network/interfaces
|
else
|
||||||
else
|
log "WOL command already present in static block for $iface."
|
||||||
log "WOL-Befehl fuer $iface ist bereits im statischen Block vorhanden."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log "Kein statischer Eintrag fuer $iface gefunden, keine Aenderung vorgenommen."
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
done
|
else
|
||||||
|
log "No static entry found for $iface, no changes made."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
write_zsync_config() {
|
write_zsync_config() {
|
||||||
local conf_file="/etc/bashclub/$SOURCEHOST.conf"
|
get_sourcehostname
|
||||||
log "Writing zsync config to $conf_file"
|
local conf_file="/etc/bashclub/$SOURCEHOST.conf"
|
||||||
{
|
log "Writing zsync config to $conf_file"
|
||||||
echo "target=$ZFSTRGT"
|
{
|
||||||
echo "source=root@$SOURCEHOST"
|
echo "target=$ZFSTRGT"
|
||||||
echo "sshport=$SSHPORT"
|
echo "source=root@$SOURCEHOST"
|
||||||
echo "tag=$ZPUSHTAG"
|
echo "sshport=$SOURCEPORT"
|
||||||
echo "snapshot_filter=\"$ZPUSHFILTER\""
|
echo "tag=$ZPUSHTAG"
|
||||||
echo "min_keep=$ZPUSHMINKEEP"
|
echo "snapshot_filter=\"$ZPUSHFILTER\""
|
||||||
echo "zfs_auto_snapshot_keep=$ZPUSHKEEP"
|
echo "min_keep=$ZPUSHMINKEEP"
|
||||||
echo "zfs_auto_snapshot_label=$ZPUSHLABEL"
|
echo "zfs_auto_snapshot_keep=$ZPUSHKEEP"
|
||||||
echo "zfs_auto_snapshot_engine=internal"
|
echo "zfs_auto_snapshot_label=$ZPUSHLABEL"
|
||||||
echo "checkzfs_disabled=0"
|
echo "zfs_auto_snapshot_engine=internal"
|
||||||
echo "checkzfs_local=0"
|
echo "checkzfs_disabled=0"
|
||||||
echo "checkzfs_prefix=miyagi-$SOURCEHOSTNAME-$(hostname)-$ZPUSHTAG"
|
echo "checkzfs_local=0"
|
||||||
echo "checkzfs_max_age=1500,2000"
|
echo "checkzfs_prefix=miyagi-$SOURCEHOSTNAME-$(hostname)-$ZPUSHTAG"
|
||||||
echo "checkzfs_max_snapshot_count=180,200"
|
echo "checkzfs_max_age=1500,2000"
|
||||||
echo "checkzfs_spool=1"
|
echo "checkzfs_max_snapshot_count=180,200"
|
||||||
echo "checkzfs_spool_maxage=90000"
|
echo "checkzfs_spool=1"
|
||||||
} > "$conf_file"
|
echo "checkzfs_spool_maxage=90000"
|
||||||
|
} > "$conf_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
run_zsync() {
|
run_zsync() {
|
||||||
if [[ "$ZSYNC" != "no" ]]; then
|
write_zsync_config
|
||||||
/usr/bin/bashclub-zsync -c "/etc/bashclub/$SOURCEHOST.conf"
|
if [[ "$ZSYNC" != "no" ]]; then
|
||||||
else
|
/usr/bin/bashclub-zsync -c "/etc/bashclub/$SOURCEHOST.conf"
|
||||||
log "Zsync is disabled"
|
else
|
||||||
fi
|
log "Zsync is disabled."
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
run_remote_updates() {
|
run_remote_updates() {
|
||||||
if [[ "$UPDATES" == "yes" ]]; then
|
if [[ "${UPDATES,,}" == "yes" ]]; then
|
||||||
ssh "$PBSHOST" apt update && apt dist-upgrade -y
|
log "Running updates on local system..."
|
||||||
|
|
||||||
|
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 "Remote updates disabled"
|
log_msg "INFO" "Update abgeschlossen. Kein Neustart erforderlich."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
else
|
||||||
|
log "Updates disabled (UPDATES=$UPDATES)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
send_piggyback() {
|
||||||
|
get_sourcehostname
|
||||||
|
local combined_host="miyagi-${SOURCEHOSTNAME}-$(hostname)"
|
||||||
|
local filename="90000_${combined_host}"
|
||||||
|
|
||||||
|
log "Reminder: Add a host named ${combined_host} in CMK (without Agent, Piggyback enabled)!"
|
||||||
|
log "Creating piggyback file: $filename"
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "<<<<${combined_host}>>>>"
|
||||||
|
/usr/bin/check_mk_agent
|
||||||
|
echo "<<<<>>>>"
|
||||||
|
} > "$filename"
|
||||||
|
|
||||||
|
if rscp "$filename" "$SOURCEHOST:/var/lib/check_mk_agent/spool/"; then
|
||||||
|
log "Piggyback data successfully sent to $SOURCEHOST"
|
||||||
|
else
|
||||||
|
log "ERROR: Failed to send piggyback data to $SOURCEHOST"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$filename"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_pbs_log_clear() {
|
||||||
|
log "Running clear LOG on PBS host ($PBSHOST)..."
|
||||||
|
|
||||||
|
if ! ssh -p "$PBSPORT" root@"$PBSHOST" \
|
||||||
|
find /var/log/proxmox-backup/tasks/ -type f -mtime +60 -delete; then
|
||||||
|
log "Error clearing PBS LOG on $PBSHOST"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
run_remote_updates() {
|
run_pbs_backup() {
|
||||||
if [[ "$UPDATES" == "yes" ]]; then
|
if [[ "${BACKUPSERVER,,}" != "yes" ]]; then
|
||||||
ssh "$PBSHOST" apt update && apt dist-upgrade -y
|
log "PBS Backup übersprungen: BACKUPSERVER='$BACKUPSERVER' (muss 'yes' sein)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
get_sourcehostname
|
||||||
|
log "Starte PBS Backup auf Host: $SOURCEHOST"
|
||||||
|
|
||||||
|
# PBS-Storage prüfen und ggf. aktivieren
|
||||||
|
log "Prüfe, ob PBS-Storage '$BACKUPSTORE' auf $SOURCEHOST aktiviert ist..."
|
||||||
|
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..."
|
||||||
|
rssh root@"$SOURCEHOST" "pvesm set '$BACKUPSTORE' --disable 0 && sleep 10"
|
||||||
|
pbs_enabled_by_script=true
|
||||||
|
else
|
||||||
|
log "PBS-Storage '$BACKUPSTORE' ist bereits aktiviert."
|
||||||
|
pbs_enabled_by_script=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
vzdump_success=false
|
||||||
|
|
||||||
|
# Versuch: vzdump mit Change Detection
|
||||||
|
## Eintrag muss in /etc/vzdump.conf entries-max 100000000
|
||||||
|
log "Starte vzdump mit Change Detection..."
|
||||||
|
if rssh root@"$SOURCEHOST" vzdump --pbs-change-detection-mode metadata \
|
||||||
|
--node "$SOURCEHOSTNAME" --storage "$BACKUPSTORE" \
|
||||||
|
--exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \
|
||||||
|
--notes-template '{{guestname}}'; then
|
||||||
|
|
||||||
|
log "vzdump (Change Detection) erfolgreich."
|
||||||
|
vzdump_success=true
|
||||||
|
else
|
||||||
|
log "vzdump (Change Detection) fehlgeschlagen – versuche Fallback ohne Change Detection..."
|
||||||
|
|
||||||
|
if rssh root@"$SOURCEHOST" vzdump \
|
||||||
|
--node "$SOURCEHOSTNAME" --storage "$BACKUPSTORE" \
|
||||||
|
--exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \
|
||||||
|
--notes-template '{{guestname}}'; then
|
||||||
|
|
||||||
|
log "Fallback-vzdump erfolgreich."
|
||||||
|
vzdump_success=true
|
||||||
else
|
else
|
||||||
log "Remote updates disabled"
|
log "FEHLER: vzdump fehlgeschlagen – auch im Fallback."
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PBS-Storage ggf. wieder deaktivieren
|
||||||
|
if [[ "$vzdump_success" == true && "$pbs_enabled_by_script" == true ]]; then
|
||||||
|
log "Deaktiviere temporär aktiviertes PBS-Storage '$BACKUPSTORE' auf $SOURCEHOST..."
|
||||||
|
rssh root@"$SOURCEHOST" "pvesm set '$BACKUPSTORE' --disable 1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Monitoring-Ausgabe
|
||||||
|
if [[ "$vzdump_success" == true ]]; then
|
||||||
|
echo "0 DailyPBS - Daily Backup erfolgreich" > /tmp/cmk_tmp.out
|
||||||
|
else
|
||||||
|
echo "2 DailyPBS - Daily Backup FEHLGESCHLAGEN" > /tmp/cmk_tmp.out
|
||||||
|
fi
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "<<<local>>>"
|
||||||
|
cat /tmp/cmk_tmp.out
|
||||||
|
} > /tmp/90000_checkpbs
|
||||||
|
|
||||||
|
rscp /tmp/90000_checkpbs root@"$SOURCEHOST":/var/lib/check_mk_agent/spool \
|
||||||
|
|| log "Fehler beim Übertragen des Monitoring-Outputs via SCP"
|
||||||
|
|
||||||
|
rm -f /tmp/cmk_tmp.out /tmp/90000_checkpbs
|
||||||
|
#write_pbs_status
|
||||||
}
|
}
|
||||||
|
|
||||||
send_piggyback_data() {
|
|
||||||
# Falls SOURCEHOSTNAME leer ist, ueber SSH vom Zielhost ermitteln
|
run_maintenance() {
|
||||||
if [[ -z "${SOURCEHOSTNAME:-}" ]]; then
|
if [[ "${BACKUPSERVER,,}" != "yes" ]]; then
|
||||||
log "SOURCEHOSTNAME is empty retrieving via SSH from $SOURCEHOST..."
|
log "PBS Backup wird übersprungen (BACKUPSERVER=$BACKUPSERVER)"
|
||||||
SOURCEHOSTNAME=$(ssh -p "$SSHPORT" "$SOURCEHOST" hostname)
|
return
|
||||||
log "Detected SOURCEHOSTNAME: $SOURCEHOSTNAME"
|
fi
|
||||||
|
|
||||||
|
if [[ "$(date +%u)" == "$MAINTDAY" ]]; then
|
||||||
|
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)
|
||||||
|
ssh -p $PBSPORT root@"$PBSHOST" proxmox-backup-manager prune-job run "$PRUNEJOB"
|
||||||
|
ssh -p $PBSPORT root@"$PBSHOST" proxmox-backup-manager garbage-collection start "$BACKUPSTOREPBS"
|
||||||
|
ssh -p $PBSPORT root@"$PBSHOST" proxmox-backup-manager verify backup
|
||||||
|
else
|
||||||
|
log "No maintenance scheduled for today."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_scrub_stop() {
|
||||||
|
local mode="$1" # "local" oder "remote"
|
||||||
|
local ssh_cmd=()
|
||||||
|
|
||||||
|
if [[ "$mode" == "remote" ]]; then
|
||||||
|
ssh_cmd=(ssh -p "$SOURCEPORT" root@"$SOURCEHOST")
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${ssh_cmd[@]}" bash -c '
|
||||||
|
for pool in $(zpool list -H -o name); do
|
||||||
|
echo "Stopping scrub on pool: $pool"
|
||||||
|
if zpool status "$pool" | grep -q "scrub in progress"; then
|
||||||
|
if zpool scrub -s "$pool"; then
|
||||||
|
echo "Scrub stopped on $pool"
|
||||||
|
else
|
||||||
|
echo "Error stopping scrub on $pool"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No active scrub on $pool"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
write_pbs_status() {
|
||||||
|
local spool_file="/var/lib/check_mk_agent/spool/90000_checkpbs_local"
|
||||||
|
local repo="$BACKUPSTOREPBS"
|
||||||
|
local tmpfile
|
||||||
|
tmpfile=$(mktemp)
|
||||||
|
|
||||||
|
if ! command -v proxmox-backup-client >/dev/null 2>&1; then
|
||||||
|
log "proxmox-backup-client nicht installiert – PBS-Status-Ausgabe übersprungen."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "<<<local>>>" > "$tmpfile"
|
||||||
|
|
||||||
|
local now
|
||||||
|
now=$(date +%s)
|
||||||
|
|
||||||
|
# === Schwellenwert(e) parsen ===
|
||||||
|
local warn_threshold=0
|
||||||
|
local crit_threshold=86400 # Default kritisch ab 24h
|
||||||
|
|
||||||
|
if [[ -n "${PBSBACKUP_STATUS:-}" ]]; then
|
||||||
|
IFS=',' read -r t1 t2 <<< "$PBSBACKUP_STATUS"
|
||||||
|
if [[ "$t1" =~ ^[0-9]+$ && -z "$t2" ]]; then
|
||||||
|
# Nur ein Wert vorhanden
|
||||||
|
crit_threshold=$t1
|
||||||
|
log "Verwende einfachen Schwellenwert: CRIT=${crit_threshold}s"
|
||||||
|
elif [[ "$t1" =~ ^[0-9]+$ && "$t2" =~ ^[0-9]+$ ]]; then
|
||||||
|
warn_threshold=$t1
|
||||||
|
crit_threshold=$t2
|
||||||
|
log "Verwende gestaffelte Schwellenwerte: WARN=${warn_threshold}s, CRIT=${crit_threshold}s"
|
||||||
|
else
|
||||||
|
log "WARNUNG: Ungültiges PBSBACKUP_STATUS-Format – Fallback: CRIT=${crit_threshold}s"
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backup-Daten extrahieren
|
||||||
|
rssh root@"$SOURCEHOST" proxmox-backup-client list --repository "$repo" --output-format json 2>/dev/null | \
|
||||||
|
grep -E '"backup-id"|"backup-type"|"backup-time"' | \
|
||||||
|
sed -E 's/[",]//g; s/^ *//' > "$tmpfile.json"
|
||||||
|
|
||||||
|
local backup_type="" backup_id="" backup_time=""
|
||||||
|
while read -r line; do
|
||||||
|
case "$line" in
|
||||||
|
backup-type:*)
|
||||||
|
backup_type="${line#*: }"
|
||||||
|
;;
|
||||||
|
backup-id:*)
|
||||||
|
backup_id="${line#*: }"
|
||||||
|
;;
|
||||||
|
backup-time:*)
|
||||||
|
backup_time="${line#*: }"
|
||||||
|
|
||||||
|
if [[ -n "$backup_type" && -n "$backup_id" && -n "$backup_time" ]]; then
|
||||||
|
local age status timestamp msg
|
||||||
|
age=$((now - backup_time))
|
||||||
|
timestamp=$(date -d "@$backup_time" "+%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
if (( age < warn_threshold )); then
|
||||||
|
status=0
|
||||||
|
elif (( age < crit_threshold )); then
|
||||||
|
status=1
|
||||||
|
else
|
||||||
|
status=2
|
||||||
|
fi
|
||||||
|
|
||||||
|
msg="$backup_type/$backup_id last backup $timestamp (age: $((age / 3600))h)"
|
||||||
|
echo "$status PBS_${backup_type}_${backup_id} - $msg" >> "$tmpfile"
|
||||||
|
|
||||||
|
# Reset
|
||||||
|
backup_type="" backup_id="" backup_time=""
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < "$tmpfile.json"
|
||||||
|
|
||||||
|
rm -f "$tmpfile.json"
|
||||||
|
mv "$tmpfile" "$spool_file"
|
||||||
|
log "PBS Backup Status lokal geschrieben: $spool_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown_now() {
|
||||||
|
if [[ "${SHUTDOWN,,}" == "yes" ]]; then
|
||||||
|
if zfs_replace; then
|
||||||
|
log "ZFS Replace-Vorgang erkannt – warte bis zum Abschluss..."
|
||||||
|
if ! wait_replace; then
|
||||||
|
log "Shutdown abgebrochen – Replace ist nach max. Wartezeit noch nicht abgeschlossen."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
send_piggyback
|
||||||
|
send_piggyback_external
|
||||||
|
send_checkzfs_external
|
||||||
|
log "Shutting down now...in 60sec"
|
||||||
|
sleep 60
|
||||||
|
shutdown now
|
||||||
|
else
|
||||||
|
log "No shutdown requested."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
send_piggyback_external() {
|
||||||
|
if [[ "${EPIGGYBACK,,}" != "yes" ]]; then
|
||||||
|
log "Externer Piggyback-Export deaktiviert."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
write_zsync_config
|
||||||
|
get_sourcehostname
|
||||||
|
log "Ermittelter SOURCEHOSTNAME: $SOURCEHOSTNAME"
|
||||||
|
|
||||||
local combined_host="miyagi-${SOURCEHOSTNAME}-$(hostname)"
|
local combined_host="miyagi-${SOURCEHOSTNAME}-$(hostname)"
|
||||||
local filename="90000_${combined_host}"
|
local spool_file="90000_${combined_host}_external"
|
||||||
|
local temp_dir
|
||||||
|
temp_dir=$(mktemp -d)
|
||||||
|
|
||||||
log "Do nott forget to add a Host in CMK named: ${combined_host} (without Agent, Piggyback enabled)!"
|
log "Erzeuge temporäre Piggyback-Datei: $temp_dir/$spool_file"
|
||||||
log "Creating piggyback file: $filename"
|
|
||||||
|
|
||||||
{
|
{
|
||||||
echo "<<<<${combined_host}>>>>"
|
echo "<<<<${combined_host}>>>>"
|
||||||
/usr/bin/check_mk_agent
|
/usr/bin/check_mk_agent
|
||||||
echo "<<<<>>>>"
|
echo "<<<<>>>>"
|
||||||
} > "$filename"
|
} > "$temp_dir/$spool_file"
|
||||||
|
|
||||||
if scp -P "$SSHPORT" "$filename" "$SOURCEHOST:/var/lib/check_mk_agent/spool/"; then
|
if scp -P "$EPIGGYBACK_PORT" "$temp_dir/$spool_file" "$EPIGGYBACK_HOST:/var/lib/check_mk_agent/spool/"; then
|
||||||
log "Piggyback data successfully sent to $SOURCEHOST"
|
log "Piggyback-Daten erfolgreich an $EPIGGYBACK_HOST gesendet: $spool_file"
|
||||||
else
|
else
|
||||||
log "ERROR: Failed to send piggyback data to $SOURCEHOST"
|
log "Fehler beim Übertragen der Piggyback-Daten an $EPIGGYBACK_HOST"
|
||||||
|
rm -rf "$temp_dir"
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
rm -rf "$temp_dir"
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
run_pbs_backup() {
|
send_checkzfs_external() {
|
||||||
if [[ -z "${SOURCEHOSTNAME:-}" ]]; then
|
if [[ "${ECHECKZFS,,}" != "yes" ]]; then
|
||||||
SOURCEHOSTNAME=$(ssh "$SOURCEHOST" hostname)
|
log "Externer Piggyback-Export deaktiviert."
|
||||||
|
return
|
||||||
fi
|
fi
|
||||||
|
local config="/etc/bashclub/${SOURCEHOST}.conf"
|
||||||
log "Running PBS vzdump job..."
|
if [[ ! -f "$config" ]]; then
|
||||||
|
log "Konfigurationsdatei fehlt: $config"
|
||||||
# PBS-Storage ggf. aktivieren
|
return 1
|
||||||
log "Checking if PBS storage '$BACKUPSTORE' is enabled on $SOURCEHOST..."
|
|
||||||
if ssh root@"$SOURCEHOST" "pvesm status | grep -w '$BACKUPSTORE' | grep -q 'disabled'"; then
|
|
||||||
log "PBS storage '$BACKUPSTORE' is disabled. Attempting to enable...sleep 10 Sekunden"
|
|
||||||
ssh root@"$SOURCEHOST" "pvesm set '$BACKUPSTORE' --disable 0 && sleep 10"
|
|
||||||
pbs_enabled_by_script=true
|
|
||||||
else
|
|
||||||
log "PBS storage '$BACKUPSTORE' is already enabled."
|
|
||||||
pbs_enabled_by_script=false
|
|
||||||
fi
|
fi
|
||||||
|
source "$config"
|
||||||
|
|
||||||
vzdump_success=false
|
if [[ "${ECHECKZFS,,}" != "yes" ]]; then
|
||||||
|
log "Externer Check-ZFS deaktiviert (ECHECKZFS=$ECHECKZFS)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
write_zsync_config
|
||||||
|
get_sourcehostname # Setzt SOURCEHOSTNAME, wenn nicht vorhanden
|
||||||
|
|
||||||
# Hauptversuch mit --pbs-change-detection-mode
|
local checkzfs_cmd="${checkzfs_cmd:-$(command -v checkzfs)}"
|
||||||
if ssh root@"$SOURCEHOST" vzdump --pbs-change-detection-mode metadata \
|
if [[ -z "$checkzfs_cmd" || ! -x "$checkzfs_cmd" ]]; then
|
||||||
--node "$SOURCEHOSTNAME" --storage "$BACKUPSTORE" \
|
log "checkzfs nicht gefunden oder nicht ausführbar – Abbruch."
|
||||||
--exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \
|
return 1
|
||||||
--notes-template '{{guestname}}'; then
|
fi
|
||||||
|
log "Verwende checkzfs: $checkzfs_cmd"
|
||||||
|
|
||||||
log "vzdump with change-detection-mode succeeded"
|
# Filter anhand ZFS-Tags auf dem SOURCEHOST aufbauen
|
||||||
vzdump_success=true
|
local filter=""
|
||||||
|
while IFS=$'\t' read -r name value source; do
|
||||||
else
|
if [[ "$source" == "local" && "$value" == "subvols" ]]; then
|
||||||
log "Fallback: vzdump with change-detection-mode failed, trying without it..."
|
filter+="${name}/|"
|
||||||
|
elif [[ "$source" == "local" && "$value" == "all" ]]; then
|
||||||
if ssh root@"$SOURCEHOST" vzdump \
|
filter+="${name}|"
|
||||||
--node "$SOURCEHOSTNAME" --storage "$BACKUPSTORE" \
|
|
||||||
--exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \
|
|
||||||
--notes-template '{{guestname}}'; then
|
|
||||||
|
|
||||||
log "Fallback vzdump succeeded"
|
|
||||||
vzdump_success=true
|
|
||||||
|
|
||||||
else
|
|
||||||
log "ERROR: vzdump failed even after fallback"
|
|
||||||
fi
|
fi
|
||||||
fi
|
done < <(ssh -p "$SOURCEPORT" "$SOURCEHOST" "zfs get -H -o name,value,source -t filesystem,volume $ZPUSHTAG")
|
||||||
|
|
||||||
# PBS-Storage wieder deaktivieren, wenn zuvor aktiviert und erfolgreich
|
filter="#${filter%|}"
|
||||||
if [[ "$vzdump_success" == true && "$pbs_enabled_by_script" == true ]]; then
|
log "Generierter ZFS-Filter: $filter"
|
||||||
log "Disabling PBS storage '$BACKUPSTORE' again on $SOURCEHOST..."
|
|
||||||
ssh root@"$SOURCEHOST" "pvesm set '$BACKUPSTORE' --disable 1"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Monitoring-Output für Checkmk
|
local combined="miyagi-${SOURCEHOSTNAME}-$(hostname)-${ZPUSHTAG}"
|
||||||
if [[ "$vzdump_success" == true ]]; then
|
local spoolfile="/tmp/${combined}_checkzfs_external"
|
||||||
echo "0 DailyPBS - Daily Backup" > /tmp/cmk_tmp.out
|
local spooldest="120000_${combined}_checkzfs_external"
|
||||||
|
|
||||||
|
log "Führe checkzfs aus..."
|
||||||
|
{
|
||||||
|
echo "<<<local>>>"
|
||||||
|
"$checkzfs_cmd" \
|
||||||
|
--source root@"${SOURCEHOST}:${SOURCEPORT}" \
|
||||||
|
--filter "$filter" \
|
||||||
|
--replicafilter "^${ZFSTRGT}" \
|
||||||
|
--prefix "$checkzfs_prefix" \
|
||||||
|
--threshold "$checkzfs_max_age" \
|
||||||
|
--maxsnapshots "$checkzfs_max_snapshot_count" \
|
||||||
|
--output checkmk
|
||||||
|
} > "$spoolfile"
|
||||||
|
|
||||||
|
if scp -P "$ECHECKZFS_PORT" "$spoolfile" root@"$ECHECKZFS_HOST":/var/lib/check_mk_agent/spool/"$spooldest"; then
|
||||||
|
log "Spool-Datei erfolgreich übertragen an $ECHECKZFS_HOST:$spooldest"
|
||||||
else
|
else
|
||||||
echo "2 DailyPBS - Daily Backup FAILED" > /tmp/cmk_tmp.out
|
log "Fehler beim Übertragen der Spool-Datei an $ECHECKZFS_HOST"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
( echo "<<<local>>>" ; cat /tmp/cmk_tmp.out ) > /tmp/90000_checkpbs
|
rm -f "$spoolfile"
|
||||||
scp /tmp/90000_checkpbs root@"$SOURCEHOST":/var/lib/check_mk_agent/spool || log "Fehler beim SCP des Monitoring-Outputs"
|
|
||||||
}
|
}
|
||||||
|
wait() {
|
||||||
|
echo
|
||||||
run_maintenance() {
|
echo "###########################################"
|
||||||
if [[ "$(date +%u)" == "$MAINTDAY" ]]; then
|
echo "# Das Backup startet in 60 Sekunden... #"
|
||||||
log "Running maintenance..."
|
echo "# Drücke [Enter], um sofort fortzufahren, #"
|
||||||
PRUNEJOB=$(ssh "$PBSHOST" proxmox-backup-manager prune-job list --output-format json-pretty | grep -m 1 "id" | cut -d'"' -f4)
|
echo "# oder warte, um automatisch zu starten. #"
|
||||||
ssh root@"$PBSHOST" proxmox-backup-manager prune-job run "$PRUNEJOB"
|
echo "###########################################"
|
||||||
ssh root@"$PBSHOST" proxmox-backup-manager garbage-collection start "$BACKUPSTOREPBS"
|
read -t 60 -p "Fortfahren (Enter drücken) oder warten... " input
|
||||||
ssh root@"$PBSHOST" proxmox-backup-manager verify backup
|
if [[ $? -eq 0 ]]; then
|
||||||
|
log "Press ENTER to continue."
|
||||||
else
|
else
|
||||||
log "No maintenance scheduled for today."
|
log "No press ENTER, wait for 60 Sec.."
|
||||||
|
sleep 60
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
set_dynamic_route() {
|
||||||
|
local ddns_hostname="$SOURCEHOST"
|
||||||
|
local gateway="$DDNS_GATEWAY"
|
||||||
|
local dns_server="$DDNS_SERVER"
|
||||||
|
|
||||||
run_scrub_stop_src() {
|
if [[ -z "$ddns_hostname" || -z "$gateway" ]]; then
|
||||||
ssh -p "$SSHPORT" root@"$SOURCEHOST" 'for pool in $(zpool list -H -o name); do
|
log "Fehler: SOURCEHOST oder DDNS_GATEWAY nicht gesetzt"
|
||||||
echo "Stoppe Scrub auf Pool: $pool"
|
return 1
|
||||||
if zpool status "$pool" | grep -q "scrub in progress"; then
|
fi
|
||||||
if zpool scrub -s "$pool"; then
|
|
||||||
echo "Scrub auf $pool gestoppt"
|
|
||||||
else
|
|
||||||
echo "Fehler beim Stoppen des Scrubs auf $pool"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Kein aktiver Scrub auf $pool"
|
|
||||||
fi
|
|
||||||
done'
|
|
||||||
}
|
|
||||||
|
|
||||||
run_scrub_stop_local() {
|
log "Setze temporäre Route zu DNS-Server $dns_server via $gateway..."
|
||||||
for pool in $(zpool list -H -o name); do
|
if ! ip route | grep -q "^$dns_server"; then
|
||||||
echo "Stoppe Scrub auf Pool: $pool"
|
ip route add "$dns_server" via "$gateway" \
|
||||||
if zpool status "$pool" | grep -q "scrub in progress"; then
|
&& log "Route zu $dns_server via $gateway gesetzt" \
|
||||||
if zpool scrub -s "$pool"; then
|
|| log "Fehler beim Setzen der DNS-Route"
|
||||||
echo "Scrub auf $pool gestoppt"
|
|
||||||
else
|
|
||||||
echo "Fehler beim Stoppen des Scrubs auf $pool"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Kein aktiver Scrub auf $pool"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
shutdown_if_requested() {
|
|
||||||
log "SHUTDOWN-Variable: '${SHUTDOWN:-nicht gesetzt}'"
|
|
||||||
#if [[ "${SHUTDOWN,,}" == "yes" ]]; then
|
|
||||||
if [[ "$(echo "$SHUTDOWN" | tr '[:upper:]' '[:lower:]')" == "yes" ]]; then
|
|
||||||
send_piggyback_data
|
|
||||||
log "Shutting down now..."
|
|
||||||
shutdown now
|
|
||||||
else
|
else
|
||||||
log "No shutdown requested."
|
log "Route zu $dns_server bereits vorhanden"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Löse IP von $ddns_hostname über DNS $dns_server..."
|
||||||
|
CURRENT_IP=$(dig +short @"$dns_server" "$ddns_hostname")
|
||||||
|
|
||||||
|
if [[ -z "$CURRENT_IP" ]]; then
|
||||||
|
log "Fehler: Konnte IP für $ddns_hostname nicht auflösen"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Ermittelte IP: $CURRENT_IP – setze Route via $gateway..."
|
||||||
|
if ! ip route | grep -q "^$CURRENT_IP"; then
|
||||||
|
ip route add "$CURRENT_IP" via "$gateway" \
|
||||||
|
&& log "Route erfolgreich gesetzt: $CURRENT_IP via $gateway" \
|
||||||
|
|| log "Fehler beim Setzen der Route zu $CURRENT_IP"
|
||||||
|
else
|
||||||
|
log "Route zu $CURRENT_IP bereits vorhanden"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
# Main execution:
|
||||||
|
if [[ $# -eq 0 ]]; then
|
||||||
|
if [[ -n "$CONFIG_FILE" ]]; then
|
||||||
|
wait
|
||||||
|
log "Running full backup using configuration file: $CONFIG_FILE"
|
||||||
|
|
||||||
main() {
|
if [[ "${DYNROUTE,,}" == "yes" ]]; then
|
||||||
log "Backup-Routine startet in 60 Sekunden..."
|
log "DYNROUTE ist aktiviert – setze dynamische Route für $SOURCEHOST..."
|
||||||
sleep 60
|
set_dynamic_route
|
||||||
log "Starting full backup routine..."
|
else
|
||||||
|
log "DYNROUTE ist deaktiviert oder nicht gesetzt."
|
||||||
|
fi
|
||||||
|
|
||||||
SOURCEHOSTNAME=$(ssh "$SOURCEHOST" hostname)
|
|
||||||
|
|
||||||
set_wol_g_enabled
|
|
||||||
write_zsync_config
|
write_zsync_config
|
||||||
run_zsync
|
run_zsync
|
||||||
run_scrub_stop_local
|
|
||||||
run_scrub_stop_src
|
|
||||||
|
|
||||||
if [[ "${BACKUPSERVER,,}" == "yes" ]]; then
|
if [[ "${BACKUPSERVER,,}" == "yes" ]]; then
|
||||||
log "BACKUPSERVER ist aktiviert, führe Backup aus..."
|
log "BACKUPSERVER ist aktiviert, führe Backup aus..."
|
||||||
run_maintenance
|
|
||||||
run_pbs_backup
|
run_pbs_backup
|
||||||
|
run_maintenance
|
||||||
|
run_pbs_log_clear
|
||||||
else
|
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
|
||||||
run_updates
|
if [[ "${EPIGGYBACK,,}" == "yes" ]]; then
|
||||||
shutdown_if_requested
|
send_piggyback_external
|
||||||
|
fi
|
||||||
|
|
||||||
}
|
if [[ "${ECHECKZFS,,}" == "yes" ]]; then
|
||||||
|
send_checkzfs_external
|
||||||
# Funktionsbasierter Aufruf
|
fi
|
||||||
if [[ "${1:-}" == "help" ]]; then
|
shutdown_now
|
||||||
echo "Verfuegbare Funktionen:"
|
else
|
||||||
declare -F | awk '{print " - " $3}' | grep -v "^ - _"
|
usage
|
||||||
exit 0
|
fi
|
||||||
elif [[ "${1:-}" =~ ^[a-zA-Z0-9_]+$ && "$(type -t "$1")" == "function" ]]; then
|
|
||||||
FUNC="$1"
|
|
||||||
shift
|
|
||||||
"$FUNC" "$@"
|
|
||||||
exit 0
|
|
||||||
else
|
else
|
||||||
main
|
case "$1" in
|
||||||
|
help)
|
||||||
|
declare -F | awk '{print $3}' | grep -v "^_" | grep -v "^main$"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if declare -F "$1" >/dev/null 2>&1; then
|
||||||
|
"$@"
|
||||||
|
else
|
||||||
|
log "Function $1 not found."
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
fi
|
fi
|
||||||
|
|||||||
+43
-26
@@ -2,6 +2,7 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
IFS=$'\n\t'
|
IFS=$'\n\t'
|
||||||
|
|
||||||
|
PERMITROOT_YES_HOSTS=()
|
||||||
LOG() {
|
LOG() {
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
||||||
}
|
}
|
||||||
@@ -9,39 +10,31 @@ LOG() {
|
|||||||
CONFIG_FILE="${1:-}"
|
CONFIG_FILE="${1:-}"
|
||||||
|
|
||||||
if [[ -z "$CONFIG_FILE" ]]; then
|
if [[ -z "$CONFIG_FILE" ]]; then
|
||||||
LOG "❌ Keine Konfigurationsdatei übergeben."
|
LOG "Keine Konfigurationsdatei übergeben."
|
||||||
echo "Usage: $0 /pfad/zur/config"
|
echo "Usage: $0 /pfad/zur/config"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||||
LOG "❌ Konfigurationsdatei nicht gefunden: $CONFIG_FILE"
|
LOG "Konfigurationsdatei nicht gefunden: $CONFIG_FILE"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! bash -n "$CONFIG_FILE"; then
|
if ! bash -n "$CONFIG_FILE"; then
|
||||||
LOG "❌ Syntaxfehler in der Konfigurationsdatei!"
|
LOG "Syntaxfehler in der Konfigurationsdatei!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
source "$CONFIG_FILE"
|
source "$CONFIG_FILE"
|
||||||
|
|
||||||
REQUIRED_VARS=(
|
REQUIRED_VARS=(
|
||||||
SSHPORT
|
SOURCEPORT
|
||||||
BACKUPSERVER
|
BACKUPSERVER
|
||||||
ZSYNC
|
ZSYNC
|
||||||
MAINTDAY
|
MAINTDAY
|
||||||
SHUTDOWN
|
SHUTDOWN
|
||||||
UPDATES
|
UPDATES
|
||||||
SOURCEHOST
|
SOURCEHOST
|
||||||
ZFSROOT
|
|
||||||
ZFSSECOND
|
|
||||||
ZFSTRGT
|
|
||||||
ZPUSHTAG
|
|
||||||
ZPUSHMINKEEP
|
|
||||||
ZPUSHKEEP
|
|
||||||
ZPUSHLABEL
|
|
||||||
ZPUSHFILTER
|
|
||||||
PBSHOST
|
PBSHOST
|
||||||
BACKUPSTORE
|
BACKUPSTORE
|
||||||
BACKUPSTOREPBS
|
BACKUPSTOREPBS
|
||||||
@@ -96,7 +89,7 @@ esac
|
|||||||
check_ssh_connection() {
|
check_ssh_connection() {
|
||||||
local host=$1
|
local host=$1
|
||||||
LOG "Prüfe SSH-Verbindung zu $host ..."
|
LOG "Prüfe SSH-Verbindung zu $host ..."
|
||||||
if ssh -p "$SSHPORT" -o BatchMode=yes -o ConnectTimeout=5 "$host" "echo OK" 2>/dev/null | grep -q OK; then
|
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."
|
LOG " SSH-Verbindung zu $host erfolgreich."
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
@@ -113,20 +106,19 @@ check_and_copy_ssh_key() {
|
|||||||
LOG " Lokaler SSH-Public-Key ($keyfile) nicht gefunden!"
|
LOG " Lokaler SSH-Public-Key ($keyfile) nicht gefunden!"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local pubkey
|
local pubkey
|
||||||
pubkey=$(<"$keyfile")
|
pubkey=$(<"$keyfile")
|
||||||
|
|
||||||
LOG " Prüfe, ob SSH-Key auf $host autorisiert ist ..."
|
LOG " Prüfe, ob SSH-Key auf $host autorisiert ist ..."
|
||||||
|
|
||||||
if ssh -p "$SSHPORT" "$host" "grep -qF '$pubkey' ~/.ssh/authorized_keys" 2>/dev/null; then
|
if ssh -p "$SOURCEPORT" "$host" "grep -qF '$pubkey' ~/.ssh/authorized_keys" 2>/dev/null; then
|
||||||
LOG " SSH-Key ist bereits auf $host hinterlegt."
|
LOG " SSH-Key ist bereits auf $host hinterlegt."
|
||||||
else
|
else
|
||||||
LOG " SSH-Key nicht auf $host vorhanden."
|
LOG " SSH-Key nicht auf $host vorhanden."
|
||||||
|
|
||||||
read -rp " Möchtest du den SSH-Key jetzt via ssh-copy-id übertragen? [j/N] " ans
|
read -rp " Möchtest du den SSH-Key jetzt via ssh-copy-id übertragen? [j/N] " ans
|
||||||
if [[ "$ans" =~ ^[JjYy]$ ]]; then
|
if [[ "$ans" =~ ^[JjYy]$ ]]; then
|
||||||
ssh-copy-id -p "$SSHPORT" "$host"
|
ssh-copy-id -p "$SOURCEPORT" "$host"
|
||||||
else
|
else
|
||||||
LOG " SSH-Key nicht übertragen."
|
LOG " SSH-Key nicht übertragen."
|
||||||
fi
|
fi
|
||||||
@@ -138,7 +130,7 @@ check_sshd_config_recommendation() {
|
|||||||
LOG " Prüfe sshd_config auf $host bzgl. 'PermitRootLogin'..."
|
LOG " Prüfe sshd_config auf $host bzgl. 'PermitRootLogin'..."
|
||||||
|
|
||||||
local current_setting
|
local current_setting
|
||||||
current_setting=$(ssh -p "$SSHPORT" "$host" "grep -i '^PermitRootLogin' /etc/ssh/sshd_config" 2>/dev/null || echo "")
|
current_setting=$(ssh -p "$SOURCEPORT" "$host" "grep -i '^PermitRootLogin' /etc/ssh/sshd_config" 2>/dev/null || echo "")
|
||||||
|
|
||||||
if [[ -z "$current_setting" ]]; then
|
if [[ -z "$current_setting" ]]; then
|
||||||
LOG " Keine explizite 'PermitRootLogin'-Einstellung gefunden."
|
LOG " Keine explizite 'PermitRootLogin'-Einstellung gefunden."
|
||||||
@@ -154,31 +146,56 @@ check_sshd_config_recommendation() {
|
|||||||
check_pveversion() {
|
check_pveversion() {
|
||||||
local host=$1
|
local host=$1
|
||||||
LOG "Prüfe PVE-Version auf $host ..."
|
LOG "Prüfe PVE-Version auf $host ..."
|
||||||
if ssh -p "$SSHPORT" "$host" "command -v pveversion >/dev/null"; then
|
if ssh -p "$SOURCEPORT" "$host" "command -v pveversion >/dev/null"; then
|
||||||
ssh -p "$SSHPORT" "$host" "pveversion" | while read -r line; do
|
ssh -p "$SOURCEPORT" "$host" "pveversion" | while read -r line; do
|
||||||
LOG " $host: $line"
|
LOG " $host: $line"
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
LOG " 'pveversion' ist auf $host nicht verfügbar – kein Proxmox?"
|
LOG " 'pveversion' ist auf $host nicht verfügbar – kein Proxmox?"
|
||||||
fi
|
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() {
|
run_host_check() {
|
||||||
local host=$1
|
local host=$1
|
||||||
|
local type=${2:-pve}
|
||||||
|
local port=$SOURCEPORT
|
||||||
|
local pbsport=$PBSPORT
|
||||||
|
|
||||||
LOG ""
|
LOG ""
|
||||||
LOG "=== Prüfung für Host: $host ==="
|
LOG "=== Prüfung für Host: $host (Typ: $type) ==="
|
||||||
if check_ssh_connection "$host"; then
|
|
||||||
|
if check_ssh_connection "$host" "$port"; then
|
||||||
check_and_copy_ssh_key "$host"
|
check_and_copy_ssh_key "$host"
|
||||||
check_sshd_config_recommendation "$host"
|
check_sshd_config_recommendation "$host"
|
||||||
check_pveversion "$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
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
run_host_check "$SOURCEHOST"
|
|
||||||
|
|
||||||
|
run_host_check "$SOURCEHOST" pve
|
||||||
|
|
||||||
if [[ "$BACKUPSERVER" == "yes" ]]; then
|
if [[ "$BACKUPSERVER" == "yes" ]]; then
|
||||||
run_host_check "$PBSHOST"
|
run_host_check "$PBSHOST" pbs
|
||||||
else
|
else
|
||||||
LOG " BACKUPSERVER ist deaktiviert PBSHOST wird übersprungen."
|
LOG " BACKUPSERVER ist deaktiviert PBSHOST wird übersprungen."
|
||||||
fi
|
fi
|
||||||
@@ -195,7 +212,7 @@ if [[ ${#PERMITROOT_YES_HOSTS[@]} -gt 0 ]]; then
|
|||||||
if [[ "$change_ans" =~ ^[JjYy]$ ]]; then
|
if [[ "$change_ans" =~ ^[JjYy]$ ]]; then
|
||||||
for h in "${PERMITROOT_YES_HOSTS[@]}"; do
|
for h in "${PERMITROOT_YES_HOSTS[@]}"; do
|
||||||
echo "Ändere sshd_config auf $h ..."
|
echo "Ändere sshd_config auf $h ..."
|
||||||
ssh -p "$SSHPORT" "$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'"
|
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
|
done
|
||||||
else
|
else
|
||||||
echo " Änderung von sshd_config übersprungen."
|
echo " Änderung von sshd_config übersprungen."
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
#!/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"
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Author: (C) 2025 Patrick Perlbach <patrick@perlbach24.de>
|
||||||
|
# --- Konfiguration und Initiale Variablen ---
|
||||||
|
FORCE_YES=0
|
||||||
|
DRY_RUN=0
|
||||||
|
CONFIG_FILE=""
|
||||||
|
|
||||||
|
# --- Hilfsfunktionen ---
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $0 -c <config> [--yes] [--dry-run]
|
||||||
|
|
||||||
|
-c <file> Pfad zur Konfigurationsdatei (Pflicht)
|
||||||
|
--yes Automatische Ausführung ohne Rückfrage
|
||||||
|
--dry-run Zeigt nur, was gemacht würde
|
||||||
|
--help Diese Hilfe anzeigen
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
run_or_echo() {
|
||||||
|
if [[ "$DRY_RUN" == "1" ]]; then
|
||||||
|
echo "[DRY-RUN] $*"
|
||||||
|
else
|
||||||
|
eval "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args() {
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-c) CONFIG_FILE="$2"; shift 2 ;;
|
||||||
|
--yes) FORCE_YES=1; shift ;;
|
||||||
|
--dry-run) DRY_RUN=1; shift ;;
|
||||||
|
--help) usage ;;
|
||||||
|
*) echo "[ERROR] Unbekannter Parameter: $1"; usage ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -z "$CONFIG_FILE" || ! -f "$CONFIG_FILE" ]] && {
|
||||||
|
echo "[ERROR] Konfigurationsdatei fehlt oder ungültig!"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
source "$CONFIG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm() {
|
||||||
|
if [[ "$FORCE_YES" -eq 1 ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
read -rp "$1 [j/N]: " yn
|
||||||
|
case $yn in
|
||||||
|
[JjYy]*) return 0 ;;
|
||||||
|
[Nn]*|"") return 1 ;;
|
||||||
|
*) echo "Bitte j (ja) oder n (nein) eingeben." ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
install_cmk() {
|
||||||
|
echo "[INFO] Lade cmk.deb herunter..."
|
||||||
|
run_or_echo "curl -fsSL \"$CMK_URL\" -o \"$CMK_FILE\""
|
||||||
|
|
||||||
|
echo "[INFO] Installiere cmk.deb..."
|
||||||
|
run_or_echo "sudo apt install -y \"$CMK_FILE\""
|
||||||
|
}
|
||||||
|
|
||||||
|
install_fix_interface_names() {
|
||||||
|
echo "[INFO] Installiere Fix für Interface-Namen..."
|
||||||
|
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() {
|
||||||
|
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() {
|
||||||
|
echo "[INFO] Installiere disk_smart_info Plugin..."
|
||||||
|
run_or_echo "curl -fsSL \"$DISK_SMART_INFO_URL\" -o /usr/lib/check_mk_agent/plugins/disk_smart_info"
|
||||||
|
run_or_echo "chmod +x /usr/lib/check_mk_agent/plugins/disk_smart_info"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_cleansnaps() {
|
||||||
|
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() {
|
||||||
|
echo "[INFO] Installiere check-snapshot-age..."
|
||||||
|
run_or_echo "wget -O /usr/local/bin/check-snapshot-age \"$CHECK_SNAPSHOT_AGE_URL\""
|
||||||
|
run_or_echo "chmod +x /usr/local/bin/check-snapshot-age"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_checkzfs() {
|
||||||
|
echo "[INFO] Installiere checkzfs..."
|
||||||
|
run_or_echo "wget -O /usr/local/bin/checkzfs \"$CHECKZFS_URL\""
|
||||||
|
run_or_echo "chmod +x /usr/local/bin/checkzfs"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_ssh() {
|
||||||
|
echo "[INFO] Teste SSH-Verbindung zu $SOURCEHOST ..."
|
||||||
|
|
||||||
|
if ssh -p "$SOURCEPORT" -o BatchMode=yes -o ConnectTimeout=5 root@"$SOURCEHOST" "echo Verbindung erfolgreich" 2>/dev/null; then
|
||||||
|
echo "[OK] SSH-Verbindung über Schlüssel funktioniert."
|
||||||
|
else
|
||||||
|
echo "[WARN] Keine Schlüsselbasierte SSH-Verbindung möglich oder abgelehnt."
|
||||||
|
|
||||||
|
echo "[HINWEIS] Eine Passwortabfrage erfolgt jetzt – dies ist normal."
|
||||||
|
if ssh -p "$SOURCEPORT" root@"$SOURCEHOST" "echo Verbindung erfolgreich"; then
|
||||||
|
echo "[OK] SSH-Verbindung per Passwort erfolgreich."
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
select_zfs_pools() {
|
||||||
|
echo "[INFO] Lade ZFS-Datasets vom Remote-Host..."
|
||||||
|
|
||||||
|
LOCAL_HOSTNAME=$(hostname -s)
|
||||||
|
REMOTE_HOSTNAME=$(ssh -p "$SOURCEPORT" root@"$SOURCEHOST" "hostname -s")
|
||||||
|
TAG_KEY="bashclub:miyagi-${REMOTE_HOSTNAME}-${LOCAL_HOSTNAME}"
|
||||||
|
TAG_VALUE="subvols"
|
||||||
|
|
||||||
|
# Hole alle relevanten Pool-Namen
|
||||||
|
mapfile -t pools < <(ssh -p "$SOURCEPORT" root@"$SOURCEHOST" \
|
||||||
|
"zfs list -H -o name" | \
|
||||||
|
grep -E '/(vm|subvol)-' | \
|
||||||
|
grep -viE 'repl|replica' | \
|
||||||
|
sed 's:/[^/]*$::' | \
|
||||||
|
sort -u)
|
||||||
|
|
||||||
|
if [[ ${#pools[@]} -eq 0 ]]; then
|
||||||
|
echo "[WARN] Keine geeigneten Datasets gefunden!"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local options=()
|
||||||
|
local tagged_pools=()
|
||||||
|
|
||||||
|
for pool in "${pools[@]}"; do
|
||||||
|
local tag status
|
||||||
|
tag=$(ssh -p "$SOURCEPORT" root@"$SOURCEHOST" zfs get -H -o value "$TAG_KEY" "$pool" 2>/dev/null)
|
||||||
|
if [[ "$tag" == "$TAG_VALUE" ]]; then
|
||||||
|
status="on"
|
||||||
|
tagged_pools+=("$pool")
|
||||||
|
else
|
||||||
|
status="off"
|
||||||
|
fi
|
||||||
|
options+=("$pool" "$([[ "$status" == "on" ]] && echo 'TAGGED' || echo 'untagged')" "$status")
|
||||||
|
done
|
||||||
|
|
||||||
|
local selected
|
||||||
|
selected=$(whiptail --title "ZFS Pools auswählen" \
|
||||||
|
--checklist "Wähle Datasets zum Taggen aus:" 20 78 12 \
|
||||||
|
"${options[@]}" \
|
||||||
|
3>&1 1>&2 2>&3)
|
||||||
|
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
echo "[INFO] Auswahl abgebrochen."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local selected_array=()
|
||||||
|
eval "selected_array=($selected)"
|
||||||
|
|
||||||
|
echo "[INFO] Verarbeite Tagging für ausgewählte Pools:"
|
||||||
|
|
||||||
|
for ((i = 0; i < ${#options[@]}; i += 3)); do
|
||||||
|
local pool="${options[i]}"
|
||||||
|
local prev_status="${options[i+2]}"
|
||||||
|
local is_selected=0
|
||||||
|
|
||||||
|
for sel in "${selected_array[@]}"; do
|
||||||
|
if [[ "$sel" == "$pool" ]]; then
|
||||||
|
is_selected=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$is_selected" -eq 1 && "$prev_status" == "off" ]]; then
|
||||||
|
echo " → Tagge $pool"
|
||||||
|
run_or_echo ssh -p "$SOURCEPORT" root@"$SOURCEHOST" zfs set "$TAG_KEY=$TAG_VALUE" "$pool"
|
||||||
|
elif [[ "$is_selected" -eq 0 && "$prev_status" == "on" ]]; then
|
||||||
|
echo " → Entferne Tag von $pool"
|
||||||
|
run_or_echo ssh -p "$SOURCEPORT" root@"$SOURCEHOST" zfs inherit "$TAG_KEY" "$pool"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
# URLs und Dateipfade als Variablen, ggf. in Kopfbereich auslagern
|
||||||
|
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"
|
||||||
|
|
||||||
|
MIYAGI_BACKUP_URL="https://gitea.perlbach24.de/scripte/miyagi-backup/raw/branch/main/miyagi-backup.sh"
|
||||||
|
MIYAGI_CHECK_URL="https://gitea.perlbach24.de/scripte/miyagi-backup/raw/branch/main/miyagi-check.sh"
|
||||||
|
MIYAGI_CONVERT_URL="https://gitea.perlbach24.de/scripte/miyagi-backup/raw/branch/main/miyagi-convert.sh"
|
||||||
|
|
||||||
|
DISK_SMART_INFO_URL="https://raw.githubusercontent.com/bashclub/checkmk-smart/main/disk_smart_info.py"
|
||||||
|
|
||||||
|
CLEANSNAPS_URL="https://raw.githubusercontent.com/bashclub/zfs-housekeeping/refs/heads/main/cleansnaps.sh"
|
||||||
|
|
||||||
|
CHECK_SNAPSHOT_AGE_URL="https://gitea.perlbach24.de/scripte/check-zfs-replication/raw/branch/main/check-snapshot-age"
|
||||||
|
|
||||||
|
CHECKZFS_URL="https://gitea.perlbach24.de/scripte/check-zfs-replication/raw/branch/main/checkzfs.py"
|
||||||
|
|
||||||
|
echo "[INFO] Starte Installation mit Konfig: $CONFIG_FILE"
|
||||||
|
|
||||||
|
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 "$@"
|
||||||
|
main
|
||||||
Reference in New Issue
Block a user