All FALLBACK_${MY_ID}_COVERS_${REMOTE_ID}_TIER* references updated to
FALLBACK_${REMOTE_ID}_TIER* across fallback_test.sh, partnership_manager.sh,
docker_update.sh, mesh_monitor.sh, and monitor.php. mesh_monitor.sh drops
the inner covering-host loop — tier data now lives in the covered host's own
conf so no cross-host scan is needed. monitor.php reads from the covered
host's conf file rather than the local host's.
219 lines
9.5 KiB
Bash
Executable File
219 lines
9.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# ==============================================================================================
|
|
# ================================= Mesh Monitor ===============================================
|
|
# ==============================================================================================
|
|
#
|
|
# PURPOSE
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# At-a-glance view of everyone in the mesh — who they are, how to reach them,
|
|
# and what services each node has put up for mutual fallback protection.
|
|
# Read-only — sources conf data only, no SSH or API calls, no sensitive data.
|
|
#
|
|
# ==============================================================================================
|
|
# OPERATIONAL MODEL
|
|
# ==============================================================================================
|
|
#
|
|
# Reads directly from all three conf files without detect_hosts().
|
|
# Iterates all defined HOST* vars — scales automatically as new nodes join.
|
|
#
|
|
# Sections:
|
|
# MEMBERS — server name, owner, contact email for each HOST*
|
|
# COVERAGE — what each host runs for others when they go down, by tier
|
|
# PARTNERSHIP — enabled/disabled, current owner, sync interval
|
|
#
|
|
# Safe to share with mesh members — contains no API keys, passwords, or SSH keys.
|
|
#
|
|
# ==============================================================================================
|
|
# CONFIGURATION
|
|
# ==============================================================================================
|
|
#
|
|
# host*.conf — per-host identity vars read by this script:
|
|
#
|
|
# HOST*_OWNER human-readable owner name
|
|
# HOST*_OWNER_EMAIL contact email — also used by NPM for Let's Encrypt
|
|
#
|
|
# ==============================================================================================
|
|
# RUNTIME MODES
|
|
# ==============================================================================================
|
|
#
|
|
# mesh_monitor.sh — show full mesh overview
|
|
# mesh_monitor.sh --log — verbose output
|
|
# mesh_monitor.sh --status — show config and exit
|
|
#
|
|
# ==============================================================================================
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
source "$SCRIPT_DIR/../load_config.sh"
|
|
|
|
parse_args "$@"
|
|
|
|
# ── Helpers ───────────────────────────────────────────────────────────────────────────────────
|
|
|
|
# Populate ALL_HOST_IDS with every defined HOST* var
|
|
collect_hosts() {
|
|
ALL_HOST_IDS=()
|
|
for h in HOST1 HOST2 HOST3 HOST4 HOST5 HOST6 HOST7 HOST8; do
|
|
[[ -n "${!h:-}" ]] && ALL_HOST_IDS+=("$h")
|
|
done
|
|
}
|
|
|
|
# Read a bash array by computed name — returns space-separated elements
|
|
get_array() { eval "echo \"\${${1}[*]}\""; }
|
|
|
|
# Convert minutes to human-readable delay string
|
|
format_delay() {
|
|
local m="${1:-0}"
|
|
(( m == 0 )) && { echo "immediate"; return; }
|
|
(( m >= 1440 )) && { echo "$((m / 1440))d"; return; }
|
|
(( m >= 60 )) && { echo "$((m / 60))hr"; return; }
|
|
echo "${m}min"
|
|
}
|
|
|
|
collect_hosts
|
|
log "$ICON_HOST Hosts: ${ALL_HOST_IDS[*]} (${#ALL_HOST_IDS[@]} in mesh)"
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Status ━━━
|
|
# ==============================================================================================
|
|
if [[ "$SHOW_STATUS" == true ]]; then
|
|
echo ""
|
|
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
|
|
echo " Hosts: ${ALL_HOST_IDS[*]}"
|
|
for h in "${ALL_HOST_IDS[@]}"; do
|
|
owner_var="${h}_OWNER"; owner="${!owner_var:-unknown}"
|
|
email_var="${h}_OWNER_EMAIL"; email="${!email_var:-(not set)}"
|
|
echo " $h: ${!h} / $owner / $email"
|
|
done
|
|
echo " Mode: read-only — conf data only, no SSH or API calls"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━"
|
|
exit 0
|
|
fi
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Mesh Overview ━━━
|
|
# ==============================================================================================
|
|
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo " MESH OVERVIEW"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
# ── Members ───────────────────────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "━━━ $ICON_HOST Members ━━━"
|
|
echo ""
|
|
|
|
# Dynamic column widths
|
|
W_HOST=4; W_SERVER=6; W_OWNER=5
|
|
for h in "${ALL_HOST_IDS[@]}"; do
|
|
server="${!h}"
|
|
owner_var="${h}_OWNER"; owner="${!owner_var:-unknown}"
|
|
(( ${#h} > W_HOST )) && W_HOST=${#h}
|
|
(( ${#server} > W_SERVER )) && W_SERVER=${#server}
|
|
(( ${#owner} > W_OWNER )) && W_OWNER=${#owner}
|
|
done
|
|
|
|
printf " %-${W_HOST}s %-${W_SERVER}s %-${W_OWNER}s %s\n" \
|
|
"HOST" "SERVER" "OWNER" "EMAIL"
|
|
printf " %-${W_HOST}s %-${W_SERVER}s %-${W_OWNER}s %s\n" \
|
|
"$(printf '─%.0s' $(seq 1 $W_HOST))" \
|
|
"$(printf '─%.0s' $(seq 1 $W_SERVER))" \
|
|
"$(printf '─%.0s' $(seq 1 $W_OWNER))" \
|
|
"─────────────────────────"
|
|
|
|
for h in "${ALL_HOST_IDS[@]}"; do
|
|
server="${!h}"
|
|
owner_var="${h}_OWNER"; owner="${!owner_var:-unknown}"
|
|
email_var="${h}_OWNER_EMAIL"; email="${!email_var:-(not set)}"
|
|
printf " %-${W_HOST}s %-${W_SERVER}s %-${W_OWNER}s %s\n" \
|
|
"$h" "$server" "$owner" "$email"
|
|
log "$h: server=$server owner=$owner email=$email"
|
|
done
|
|
|
|
# ── Protected Services ────────────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "━━━ $ICON_SHIELD Protected Services ━━━"
|
|
|
|
COVERAGE_FOUND=false
|
|
|
|
# Iterate from the perspective of each protected host — what do they want covered
|
|
for covered in "${ALL_HOST_IDS[@]}"; do
|
|
covered_owner_var="${covered}_OWNER"; covered_owner="${!covered_owner_var:-$covered}"
|
|
covered_email_var="${covered}_OWNER_EMAIL"; covered_email="${!covered_email_var:-}"
|
|
|
|
# Each host defines its own recovery profile — read directly from covered host's vars
|
|
any_tiers=false
|
|
declare -A tier_containers=()
|
|
declare -A tier_delays=()
|
|
covered_by=""
|
|
|
|
for tier in 1 2 3 4; do
|
|
containers=$(get_array "FALLBACK_${covered}_TIER${tier}")
|
|
[[ -z "$containers" ]] && continue
|
|
any_tiers=true
|
|
tier_containers[$tier]="$containers"
|
|
delay_var="${covered}_TIER${tier}_DELAY"
|
|
tier_delays[$tier]="${!delay_var:-0}"
|
|
done
|
|
|
|
if [[ "$any_tiers" == true ]]; then
|
|
for covering in "${ALL_HOST_IDS[@]}"; do
|
|
[[ "$covering" == "$covered" ]] && continue
|
|
covering_owner_var="${covering}_OWNER"
|
|
covering_owner="${!covering_owner_var:-$covering}"
|
|
covered_by="${covered_by:+$covered_by, }$covering ($covering_owner)"
|
|
done
|
|
fi
|
|
|
|
[[ "$any_tiers" == false ]] && continue
|
|
|
|
COVERAGE_FOUND=true
|
|
echo ""
|
|
|
|
header="$covered $covered_owner"
|
|
[[ -n "$covered_email" ]] && header+=" — $covered_email"
|
|
echo " $header"
|
|
|
|
for tier in 1 2 3 4; do
|
|
[[ -z "${tier_containers[$tier]:-}" ]] && continue
|
|
if [[ "$tier" -eq 1 ]]; then
|
|
printf " Tier 1 %-12s %s\n" "(immediate)" "${tier_containers[$tier]// /, }"
|
|
else
|
|
delay=$(format_delay "${tier_delays[$tier]:-0}")
|
|
printf " Tier %s %-12s %s\n" "$tier" "($delay)" "${tier_containers[$tier]// /, }"
|
|
fi
|
|
done
|
|
|
|
[[ -n "$covered_by" ]] && echo " Covered by: $covered_by"
|
|
|
|
unset tier_containers tier_delays
|
|
declare -A tier_containers=()
|
|
declare -A tier_delays=()
|
|
done
|
|
|
|
if [[ "$COVERAGE_FOUND" == false ]]; then
|
|
echo ""
|
|
echo " No fallback coverage configured"
|
|
fi
|
|
|
|
# ── Partnership ───────────────────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "━━━ $ICON_NET Partnership ━━━"
|
|
echo ""
|
|
|
|
if [[ "${PARTNERSHIP_ENABLED:-false}" == true ]]; then
|
|
owner_host="${PARTNERSHIP_OWNER_HOST:-HOST1}"
|
|
owner_server="${!owner_host:-unknown}"
|
|
owner_name_var="${owner_host}_OWNER"; owner_name="${!owner_name_var:-unknown}"
|
|
echo " Status enabled"
|
|
echo " Owner $owner_host ($owner_server / $owner_name)"
|
|
echo " Sync every ${PARTNERSHIP_SYNC_INTERVAL:-15}min"
|
|
else
|
|
echo " Status disabled"
|
|
fi
|
|
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
exit 0
|