#!/bin/bash # ----------------------------------------------------------------------------------------------- # ----------------- UNRAID OPS COMMON LIBRARY (FINAL STABLE FRAMEWORK v2) ----------------------- # ----------------------------------------------------------------------------------------------- # ----------------------------- ICONS ----------------------------- ICON_INFO="ℹ️" ICON_WARN="⚠️" ICON_ERROR="❌" ICON_SUCCESS="✅" ICON_RUN="🚀" ICON_STOP="🛑" ICON_STOPPED="🟢" ICON_SYNC="🔄" # ----------------------------- LOGGING ----------------------------- info() { echo "$ICON_INFO [INFO] $*"; } warn() { echo "$ICON_WARN [WARN] $*"; } error() { echo "$ICON_ERROR [ERROR] $*"; } success() { echo "$ICON_SUCCESS [OK] $*"; } log() { [[ "${ENABLE_LOGGING:-false}" == true ]] && echo "[LOG] $*"; } # ----------------------------- LOAD MASTER CONF ----------------------------- [[ -n "${MASTER_CONF_PATH:-}" ]] && source "$MASTER_CONF_PATH" # ----------------------------- ARG PARSING ----------------------------- parse_args() { ENABLE_LOGGING=${ENABLE_LOGGING:-false} DRY_RUN=${DRY_RUN:-false} CLEAN_ARGS=() for ARG in "$@"; do case "$ARG" in --dry-run|-n) DRY_RUN=true ;; --log) ENABLE_LOGGING=true ;; --no-log) ENABLE_LOGGING=false ;; *) CLEAN_ARGS+=("$ARG") ;; esac done PARSED_ARGS=("${CLEAN_ARGS[@]}") } # ----------------------------- HOST ----------------------------- detect_hosts() { LOCAL_HOSTNAME="$(hostname)" if [[ "$LOCAL_HOSTNAME" == "$HOST1" ]]; then LOCAL_SERVER_NAME="$HOST1" REMOTE_SERVER_NAME="$HOST2" else LOCAL_SERVER_NAME="$HOST2" REMOTE_SERVER_NAME="$HOST1" fi info "Host: $LOCAL_SERVER_NAME → $REMOTE_SERVER_NAME" } resolve_remote_ip() { REMOTE_SERVER=$(tailscale ip -4 "$REMOTE_SERVER_NAME") info "Remote IP: $REMOTE_SERVER" } # ----------------------------- CONTAINERS ----------------------------- RUNNING_CONTAINERS=() stop_containers() { info "Stopping containers..." RUNNING_CONTAINERS=() for c in "${CRITICAL_CONTAINER_NAMES[@]}"; do STATUS=$(ssh root@"$REMOTE_SERVER" "docker inspect -f '{{.State.Running}}' $c 2>/dev/null" || echo "false") if [[ "$STATUS" == "true" ]]; then warn "Stopping $c" RUNNING_CONTAINERS+=("$c") ssh root@"$REMOTE_SERVER" "docker stop $c" >/dev/null && \ success "$c stopped" || error "$c failed to stop" else echo "🟢 $c already stopped" fi done } start_containers() { info "Starting containers..." for c in "${RUNNING_CONTAINERS[@]}"; do info "Starting $c" ssh root@"$REMOTE_SERVER" "docker start $c" >/dev/null && \ success "$c started" || error "$c failed to start" done } # ----------------------------- RSYNC LIMITER ----------------------------- wait_for_rsync() { COUNT=$(pgrep -x rsync | wc -l || true) while [[ "$COUNT" -ge "${MAX_RSYNC_PROCS:-2}" ]]; do warn "Waiting rsync slot..." sleep 2 COUNT=$(pgrep -x rsync | wc -l || true) done } # ----------------------------- RSYNC OPTIONS ----------------------------- DEFAULT_RSYNC_OPTS=( --archive --delete --partial --compress --info=progress2 --stats ) get_rsync_opts() { RSYNC_OPTS=("${DEFAULT_RSYNC_OPTS[@]}") BW_LIMIT="${PROFILE_BW_LIMIT[$PROFILE_NAME]:-0}" if [[ "$BW_LIMIT" -gt 0 ]]; then RSYNC_OPTS+=(--bwlimit="$BW_LIMIT") log "bwlimit=${BW_LIMIT}KB/s applied (profile: $PROFILE_NAME)" else log "bwlimit unlimited (profile: $PROFILE_NAME)" fi } # ----------------------------- RSYNC EXECUTION (WITH METRICS) ----------------------------- run_rsync() { wait_for_rsync get_rsync_opts echo "🚀 Running rsync..." RSYNC_START=$(date +%s) RSYNC_OUTPUT=$(rsync "${RSYNC_OPTS[@]}" \ "$SRC/" \ "root@$REMOTE_SERVER:$DEST/") RSYNC_END=$(date +%s) DURATION=$((RSYNC_END - RSYNC_START)) # ----------------------------- METRICS EXTRACTION ----------------------------- TRANSFERRED=$(echo "$RSYNC_OUTPUT" | grep "Total transferred file size" | awk '{print $5}') FILES_CHANGED=$(echo "$RSYNC_OUTPUT" | grep "Number of regular files transferred" | awk '{print $6}') DELETED=$(echo "$RSYNC_OUTPUT" | grep "Number of deleted files" | awk '{print $5}') AVG_SPEED=$(echo "$RSYNC_OUTPUT" | grep -Eo '[0-9.]+[ ]*bytes/sec' | tail -1) } # ----------------------------- SUMMARY (SINGLE SOURCE OF TRUTH) ----------------------------- show_summary() { echo "" echo "===== SUMMARY =====" echo "Directory: $SRC" echo "Profile: $PROFILE_NAME" echo "" echo "Transferred: ${TRANSFERRED:-unknown}" echo "Changed files: ${FILES_CHANGED:-0}" echo "Deleted files: ${DELETED:-0}" echo "" echo "Duration: ${DURATION:-0}s" echo "Avg speed: ${AVG_SPEED:-unknown}" echo "" echo "Status: DONE" echo "===================" }