Compare commits
22 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 |
+4
-2
@@ -1,13 +1,14 @@
|
||||
# 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
|
||||
|
||||
```
|
||||
|
||||
####
|
||||
|
||||
@@ -24,4 +25,5 @@ Dieses Skript überprüft eine Bash-basierte Konfigurationsdatei für ein Backup
|
||||
|
||||
## Nutzung:
|
||||
```bash
|
||||
./miyagi-check.sh /pfad/zur/config
|
||||
./miyagi-check.sh /pfad/zur/config
|
||||
```
|
||||
+8
-4
@@ -2,6 +2,10 @@
|
||||
|
||||
UPDATES='yes' # Proxmox VE und PBS Updates nach dem Lauf
|
||||
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)
|
||||
SOURCEPORT='22' # SSH-Port, normalerweise 22
|
||||
@@ -10,14 +14,14 @@ SOURCEHOST='192.168.50.200' # IP des Quell-Proxmox-Servers
|
||||
# Replikation (ZFS)
|
||||
ZFSROOT='rpool/data' # Erstes Dataset vom Quellsystem
|
||||
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
|
||||
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
|
||||
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.)
|
||||
|
||||
# Backup mit Proxmox Backup Server
|
||||
@@ -27,7 +31,7 @@ PBSHOST='192.168.50.199' # IP des Proxmox Backup Servers
|
||||
PBSPORT='22'
|
||||
BACKUPSTORE='backup' # Datastore auf Quell-Proxmox
|
||||
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
|
||||
|
||||
# Zusätzliche Monitoring-Ziele
|
||||
|
||||
+278
-15
@@ -43,9 +43,13 @@ if [[ -n "${CONFIG_FILE:-}" ]]; then
|
||||
REQUIRED_VARS=(
|
||||
SOURCEPORT BACKUPSERVER ZSYNC MAINTDAY SHUTDOWN UPDATES
|
||||
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=()
|
||||
for var in "${REQUIRED_VARS[@]}"; do
|
||||
if [[ -z "${!var:-}" ]]; then
|
||||
@@ -76,6 +80,38 @@ get_sourcehostname() {
|
||||
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() {
|
||||
log "Checking if ethtool is installed..."
|
||||
|
||||
@@ -142,6 +178,7 @@ write_zsync_config() {
|
||||
}
|
||||
|
||||
run_zsync() {
|
||||
write_zsync_config
|
||||
if [[ "$ZSYNC" != "no" ]]; then
|
||||
/usr/bin/bashclub-zsync -c "/etc/bashclub/$SOURCEHOST.conf"
|
||||
else
|
||||
@@ -152,16 +189,80 @@ run_zsync() {
|
||||
run_remote_updates() {
|
||||
if [[ "${UPDATES,,}" == "yes" ]]; then
|
||||
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
|
||||
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"
|
||||
}
|
||||
else
|
||||
log "PBS updates skipped (BACKUPSERVER=$BACKUPSERVER)"
|
||||
fi
|
||||
|
||||
else
|
||||
log "Updates disabled (UPDATES=$UPDATES)"
|
||||
fi
|
||||
@@ -190,6 +291,15 @@ send_piggyback() {
|
||||
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() {
|
||||
if [[ "${BACKUPSERVER,,}" != "yes" ]]; then
|
||||
log "PBS Backup übersprungen: BACKUPSERVER='$BACKUPSERVER' (muss 'yes' sein)"
|
||||
@@ -271,10 +381,10 @@ run_maintenance() {
|
||||
|
||||
if [[ "$(date +%u)" == "$MAINTDAY" ]]; then
|
||||
log "Running maintenance..."
|
||||
PRUNEJOB=$(ssh "$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 root@"$PBSHOST" proxmox-backup-manager garbage-collection start "$BACKUPSTOREPBS"
|
||||
ssh root@"$PBSHOST" proxmox-backup-manager verify backup
|
||||
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
|
||||
@@ -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() {
|
||||
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..."
|
||||
log "Shutting down now...in 60sec"
|
||||
sleep 60
|
||||
shutdown now
|
||||
else
|
||||
log "No shutdown requested."
|
||||
@@ -321,6 +519,7 @@ send_piggyback_external() {
|
||||
log "Externer Piggyback-Export deaktiviert."
|
||||
return
|
||||
fi
|
||||
write_zsync_config
|
||||
get_sourcehostname
|
||||
log "Ermittelter SOURCEHOSTNAME: $SOURCEHOSTNAME"
|
||||
|
||||
@@ -365,7 +564,7 @@ send_checkzfs_external() {
|
||||
log "Externer Check-ZFS deaktiviert (ECHECKZFS=$ECHECKZFS)"
|
||||
return
|
||||
fi
|
||||
|
||||
write_zsync_config
|
||||
get_sourcehostname # Setzt SOURCEHOSTNAME, wenn nicht vorhanden
|
||||
|
||||
local checkzfs_cmd="${checkzfs_cmd:-$(command -v checkzfs)}"
|
||||
@@ -388,9 +587,9 @@ send_checkzfs_external() {
|
||||
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 spooldest="90000_${combined}_checkzfs_external"
|
||||
local spooldest="120000_${combined}_checkzfs_external"
|
||||
|
||||
log "Führe checkzfs aus..."
|
||||
{
|
||||
@@ -413,25 +612,89 @@ send_checkzfs_external() {
|
||||
|
||||
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:
|
||||
if [[ $# -eq 0 ]]; then
|
||||
if [[ -n "$CONFIG_FILE" ]]; then
|
||||
log "Backup-Routine startet in 60 Sekunden..."
|
||||
sleep 60
|
||||
#log "Starting full backup routine..."
|
||||
wait
|
||||
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
|
||||
run_zsync
|
||||
|
||||
if [[ "${BACKUPSERVER,,}" == "yes" ]]; then
|
||||
log "BACKUPSERVER ist aktiviert, führe Backup aus..."
|
||||
run_maintenance
|
||||
run_pbs_backup
|
||||
run_maintenance
|
||||
run_pbs_log_clear
|
||||
else
|
||||
log "BACKUPSERVER ist nicht aktiviert (BACKUPSERVER=$BACKUPSERVER), überspringe Backup."
|
||||
fi
|
||||
run_remote_updates
|
||||
if [[ "${EPIGGYBACK,,}" == "yes" ]]; then
|
||||
send_piggyback_external
|
||||
fi
|
||||
|
||||
if [[ "${ECHECKZFS,,}" == "yes" ]]; then
|
||||
send_checkzfs_external
|
||||
fi
|
||||
shutdown_now
|
||||
else
|
||||
usage
|
||||
|
||||
@@ -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