#!/bin/bash # Collects MSP360 backup plan status from all known hosts and writes a JSON # status file, then pushes it to web.orbishosting.com (VM110). set -u CBB="/opt/local/MSP360 Backup/bin/cbb" CBBV2="/opt/local/MSP360 Backup/bin/cbbV2" SSHPW="Joker1974!!!" OUT=/tmp/backup-status.json NOW="$(date -u +%Y-%m-%dT%H:%M:%SZ)" get_plan_status() { # $1=host $2=user $3=planname $4=mode(local|hop) local host="$1" user="$2" plan="$3" mode="$4" out if [ "$mode" = "local" ]; then out="$("$CBB" plan -l 2>&1)" elif [ "$mode" = "hop" ]; then out="$(sshpass -p "$SSHPW" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=8 "$user@$host" "'$CBB' plan -l 2>&1")" fi local state result id state=$(echo "$out" | grep 'State:' | head -1 | sed 's/.*State: *//') result=$(echo "$out" | grep 'Last result:' | head -1 | sed 's/.*Last result: *//') id=$(echo "$out" | grep 'Id:' | head -1 | sed 's/.*Id: *//') echo "${state:-unreachable}|${result:-unreachable}|${id:-}" } # Fixed 2026-07-20: previously read CBB_Configuration/.cbb$/ generation # folders on the NAS, assuming that was "more trustworthy" than the plan's own # self-reported status. That folder turned out to only get written once, at # initial plan setup/first test run, and never again on subsequent scheduled # runs (even though those runs were genuinely succeeding daily and writing # real file data elsewhere in the destination tree) - so every host showed a # stale last_success frozen at its original setup date, making working daily # backups look dead for weeks. The plan's own "Last run time" (from cbbV2 # plan details) reflects the actual most recent run and is accurate. get_last_run_time() { # $1=host $2=user $3=mode $4=plan_id local host="$1" user="$2" mode="$3" plan_id="$4" out line [ -z "$plan_id" ] && { echo "null"; return; } if [ "$mode" = "local" ]; then out="$("$CBBV2" plan details -i "$plan_id" --progress 2>&1)" else out="$(sshpass -p "$SSHPW" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=8 "$user@$host" "'$CBBV2' plan details -i $plan_id --progress 2>&1")" fi line=$(echo "$out" | grep '^Last run time:' | head -1 | sed 's/^Last run time: *//') [ -z "$line" ] && { echo "null"; return; } date -u -d "$line" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "null" } echo "[" > "$OUT" first=1 emit() { local name="$1" ip="$2" state="$3" result="$4" last_success="$5" [ "$first" -eq 1 ] && first=0 || echo "," >> "$OUT" if [ "$last_success" = "null" ]; then cat >> "$OUT" <> "$OUT" </dev/null | sort | tail -1) if [ -z "$latest_dump" ]; then emit "$name" "$ip" "Unknown" "unreachable" "null" return fi ts=$(basename "$latest_dump" | sed -E 's/vzdump-qemu-[0-9]+-([0-9_]+-[0-9_]+)\.vma\.zst/\1/') iso=$(date -u -d "$(echo "$ts" | sed -E 's/(....)_(..)_(..)-(..)_(..)_(..)/\1-\2-\3 \4:\5:\6/')" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "") latest_log="$dump_dir/vzdump-qemu-$vmid-$ts.log" if [ -f "$latest_log" ] && grep -q "Finished Backup" "$latest_log"; then result="Success" else result="Warning" fi state="Proxmox nightly" if [ -n "$iso" ]; then emit "$name" "$ip" "$state" "$result" "$iso" else emit "$name" "$ip" "$state" "$result" "null" fi } collect "PVE1" "10.48.200.90" "" "local" collect "JARVIS" "10.48.200.211" "root" "hop" collect "NovaCPX" "10.48.200.110" "root" "hop" collect "Jellyfin" "10.48.200.33" "root" "hop" collect "MediaStack" "10.48.200.35" "root" "hop" collect_proxmox_vm "Homebridge" "10.48.200.18" "118" echo "]" >> "$OUT" python3 -c " import json data = json.load(open('$OUT')) data = {'updated': '$NOW', 'hosts': data} json.dump(data, open('$OUT', 'w'), indent=2) " sshpass -p "$SSHPW" scp -o StrictHostKeyChecking=no "$OUT" root@10.48.200.110:/home/webacct/public_html/downloads/backup-status.json