Initial commit

This commit is contained in:
Thorsten committed 2026-05-12 15:51:25 +02:00
commit f192ba5548
7 files changed
+316

No files matched your search

+1
View File
@@ -0,0 +1 @@
*.own
+111
View File
@@ -0,0 +1,111 @@
# Proxmox Backup Status → ntfy
Sendet nach jedem Proxmox Backup automatisch eine kompakte Statusmeldung an einen ntfy-Server.
Der Hauptunterschied zum integrierten Benachrichtigungssystem von Proxmox ist die `Übersichtlichkeit` und die `korrekte Priorisierung` der Benachrichtigungen.
Erfolgreiche Backups haben `normale` Priorität, fehlgeschlagene die `höchste` Priorität. Das erlaubt es, sich ausschließlich bei Fehlern benachrichtigen zu lassen.
Die Nachricht enthält unter anderem:
* Backup-Status (Erfolg/Fehler)
* Start- und Endzeit
* Gesicherte Systeme
* Laufzeit
* Optional komplettes Logfile bei Fehlern
## Beispiel Benachrichtigungen
| Erfolgreiches Backup | Fehlgeschlagenes Backup |
|---|---|
| <img src="./images/ntfy-erfolg.png" width="400"> | <img src="./images/ntfy-fehlgeschlagen.png" width="400"> |
---
# Installation
Scripte z. B. nach:
```bash
/root/scripts/shell_proxmox_backup_status_to_ntfy.sh
/root/scripts/shell_proxmox_backup_status_to_ntfy_hook.sh
```
kopieren und ausführbar machen:
```bash
chmod +x /root/scripts/shell_proxmox_backup_status_to_ntfy.sh
chmod +x /root/scripts/shell_proxmox_backup_status_to_ntfy_hook.sh
```
---
# Proxmox konfigurieren
Damit Proxmox das Script nach jedem Backup automatisch ausführt, muss die Datei:
```bash
nano /etc/vzdump.conf
```
angepasst werden.
Folgende Zeile ergänzen:
```ini
script: /root/scripts/shell_proxmox_backup_status_to_ntfy_hook.sh
```
Beispiel:
```ini
# vzdump default settings
#tmpdir: DIR
#dumpdir: DIR
#storage: STORAGE_ID
#mode: snapshot|suspend|stop
#bwlimit: KBPS
script: /root/scripts/shell_proxmox_backup_status_to_ntfy_hook.sh
#exclude-path: PATHLIST
```
Danach wird das Script automatisch von Proxmox bei jedem vzdump-Backup aufgerufen.
---
# Konfiguration
Die Konfiguration erfolgt über eine `.env` Datei.
Beispiel:
```env
# =========================================================
# NTFY KONFIGURATION
# =========================================================
# IP oder Domain deines ntfy-Servers
SERVER="192.168.178.25:5885"
# Der Name des Kanals (Topic)
TOPIC="Proxmox"
# Authentifizierungs-Token
TOKEN="tk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Optional: Link zu einem Icon für die Benachrichtigung
NTFY_ICON=""
# =========================================================
# BACKUP-LOG EINSTELLUNGEN
# =========================================================
# Steuert, ob das detaillierte Backup-Log angehängt werden soll.
# Mögliche Werte:
# "true" oder "ja" -> Log wird gesendet
# "false" oder "nein" -> Log wird nicht gesendet
# "error" -> Log wird nur bei Fehlern gesendet (default)
SEND_LOGFILE=error
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

+26
View File
@@ -0,0 +1,26 @@
# =========================================================
# NTFY KONFIGURATION
# =========================================================
# IP oder Domain deines ntfy-Servers
SERVER="192.168.178.25:5885"
# Der Name des Kanals (Topic)
TOPIC="Proxmox"
# Authentifizierungs-Token
TOKEN="tk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Optional: Link zu einem Icon für die Benachrichtigung
NTFY_ICON=""
# =========================================================
# BACKUP-LOG EINSTELLUNGEN
# =========================================================
# Steuert, ob das detaillierte Backup-Log angehängt werden soll.
# Mögliche Werte:
# "true" oder "ja" -> Log wird gesendet
# "false" oder "nein" -> Log wird nicht gesendet
# "error" -> Log wird nur bei Fehlern gesendet (default)
SEND_LOGFILE=error
+170
View File
@@ -0,0 +1,170 @@
#!/usr/bin/env bash
# =========================================================
# 1. UMGEBUNG & INITIALISIERUNG
# =========================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${SCRIPT_DIR}/shell_proxmox_backup_status_to_ntfy.env"
[[ -f "${ENV_FILE}" ]] && source "${ENV_FILE}"
PHASE="${1:-}"
[[ "${PHASE}" != "job-end" ]] && exit 0
sleep 5
format_compact_time() {
local T=$1
[[ -z "$T" || "$T" -lt 0 ]] && T=0
[[ "$T" -eq 0 ]] && { echo "< 1s"; return; }
local H=$((T/3600)) M=$((T%3600/60)) S=$((T%60))
local RES=""
[[ $H -gt 0 ]] && RES+="${H}h "
[[ $M -gt 0 ]] && RES+="${M}m "
[[ $S -gt 0 || -z "$RES" ]] && RES+="${S}s"
echo "$RES" | xargs
}
# =========================================================
# 2. TASK & LOG DATEN FINDEN
# =========================================================
TASK_LINE=$(tac /var/log/pve/tasks/index | grep -m 1 ":vzdump:")
[[ -z "$TASK_LINE" ]] && exit 0
UPID=$(echo "$TASK_LINE" | awk '{print $1}')
FINAL_TASK_STATUS=$(echo "$TASK_LINE" | awk '{print $NF}')
LOGFILE="/tmp/proxmox_backup_$(date +%Y-%m-%d_%H-%M-%S).log"
pvesh get "/nodes/$(hostname)/tasks/${UPID}/log" --limit 0 --output-format json | jq -r '.[].t // empty' > "$LOGFILE"
[[ ! -s "$LOGFILE" ]] && { rm -f "$LOGFILE"; exit 0; }
# =========================================================
# 3. ZEITEN & JOB-INFO (DIREKT AUS LOG)
# =========================================================
RAW_START=$(grep -m1 "INFO: Backup started at" "$LOGFILE" | sed 's/^.*at //')
RAW_END=$(grep -E "INFO: Backup finished at|INFO: Backup job finished" "$LOGFILE" | tail -n1 | sed 's/^.*at //')
SEC_START=$(date -d "$RAW_START" +%s 2>/dev/null || date +%s)
SEC_END=$(date -d "$RAW_END" +%s 2>/dev/null || date +%s)
DISPLAY_START=$(date -d "@$SEC_START" "+%H:%M" 2>/dev/null)
DISPLAY_END=$(date -d "@$SEC_END" "+%H:%M" 2>/dev/null)
# Extrahiere Modus und Storage direkt aus der vzdump-Aufrufzeile im Log
L_STORAGE=$(grep -m1 "vzdump" "$LOGFILE" | sed -n "s/.*--storage \([^ ]*\).*/\1/p")
L_MODE=$(grep -m1 "vzdump" "$LOGFILE" | sed -n "s/.*--mode \([^ ]*\).*/\1/p")
# Formatiere den Namen für die COMMENT Variable (z.B. Snapshot an unRAID-pbs)
MODE_CAP=$(echo "${L_MODE:-Backup}" | sed 's/.*/\L&/; s/.*/\u&/')
COMMENT="${MODE_CAP} auf ${L_STORAGE:-Storage}"
# =========================================================
# 4. SYSTEM-LISTE AUFBEREITEN
# =========================================================
VM_STARTS=$(grep -n "Starting Backup of VM" "$LOGFILE")
SYSTEM_COUNT=$(echo "$VM_STARTS" | wc -l)
BACKUP_LIST=""
CURRENT_STEP=0
while read -r start_line; do
((CURRENT_STEP++))
LINE_NUM=$(echo "$start_line" | cut -d: -f1)
C_VMID=$(echo "$start_line" | grep -oP "VM \K[0-9]+")
VM_BLOCK=$(sed -n "${LINE_NUM},/Finished Backup of VM\|Failed at/p" "$LOGFILE")
V_S_RAW_VM=$(echo "$VM_BLOCK" | grep -m1 "Backup started at" | sed 's/^.*at //')
V_S_SEC_VM=$(date -d "$V_S_RAW_VM" +%s 2>/dev/null || echo 0)
if echo "$VM_BLOCK" | grep -qiE "ERROR:|failed"; then
STATUS_ICON="✗"
V_E_RAW_VM=$(echo "$VM_BLOCK" | grep -m1 "Failed at" | sed 's/^.*at //')
V_E_SEC_VM=$(date -d "$V_E_RAW_VM" +%s 2>/dev/null || echo 0)
[[ $V_E_SEC_VM -gt 0 ]] && V_DUR_VAL=$(format_compact_time $((V_E_SEC_VM - V_S_SEC_VM))) || V_DUR_VAL=""
else
STATUS_ICON="✓"
V_DUR_RAW=$(echo "$VM_BLOCK" | grep -oP "Finished Backup of VM $C_VMID \(\K[0-9:]+")
if [[ -n "$V_DUR_RAW" ]]; then
IFS=: read -r h m s <<< "$V_DUR_RAW"
V_SEC=$((10#$h * 3600 + 10#$m * 60 + 10#$s))
V_DUR_VAL=$(format_compact_time $V_SEC)
else
V_DUR_VAL="< 1s"
fi
fi
V_DUR_SUFFIX=""
[[ -n "$V_DUR_VAL" ]] && V_DUR_SUFFIX=" \`${V_DUR_VAL}\`"
V_NAME=""; V_TYPE=""
if pct status "$C_VMID" >/dev/null 2>&1; then
V_TYPE="CT"; V_NAME=$(pct config "$C_VMID" | grep "^hostname:" | awk '{print $2}')
elif qm status "$C_VMID" >/dev/null 2>&1; then
V_TYPE="VM"; V_NAME=$(qm config "$C_VMID" | grep "^name:" | awk '{print $2}')
fi
[[ -z "$V_NAME" ]] && V_NAME="VM $C_VMID"
[[ -z "$V_TYPE" ]] && V_TYPE="?"
PREFIX=$([[ $CURRENT_STEP -eq $SYSTEM_COUNT ]] && echo "└─" || echo "├─")
BACKUP_LIST+="${PREFIX} ${STATUS_ICON} **${V_NAME}** (${V_TYPE} ${C_VMID})${V_DUR_SUFFIX}"$'\n'
done <<< "$VM_STARTS"
TOTAL_DIFF=$((SEC_END - SEC_START))
TOTAL_DURATION=$(format_compact_time $TOTAL_DIFF)
# =========================================================
# 5. SENDEN
# =========================================================
if [[ "$FINAL_TASK_STATUS" == "OK" ]]; then
TITLE="✅ Proxmox Backup erfolgreich"
PRIO="default"
IS_ERROR="false"
else
TITLE="❌ Proxmox Backup FEHLGESCHLAGEN"
PRIO="high"
IS_ERROR="true"
fi
MESSAGE=$(cat <<EOF
🖥️ \`Node \` \`$(hostname)\`
📝 \`Typ \` \`${COMMENT}\`
⏱️ \`Start\` \`${DISPLAY_START} Uhr\`
⏱️ \`Ende \` \`${DISPLAY_END} Uhr\`
📦 \`Backups\`
${BACKUP_LIST%?}
⌛ \`Gesamtlaufzeit\` \`${TOTAL_DURATION}\`
EOF
)
SEND_VAL="${SEND_LOGFILE,,}"
if [[ "$SEND_VAL" == "true" || "$SEND_VAL" == "ja" ]]; then
SHOULD_SEND_LOG="true"
elif [[ "$SEND_VAL" == "false" || "$SEND_VAL" == "nein" ]]; then
SHOULD_SEND_LOG="false"
else
[[ "$IS_ERROR" == "true" ]] && SHOULD_SEND_LOG="true" || SHOULD_SEND_LOG="false"
fi
if [[ "$SHOULD_SEND_LOG" == "true" ]]; then
curl -s \
-H "Title: Backup Log (${COMMENT})" \
-H "Priority: min" \
-H "Markdown: yes" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Filename: backup_$(date +%Y-%m-%d_%H-%M).log" \
-T "${LOGFILE}" \
"http://${SERVER}/${TOPIC}" >/dev/null 2>&1
sleep 1
fi
curl -s \
-H "Title: ${TITLE}" \
-H "Priority: ${PRIO}" \
-H "Markdown: yes" \
-H "Authorization: Bearer ${TOKEN}" \
-d "${MESSAGE}" \
"http://${SERVER}/${TOPIC}" >/dev/null 2>&1
rm -f "$LOGFILE"
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
setsid "${SCRIPT_DIR}/shell_proxmox_backup_status_to_ntfy.sh" "$@" \
>/dev/null 2>&1 < /dev/null &
exit 0