168 lines
7.5 KiB
Bash
168 lines
7.5 KiB
Bash
#!/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" |