22 Commits

Author SHA1 Message Date
patrick 2fa79afd66 miyagi-backup.sh aktualisiert 2026-02-04 17:09:40 +01:00
patrick 15e86400f0 miyagi-backup.sh aktualisiert 2026-02-04 00:51:31 +01:00
patrick 1166a9c03e config.example aktualisiert 2026-01-07 11:46:23 +01:00
patrick c519aac1d9 config.example aktualisiert 2026-01-07 11:44:55 +01:00
patrick effc9646be miyagi-backup.sh aktualisiert 2026-01-07 11:44:25 +01:00
patrick e2960c19b4 miyagi-backup.sh aktualisiert
add function run_pbs_log_clear, last 60 days
2025-12-11 01:19:43 +01:00
patrick 7d42984bfa miyagi-backup.sh aktualisiert
PBS PORT
2025-11-26 13:26:37 +01:00
patrick f0890c61ff config.example aktualisiert 2025-11-26 13:09:03 +01:00
patrick eab882aa44 config.example aktualisiert 2025-11-26 13:08:47 +01:00
patrick 87eb3307cb miyagi-backup.sh aktualisiert 2025-08-22 23:01:50 +02:00
patrick 067fe8f1af v2.05
fix piggyback_external in main
2025-08-22 23:00:55 +02:00
patrick e2f64a4491 config.example aktualisiert 2025-08-22 16:33:16 +02:00
patrick ca8c2854a9 v2.04 2025-08-22 00:05:43 +02:00
patrick 3fbb50e2bb config.example aktualisiert 2025-08-19 01:12:13 +02:00
patrick f7b9acd73b README.md aktualisiert 2025-08-11 12:03:51 +02:00
patrick 76a081468d README.md aktualisiert 2025-08-06 01:24:11 +02:00
patrick 75534137ff miyagi-backup.sh aktualisiert 2025-08-04 00:13:17 +02:00
patrick beb53968a4 miyagi-backup.sh aktualisiert 2025-08-02 01:09:12 +02:00
patrick dcae51da9f v2.03
wait replace
2025-08-02 00:50:14 +02:00
patrick 87c2d13069 v2.02
shutdown stop by zpool replace
2025-08-02 00:36:40 +02:00
patrick 602cfe21b0 Dateien nach "/" hochladen 2025-07-30 01:31:44 +02:00
patrick 7d3e53b42a v2.01
Press ENTER to continue.
2025-07-26 02:01:58 +02:00
4 changed files with 545 additions and 21 deletions
+3 -1
View File
@@ -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
```
+8 -4
View File
@@ -2,6 +2,10 @@
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 SOURCEPORT='22' # SSH-Port, normalerweise 22
@@ -10,14 +14,14 @@ 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
@@ -27,7 +31,7 @@ PBSHOST='192.168.50.199' # IP des Proxmox Backup Servers
PBSPORT='22' 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 # Zusätzliche Monitoring-Ziele
+278 -15
View File
@@ -43,9 +43,13 @@ if [[ -n "${CONFIG_FILE:-}" ]]; then
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 ZFSROOT ZFSSECOND ZFSTRGT ZPUSHTAG ZPUSHMINKEEP ZPUSHKEEP ZPUSHLABEL
PBSHOST BACKUPSTORE BACKUPSTOREPBS BACKUPEXCLUDE REPLEXCLUDE PBSHOST BACKUPSTORE BACKUPSTOREPBS BACKUPEXCLUDE REPLEXCLUDE DYNROUTE REBOOT DDNS_GATEWAY
) )
if [[ "${DYNROUTE,,}" == "yes" ]]; then
REQUIRED_VARS+=(DDNS_GATEWAY)
fi
MISSING_VARS=() MISSING_VARS=()
for var in "${REQUIRED_VARS[@]}"; do for var in "${REQUIRED_VARS[@]}"; do
if [[ -z "${!var:-}" ]]; then if [[ -z "${!var:-}" ]]; then
@@ -76,6 +80,38 @@ get_sourcehostname() {
fi fi
} }
zfs_replace() {
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 "Checking if ethtool is installed..." log "Checking if ethtool is installed..."
@@ -142,6 +178,7 @@ write_zsync_config() {
} }
run_zsync() { run_zsync() {
write_zsync_config
if [[ "$ZSYNC" != "no" ]]; then if [[ "$ZSYNC" != "no" ]]; then
/usr/bin/bashclub-zsync -c "/etc/bashclub/$SOURCEHOST.conf" /usr/bin/bashclub-zsync -c "/etc/bashclub/$SOURCEHOST.conf"
else else
@@ -152,16 +189,80 @@ run_zsync() {
run_remote_updates() { run_remote_updates() {
if [[ "${UPDATES,,}" == "yes" ]]; then if [[ "${UPDATES,,}" == "yes" ]]; then
log "Running updates on local system..." log "Running updates on local system..."
apt update && apt dist-upgrade -y || log "Error during local updates"
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
log_msg "INFO" "Update abgeschlossen. Kein Neustart erforderlich."
fi
# PBS-Host-Update
if [[ "${BACKUPSERVER,,}" == "yes" ]]; then if [[ "${BACKUPSERVER,,}" == "yes" ]]; then
log "Running updates on PBS host ($PBSHOST)..." log "Running updates on PBS host ($PBSHOST)..."
ssh root@"$PBSHOST" apt update && ssh root@"$PBSHOST" apt dist-upgrade -y || { ssh -p $PBSPORT root@"$PBSHOST" apt update && ssh -p $PBSPORT root@"$PBSHOST" apt dist-upgrade -y || {
log "Error during updates on $PBSHOST" log "Error during updates on $PBSHOST"
} }
else else
log "PBS updates skipped (BACKUPSERVER=$BACKUPSERVER)" log "PBS updates skipped (BACKUPSERVER=$BACKUPSERVER)"
fi fi
else else
log "Updates disabled (UPDATES=$UPDATES)" log "Updates disabled (UPDATES=$UPDATES)"
fi fi
@@ -190,6 +291,15 @@ send_piggyback() {
rm -f "$filename" 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
}
run_pbs_backup() { run_pbs_backup() {
if [[ "${BACKUPSERVER,,}" != "yes" ]]; then if [[ "${BACKUPSERVER,,}" != "yes" ]]; then
log "PBS Backup übersprungen: BACKUPSERVER='$BACKUPSERVER' (muss 'yes' sein)" log "PBS Backup übersprungen: BACKUPSERVER='$BACKUPSERVER' (muss 'yes' sein)"
@@ -271,10 +381,10 @@ run_maintenance() {
if [[ "$(date +%u)" == "$MAINTDAY" ]]; then if [[ "$(date +%u)" == "$MAINTDAY" ]]; then
log "Running maintenance..." log "Running maintenance..."
PRUNEJOB=$(ssh "$PBSHOST" proxmox-backup-manager prune-job list --output-format json-pretty | grep -m 1 "id" | cut -d'"' -f4) PRUNEJOB=$(ssh -p $PBSPORT "$PBSHOST" proxmox-backup-manager prune-job list --output-format json-pretty | grep -m 1 "id" | cut -d'"' -f4)
ssh root@"$PBSHOST" proxmox-backup-manager prune-job run "$PRUNEJOB" ssh -p $PBSPORT root@"$PBSHOST" proxmox-backup-manager prune-job run "$PRUNEJOB"
ssh root@"$PBSHOST" proxmox-backup-manager garbage-collection start "$BACKUPSTOREPBS" ssh -p $PBSPORT root@"$PBSHOST" proxmox-backup-manager garbage-collection start "$BACKUPSTOREPBS"
ssh root@"$PBSHOST" proxmox-backup-manager verify backup ssh -p $PBSPORT root@"$PBSHOST" proxmox-backup-manager verify backup
else else
log "No maintenance scheduled for today." log "No maintenance scheduled for today."
fi fi
@@ -304,12 +414,100 @@ run_scrub_stop() {
' '
} }
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
# 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() { shutdown_now() {
if [[ "${SHUTDOWN,,}" == "yes" ]]; then 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
send_piggyback_external send_piggyback_external
send_checkzfs_external send_checkzfs_external
log "Shutting down now..." log "Shutting down now...in 60sec"
sleep 60
shutdown now shutdown now
else else
log "No shutdown requested." log "No shutdown requested."
@@ -321,6 +519,7 @@ 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"
@@ -365,7 +564,7 @@ send_checkzfs_external() {
log "Externer Check-ZFS deaktiviert (ECHECKZFS=$ECHECKZFS)" log "Externer Check-ZFS deaktiviert (ECHECKZFS=$ECHECKZFS)"
return return
fi 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)}"
@@ -388,9 +587,9 @@ send_checkzfs_external() {
filter="#${filter%|}" filter="#${filter%|}"
log "Generierter ZFS-Filter: $filter" log "Generierter ZFS-Filter: $filter"
local combined="miyagi-${SOURCEHOSTNAME}-$(hostname)" local combined="miyagi-${SOURCEHOSTNAME}-$(hostname)-${ZPUSHTAG}"
local spoolfile="/tmp/${combined}_checkzfs_external" local spoolfile="/tmp/${combined}_checkzfs_external"
local spooldest="90000_${combined}_checkzfs_external" local spooldest="120000_${combined}_checkzfs_external"
log "Führe checkzfs aus..." log "Führe checkzfs aus..."
{ {
@@ -413,25 +612,89 @@ send_checkzfs_external() {
rm -f "$spoolfile" rm -f "$spoolfile"
} }
wait() {
echo
echo "###########################################"
echo "# Das Backup startet in 60 Sekunden... #"
echo "# Drücke [Enter], um sofort fortzufahren, #"
echo "# oder warte, um automatisch zu starten. #"
echo "###########################################"
read -t 60 -p "Fortfahren (Enter drücken) oder warten... " input
if [[ $? -eq 0 ]]; then
log "Press ENTER to continue."
else
log "No press ENTER, wait for 60 Sec.."
sleep 60
fi
}
set_dynamic_route() {
local ddns_hostname="$SOURCEHOST"
local gateway="$DDNS_GATEWAY"
local dns_server="$DDNS_SERVER"
if [[ -z "$ddns_hostname" || -z "$gateway" ]]; then
log "Fehler: SOURCEHOST oder DDNS_GATEWAY nicht gesetzt"
return 1
fi
log "Setze temporäre Route zu DNS-Server $dns_server via $gateway..."
if ! ip route | grep -q "^$dns_server"; then
ip route add "$dns_server" via "$gateway" \
&& log "Route zu $dns_server via $gateway gesetzt" \
|| log "Fehler beim Setzen der DNS-Route"
else
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
}
# Main execution: # Main execution:
if [[ $# -eq 0 ]]; then if [[ $# -eq 0 ]]; then
if [[ -n "$CONFIG_FILE" ]]; then if [[ -n "$CONFIG_FILE" ]]; then
log "Backup-Routine startet in 60 Sekunden..." wait
sleep 60
#log "Starting full backup routine..."
log "Running full backup using configuration file: $CONFIG_FILE" log "Running full backup using configuration file: $CONFIG_FILE"
if [[ "${DYNROUTE,,}" == "yes" ]]; then
log "DYNROUTE ist aktiviert setze dynamische Route für $SOURCEHOST..."
set_dynamic_route
else
log "DYNROUTE ist deaktiviert oder nicht gesetzt."
fi
write_zsync_config write_zsync_config
run_zsync run_zsync
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
if [[ "${EPIGGYBACK,,}" == "yes" ]]; then
send_piggyback_external
fi
if [[ "${ECHECKZFS,,}" == "yes" ]]; then
send_checkzfs_external
fi
shutdown_now shutdown_now
else else
usage usage
+255
View File
@@ -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