16 Commits

Author SHA1 Message Date
patrick a15a7bad54 2.0 2025-07-24 12:52:46 +02:00
patrick 849b5e9570 miyagi-backup.sh aktualisiert 2025-07-17 11:11:11 +02:00
patrick 1221a6cf23 config.example aktualisiert
REPLEXCLUDE=$BACKUPEXCLUDE
2025-07-15 15:13:32 +02:00
patrick 0db8fee019 config.example aktualisiert
PBSPORT
2025-07-15 01:49:56 +02:00
patrick 564939053a miyagi-check.sh aktualisiert 2025-07-15 01:49:11 +02:00
patrick 127431689a miyagi-convert.sh aktualisiert
fix BACKUPEXCLUDE
2025-07-15 00:01:56 +02:00
patrick f999ff6a87 miyagi-check.sh aktualisiert 2025-07-14 18:19:37 +02:00
patrick 8e121cb75c miyagi-convert.sh aktualisiert 2025-07-14 17:49:44 +02:00
patrick 2329aa7377 miyag-convert.sh hinzugefügt 2025-07-08 23:56:52 +02:00
patrick 7eb712fae4 miyagi-backup.sh aktualisiert 2025-07-02 15:55:49 +02:00
patrick 93780dad1b miyagi-check.sh aktualisiert 2025-07-02 15:43:47 +02:00
patrick 802b7b8c63 config.example aktualisiert 2025-07-02 15:07:17 +02:00
patrick e6842f1de8 miyagi-backup.sh aktualisiert 2025-06-30 14:55:07 +02:00
patrick 25220beae2 miyagi-backup.sh aktualisiert
ssh -p "$SSHPORT""$SOURCEHOST" hostname)
2025-06-30 13:37:35 +02:00
patrick 2954647d87 miyagi-backup.sh aktualisiert
extra monitoring hosts
2025-06-29 20:52:26 +02:00
patrick 0efa02e0c2 config.example aktualisiert
extra monitoring hosts
2025-06-29 20:50:29 +02:00
4 changed files with 525 additions and 307 deletions
+14 -2
View File
@@ -1,10 +1,10 @@
#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?
# 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)
@@ -24,7 +24,19 @@ ZPUSHFILTER='hourly|daily|weekly|monthly' # Weitere Filter (leer lassen oder Mus
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='10,3252,3253,3254' # 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
+232 -152
View File
@@ -25,8 +25,6 @@ while getopts "c:" opt; do
*) 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
@@ -35,118 +33,98 @@ if [[ -n "${CONFIG_FILE:-}" ]]; then
exit 1 exit 1
fi fi
if ! bash -n "$CONFIG_FILE"; then if ! bash -n "$CONFIG_FILE"; then
log "Syntaxfehler in Konfigurationsdatei $CONFIG_FILE" log "Syntax error in configuration file $CONFIG_FILE"
exit 1 exit 1
fi fi
source "$CONFIG_FILE" source "$CONFIG_FILE"
# ==========================
# Konfigurationsprüfung
# ==========================
REQUIRED_VARS=( REQUIRED_VARS=(
SSHPORT SOURCEPORT BACKUPSERVER ZSYNC MAINTDAY SHUTDOWN UPDATES
BACKUPSERVER SOURCEHOST ZFSROOT ZFSSECOND ZFSTRGT ZPUSHTAG ZPUSHMINKEEP ZPUSHKEEP ZPUSHLABEL
ZSYNC PBSHOST BACKUPSTORE BACKUPSTOREPBS BACKUPEXCLUDE REPLEXCLUDE
MAINTDAY
SHUTDOWN
UPDATES
SOURCEHOST
ZFSROOT
ZFSSECOND
ZFSTRGT
ZPUSHTAG
ZPUSHMINKEEP
ZPUSHKEEP
ZPUSHLABEL
ZPUSHFILTER
PBSHOST
BACKUPSTORE
BACKUPSTOREPBS
BACKUPEXCLUDE
REPLEXCLUDE
) )
MISSING_VARS=() MISSING_VARS=()
for var in "${REQUIRED_VARS[@]}"; do for var in "${REQUIRED_VARS[@]}"; do
#if [[ -z "${!var:-}" ]]; then if [[ -z "${!var:-}" ]]; then
if ! declare -p "$var" &>/dev/null || [[ -z "${!var}" ]]; then
MISSING_VARS+=("$var") MISSING_VARS+=("$var")
fi fi
done done
if [[ "${#MISSING_VARS[@]}" -ne 0 ]]; then if [[ "${#MISSING_VARS[@]}" -gt 0 ]]; then
log "Fehlende Konfigurationsvariablen:" log "Missing configuration variables:"
for var in "${MISSING_VARS[@]}"; do for var in "${MISSING_VARS[@]}"; do
log " - $var" log " - $var"
done done
log "Breche ab bitte Konfigurationsdatei prüfen." log "Aborting — please check your configuration file."
exit 1 exit 1
else else
log "Alle erforderlichen Konfigurationsvariablen sind gesetzt." log "All required configuration variables are set."
fi fi
fi fi
# Funktionen rssh() { ssh -p "$SOURCEPORT" "$@"; }
remote_ssh() { rscp() { scp -P "$SOURCEPORT" "$@"; }
ssh -p "$SSHPORT" "$@"
}
remote_scp() { get_sourcehostname() {
scp -P "$SSHPORT" "$@" if [[ -z "${SOURCEHOSTNAME:-}" ]]; then
log "SOURCEHOSTNAME is empty, retrieving via SSH from $SOURCEHOST..."
SOURCEHOSTNAME=$(rssh "$SOURCEHOST" hostname)
log "Detected SOURCEHOSTNAME: $SOURCEHOSTNAME"
fi
} }
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 "Setze WOL auf 'g' fuer $iface..." log "Setting WOL to 'g' for $iface..."
ethtool -s "$iface" wol g || log "Fehler beim Setzen von WOL auf $iface" ethtool -s "$iface" wol g || log "Error setting WOL on $iface"
else else
log "WOL ist bereits korrekt auf 'g' fuer $iface" log "WOL already set to 'g' for $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 "Ergaenze WOL-Befehl im statischen Block fuer $iface..." log "Adding WOL command in static block for $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-Befehl fuer $iface ist bereits im statischen Block vorhanden." log "WOL command already present in static block for $iface."
fi fi
else else
log "Kein statischer Eintrag fuer $iface gefunden, keine Aenderung vorgenommen." log "No static entry found for $iface, no changes made."
fi fi
fi fi
done done
} }
write_zsync_config() { write_zsync_config() {
get_sourcehostname
local conf_file="/etc/bashclub/$SOURCEHOST.conf" local conf_file="/etc/bashclub/$SOURCEHOST.conf"
log "Writing zsync config to $conf_file" log "Writing zsync config to $conf_file"
{ {
echo "target=$ZFSTRGT" echo "target=$ZFSTRGT"
echo "source=root@$SOURCEHOST" echo "source=root@$SOURCEHOST"
echo "sshport=$SSHPORT" echo "sshport=$SOURCEPORT"
echo "tag=$ZPUSHTAG" echo "tag=$ZPUSHTAG"
echo "snapshot_filter=\"$ZPUSHFILTER\"" echo "snapshot_filter=\"$ZPUSHFILTER\""
echo "min_keep=$ZPUSHMINKEEP" echo "min_keep=$ZPUSHMINKEEP"
@@ -167,38 +145,34 @@ run_zsync() {
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
log "Zsync is disabled" log "Zsync is disabled."
fi 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..."
apt update && apt dist-upgrade -y || log "Error during local updates"
if [[ "${BACKUPSERVER,,}" == "yes" ]]; then
log "Running updates on PBS host ($PBSHOST)..."
ssh root@"$PBSHOST" apt update && ssh root@"$PBSHOST" apt dist-upgrade -y || {
log "Error during updates on $PBSHOST"
}
else else
log "Remote updates disabled" log "PBS updates skipped (BACKUPSERVER=$BACKUPSERVER)"
fi
else
log "Updates disabled (UPDATES=$UPDATES)"
fi fi
} }
run_remote_updates() { send_piggyback() {
if [[ "$UPDATES" == "yes" ]]; then get_sourcehostname
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 combined_host="miyagi-${SOURCEHOSTNAME}-$(hostname)"
local filename="90000_${combined_host}" local filename="90000_${combined_host}"
log "Do nott forget to add a Host in CMK named: ${combined_host} (without Agent, Piggyback enabled)!" log "Reminder: Add a host named ${combined_host} in CMK (without Agent, Piggyback enabled)!"
log "Creating piggyback file: $filename" log "Creating piggyback file: $filename"
{ {
@@ -207,77 +181,94 @@ send_piggyback_data() {
echo "<<<<>>>>" echo "<<<<>>>>"
} > "$filename" } > "$filename"
if scp -P "$SSHPORT" "$filename" "$SOURCEHOST:/var/lib/check_mk_agent/spool/"; then if rscp "$filename" "$SOURCEHOST:/var/lib/check_mk_agent/spool/"; then
log "Piggyback data successfully sent to $SOURCEHOST" log "Piggyback data successfully sent to $SOURCEHOST"
else else
log "ERROR: Failed to send piggyback data to $SOURCEHOST" log "ERROR: Failed to send piggyback data to $SOURCEHOST"
fi fi
rm -f "$filename"
} }
run_pbs_backup() { run_pbs_backup() {
if [[ -z "${SOURCEHOSTNAME:-}" ]]; then if [[ "${BACKUPSERVER,,}" != "yes" ]]; then
SOURCEHOSTNAME=$(ssh "$SOURCEHOST" hostname) log "PBS Backup übersprungen: BACKUPSERVER='$BACKUPSERVER' (muss 'yes' sein)"
return 0
fi fi
log "Running PBS vzdump job..." get_sourcehostname
log "Starte PBS Backup auf Host: $SOURCEHOST"
# PBS-Storage ggf. aktivieren # PBS-Storage prüfen und ggf. aktivieren
log "Checking if PBS storage '$BACKUPSTORE' is enabled on $SOURCEHOST..." log "Prüfe, ob PBS-Storage '$BACKUPSTORE' auf $SOURCEHOST aktiviert ist..."
if ssh root@"$SOURCEHOST" "pvesm status | grep -w '$BACKUPSTORE' | grep -q 'disabled'"; then if rssh root@"$SOURCEHOST" "pvesm status | grep -w '$BACKUPSTORE' | grep -q 'disabled'"; then
log "PBS storage '$BACKUPSTORE' is disabled. Attempting to enable...sleep 10 Sekunden" log "PBS-Storage '$BACKUPSTORE' ist deaktiviert aktiviere temporär und warte 10 Sekunden..."
ssh root@"$SOURCEHOST" "pvesm set '$BACKUPSTORE' --disable 0 && sleep 10" rssh root@"$SOURCEHOST" "pvesm set '$BACKUPSTORE' --disable 0 && sleep 10"
pbs_enabled_by_script=true pbs_enabled_by_script=true
else else
log "PBS storage '$BACKUPSTORE' is already enabled." log "PBS-Storage '$BACKUPSTORE' ist bereits aktiviert."
pbs_enabled_by_script=false pbs_enabled_by_script=false
fi fi
vzdump_success=false vzdump_success=false
# Hauptversuch mit --pbs-change-detection-mode # Versuch: vzdump mit Change Detection
if ssh root@"$SOURCEHOST" vzdump --pbs-change-detection-mode metadata \ ## 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" \ --node "$SOURCEHOSTNAME" --storage "$BACKUPSTORE" \
--exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \ --exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \
--notes-template '{{guestname}}'; then --notes-template '{{guestname}}'; then
log "vzdump with change-detection-mode succeeded" log "vzdump (Change Detection) erfolgreich."
vzdump_success=true vzdump_success=true
else else
log "Fallback: vzdump with change-detection-mode failed, trying without it..." log "vzdump (Change Detection) fehlgeschlagen versuche Fallback ohne Change Detection..."
if ssh root@"$SOURCEHOST" vzdump \ if rssh root@"$SOURCEHOST" vzdump \
--node "$SOURCEHOSTNAME" --storage "$BACKUPSTORE" \ --node "$SOURCEHOSTNAME" --storage "$BACKUPSTORE" \
--exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \ --exclude "$BACKUPEXCLUDE" --mode snapshot --all 1 \
--notes-template '{{guestname}}'; then --notes-template '{{guestname}}'; then
log "Fallback vzdump succeeded" log "Fallback-vzdump erfolgreich."
vzdump_success=true vzdump_success=true
else else
log "ERROR: vzdump failed even after fallback" log "FEHLER: vzdump fehlgeschlagen auch im Fallback."
fi fi
fi fi
# PBS-Storage wieder deaktivieren, wenn zuvor aktiviert und erfolgreich # PBS-Storage ggf. wieder deaktivieren
if [[ "$vzdump_success" == true && "$pbs_enabled_by_script" == true ]]; then if [[ "$vzdump_success" == true && "$pbs_enabled_by_script" == true ]]; then
log "Disabling PBS storage '$BACKUPSTORE' again on $SOURCEHOST..." log "Deaktiviere temporär aktiviertes PBS-Storage '$BACKUPSTORE' auf $SOURCEHOST..."
ssh root@"$SOURCEHOST" "pvesm set '$BACKUPSTORE' --disable 1" rssh root@"$SOURCEHOST" "pvesm set '$BACKUPSTORE' --disable 1"
fi fi
# Monitoring-Output für Checkmk # Monitoring-Ausgabe
if [[ "$vzdump_success" == true ]]; then if [[ "$vzdump_success" == true ]]; then
echo "0 DailyPBS - Daily Backup" > /tmp/cmk_tmp.out echo "0 DailyPBS - Daily Backup erfolgreich" > /tmp/cmk_tmp.out
else else
echo "2 DailyPBS - Daily Backup FAILED" > /tmp/cmk_tmp.out echo "2 DailyPBS - Daily Backup FEHLGESCHLAGEN" > /tmp/cmk_tmp.out
fi 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" 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
} }
run_maintenance() { run_maintenance() {
if [[ "${BACKUPSERVER,,}" != "yes" ]]; then
log "PBS Backup wird übersprungen (BACKUPSERVER=$BACKUPSERVER)"
return
fi
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 "$PBSHOST" proxmox-backup-manager prune-job list --output-format json-pretty | grep -m 1 "id" | cut -d'"' -f4)
@@ -289,41 +280,35 @@ run_maintenance() {
fi fi
} }
run_scrub_stop_src() { run_scrub_stop() {
ssh -p "$SSHPORT" root@"$SOURCEHOST" 'for pool in $(zpool list -H -o name); do local mode="$1" # "local" oder "remote"
echo "Stoppe Scrub auf Pool: $pool" local ssh_cmd=()
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() { 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 for pool in $(zpool list -H -o name); do
echo "Stoppe Scrub auf Pool: $pool" echo "Stopping scrub on pool: $pool"
if zpool status "$pool" | grep -q "scrub in progress"; then if zpool status "$pool" | grep -q "scrub in progress"; then
if zpool scrub -s "$pool"; then if zpool scrub -s "$pool"; then
echo "Scrub auf $pool gestoppt" echo "Scrub stopped on $pool"
else else
echo "Fehler beim Stoppen des Scrubs auf $pool" echo "Error stopping scrub on $pool"
fi fi
else else
echo "Kein aktiver Scrub auf $pool" echo "No active scrub on $pool"
fi fi
done done
'
} }
shutdown_if_requested() { shutdown_now() {
log "SHUTDOWN-Variable: '${SHUTDOWN:-nicht gesetzt}'" if [[ "${SHUTDOWN,,}" == "yes" ]]; then
#if [[ "${SHUTDOWN,,}" == "yes" ]]; then send_piggyback
if [[ "$(echo "$SHUTDOWN" | tr '[:upper:]' '[:lower:]')" == "yes" ]]; then send_piggyback_external
send_piggyback_data send_checkzfs_external
log "Shutting down now..." log "Shutting down now..."
shutdown now shutdown now
else else
@@ -331,18 +316,113 @@ shutdown_if_requested() {
fi fi
} }
main() { send_piggyback_external() {
if [[ "${EPIGGYBACK,,}" != "yes" ]]; then
log "Externer Piggyback-Export deaktiviert."
return
fi
get_sourcehostname
log "Ermittelter SOURCEHOSTNAME: $SOURCEHOSTNAME"
local combined_host="miyagi-${SOURCEHOSTNAME}-$(hostname)"
local spool_file="90000_${combined_host}_external"
local temp_dir
temp_dir=$(mktemp -d)
log "Erzeuge temporäre Piggyback-Datei: $temp_dir/$spool_file"
{
echo "<<<<${combined_host}>>>>"
/usr/bin/check_mk_agent
echo "<<<<>>>>"
} > "$temp_dir/$spool_file"
if scp -P "$EPIGGYBACK_PORT" "$temp_dir/$spool_file" "$EPIGGYBACK_HOST:/var/lib/check_mk_agent/spool/"; then
log "Piggyback-Daten erfolgreich an $EPIGGYBACK_HOST gesendet: $spool_file"
else
log "Fehler beim Übertragen der Piggyback-Daten an $EPIGGYBACK_HOST"
rm -rf "$temp_dir"
return 1
fi
rm -rf "$temp_dir"
return 0
}
send_checkzfs_external() {
if [[ "${ECHECKZFS,,}" != "yes" ]]; then
log "Externer Piggyback-Export deaktiviert."
return
fi
local config="/etc/bashclub/${SOURCEHOST}.conf"
if [[ ! -f "$config" ]]; then
log "Konfigurationsdatei fehlt: $config"
return 1
fi
source "$config"
if [[ "${ECHECKZFS,,}" != "yes" ]]; then
log "Externer Check-ZFS deaktiviert (ECHECKZFS=$ECHECKZFS)"
return
fi
get_sourcehostname # Setzt SOURCEHOSTNAME, wenn nicht vorhanden
local checkzfs_cmd="${checkzfs_cmd:-$(command -v checkzfs)}"
if [[ -z "$checkzfs_cmd" || ! -x "$checkzfs_cmd" ]]; then
log "checkzfs nicht gefunden oder nicht ausführbar Abbruch."
return 1
fi
log "Verwende checkzfs: $checkzfs_cmd"
# Filter anhand ZFS-Tags auf dem SOURCEHOST aufbauen
local filter=""
while IFS=$'\t' read -r name value source; do
if [[ "$source" == "local" && "$value" == "subvols" ]]; then
filter+="${name}/|"
elif [[ "$source" == "local" && "$value" == "all" ]]; then
filter+="${name}|"
fi
done < <(ssh -p "$SOURCEPORT" "$SOURCEHOST" "zfs get -H -o name,value,source -t filesystem,volume $ZPUSHTAG")
filter="#${filter%|}"
log "Generierter ZFS-Filter: $filter"
local combined="miyagi-${SOURCEHOSTNAME}-$(hostname)"
local spoolfile="/tmp/${combined}_checkzfs_external"
local spooldest="90000_${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
log "Fehler beim Übertragen der Spool-Datei an $ECHECKZFS_HOST"
fi
rm -f "$spoolfile"
}
# Main execution:
if [[ $# -eq 0 ]]; then
if [[ -n "$CONFIG_FILE" ]]; then
log "Backup-Routine startet in 60 Sekunden..." log "Backup-Routine startet in 60 Sekunden..."
sleep 60 sleep 60
log "Starting full backup routine..." #log "Starting full backup routine..."
log "Running full backup using configuration file: $CONFIG_FILE"
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..."
@@ -351,23 +431,23 @@ main() {
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 shutdown_now
shutdown_if_requested else
usage
} fi
# 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 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
View File
@@ -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."
+109
View File
@@ -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"