Files
miyagi-backup/miyagi-backup.sh
T
patrick 2954647d87 miyagi-backup.sh aktualisiert
extra monitoring hosts
2025-06-29 20:52:26 +02:00

433 lines
13 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
set -uo pipefail
IFS=$'\n\t'
PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
SCRIPT_NAME=$(basename "$0")
LOGFILE="/var/log/${SCRIPT_NAME%.sh}.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"
}
usage() {
echo "Usage:"
echo " $0 -c /path/to/config # Full backup run"
echo " $0 [function] # Run individual function"
echo " $0 help # Show available functions"
exit 1
}
CONFIG_FILE=""
while getopts "c:" opt; do
case "$opt" in
c) CONFIG_FILE="$OPTARG" ;;
*) usage ;;
esac
done
# Bei Einzelaufruf muss Konfiguration geladen sein
shift $((OPTIND - 1))
if [[ -n "${CONFIG_FILE:-}" ]]; then
if [[ ! -f "$CONFIG_FILE" ]]; then
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
fi
source "$CONFIG_FILE"
# ==========================
# Konfigurationsprüfung
# ==========================
REQUIRED_VARS=(
SSHPORT
BACKUPSERVER
ZSYNC
MAINTDAY
SHUTDOWN
UPDATES
SOURCEHOST
ZFSROOT
ZFSSECOND
ZFSTRGT
ZPUSHTAG
ZPUSHMINKEEP
ZPUSHKEEP
ZPUSHLABEL
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
# Funktionen
remote_ssh() {
ssh -p "$SSHPORT" "$@"
}
remote_scp() {
scp -P "$SSHPORT" "$@"
}
set_wol_g_enabled() {
log "Pruefe, ob ethtool installiert ist..."
if ! command -v ethtool >/dev/null 2>&1; then
log "ethtool ist nicht installiert, versuche Installation..."
apt update && apt install -y ethtool || {
log "Fehler: ethtool konnte nicht installiert werden."
return 1
}
else
log "ethtool ist bereits installiert."
fi
log "Pruefe und setze Wake-on-LAN (WOL) auf 'g' nur bei Interfaces mit statischer IP..."
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
log "Bearbeite physisches Interface: $iface"
# Aktuellen WOL-Status pruefen
current_wol=$(ethtool "$iface" 2>/dev/null | awk '/Wake-on:/ {print $2}')
if [[ "$current_wol" != "g" ]]; then
log "Setze WOL auf 'g' fuer $iface..."
ethtool -s "$iface" wol g || log "Fehler beim Setzen von WOL auf $iface"
else
log "WOL ist bereits korrekt auf 'g' fuer $iface"
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 -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 "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
else
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
done
}
write_zsync_config() {
local conf_file="/etc/bashclub/$SOURCEHOST.conf"
log "Writing zsync config to $conf_file"
{
echo "target=$ZFSTRGT"
echo "source=root@$SOURCEHOST"
echo "sshport=$SSHPORT"
echo "tag=$ZPUSHTAG"
echo "snapshot_filter=\"$ZPUSHFILTER\""
echo "min_keep=$ZPUSHMINKEEP"
echo "zfs_auto_snapshot_keep=$ZPUSHKEEP"
echo "zfs_auto_snapshot_label=$ZPUSHLABEL"
echo "zfs_auto_snapshot_engine=internal"
echo "checkzfs_disabled=0"
echo "checkzfs_local=0"
echo "checkzfs_prefix=miyagi-$SOURCEHOSTNAME-$(hostname)-$ZPUSHTAG"
echo "checkzfs_max_age=1500,2000"
echo "checkzfs_max_snapshot_count=180,200"
echo "checkzfs_spool=1"
echo "checkzfs_spool_maxage=90000"
} > "$conf_file"
}
run_zsync() {
if [[ "$ZSYNC" != "no" ]]; then
/usr/bin/bashclub-zsync -c "/etc/bashclub/$SOURCEHOST.conf"
else
log "Zsync is disabled"
fi
}
run_remote_updates() {
if [[ "$UPDATES" == "yes" ]]; then
ssh "$PBSHOST" apt update && apt dist-upgrade -y
else
log "Remote updates disabled"
fi
}
run_remote_updates() {
if [[ "$UPDATES" == "yes" ]]; then
ssh "$PBSHOST" apt update && apt dist-upgrade -y
else
log "Remote updates disabled"
fi
}
send_piggyback_data() {
# Falls SOURCEHOSTNAME leer ist, ueber SSH vom Zielhost ermitteln
if [[ -z "${SOURCEHOSTNAME:-}" ]]; then
log "SOURCEHOSTNAME is empty retrieving via SSH from $SOURCEHOST..."
SOURCEHOSTNAME=$(ssh -p "$SSHPORT" "$SOURCEHOST" hostname)
log "Detected SOURCEHOSTNAME: $SOURCEHOSTNAME"
fi
local combined_host="miyagi-${SOURCEHOSTNAME}-$(hostname)"
local filename="90000_${combined_host}"
log "Do nott forget to add a Host in CMK named: ${combined_host} (without Agent, Piggyback enabled)!"
log "Creating piggyback file: $filename"
{
echo "<<<<${combined_host}>>>>"
/usr/bin/check_mk_agent
echo "<<<<>>>>"
} > "$filename"
if scp -P "$SSHPORT" "$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
}
run_pbs_backup() {
if [[ -z "${SOURCEHOSTNAME:-}" ]]; then
SOURCEHOSTNAME=$(ssh "$SOURCEHOST" hostname)
fi
log "Running PBS vzdump job..."
# PBS-Storage ggf. aktivieren
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
vzdump_success=false
# Hauptversuch mit --pbs-change-detection-mode
if ssh root@"$SOURCEHOST" vzdump --pbs-change-detection-mode metadata \
--node "$SOURCEHOSTNAME" --storage "$BACKUPSTORE" \
--exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \
--notes-template '{{guestname}}'; then
log "vzdump with change-detection-mode succeeded"
vzdump_success=true
else
log "Fallback: vzdump with change-detection-mode failed, trying without it..."
if ssh root@"$SOURCEHOST" vzdump \
--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
# PBS-Storage wieder deaktivieren, wenn zuvor aktiviert und erfolgreich
if [[ "$vzdump_success" == true && "$pbs_enabled_by_script" == true ]]; then
log "Disabling PBS storage '$BACKUPSTORE' again on $SOURCEHOST..."
ssh root@"$SOURCEHOST" "pvesm set '$BACKUPSTORE' --disable 1"
fi
# Monitoring-Output für Checkmk
if [[ "$vzdump_success" == true ]]; then
echo "0 DailyPBS - Daily Backup" > /tmp/cmk_tmp.out
else
echo "2 DailyPBS - Daily Backup FAILED" > /tmp/cmk_tmp.out
fi
( echo "<<<local>>>" ; cat /tmp/cmk_tmp.out ) > /tmp/90000_checkpbs
scp /tmp/90000_checkpbs root@"$SOURCEHOST":/var/lib/check_mk_agent/spool || log "Fehler beim SCP des Monitoring-Outputs"
}
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
else
log "No maintenance scheduled for today."
fi
}
run_scrub_stop_src() {
ssh -p "$SSHPORT" root@"$SOURCEHOST" 'for pool in $(zpool list -H -o name); do
echo "Stoppe Scrub auf Pool: $pool"
if zpool status "$pool" | grep -q "scrub in progress"; then
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() {
for pool in $(zpool list -H -o name); do
echo "Stoppe Scrub auf Pool: $pool"
if zpool status "$pool" | grep -q "scrub in progress"; then
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
}
shutdown_if_requested() {
log "SHUTDOWN-Variable: '${SHUTDOWN:-nicht gesetzt}'"
#if [[ "${SHUTDOWN,,}" == "yes" ]]; then
if [[ "$(echo "$SHUTDOWN" | tr '[:upper:]' '[:lower:]')" == "yes" ]]; then
send_piggyback_data
send_piggyback_data_external
log "Shutting down now..."
shutdown now
else
log "No shutdown requested."
fi
}
send_piggyback_data_external() {
if [[ "${EPIGGYBACK,,}" != "yes" ]]; then
log "Externer Piggyback-Export deaktiviert."
return
fi
if [[ -z "$EPIGGYBACK_HOST" || -z "$EPIGGYBACK_PORT" ]]; then
log "EPIGGYBACK_HOST oder EPIGGYBACK_PORT nicht gesetzt Abbruch."
return 1
fi
local combined_host="miyagi-${SOURCEHOSTNAME}-$(hostname)"
local filename="90000_${combined_host}_external"
log "Erzeuge externe Piggyback-Datei: $filename"
{
echo "<<<<${combined_host}>>>>"
/usr/bin/check_mk_agent
echo "<<<<>>>>"
} > "$filename"
if scp -P "$EPIGGYBACK_PORT" "$filename" "$EPIGGYBACK_HOST:/var/lib/check_mk_agent/spool/"; then
log "Piggyback-Daten erfolgreich an $EPIGGYBACK_HOST gesendet."
else
log "Fehler beim Senden der Piggyback-Daten an $EPIGGYBACK_HOST"
fi
rm -f "$filename"
}
send_checkzfs_output_external() {
if [[ "${ECHECKZFS,,}" != "yes" ]]; then
log "Externer check_zfs-Export deaktiviert."
return
fi
if [[ -z "$ECHECKZFS_HOST" || -z "$ECHECKZFS_PORT" ]]; then
log "ECHECKZFS_HOST oder ECHECKZFS_PORT nicht gesetzt Abbruch."
return 1
fi
local checkfile="/tmp/90000_checkzfs_external"
log "Führe check_zfs aus und schreibe Output nach $checkfile"
/usr/lib/nagios/plugins/check_zfs > "$checkfile" 2>&1 || log "check_zfs Befehl fehlgeschlagen"
log "Sende check_zfs-Daten an $ECHECKZFS_HOST..."
if scp -P "$ECHECKZFS_PORT" "$checkfile" "$ECHECKZFS_HOST:/var/lib/check_mk_agent/spool/"; then
log "Check_zfs-Daten erfolgreich an $ECHECKZFS_HOST gesendet."
else
log "Fehler beim Senden der Check_zfs-Daten an $ECHECKZFS_HOST"
fi
rm -f "$checkfile"
}
main() {
log "Backup-Routine startet in 60 Sekunden..."
sleep 60
log "Starting full backup routine..."
SOURCEHOSTNAME=$(ssh "$SOURCEHOST" hostname)
set_wol_g_enabled
write_zsync_config
run_zsync
send_checkzfs_output_external
run_scrub_stop_local
run_scrub_stop_src
if [[ "${BACKUPSERVER,,}" == "yes" ]]; then
log "BACKUPSERVER ist aktiviert, führe Backup aus..."
run_maintenance
run_pbs_backup
else
log "BACKUPSERVER ist nicht aktiviert (BACKUPSERVER=$BACKUPSERVER), überspringe Backup."
fi
run_remote_updates
run_updates
shutdown_if_requested
}
# Funktionsbasierter Aufruf
if [[ "${1:-}" == "help" ]]; then
echo "Verfuegbare Funktionen:"
declare -F | awk '{print " - " $3}' | grep -v "^ - _"
exit 0
elif [[ "${1:-}" =~ ^[a-zA-Z0-9_]+$ && "$(type -t "$1")" == "function" ]]; then
FUNC="$1"
shift
"$FUNC" "$@"
exit 0
else
main
fi