#!/bin/bash # ----------------------------------------------------------------------------------------------- # --------------------------------- Bandwidth Monitor ------------------------------------------ # ----------------------------------------------------------------------------------------------- # Logs rsync transfer history and generates weekly summary reports. # Designed for minimal flash drive impact — one bounded write per rsync run. # # Two modes: # --log-transfer "profile" duration status — called by rsync.sh after each sync # appends one line, trims old entries # --report (or no args) — generates summary from log # # Log format — one line per transfer, version-proof, never needs rsync output parsing: # YYYY-MM-DD|HH:MM|profile|duration_seconds|status # # Log file stays bounded to BANDWIDTH_LOG_RETENTION days — old entries trimmed on every write. # Minimal flash drive impact: one append + one trim per rsync run. # # All configuration in Master.conf under Bandwidth Monitor section. # ----------------------------------------------------------------------------------------------- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../Master.conf" source "$SCRIPT_DIR/../common.sh" parse_args "$@" # ----------------------------------------------------------------------------------------------- # Parse mode from PARSED_ARGS # ----------------------------------------------------------------------------------------------- LOG_TRANSFER_MODE=false TRANSFER_PROFILE="" TRANSFER_DURATION=0 TRANSFER_STATUS="success" for arg in "${PARSED_ARGS[@]}"; do case "$arg" in --log-transfer) LOG_TRANSFER_MODE=true ;; --report) LOG_TRANSFER_MODE=false ;; *) if [[ "$LOG_TRANSFER_MODE" == true ]]; then if [[ -z "$TRANSFER_PROFILE" ]]; then TRANSFER_PROFILE="$arg" elif [[ "$TRANSFER_DURATION" -eq 0 ]]; then TRANSFER_DURATION="$arg" else TRANSFER_STATUS="$arg" fi fi ;; esac done # Ensure log directory and file exist mkdir -p "$(dirname "$BANDWIDTH_LOG")" touch "$BANDWIDTH_LOG" 2>/dev/null || { error "Cannot write to bandwidth log: $BANDWIDTH_LOG" exit 1 } # ----------------------------------------------------------------------------------------------- # LOG TRANSFER MODE # Called by rsync.sh after each sync — appends one line and trims old entries. # Usage: bandwidth_monitor.sh --log-transfer "profile" duration_seconds status # ----------------------------------------------------------------------------------------------- if [[ "$LOG_TRANSFER_MODE" == true ]]; then [[ -z "$TRANSFER_PROFILE" ]] && error "No profile specified for --log-transfer" && exit 1 TODAY=$(date '+%Y-%m-%d') NOW=$(date '+%H:%M') DURATION_FMT=$(format_duration "$TRANSFER_DURATION") # Append entry echo "${TODAY}|${NOW}|${TRANSFER_PROFILE}|${TRANSFER_DURATION}|${TRANSFER_STATUS}" >> "$BANDWIDTH_LOG" log "$ICON_BANDWIDTH Logged: $TRANSFER_PROFILE — ${DURATION_FMT} — $TRANSFER_STATUS" # Trim entries older than retention period — keeps file bounded CUTOFF=$(date -d "${BANDWIDTH_LOG_RETENTION} days ago" '+%Y-%m-%d') TEMP_FILE="${BANDWIDTH_LOG}.tmp" awk -F'|' -v cutoff="$CUTOFF" '$1 >= cutoff' "$BANDWIDTH_LOG" > "$TEMP_FILE" && \ mv "$TEMP_FILE" "$BANDWIDTH_LOG" log "$ICON_BANDWIDTH Log trimmed — retaining entries from $CUTOFF onwards" exit 0 fi # ----------------------------------------------------------------------------------------------- # REPORT MODE — generate summary from log # ----------------------------------------------------------------------------------------------- echo "" echo "━━━ $ICON_BANDWIDTH Bandwidth Report — $(date '+%Y-%m-%d %H:%M:%S') ━━━" echo "" if [[ ! -s "$BANDWIDTH_LOG" ]]; then warn "No bandwidth data yet — log is empty" warn "Data accumulates as rsync jobs complete" exit 0 fi START=$(date +%s) # Date range OLDEST=$(awk -F'|' 'NR==1{print $1}' "$BANDWIDTH_LOG") NEWEST=$(awk -F'|' 'END{print $1}' "$BANDWIDTH_LOG") ENTRY_COUNT=$(wc -l < "$BANDWIDTH_LOG") SUCCESS_COUNT=$(awk -F'|' '$5=="success"' "$BANDWIDTH_LOG" | wc -l) FAILED_COUNT=$(awk -F'|' '$5=="failed"' "$BANDWIDTH_LOG" | wc -l) info "Log covers: $OLDEST → $NEWEST ($ENTRY_COUNT runs)" echo "" # ── Per-profile breakdown ──────────────────────────────────────────────────────────────────── echo "━━━ $ICON_BANDWIDTH Per-Profile Summary ━━━" awk -F'|' '{ runs[$3]++ duration[$3] += $4 if ($5 == "failed") fails[$3]++ } END { for (profile in runs) { avg = (runs[profile] > 0) ? duration[profile] / runs[profile] : 0 mins = int(avg / 60) secs = int(avg % 60) fail_count = (profile in fails) ? fails[profile] : 0 printf " %-20s %3d runs avg %dm%ds failed: %d\n", \ profile, runs[profile], mins, secs, fail_count } }' "$BANDWIDTH_LOG" | sort echo "" # ── Last 7 days ────────────────────────────────────────────────────────────────────────────── echo "━━━ $ICON_BANDWIDTH Last 7 Days ━━━" for i in 6 5 4 3 2 1 0; do day=$(date -d "$i days ago" '+%Y-%m-%d') day_name=$(date -d "$i days ago" '+%a') day_runs=$(awk -F'|' -v d="$day" '$1==d' "$BANDWIDTH_LOG" | wc -l) day_success=$(awk -F'|' -v d="$day" '$1==d && $5=="success"' "$BANDWIDTH_LOG" | wc -l) day_failed=$(awk -F'|' -v d="$day" '$1==d && $5=="failed"' "$BANDWIDTH_LOG" | wc -l) day_duration=$(awk -F'|' -v d="$day" '$1==d{sum+=$4} END{print sum+0}' "$BANDWIDTH_LOG") day_duration_fmt=$(format_duration "$day_duration") if [[ "$day_runs" -eq 0 ]]; then echo " $ICON_TIME $day ($day_name) — no syncs" elif [[ "$day_failed" -gt 0 ]]; then echo " $ICON_WARN $day ($day_name) — $day_runs runs / ${day_duration_fmt} total / $ICON_ERROR $day_failed failed" else echo " $ICON_DONE $day ($day_name) — $day_runs runs / ${day_duration_fmt} total" fi done echo "" # ── Totals ─────────────────────────────────────────────────────────────────────────────────── TOTAL_DURATION=$(awk -F'|' '{sum+=$4} END{print sum+0}' "$BANDWIDTH_LOG") TOTAL_DURATION_FMT=$(format_duration "$TOTAL_DURATION") END=$(date +%s) echo "━━━━━ $ICON_SUMMARY BANDWIDTH SUMMARY ━━━━━" echo "$ICON_BANDWIDTH Total runs: $ENTRY_COUNT ($SUCCESS_COUNT success / $FAILED_COUNT failed)" echo "$ICON_TIME Total time: $TOTAL_DURATION_FMT" echo "$ICON_TIME Log period: $OLDEST → $NEWEST" echo "$ICON_GEAR Retention: ${BANDWIDTH_LOG_RETENTION} days" echo "$ICON_TIME Generated in: $(format_duration $((END - START)))" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" notify "Bandwidth report on $(hostname) — $ENTRY_COUNT rsync runs ($SUCCESS_COUNT success / $FAILED_COUNT failed) over ${BANDWIDTH_LOG_RETENTION} day window" "Bandwidth Monitor" "normal"