Platform-agnostic refactor: eliminate OS-specific hardcodes from core scripts
All bash scripts are now platform-neutral. Unraid-specific paths, commands, and service checks moved to Plugin/unraid/adapter.sh. Core scripts call platform_*() functions exclusively — no direct OS paths in runtime logic. New adapter functions: platform_storage_path, platform_webui_install_path, platform_scripts_dir_probe_cmd, platform_setup_db_path, platform_storage_healthy, platform_is_service_enabled, platform_get_temp_thresholds, platform_disk_states_path, platform_rebuild_container, platform_push_conf, platform_push_setup_state, platform_get_templates_dir, platform_send_os_notification. Partnership services stack (Emby/Jellyfin/Seerr/SeerrFin) added as third onboarding stack alongside auth and arr stacks.
This commit is contained in:
@@ -131,7 +131,6 @@ fi
|
||||
detect_hosts
|
||||
|
||||
# Validate unRAID notify script — used for network creation alerts
|
||||
platform_require_cmd "/usr/local/emhttp/plugins/dynamix/scripts/notify" "" "" "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
# Docker daemon check — network operations are useless if daemon is hung
|
||||
DOCKER_TIMEOUT=15
|
||||
|
||||
@@ -336,7 +336,7 @@ if [[ ${#UPDATED[@]} -gt 0 ]]; then
|
||||
continue
|
||||
fi
|
||||
log "$ICON_SYNC Rebuilding $container from template on new image..."
|
||||
if /usr/local/emhttp/plugins/dynamix.docker.manager/scripts/rebuild_container "$container" >/dev/null 2>&1; then
|
||||
if platform_rebuild_container "$container"; then
|
||||
log "$ICON_DONE $container rebuilt ✅"
|
||||
REBUILT+=("$container")
|
||||
else
|
||||
|
||||
@@ -251,7 +251,7 @@ if [[ ${#UPDATED[@]} -gt 0 ]]; then
|
||||
fi
|
||||
|
||||
log "$ICON_RUNNING $container — recreating from template on new image..."
|
||||
if /usr/local/emhttp/plugins/dynamix.docker.manager/scripts/rebuild_container "$container" >/dev/null 2>&1; then
|
||||
if platform_rebuild_container "$container"; then
|
||||
if verify_running "$container"; then
|
||||
log "$ICON_DONE $container recreated and running ✅"
|
||||
RESTARTED+=("$container")
|
||||
|
||||
@@ -292,10 +292,6 @@ detect_hosts
|
||||
resolve_remote_ip
|
||||
|
||||
# Validate unRAID notify script — used throughout for state change notifications
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no container or DDNS changes will be made"
|
||||
|
||||
|
||||
@@ -171,10 +171,6 @@ platform_require_cmd \
|
||||
"--version" "iptables" \
|
||||
"iptables" || { error "iptables not found — required for connectivity simulation"; exit 1; }
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
if [[ ! -f "$FALLBACK_SCRIPT" ]]; then
|
||||
error "fallback.sh not found at $FALLBACK_SCRIPT"
|
||||
|
||||
@@ -112,10 +112,6 @@ if ! command -v jq >/dev/null 2>&1; then
|
||||
fi
|
||||
log "jq found"
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no items will be blocklisted or searched"
|
||||
|
||||
|
||||
@@ -153,10 +153,6 @@ if ! command -v jq >/dev/null 2>&1; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
# Lock before detect_hosts — large library scans take time, wait mode appropriate
|
||||
[[ -n "${LIDARR_LOCK_WARN_AGE:-}" ]] && LOCK_WARN_AGE="$LIDARR_LOCK_WARN_AGE"
|
||||
|
||||
@@ -117,10 +117,6 @@ acquire_lock "wait"
|
||||
# detect_hosts() sets MY_ID and aliases HOST*_ANIME/MEDIA_CLEAN_FOLDERS
|
||||
detect_hosts
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
# Resolve profile folders and patterns
|
||||
case "$PROFILE" in
|
||||
|
||||
@@ -70,10 +70,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock "wait"
|
||||
|
||||
|
||||
+17
-11
@@ -301,24 +301,22 @@ for lname in "${!USER_MAP[@]}"; do
|
||||
|
||||
for _si in "${!U_IDX[@]}"; do
|
||||
_uid="${U_UID[$_si]}"
|
||||
_endpoint="Users/${_uid}/Items?Recursive=true&Fields=ProviderIds,UserData,Type,ParentIndexNumber,IndexNumber,SeriesName&IncludeItemTypes=${SYNC_TYPES}&Filters=IsPlayed${_date_filter}&Limit=5000"
|
||||
# All played items — no limit, covers both date-stamped and batch-marked (null date) entries
|
||||
_endpoint="Users/${_uid}/Items?Recursive=true&Fields=ProviderIds,UserData,Type,ParentIndexNumber,IndexNumber,SeriesName&IncludeItemTypes=${SYNC_TYPES}&Filters=IsPlayed&SortBy=DatePlayed&SortOrder=Descending"
|
||||
_resp=$(_api_get "${SRV_URL[$_si]}" "${SRV_KEY[$_si]}" "$_endpoint")
|
||||
if [[ -z "$_resp" ]]; then
|
||||
warn " ${SRV_NAME[$_si]} — failed to fetch items for $lname"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Also fetch items with resume position (not yet marked played)
|
||||
_endpoint2="Users/${_uid}/Items?Recursive=true&Fields=ProviderIds,UserData,Type,ParentIndexNumber,IndexNumber,SeriesName&IncludeItemTypes=${SYNC_TYPES}&SortBy=DatePlayed&SortOrder=Descending&Filters=IsResumable${_date_filter}&Limit=500"
|
||||
# Resume positions (not yet marked played)
|
||||
_endpoint2="Users/${_uid}/Items?Recursive=true&Fields=ProviderIds,UserData,Type,ParentIndexNumber,IndexNumber,SeriesName&IncludeItemTypes=${SYNC_TYPES}&SortBy=DatePlayed&SortOrder=Descending&Filters=IsResumable"
|
||||
_resp2=$(_api_get "${SRV_URL[$_si]}" "${SRV_KEY[$_si]}" "$_endpoint2")
|
||||
|
||||
# Combine and deduplicate by Id
|
||||
if [[ -n "$_resp2" ]]; then
|
||||
_combined=$(printf '%s\n%s' "$_resp" "$_resp2" | jq -s \
|
||||
'[.[0].Items // [], .[1].Items // []] | add // [] | unique_by(.Id)' 2>/dev/null)
|
||||
else
|
||||
_combined=$(echo "$_resp" | jq '.Items // []' 2>/dev/null)
|
||||
fi
|
||||
[[ -z "$_resp2" ]] && _resp2='{"Items":[]}'
|
||||
_combined=$(printf '%s\n%s' "$_resp" "$_resp2" | jq -s \
|
||||
'[.[0].Items // [], .[1].Items // []] | add // [] | unique_by(.Id)' 2>/dev/null)
|
||||
|
||||
_count=$(echo "$_combined" | jq 'length' 2>/dev/null || echo 0)
|
||||
log " ${SRV_NAME[$_si]} — $_count item(s) with state for $lname"
|
||||
@@ -422,8 +420,16 @@ for lname in "${!USER_MAP[@]}"; do
|
||||
_their_played="${E_PLAYED[$_si]:-false}"
|
||||
|
||||
# Skip if they already have the same/newer state
|
||||
if [[ "$_their_epoch" -ge "$_auth_epoch" ]] && \
|
||||
[[ "$_their_played" == "$_auth_played" ]]; then
|
||||
_skip=false
|
||||
if [[ "$_auth_played" == "true" ]]; then
|
||||
# Played: skip if target is already marked played with same/newer date
|
||||
[[ "$_their_epoch" -ge "$_auth_epoch" && "$_their_played" == "true" ]] && _skip=true
|
||||
else
|
||||
# Resume only: skip if target already has same or more ticks
|
||||
# (posting ticks via /UserData doesn't set LastPlayedDate, so epoch comparison is useless here)
|
||||
[[ "${E_TICKS[$_si]:-0}" -ge "${_auth_ticks:-0}" && "$_their_played" == "false" ]] && _skip=true
|
||||
fi
|
||||
if [[ "$_skip" == true ]]; then
|
||||
log " SKIP $_pkey → ${SRV_NAME[$_si]} already up to date"
|
||||
(( TOTAL_SKIPPED++ ))
|
||||
continue
|
||||
|
||||
@@ -141,10 +141,6 @@ if ! command -v jq >/dev/null 2>&1; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock "wait"
|
||||
TMP_DIR="/tmp/radarr_cleanup_$$"
|
||||
|
||||
@@ -141,10 +141,6 @@ if ! command -v jq >/dev/null 2>&1; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock "wait"
|
||||
TMP_DIR="/tmp/sonarr_cleanup_$$"
|
||||
|
||||
@@ -119,10 +119,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -134,10 +134,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
# detect_hosts() sets MY_ID for report header
|
||||
detect_hosts
|
||||
|
||||
@@ -110,10 +110,6 @@ platform_require_cmd \
|
||||
"version" "OpenSSL" \
|
||||
"openssl" || { error "openssl not found — required for certificate checks"; exit 1; }
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -111,10 +111,6 @@ if ! command -v jq >/dev/null 2>&1; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -104,10 +104,6 @@ platform_require_cmd \
|
||||
exit 1
|
||||
}
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -113,10 +113,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -126,10 +126,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
platform_require_cmd \
|
||||
"$(command -v openssl 2>/dev/null || echo /usr/bin/openssl)" \
|
||||
|
||||
@@ -125,10 +125,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -74,10 +74,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -53,10 +53,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -59,10 +59,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock "strict"
|
||||
|
||||
|
||||
@@ -83,10 +83,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
if ! command -v docker &>/dev/null; then
|
||||
error "Docker command not found"
|
||||
|
||||
@@ -71,10 +71,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ if ! platform_storage_healthy; then
|
||||
echo "Array not started — skipping watchdog cycle"
|
||||
exit 0
|
||||
fi
|
||||
log "$ICON_DISK Array: /mnt/user mounted (shfs) ✅"
|
||||
log "$ICON_DISK Array: $(platform_storage_path) mounted ✅"
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Startup Grace ━━━
|
||||
|
||||
@@ -76,10 +76,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ if [[ "$DIRECTION" == "h1" || "$DIRECTION" == "both" ]]; then
|
||||
timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \
|
||||
-o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes root@"$MIRROR_IP" \
|
||||
"sed -i \"|${KEY_BLOB}|d\" /root/.ssh/authorized_keys 2>/dev/null
|
||||
sed -i \"/^${MIRROR_ID}_PHASE\|^${MIRROR_ID}_KEY_READY/d\" /boot/config/varaverk_setup.db 2>/dev/null
|
||||
sed -i \"/^${MIRROR_ID}_PHASE\|^${MIRROR_ID}_KEY_READY/d\" $(platform_setup_db_path) 2>/dev/null
|
||||
echo ok" 2>/dev/null | grep -q ok && {
|
||||
log "HOST1 key removed from $MIRROR authorized_keys ✅"
|
||||
H1_DONE=true
|
||||
|
||||
@@ -155,6 +155,10 @@
|
||||
# Containers parked here during partnership, restarted on offboard.
|
||||
# Aliased by detect_hosts() → PARTNERSHIP_OWN_CONTAINERS
|
||||
#
|
||||
# HOST*_PARTNERSHIP_SERVICES_STACK
|
||||
# Shared services XMLs (Emby, Jellyfin, Seerr, SeerrFin) deployed on mirror during onboard.
|
||||
# Aliased by detect_hosts() → PARTNERSHIP_SERVICES_STACK
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
@@ -187,6 +191,7 @@
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
source "$SCRIPT_DIR/../load_config.sh"
|
||||
source "$SCRIPT_DIR/../Plugin/$PLATFORM/Partnership/containers.sh"
|
||||
|
||||
SSH_TIMEOUT=15
|
||||
BLOCKLIST_FILE="${PARTNERSHIP_BLOCKLIST_FILE:-${STATE_DIR:-/boot/config}/partnership_blocklist.db}"
|
||||
@@ -235,11 +240,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
if ! command -v docker &>/dev/null; then
|
||||
error "Docker command not found"
|
||||
exit 1
|
||||
@@ -368,73 +368,6 @@ read_remote_state() {
|
||||
"cat '$remote_file' 2>/dev/null" 2>/dev/null
|
||||
}
|
||||
|
||||
# Reconfigure a container's WebUI on the remote server
|
||||
reconfigure_webui() {
|
||||
local container="$1" port="$2" target_ip="$3"
|
||||
local ssh_key="$4" remote_ip="$5" label="${6:-remote}"
|
||||
|
||||
log "Reconfiguring $container WebUI → ${target_ip}:${port} on $label..."
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would reconfigure $container WebUI to http://${target_ip}:${port}/"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local template
|
||||
template=$(timeout "$SSH_TIMEOUT" ssh -i "$ssh_key" \
|
||||
-o ConnectTimeout="$SSH_TIMEOUT" root@"$remote_ip" \
|
||||
"grep -rl '<WebUI>' /boot/config/plugins/dockerMan/templates-user/ 2>/dev/null | \
|
||||
xargs grep -l '\"$container\"' 2>/dev/null | head -1" 2>/dev/null)
|
||||
|
||||
if [[ -z "$template" ]]; then
|
||||
warn "$container template not found on $label — WebUI needs manual reconfiguration"
|
||||
return 1
|
||||
fi
|
||||
|
||||
timeout "$SSH_TIMEOUT" ssh -i "$ssh_key" \
|
||||
-o ConnectTimeout="$SSH_TIMEOUT" root@"$remote_ip" \
|
||||
"sed -i 's|<WebUI>.*</WebUI>|<WebUI>http://${target_ip}:${port}/</WebUI>|g' '$template'" \
|
||||
2>/dev/null && \
|
||||
log "$container → http://${target_ip}:${port}/ ✅" || {
|
||||
error "Failed to reconfigure $container WebUI on $label"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
# Reconfigure local auth WebUIs to target IP
|
||||
reconfigure_local_webuis() {
|
||||
local target_ip="$1"
|
||||
log "Reconfiguring local auth WebUIs → ${target_ip}..."
|
||||
|
||||
local failures=0
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]}"; do
|
||||
[[ -z "$entry" ]] && continue
|
||||
local container="${entry%%|*}"
|
||||
local port="${entry##*|}"
|
||||
|
||||
local template
|
||||
template=$(grep -rl '<WebUI>' \
|
||||
/boot/config/plugins/dockerMan/templates-user/ 2>/dev/null | \
|
||||
xargs grep -l "\"$container\"" 2>/dev/null | head -1)
|
||||
|
||||
if [[ -z "$template" ]]; then
|
||||
warn "$container template not found locally"
|
||||
(( failures++ ))
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would reconfigure $container → http://${target_ip}:${port}/"
|
||||
continue
|
||||
fi
|
||||
|
||||
sed -i "s|<WebUI>.*</WebUI>|<WebUI>http://${target_ip}:${port}/</WebUI>|g" \
|
||||
"$template" 2>/dev/null && \
|
||||
log "$container → http://${target_ip}:${port}/ ✅" || \
|
||||
{ error "Failed to reconfigure $container"; (( failures++ )); }
|
||||
done
|
||||
return $failures
|
||||
}
|
||||
|
||||
get_tailscale_device_id() {
|
||||
local hostname="$1"
|
||||
@@ -941,15 +874,16 @@ revoke_emby_admin() {
|
||||
}
|
||||
|
||||
check_both_healthy() {
|
||||
mountpoint -q /mnt/user 2>/dev/null || { error "Local array not healthy"; return 1; }
|
||||
platform_storage_healthy || { error "Local array not healthy"; return 1; }
|
||||
|
||||
local mirror_ip
|
||||
local mirror_ip _spath
|
||||
mirror_ip=$(resolve_tailscale_ip "$MIRROR")
|
||||
[[ -z "$mirror_ip" ]] && { error "Cannot resolve $MIRROR Tailscale IP"; return 1; }
|
||||
_spath=$(platform_storage_path)
|
||||
|
||||
timeout "$SSH_TIMEOUT" ssh -i "$MIRROR_SSH_KEY" \
|
||||
-o ConnectTimeout="$SSH_TIMEOUT" root@"$mirror_ip" \
|
||||
"mountpoint -q /mnt/user && timeout 10 docker ps" >/dev/null 2>&1 || {
|
||||
"mountpoint -q '$_spath' && timeout 10 docker ps" >/dev/null 2>&1 || {
|
||||
error "Mirror $MIRROR not healthy"
|
||||
return 1
|
||||
}
|
||||
@@ -972,12 +906,7 @@ do_final_sync() {
|
||||
fi
|
||||
done
|
||||
else
|
||||
bash "$SCRIPT_DIR/../Rsync/rsync.sh" \
|
||||
"/mnt/user/appdata-Fallback/Critical-Data" \
|
||||
--profile=critical-fallback --log
|
||||
bash "$SCRIPT_DIR/../Rsync/rsync.sh" \
|
||||
"/mnt/user/Media_Server/Emby" \
|
||||
--profile=emby-fallback --log
|
||||
warn "CRITICAL_SYNC_SHARES is empty — skipping final sync (configure in host*.conf)"
|
||||
fi
|
||||
else
|
||||
warn "DRY RUN — would run final critical sync (${#CRITICAL_SYNC_SHARES[@]:-hardcoded} shares)"
|
||||
@@ -1234,26 +1163,14 @@ if [[ "$MODE" == "onboard" ]]; then
|
||||
echo ""
|
||||
echo "Enabling partnership in master.conf..."
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
if command -v php &>/dev/null; then
|
||||
php -r "
|
||||
require_once '/usr/local/emhttp/plugins/varaverk/include/config.php';
|
||||
\$master = vv_read_conf_raw('master.conf');
|
||||
\$master = preg_replace('/^\s*PARTNERSHIP_ENABLED\s*=.*/m', 'PARTNERSHIP_ENABLED=true', \$master);
|
||||
if (vv_write_conf_raw('master.conf', \$master)) {
|
||||
\$results = vv_push_master_conf();
|
||||
foreach (\$results as \$r) {
|
||||
echo \$r['host'] . ': ' . (\$r['ok'] ? 'pushed' : 'FAILED — ' . \$r['error']) . PHP_EOL;
|
||||
}
|
||||
echo 'PARTNERSHIP_ENABLED=true' . PHP_EOL;
|
||||
} else {
|
||||
echo 'write_failed' . PHP_EOL;
|
||||
}
|
||||
" 2>/dev/null | grep -v '^$' | while IFS= read -r line; do
|
||||
[[ "$line" == "PARTNERSHIP_ENABLED=true" ]] && log "PARTNERSHIP_ENABLED=true in master.conf ✅" || log "$line"
|
||||
done
|
||||
local _master_conf="${SCRIPTS_ROOT:-$(dirname "$SCRIPT_DIR")}/master.conf"
|
||||
if grep -q "^[[:space:]]*PARTNERSHIP_ENABLED=" "$_master_conf" 2>/dev/null; then
|
||||
sed -i "s|^[[:space:]]*PARTNERSHIP_ENABLED=.*|PARTNERSHIP_ENABLED=true|" "$_master_conf"
|
||||
else
|
||||
warn "php not available — set PARTNERSHIP_ENABLED=true in master.conf manually"
|
||||
echo "PARTNERSHIP_ENABLED=true" >> "$_master_conf"
|
||||
fi
|
||||
log "PARTNERSHIP_ENABLED=true in master.conf ✅"
|
||||
platform_push_conf | while IFS= read -r line; do log "$line"; done
|
||||
else
|
||||
warn "DRY RUN — would set PARTNERSHIP_ENABLED=true in master.conf and push"
|
||||
fi
|
||||
@@ -1267,8 +1184,7 @@ if (vv_write_conf_raw('master.conf', \$master)) {
|
||||
else
|
||||
echo "${flag_key}=true" >> "$local_state_file"
|
||||
fi
|
||||
command -v php &>/dev/null && \
|
||||
php -r "require_once '/usr/local/emhttp/plugins/varaverk/include/config.php'; vv_push_setup_state();" 2>/dev/null || true
|
||||
platform_push_setup_state
|
||||
log "${MY_ID}_LOCAL_DONE=true written to setup.db ✅"
|
||||
else
|
||||
warn "DRY RUN — would write ${MY_ID}_LOCAL_DONE=true"
|
||||
|
||||
@@ -50,6 +50,9 @@
|
||||
# HOST*_PARTNERSHIP_ARR_STACK
|
||||
# Arr container XMLs — same cleanup logic as auth stack.
|
||||
#
|
||||
# HOST*_PARTNERSHIP_SERVICES_STACK
|
||||
# Shared services XMLs (Emby, Jellyfin, Seerr, SeerrFin) — same cleanup logic.
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
|
||||
@@ -18,16 +18,18 @@
|
||||
# Step 1: SSH key setup — generate keypair, copy to owner, update conf
|
||||
# Owner completes the rest remotely. Mirror is done.
|
||||
#
|
||||
# OWNER PATH (8 steps)
|
||||
# Step 1: SSH key setup — generate keypair, install on mirror, update conf
|
||||
# Step 2: Stop mirror auth — stop mirror's existing auth containers before replacing
|
||||
# Step 3: Deploy auth stack — push XMLs, pull images, create + start on mirror
|
||||
# Mariadb/Redis health-checked before Authelia deploys
|
||||
# Step 4: Stop mirror arr — stop mirror's existing arr containers before replacing
|
||||
# Step 5: Deploy arr stack — push arr XMLs, pull images, create + start on mirror
|
||||
# Step 6: Partnership onboard — configure WebUIs → owner IP, write state, Emby
|
||||
# Step 7: Arr bootstrap — bidirectional library sync (arr_sync.sh)
|
||||
# Step 8: Conf push — push master.conf + setup state to all listed hosts
|
||||
# OWNER PATH (10 steps)
|
||||
# Step 1: SSH key setup — generate keypair, install on mirror, update conf
|
||||
# Step 2: Stop mirror auth — stop mirror's existing auth containers before replacing
|
||||
# Step 3: Deploy auth stack — push XMLs, pull images, create + start on mirror
|
||||
# Mariadb/Redis health-checked before Authelia deploys
|
||||
# Step 4: Stop mirror arr — stop mirror's existing arr containers before replacing
|
||||
# Step 5: Deploy arr stack — push arr XMLs, pull images, create + start on mirror
|
||||
# Step 6: Stop mirror services — stop mirror's existing services containers before replacing
|
||||
# Step 7: Deploy services stack — push Emby/Jellyfin/Seerr XMLs, pull images, create + start
|
||||
# Step 8: Partnership onboard — configure WebUIs → owner IP, write state, Emby
|
||||
# Step 9: Arr bootstrap — bidirectional library sync (arr_sync.sh)
|
||||
# Step 10: Conf push — push master.conf + setup state to all listed hosts
|
||||
#
|
||||
# ==============================================================================================
|
||||
# DESIGN PRINCIPLES
|
||||
@@ -98,6 +100,16 @@
|
||||
# Same rule as PARTNERSHIP_REPLACE_CONTAINERS: defined in mirror's own conf, never HOST1's.
|
||||
# Aliased by detect_hosts() → PARTNERSHIP_ARR_REPLACE_CONTAINERS (on the mirror)
|
||||
#
|
||||
# HOST*_PARTNERSHIP_SERVICES_STACK
|
||||
# XML filenames to push and deploy on the mirror as its shared services stack.
|
||||
# Includes Emby, Jellyfin, Seerr, SeerrFin. Leave empty to skip services stack deploy.
|
||||
# Aliased by detect_hosts() → PARTNERSHIP_SERVICES_STACK
|
||||
#
|
||||
# HOST*_PARTNERSHIP_SERVICES_REPLACE_CONTAINERS
|
||||
# Services containers to stop on the mirror before deploying the services stack.
|
||||
# Same rule as PARTNERSHIP_REPLACE_CONTAINERS: defined in mirror's own conf, never HOST1's.
|
||||
# Aliased by detect_hosts() → PARTNERSHIP_SERVICES_REPLACE_CONTAINERS (on the mirror)
|
||||
#
|
||||
# ==============================================================================================
|
||||
# RUNTIME MODES
|
||||
# ==============================================================================================
|
||||
@@ -118,10 +130,13 @@
|
||||
# Skip auth stack stop + deploy (Steps 3-4)
|
||||
#
|
||||
# Partnership/partnership_onboard.sh --skip-arr-stack
|
||||
# Skip arr stack stop + deploy (Steps 5-6)
|
||||
# Skip arr stack stop + deploy (Steps 4-5)
|
||||
#
|
||||
# Partnership/partnership_onboard.sh --skip-services-stack
|
||||
# Skip services stack stop + deploy (Steps 6-7)
|
||||
#
|
||||
# Partnership/partnership_onboard.sh --skip-arr-sync
|
||||
# Skip arr library bootstrap (Step 8)
|
||||
# Skip arr library bootstrap (Step 9)
|
||||
#
|
||||
# Partnership/partnership_onboard.sh --phase1-only
|
||||
# OWNER only: SSH key exchange + conf push. Safe to run before HOST2 has Varaverk.
|
||||
@@ -145,6 +160,7 @@ source "$SCRIPTS_ROOT/Plugin/$PLATFORM/Partnership/containers.sh"
|
||||
SKIP_SSH=false
|
||||
SKIP_AUTH_STACK=false
|
||||
SKIP_ARR_STACK=false
|
||||
SKIP_SERVICES_STACK=false
|
||||
SKIP_ARR_SYNC=false
|
||||
PHASE1_ONLY=false # OWNER: SSH + conf push only (HOST2 not yet installed)
|
||||
PHASE2_ONLY=false # OWNER: containers/arr/onboard only (triggered by HOST2 after it onboards)
|
||||
@@ -154,8 +170,9 @@ for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--skip-ssh) SKIP_SSH=true ;;
|
||||
--skip-auth-stack) SKIP_AUTH_STACK=true ;;
|
||||
--skip-arr-stack) SKIP_ARR_STACK=true ;;
|
||||
--skip-arr-sync) SKIP_ARR_SYNC=true ;;
|
||||
--skip-arr-stack) SKIP_ARR_STACK=true ;;
|
||||
--skip-services-stack) SKIP_SERVICES_STACK=true ;;
|
||||
--skip-arr-sync) SKIP_ARR_SYNC=true ;;
|
||||
--phase1-only) PHASE1_ONLY=true ;;
|
||||
--phase2-only) PHASE2_ONLY=true; SKIP_SSH=true ;;
|
||||
*) FILTERED_ARGS+=("$arg") ;;
|
||||
@@ -210,8 +227,7 @@ write_onboard_phase() {
|
||||
else
|
||||
echo "${key}=true" >> "$state_file"
|
||||
fi
|
||||
command -v php &>/dev/null && \
|
||||
php -r "require_once '/usr/local/emhttp/plugins/varaverk/include/config.php'; vv_push_setup_state();" 2>/dev/null || true
|
||||
platform_push_setup_state
|
||||
}
|
||||
|
||||
echo ""
|
||||
@@ -293,11 +309,12 @@ if [[ "$AM_MIRROR" == true ]]; then
|
||||
PHASE2_TRIGGERED=false
|
||||
|
||||
if [[ -n "$OWNER_IP" ]]; then
|
||||
# Read OWNER's SCRIPTS_DIR from their varaverk.cfg — don't assume same path as mirror
|
||||
# Read OWNER's SCRIPTS_DIR via platform probe command — don't assume same path as mirror
|
||||
_probe_cmd=$(platform_scripts_dir_probe_cmd)
|
||||
OWNER_SCRIPTS_DIR=$(timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \
|
||||
-o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes root@"$OWNER_IP" \
|
||||
'grep SCRIPTS_DIR /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d "\"'"'"'" 2>/dev/null' 2>/dev/null | tr -d '[:space:]')
|
||||
OWNER_SCRIPTS_DIR="${OWNER_SCRIPTS_DIR:-/boot/config/plugins/varaverk}"
|
||||
"$_probe_cmd" 2>/dev/null | tr -d '[:space:]')
|
||||
OWNER_SCRIPTS_DIR="${OWNER_SCRIPTS_DIR:-$SCRIPTS_DIR}"
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would SSH to $OWNER ($OWNER_IP) and trigger Phase 2"
|
||||
@@ -349,6 +366,10 @@ STEP_STOP_ARR_OK=true
|
||||
STEP_ARR_OK=true
|
||||
ARR_DEPLOYED=0
|
||||
ARR_FAILED=0
|
||||
STEP_STOP_SERVICES_OK=true
|
||||
STEP_SERVICES_OK=true
|
||||
SERVICES_DEPLOYED=0
|
||||
SERVICES_FAILED=0
|
||||
ONBOARD_OK=false
|
||||
ARR_SYNC_OK=false
|
||||
MASTER_PUSH_OK=false
|
||||
@@ -444,23 +465,11 @@ if [[ "$PHASE1_ONLY" == true ]]; then
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would push master.conf + state file to $MIRROR"
|
||||
CONF_PUSH_OK=true
|
||||
elif ! command -v php &>/dev/null; then
|
||||
warn "php not available — push master.conf manually via Scheduler → master.conf → Save Conf"
|
||||
else
|
||||
push_output=$(php -r "
|
||||
require_once '/usr/local/emhttp/plugins/varaverk/include/config.php';
|
||||
\$results = vv_push_master_conf();
|
||||
vv_push_setup_state();
|
||||
if (empty(\$results)) { echo 'no remote hosts'; exit(0); }
|
||||
\$failed = 0;
|
||||
foreach (\$results as \$r) {
|
||||
echo \$r['host'] . ': ' . (\$r['ok'] ? 'pushed' : 'FAILED — ' . \$r['error']) . PHP_EOL;
|
||||
if (!\$r['ok']) \$failed++;
|
||||
}
|
||||
exit(\$failed > 0 ? 1 : 0);
|
||||
" 2>/dev/null)
|
||||
push_output=$(platform_push_conf)
|
||||
push_rc=$?
|
||||
echo "$push_output"
|
||||
[[ -n "$push_output" ]] && echo "$push_output"
|
||||
platform_push_setup_state
|
||||
if [[ $push_rc -eq 0 ]]; then
|
||||
log "Conf push complete ✅"
|
||||
CONF_PUSH_OK=true
|
||||
@@ -533,7 +542,7 @@ else
|
||||
stop_mirror_stack "PARTNERSHIP_ARR_REPLACE_CONTAINERS" "arr stack"
|
||||
fi
|
||||
|
||||
# ── Step 6: Deploy arr stack on mirror ───────────────────────────────────────────────────────
|
||||
# ── Step 5: Deploy arr stack on mirror ───────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "━━━ Step 5 — Deploy Arr Stack on Mirror ━━━"
|
||||
|
||||
@@ -547,9 +556,36 @@ else
|
||||
[[ "$ARR_FAILED" -gt 0 ]] && STEP_ARR_OK=false
|
||||
fi
|
||||
|
||||
# ── Step 7: Partnership onboard ───────────────────────────────────────────────────────────────
|
||||
# ── Step 6: Stop mirror's existing services stack ─────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "━━━ Step 6 — Partnership Onboard ━━━"
|
||||
echo "━━━ Step 6 — Stop Mirror Services Stack ━━━"
|
||||
|
||||
if [[ "$SKIP_SERVICES_STACK" == true ]]; then
|
||||
warn "Skipping (--skip-services-stack)"
|
||||
elif [[ ${#PARTNERSHIP_SERVICES_STACK[@]} -eq 0 ]]; then
|
||||
log "PARTNERSHIP_SERVICES_STACK not configured — skipping services stack deploy"
|
||||
SKIP_SERVICES_STACK=true
|
||||
else
|
||||
stop_mirror_stack "PARTNERSHIP_SERVICES_REPLACE_CONTAINERS" "services stack"
|
||||
fi
|
||||
|
||||
# ── Step 7: Deploy services stack on mirror ───────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "━━━ Step 7 — Deploy Services Stack on Mirror ━━━"
|
||||
|
||||
if [[ "$SKIP_SERVICES_STACK" == true ]]; then
|
||||
warn "Skipping (--skip-services-stack)"
|
||||
else
|
||||
deploy_xml_stack PARTNERSHIP_SERVICES_STACK
|
||||
SERVICES_DEPLOYED=$_STACK_DEPLOYED
|
||||
SERVICES_FAILED=$_STACK_FAILED
|
||||
echo "Services stack: $SERVICES_DEPLOYED deployed, $SERVICES_FAILED failed"
|
||||
[[ "$SERVICES_FAILED" -gt 0 ]] && STEP_SERVICES_OK=false
|
||||
fi
|
||||
|
||||
# ── Step 8: Partnership onboard ───────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "━━━ Step 8 — Partnership Onboard ━━━"
|
||||
|
||||
if bash "$SCRIPTS_ROOT/Partnership/partnership_manager.sh" --onboard "${EXTRA_FLAGS[@]}"; then
|
||||
echo "Partnership onboard complete ✅"
|
||||
@@ -559,9 +595,9 @@ else
|
||||
ONBOARD_OK=false
|
||||
fi
|
||||
|
||||
# ── Step 8: Arr library bootstrap ─────────────────────────────────────────────────────────────
|
||||
# ── Step 9: Arr library bootstrap ─────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "━━━ Step 7 — Arr Library Bootstrap ━━━"
|
||||
echo "━━━ Step 9 — Arr Library Bootstrap ━━━"
|
||||
|
||||
if [[ "$ONBOARD_OK" == false ]]; then
|
||||
warn "Skipping — onboard did not complete"
|
||||
@@ -577,34 +613,22 @@ else
|
||||
warn "Re-run Media/arr_sync.sh once all arr containers are live"
|
||||
fi
|
||||
|
||||
# ── Step 9: Push master.conf to all listed hosts ──────────────────────────────────────────────
|
||||
# ── Step 10: Push master.conf to all listed hosts ─────────────────────────────────────────────
|
||||
# SSH is now established and all partners have the plugin installed.
|
||||
# Push the authoritative master.conf so every listed host is in sync immediately.
|
||||
echo ""
|
||||
echo "━━━ $ICON_GEAR Step 8 — master.conf Push ━━━"
|
||||
echo "━━━ $ICON_GEAR Step 10 — master.conf Push ━━━"
|
||||
|
||||
if [[ "$ONBOARD_OK" == false ]]; then
|
||||
warn "Skipping — onboard did not complete"
|
||||
elif [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would push master.conf to all listed hosts"
|
||||
MASTER_PUSH_OK=true
|
||||
elif ! command -v php &>/dev/null; then
|
||||
warn "php not available — push master.conf manually via Scheduler → master.conf → Save Conf"
|
||||
else
|
||||
push_output=$(php -r "
|
||||
require_once '/usr/local/emhttp/plugins/varaverk/include/config.php';
|
||||
\$results = vv_push_master_conf();
|
||||
vv_push_setup_state();
|
||||
if (empty(\$results)) { echo 'no remote hosts'; exit(0); }
|
||||
\$failed = 0;
|
||||
foreach (\$results as \$r) {
|
||||
echo \$r['host'] . ': ' . (\$r['ok'] ? 'pushed' : 'FAILED — ' . \$r['error']) . PHP_EOL;
|
||||
if (!\$r['ok']) \$failed++;
|
||||
}
|
||||
exit(\$failed > 0 ? 1 : 0);
|
||||
" 2>/dev/null)
|
||||
push_output=$(platform_push_conf)
|
||||
push_rc=$?
|
||||
echo "$push_output"
|
||||
[[ -n "$push_output" ]] && echo "$push_output"
|
||||
platform_push_setup_state
|
||||
if [[ $push_rc -eq 0 ]]; then
|
||||
echo "master.conf sync complete ✅"
|
||||
MASTER_PUSH_OK=true
|
||||
@@ -628,14 +652,16 @@ echo ""
|
||||
_ok() { [[ "$1" == true ]] && echo "✅" || echo "❌"; }
|
||||
_skip() { [[ "$1" == true ]] && echo "skipped" || echo "$(_ok "$2")"; }
|
||||
|
||||
echo " Step 1 — SSH keys: $(_skip "$SKIP_SSH" "$STEP_SSH_OK")"
|
||||
echo " Step 2 — Stop auth: $(_skip "$SKIP_AUTH_STACK" "$STEP_STOP_AUTH_OK")"
|
||||
echo " Step 3 — Auth stack: $( [[ "$SKIP_AUTH_STACK" == true ]] && echo "skipped" || echo "${AUTH_DEPLOYED} deployed, ${AUTH_FAILED} failed" )"
|
||||
echo " Step 4 — Stop arr: $(_skip "$SKIP_ARR_STACK" "$STEP_STOP_ARR_OK")"
|
||||
echo " Step 5 — Arr stack: $( [[ "$SKIP_ARR_STACK" == true ]] && echo "skipped" || echo "${ARR_DEPLOYED} deployed, ${ARR_FAILED} failed" )"
|
||||
echo " Step 6 — Onboard: $(_ok "$ONBOARD_OK")"
|
||||
echo " Step 7 — Arr bootstrap: $( [[ "$SKIP_ARR_SYNC" == true || "$ONBOARD_OK" == false ]] && echo "skipped" || echo "$(_ok "$ARR_SYNC_OK")" )"
|
||||
echo " Step 8 — Conf push: $( [[ "$ONBOARD_OK" == false ]] && echo "skipped" || echo "$(_ok "$MASTER_PUSH_OK")" )"
|
||||
echo " Step 1 — SSH keys: $(_skip "$SKIP_SSH" "$STEP_SSH_OK")"
|
||||
echo " Step 2 — Stop auth: $(_skip "$SKIP_AUTH_STACK" "$STEP_STOP_AUTH_OK")"
|
||||
echo " Step 3 — Auth stack: $( [[ "$SKIP_AUTH_STACK" == true ]] && echo "skipped" || echo "${AUTH_DEPLOYED} deployed, ${AUTH_FAILED} failed" )"
|
||||
echo " Step 4 — Stop arr: $(_skip "$SKIP_ARR_STACK" "$STEP_STOP_ARR_OK")"
|
||||
echo " Step 5 — Arr stack: $( [[ "$SKIP_ARR_STACK" == true ]] && echo "skipped" || echo "${ARR_DEPLOYED} deployed, ${ARR_FAILED} failed" )"
|
||||
echo " Step 6 — Stop services: $(_skip "$SKIP_SERVICES_STACK" "$STEP_STOP_SERVICES_OK")"
|
||||
echo " Step 7 — Services stack: $( [[ "$SKIP_SERVICES_STACK" == true ]] && echo "skipped" || echo "${SERVICES_DEPLOYED} deployed, ${SERVICES_FAILED} failed" )"
|
||||
echo " Step 8 — Onboard: $(_ok "$ONBOARD_OK")"
|
||||
echo " Step 9 — Arr bootstrap: $( [[ "$SKIP_ARR_SYNC" == true || "$ONBOARD_OK" == false ]] && echo "skipped" || echo "$(_ok "$ARR_SYNC_OK")" )"
|
||||
echo " Step 10 — Conf push: $( [[ "$ONBOARD_OK" == false ]] && echo "skipped" || echo "$(_ok "$MASTER_PUSH_OK")" )"
|
||||
echo ""
|
||||
|
||||
if [[ "$ONBOARD_OK" == true ]]; then
|
||||
|
||||
@@ -126,11 +126,6 @@ if ! command -v docker &>/dev/null; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
detect_hosts
|
||||
|
||||
OWNER_ID="${PARTNERSHIP_OWNER_HOST:-HOST1}"
|
||||
@@ -291,11 +286,11 @@ if [[ "$DRY_RUN" == false ]]; then
|
||||
|
||||
# Push updated master.conf to new owner so both servers agree immediately.
|
||||
# master.conf is shared — host-specific credentials live in host*.conf.
|
||||
_probe_cmd=$(platform_scripts_dir_probe_cmd)
|
||||
_REMOTE_SD=$(ssh -i "$SSH_KEY" -o ConnectTimeout=5 -o StrictHostKeyChecking=no \
|
||||
"root@${MIRROR_IP}" \
|
||||
"grep -m1 '^SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'" \
|
||||
"root@${MIRROR_IP}" "$_probe_cmd" \
|
||||
2>/dev/null | tr -d '[:space:]')
|
||||
_REMOTE_SD="${_REMOTE_SD:-/boot/config/plugins/varaverk}"
|
||||
_REMOTE_SD="${_REMOTE_SD:-$SCRIPTS_DIR}"
|
||||
scp -i "$SSH_KEY" \
|
||||
-o ConnectTimeout="$SSH_TIMEOUT" \
|
||||
-o StrictHostKeyChecking=no \
|
||||
|
||||
@@ -70,11 +70,6 @@ fi
|
||||
|
||||
acquire_lock
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
detect_hosts
|
||||
|
||||
# ── Derive key name from hostname ─────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -214,15 +214,16 @@ deploy_xml_stack() {
|
||||
cleanup_deployed_stack_on_remote() {
|
||||
local remote_ip="$1" ssh_key="$2"
|
||||
local -a xml_names=()
|
||||
[[ ${#PARTNERSHIP_AUTH_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_AUTH_STACK[@]}")
|
||||
[[ ${#PARTNERSHIP_ARR_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_ARR_STACK[@]}")
|
||||
[[ ${#PARTNERSHIP_AUTH_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_AUTH_STACK[@]}")
|
||||
[[ ${#PARTNERSHIP_ARR_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_ARR_STACK[@]}")
|
||||
[[ ${#PARTNERSHIP_SERVICES_STACK[@]} -gt 0 ]] && xml_names+=("${PARTNERSHIP_SERVICES_STACK[@]}")
|
||||
|
||||
if [[ ${#xml_names[@]} -eq 0 ]]; then
|
||||
log "No auth/arr stack arrays configured — skipping deployed stack cleanup"
|
||||
log "No auth/arr/services stack arrays configured — skipping deployed stack cleanup"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Removing owner-deployed containers (auth/arr stacks) from $MIRROR..."
|
||||
log "Removing owner-deployed containers (auth/arr/services stacks) from $MIRROR..."
|
||||
for xml_name in "${xml_names[@]}"; do
|
||||
[[ -z "$xml_name" ]] && continue
|
||||
local xml_file="${TEMPLATES_DIR}/${xml_name}"
|
||||
@@ -289,15 +290,21 @@ cleanup_deployed_stack_locally() {
|
||||
"source '$SCRIPTS_ROOT/load_config.sh' 2>/dev/null
|
||||
detect_hosts 2>/dev/null
|
||||
printf '%s\n' \"\${PARTNERSHIP_ARR_STACK[@]:-}\"" 2>/dev/null | grep -v '^$')
|
||||
xml_names=("${auth_arr[@]}" "${arr_arr[@]}")
|
||||
local -a svc_arr
|
||||
mapfile -t svc_arr < <(timeout "$SSH_TIMEOUT" ssh -i "$ssh_key" \
|
||||
-o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes root@"$owner_ip" \
|
||||
"source '$SCRIPTS_ROOT/load_config.sh' 2>/dev/null
|
||||
detect_hosts 2>/dev/null
|
||||
printf '%s\n' \"\${PARTNERSHIP_SERVICES_STACK[@]:-}\"" 2>/dev/null | grep -v '^$')
|
||||
xml_names=("${auth_arr[@]}" "${arr_arr[@]}" "${svc_arr[@]}")
|
||||
fi
|
||||
|
||||
if [[ ${#xml_names[@]} -eq 0 ]]; then
|
||||
log "Could not read deployed stack from owner — skipping auth/arr cleanup"
|
||||
log "Could not read deployed stack from owner — skipping auth/arr/services cleanup"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Removing owner-deployed containers (auth/arr stacks) locally..."
|
||||
log "Removing owner-deployed containers (auth/arr/services stacks) locally..."
|
||||
for xml_name in "${xml_names[@]}"; do
|
||||
[[ -z "$xml_name" ]] && continue
|
||||
local xml_file="${TEMPLATES_DIR}/${xml_name}"
|
||||
@@ -336,3 +343,74 @@ cleanup_deployed_stack_locally() {
|
||||
done
|
||||
}
|
||||
|
||||
# ==============================================================================================
|
||||
# ── Reconfigure a container's WebUI on the remote server ─────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
reconfigure_webui() {
|
||||
local container="$1" port="$2" target_ip="$3"
|
||||
local ssh_key="$4" remote_ip="$5" label="${6:-remote}"
|
||||
|
||||
log "Reconfiguring $container WebUI → ${target_ip}:${port} on $label..."
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would reconfigure $container WebUI to http://${target_ip}:${port}/"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local template
|
||||
template=$(timeout "$SSH_TIMEOUT" ssh -i "$ssh_key" \
|
||||
-o ConnectTimeout="$SSH_TIMEOUT" root@"$remote_ip" \
|
||||
"grep -rl '<WebUI>' '$TEMPLATES_DIR/' 2>/dev/null | \
|
||||
xargs grep -l '\"$container\"' 2>/dev/null | head -1" 2>/dev/null)
|
||||
|
||||
if [[ -z "$template" ]]; then
|
||||
warn "$container template not found on $label — WebUI needs manual reconfiguration"
|
||||
return 1
|
||||
fi
|
||||
|
||||
timeout "$SSH_TIMEOUT" ssh -i "$ssh_key" \
|
||||
-o ConnectTimeout="$SSH_TIMEOUT" root@"$remote_ip" \
|
||||
"sed -i 's|<WebUI>.*</WebUI>|<WebUI>http://${target_ip}:${port}/</WebUI>|g' '$template'" \
|
||||
2>/dev/null && \
|
||||
log "$container → http://${target_ip}:${port}/ ✅" || {
|
||||
error "Failed to reconfigure $container WebUI on $label"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
# ==============================================================================================
|
||||
# ── Reconfigure local auth WebUIs to target IP ───────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
reconfigure_local_webuis() {
|
||||
local target_ip="$1"
|
||||
log "Reconfiguring local auth WebUIs → ${target_ip}..."
|
||||
|
||||
local failures=0
|
||||
for entry in "${PARTNERSHIP_AUTH_WEBUIS[@]}"; do
|
||||
[[ -z "$entry" ]] && continue
|
||||
local container="${entry%%|*}"
|
||||
local port="${entry##*|}"
|
||||
|
||||
local template
|
||||
template=$(grep -rl '<WebUI>' "$TEMPLATES_DIR/" 2>/dev/null | \
|
||||
xargs grep -l "\"$container\"" 2>/dev/null | head -1)
|
||||
|
||||
if [[ -z "$template" ]]; then
|
||||
warn "$container template not found locally"
|
||||
(( failures++ ))
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would reconfigure $container → http://${target_ip}:${port}/"
|
||||
continue
|
||||
fi
|
||||
|
||||
sed -i "s|<WebUI>.*</WebUI>|<WebUI>http://${target_ip}:${port}/</WebUI>|g" \
|
||||
"$template" 2>/dev/null && \
|
||||
log "$container → http://${target_ip}:${port}/ ✅" || \
|
||||
{ error "Failed to reconfigure $container"; (( failures++ )); }
|
||||
done
|
||||
return $failures
|
||||
}
|
||||
|
||||
|
||||
+121
-9
@@ -17,8 +17,9 @@
|
||||
# ── FUNCTIONS ─────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_require_cmd — verify a platform command exists and is executable
|
||||
# platform_storage_healthy — array mounted and shfs active on /mnt/user
|
||||
# platform_get_disk_states — raw disks.ini content from emhttp state
|
||||
# platform_get_temp_thresholds — disk warn/crit °C from dynamix.cfg
|
||||
# platform_disk_states_path — path to the platform disk state file
|
||||
# platform_get_disk_states — raw disk state content from platform
|
||||
# platform_get_temp_thresholds — disk warn/crit °C from platform config
|
||||
# platform_is_maintenance_running — parity check/sync in progress
|
||||
# platform_is_service_enabled — docker or libvirt enabled in boot config
|
||||
# platform_restart_service — restart a named service via rc.d
|
||||
@@ -27,6 +28,14 @@
|
||||
# platform_is_mover_running — unRAID mover process check
|
||||
# platform_stop_user_scripts — kill all user.scripts background processes
|
||||
# platform_send_os_notification — native unRAID notify (dynamix)
|
||||
# platform_storage_path — root path for user shares/storage (e.g. /mnt/user)
|
||||
# platform_webui_install_path — where the platform serves the WebGUI plugin files from
|
||||
# platform_scripts_dir_probe_cmd — shell command to run on a remote to discover its SCRIPTS_DIR
|
||||
# platform_get_templates_dir — path to Unraid CA docker templates-user directory
|
||||
# platform_setup_db_path — path to the persistent Varaverk setup/wizard state database
|
||||
# platform_rebuild_container — rebuild a container from its stored XML template
|
||||
# platform_push_conf — push master.conf to all listed hosts via WebGUI PHP
|
||||
# platform_push_setup_state — push wizard setup state to WebGUI PHP
|
||||
# ==============================================================================================
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
@@ -79,23 +88,30 @@ platform_storage_healthy() {
|
||||
# Writes raw /var/local/emhttp/disks.ini to stdout.
|
||||
# Returns 1 if the file is absent (array not started or emhttp not running).
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_disk_states_path() {
|
||||
echo "/var/local/emhttp/disks.ini"
|
||||
}
|
||||
|
||||
platform_get_disk_states() {
|
||||
local disks_ini="/var/local/emhttp/disks.ini"
|
||||
local disks_ini
|
||||
disks_ini=$(platform_disk_states_path)
|
||||
[[ -f "$disks_ini" ]] || return 1
|
||||
cat "$disks_ini"
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_get_temp_thresholds
|
||||
# Writes two space-separated values to stdout: WARN_TEMP CRIT_TEMP (°C integers).
|
||||
# Falls back to 45 55 if dynamix.cfg is absent or the keys are missing.
|
||||
# Writes four space-separated values to stdout: HDD_HOT HDD_MAX SSD_HOT SSD_MAX (°C integers).
|
||||
# Falls back to 45/55/60/70 if dynamix.cfg is absent or keys are missing.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_get_temp_thresholds() {
|
||||
local cfg="/boot/config/plugins/dynamix/dynamix.cfg"
|
||||
local warn crit
|
||||
warn=$(grep -m1 '^diskWarn=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"')
|
||||
crit=$(grep -m1 '^diskCrit=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"')
|
||||
echo "${warn:-45} ${crit:-55}"
|
||||
local hdd_hot hdd_max ssd_hot ssd_max
|
||||
hdd_hot=$(grep -m1 '^hot=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"')
|
||||
hdd_max=$(grep -m1 '^max=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"')
|
||||
ssd_hot=$(grep -m1 '^hotssd=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"')
|
||||
ssd_max=$(grep -m1 '^maxssd=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"')
|
||||
echo "${hdd_hot:-45} ${hdd_max:-55} ${ssd_hot:-60} ${ssd_max:-70}"
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
@@ -200,6 +216,39 @@ platform_stop_user_scripts() {
|
||||
# severity: normal | warning | alert (default: normal)
|
||||
# Returns 1 if the notify script is absent.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# ── Platform path defaults ─────────────────────────────────────────────────────────────────────
|
||||
# Set here so master.conf and core scripts never contain OS-specific path literals.
|
||||
# host*.conf may override any of these after the adapter is sourced.
|
||||
DOCKER_APPDATA_BASE="${DOCKER_APPDATA_BASE:-/mnt/user/appdata}"
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_storage_path
|
||||
# Writes the root path where user shares and storage are accessible to stdout.
|
||||
# Used by array health checks and disk verification.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_storage_path() {
|
||||
echo "/mnt/user"
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_webui_install_path
|
||||
# Writes the path where the platform serves WebGUI plugin files from.
|
||||
# On Unraid the PHP WebGUI is served from /boot/, separate from SCRIPTS_DIR.
|
||||
# Returns 1 (empty) if the platform serves directly from SCRIPTS_DIR (no sync needed).
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_webui_install_path() {
|
||||
echo "/boot/config/plugins/varaverk"
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_scripts_dir_probe_cmd
|
||||
# Writes a shell command suitable for running on a remote via SSH to discover
|
||||
# that remote's SCRIPTS_DIR. Each platform exposes its install path differently.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_scripts_dir_probe_cmd() {
|
||||
echo "grep -m1 'SCRIPTS_DIR' /boot/config/plugins/varaverk/varaverk.cfg 2>/dev/null | cut -d= -f2 | tr -d '\"'"
|
||||
}
|
||||
|
||||
platform_send_os_notification() {
|
||||
local message="$1"
|
||||
local subject="${2:-Varaverk}"
|
||||
@@ -209,3 +258,66 @@ platform_send_os_notification() {
|
||||
[[ -x "$notify_script" ]] || return 1
|
||||
"$notify_script" -e "Varaverk" -s "$subject" -d "$message" -i "$severity" 2>/dev/null
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_get_templates_dir
|
||||
# Writes the path to the Unraid CA docker templates-user directory to stdout.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_get_templates_dir() {
|
||||
echo "/boot/config/plugins/dockerMan/templates-user"
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_setup_db_path
|
||||
# Writes the path to the persistent Varaverk setup/wizard state database.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_setup_db_path() {
|
||||
echo "/boot/config/varaverk_setup.db"
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_rebuild_container <container_name>
|
||||
# Rebuilds a container from its stored Unraid CA XML template (stops old, recreates on new
|
||||
# image digest, prunes old image). Returns 1 if the rebuild script is absent or fails.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_rebuild_container() {
|
||||
local container="$1"
|
||||
local rebuild_script="/usr/local/emhttp/plugins/dynamix.docker.manager/scripts/rebuild_container"
|
||||
[[ -x "$rebuild_script" ]] || return 1
|
||||
"$rebuild_script" "$container" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_push_conf
|
||||
# Pushes master.conf to all listed hosts via the WebGUI PHP API.
|
||||
# Writes per-host push results to stdout. Returns 1 if any host failed.
|
||||
# Returns 0 silently if php is unavailable (caller should warn manually).
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_push_conf() {
|
||||
command -v php &>/dev/null || return 0
|
||||
local rc=0
|
||||
php -r "
|
||||
require_once '/usr/local/emhttp/plugins/varaverk/include/config.php';
|
||||
\$results = vv_push_master_conf();
|
||||
if (empty(\$results)) { echo 'no remote hosts'; exit(0); }
|
||||
\$failed = 0;
|
||||
foreach (\$results as \$r) {
|
||||
echo \$r['host'] . ': ' . (\$r['ok'] ? 'pushed' : 'FAILED — ' . \$r['error']) . PHP_EOL;
|
||||
if (!\$r['ok']) \$failed++;
|
||||
}
|
||||
exit(\$failed > 0 ? 1 : 0);
|
||||
" 2>/dev/null
|
||||
rc=$?
|
||||
return $rc
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_push_setup_state
|
||||
# Pushes the Varaverk wizard setup state to all partners via the WebGUI PHP API.
|
||||
# No-op if php is unavailable. Always returns 0.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_push_setup_state() {
|
||||
command -v php &>/dev/null || return 0
|
||||
php -r "require_once '/usr/local/emhttp/plugins/varaverk/include/config.php'; vv_push_setup_state();" 2>/dev/null
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -404,9 +404,13 @@ function vv_parity_status(): array {
|
||||
if (preg_match('/^(\w+)="([^"]*)"/', $line, $m)) $var[$m[1]] = $m[2];
|
||||
}
|
||||
|
||||
$isValid = ($var['mdNumInvalid'] ?? '0') === '0';
|
||||
$exitCode = (int)($var['sbSyncExit'] ?? 0);
|
||||
$errors = (int)($var['sbSyncErrs'] ?? 0);
|
||||
$numDisabled = (int)($var['mdNumDisabled'] ?? 0);
|
||||
$numMissing = (int)($var['mdNumMissing'] ?? 0);
|
||||
$exitCode = (int)($var['sbSyncExit'] ?? 0);
|
||||
$errors = (int)($var['sbSyncErrs'] ?? 0);
|
||||
// Emulated (DISK_DSBL) disks are protected by parity and don't make parity invalid.
|
||||
// True invalidity: sync errors on the last check, or unprotectable missing slots.
|
||||
$isValid = $errors === 0 && $numMissing === 0;
|
||||
$inProgress = ($var['mdResync'] ?? '0') !== '0';
|
||||
$resyncPos = (int)($var['mdResyncPos'] ?? 0);
|
||||
$resyncSize = (int)($var['mdResyncSize'] ?? 1);
|
||||
@@ -452,6 +456,8 @@ function vv_parity_status(): array {
|
||||
$exitMap = ['0' => 'Completed', '-4' => 'Aborted', '-5' => 'Cancelled'];
|
||||
return [
|
||||
'valid' => $isValid,
|
||||
'num_disabled' => $numDisabled,
|
||||
'num_missing' => $numMissing,
|
||||
'in_progress' => $inProgress,
|
||||
'resync_pct' => $resyncPct,
|
||||
'exit_code' => $exitCode,
|
||||
|
||||
@@ -561,20 +561,23 @@ function vvDiskRow(disk) {
|
||||
const tempColor = vvTempColor(tempC, disk.transport);
|
||||
const tempStr = tempC !== null ? `${tempC}°` : '—';
|
||||
const isParity = disk.role === 'parity';
|
||||
const nameColor = isParity ? '#6a8faf' : '#aaa';
|
||||
const failed = !isParity && disk.status && disk.status !== 'DISK_OK';
|
||||
const nameColor = isParity ? '#6a8faf' : failed ? '#f44336' : '#aaa';
|
||||
const pct = disk.pct ?? 0;
|
||||
const barColor = isParity ? '#1e3a5a'
|
||||
: failed ? '#f44336'
|
||||
: pct >= vvThresholds.util_crit ? '#f44336'
|
||||
: pct >= vvThresholds.util_warn ? '#ff9800'
|
||||
: '#4caf50';
|
||||
const barWidth = isParity ? '100' : pct;
|
||||
const spinLabel = (!isParity && !disk.mounted) ? `<span style="color:#555;font-size:9px;margin-left:4px;">↓</span>` : '';
|
||||
const failLabel = failed ? `<span style="color:#f44336;font-size:9px;margin-left:4px;">${disk.status === 'DISK_DSBL' ? 'emulated' : disk.status.replace('DISK_','').toLowerCase()}</span>` : '';
|
||||
const right = isParity
|
||||
? `<span style="color:#444;font-size:10px;">${vvFmt(disk.size_gb)}</span>`
|
||||
: `<span style="color:#555;font-size:10px;">${vvFmt(disk.used_gb)} / ${vvFmt(disk.size_gb)}</span>`;
|
||||
return `<div style="margin-bottom:7px;">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;font-size:11px;margin-bottom:3px;">
|
||||
<span style="color:${nameColor};display:flex;align-items:center;">${disk.name}${spinLabel}${vvIoChip(disk.device)}</span>
|
||||
<span style="color:${nameColor};display:flex;align-items:center;">${disk.name}${spinLabel}${failLabel}${vvIoChip(disk.device)}</span>
|
||||
${right}
|
||||
<span style="color:${tempColor};font-size:10px;margin-left:6px;flex-shrink:0;">${tempStr}</span>
|
||||
</div>
|
||||
@@ -968,10 +971,8 @@ function vvPollMonitor() {
|
||||
// ── Parity ──────────────────────────────────────────────────────────────
|
||||
const par = d.parity ?? {};
|
||||
(function() {
|
||||
const valid = par.valid !== false;
|
||||
const inProg = par.in_progress;
|
||||
const validColor = valid ? '#4caf50' : '#f44336';
|
||||
const validLabel = valid ? 'Parity is valid' : 'Parity is INVALID';
|
||||
const valid = par.valid !== false;
|
||||
const inProg = par.in_progress;
|
||||
|
||||
function vvRelTime(ts) {
|
||||
if (!ts) return '';
|
||||
@@ -999,15 +1000,21 @@ function vvPollMonitor() {
|
||||
return new Date(ts * 1000).toLocaleString([], {weekday:'short',day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});
|
||||
}
|
||||
|
||||
const exitColor = par.exit_label === 'Completed' ? '#4caf50' : par.exit_label === 'Aborted' ? '#ff9800' : '#f44336';
|
||||
const errColor = (par.errors ?? 0) > 0 ? '#f44336' : '#444';
|
||||
const speedStr = par.last_speed_mb ? ` · ${par.last_speed_mb} MB/s` : '';
|
||||
const nextDate = vvFmtDate(par.next_ts);
|
||||
const dueIn = vvDueIn(par.next_ts);
|
||||
const parBannerCls = !valid ? 'vv-banner-err' : (par.errors ?? 0) > 0 ? 'vv-banner-warn' : 'vv-banner-ok';
|
||||
const numDisabled = par.num_disabled ?? 0;
|
||||
const numMissing = par.num_missing ?? 0;
|
||||
const degraded = numDisabled > 0 || numMissing > 0;
|
||||
const exitColor = par.exit_label === 'Completed' ? '#4caf50' : par.exit_label === 'Aborted' ? '#ff9800' : '#f44336';
|
||||
const errColor = (par.errors ?? 0) > 0 ? '#f44336' : '#444';
|
||||
const speedStr = par.last_speed_mb ? ` · ${par.last_speed_mb} MB/s` : '';
|
||||
const nextDate = vvFmtDate(par.next_ts);
|
||||
const dueIn = vvDueIn(par.next_ts);
|
||||
const parBannerCls = !valid ? 'vv-banner-err' : degraded || (par.errors ?? 0) > 0 ? 'vv-banner-warn' : 'vv-banner-ok';
|
||||
const emulLabel = numDisabled > 0 ? `<span style="font-size:11px;">Emulating ${numDisabled} disk${numDisabled !== 1 ? 's' : ''}</span>` : '';
|
||||
const missingLabel = numMissing > 0 ? `<span style="font-size:11px;color:#f44336;">${numMissing} slot${numMissing !== 1 ? 's' : ''} missing</span>` : '';
|
||||
|
||||
let html = `<div class="vv-banner ${parBannerCls}">
|
||||
<span>${valid ? '✓ Valid' : '✗ INVALID'}</span>
|
||||
${emulLabel}${missingLabel}
|
||||
${(par.errors ?? 0) > 0 ? `<span style="font-size:11px;">${par.errors} error${par.errors !== 1 ? 's' : ''}</span>` : ''}
|
||||
</div>`;
|
||||
|
||||
@@ -1027,6 +1034,7 @@ function vvPollMonitor() {
|
||||
<span style="color:${exitColor};">${par.exit_label ?? '—'}</span>
|
||||
<span style="color:#555;">Errors</span>
|
||||
<span style="color:${errColor};">${par.errors ?? 0}</span>
|
||||
${numDisabled > 0 ? `<span style="color:#555;">Emulating</span><span style="color:#ff9800;">${numDisabled} disk${numDisabled !== 1 ? 's' : ''}</span>` : ''}
|
||||
<span style="color:#555;">Next check</span>
|
||||
<span style="color:#888;">${nextDate}</span>
|
||||
</div>
|
||||
|
||||
@@ -170,10 +170,6 @@ if ! command -v docker &>/dev/null; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
detect_hosts
|
||||
|
||||
|
||||
@@ -93,10 +93,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ for host_var in $(compgen -v | grep -E '^HOST[0-9]+$' | sort); do
|
||||
|
||||
# ── Pull: grab partner's conf from their disk → our local cache ──────────
|
||||
if [[ "$PUSH_ONLY" == false ]]; then
|
||||
remote_conf="/boot/config/plugins/varaverk/Configurations/${partner_slot}.conf"
|
||||
remote_conf="${SCRIPTS_DIR}/Configurations/${partner_slot}.conf"
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would pull $partner_host:$remote_conf → $CACHE_DIR/${partner_slot}.conf"
|
||||
elif timeout "$SSH_TIMEOUT" scp -i "$SSH_KEY" \
|
||||
|
||||
@@ -93,10 +93,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -99,10 +99,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -114,10 +114,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -110,10 +110,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
validate_int REBOOT_SLEEP "$REBOOT_SLEEP"
|
||||
|
||||
|
||||
@@ -98,10 +98,6 @@ fi
|
||||
|
||||
acquire_lock
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
# detect_hosts() sets MY_ID — used in summary
|
||||
detect_hosts
|
||||
|
||||
@@ -93,10 +93,6 @@ if ! command -v docker &>/dev/null; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
# detect_hosts() sets MY_ID — used in summary
|
||||
detect_hosts
|
||||
|
||||
@@ -101,10 +101,6 @@ platform_require_cmd \
|
||||
"--version" "." \
|
||||
"sqlite3" || { error "sqlite3 not found — install sqlite package"; exit 1; }
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
error "jq not found — required to detect Emby config path from Docker mounts"
|
||||
|
||||
@@ -88,10 +88,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -101,10 +101,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -112,10 +112,6 @@ platform_require_cmd \
|
||||
exit 1
|
||||
}
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -109,10 +109,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -103,10 +103,6 @@ platform_require_cmd \
|
||||
exit 1
|
||||
}
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -117,10 +117,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -112,10 +112,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock "wait"
|
||||
|
||||
|
||||
@@ -150,10 +150,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock "wait"
|
||||
|
||||
|
||||
@@ -112,10 +112,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -254,12 +254,6 @@ fi
|
||||
log "$ICON_GEAR Config: grace=${WATCHDOG_STARTUP_GRACE}s mem-soft=${SOFT_MEM_THRESHOLD}% cpu-soft=${SOFT_CPU_THRESHOLD}% cpu-hard=${HARD_CPU_THRESHOLD}% cpu-limit=${CPU_FAIL_LIMIT} http-limit=${RESP_FAIL_LIMIT} daemon-timeout=${DOCKER_TIMEOUT}s"
|
||||
log "$ICON_CONTAINERS Tier1: watched=${#WATCHDOG_CONTAINERS[@]} required=${#WATCHDOG_REQUIRED_CONTAINERS[@]} urls=${#WATCHDOG_CONTAINER_URLS[@]} restart-limit=${WATCHDOG_CONTAINER_RESTART_LIMIT}/${WATCHDOG_CONTAINER_RESTART_WINDOW}h"
|
||||
|
||||
# Validate unRAID-specific commands used by this script
|
||||
# If rc.docker is missing or changed, daemon restart will fail — better to know now
|
||||
platform_require_cmd "/etc/rc.d/rc.docker" "" "" "Docker rc.d script" || warn "rc.docker not found — daemon restart unavailable if needed"
|
||||
|
||||
platform_require_cmd "/usr/local/emhttp/plugins/dynamix/scripts/notify" "" "" "unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
# Ensure state files exist
|
||||
touch "$WATCHDOG_STATE_FILE" "$WATCHDOG_CONTAINER_RESTART_LOG" \
|
||||
"$DOCKER_WATCHDOG_FAILED_FILE" 2>/dev/null
|
||||
|
||||
@@ -119,10 +119,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -45,10 +45,6 @@ if [[ "$EUID" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
platform_require_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
|
||||
@@ -228,14 +228,11 @@ notify() {
|
||||
|
||||
log "$ICON_NOTIFY Sending notification: $subject — $message"
|
||||
|
||||
# Guard is built in — scripts do NOT need to validate_unraid_cmd for the notify script.
|
||||
if [[ "${NOTIFY_UNRAID:-false}" == true ]]; then
|
||||
local notify_script="/usr/local/emhttp/plugins/dynamix/scripts/notify"
|
||||
if [[ -x "$notify_script" ]]; then
|
||||
"$notify_script" -s "$subject" -d "$message" -i "$severity" 2>/dev/null
|
||||
log "$ICON_NOTIFY unRAID notification sent"
|
||||
if platform_send_os_notification "$message" "$subject" "$severity"; then
|
||||
log "$ICON_NOTIFY OS notification sent"
|
||||
else
|
||||
log "$ICON_NOTIFY unRAID notify script not found — skipping"
|
||||
log "$ICON_NOTIFY OS notify not available — skipping"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -586,6 +583,8 @@ detect_hosts() {
|
||||
_alias_array "PARTNERSHIP_REPLACE_CONTAINERS"
|
||||
_alias_array "PARTNERSHIP_ARR_STACK"
|
||||
_alias_array "PARTNERSHIP_ARR_REPLACE_CONTAINERS"
|
||||
_alias_array "PARTNERSHIP_SERVICES_STACK"
|
||||
_alias_array "PARTNERSHIP_SERVICES_REPLACE_CONTAINERS"
|
||||
_alias_array "RW_PAUSE_CONTAINERS"
|
||||
_alias_array "RW_STOP_CONTAINERS"
|
||||
|
||||
@@ -727,34 +726,35 @@ check_rsync_enabled() {
|
||||
}
|
||||
|
||||
# ==============================================================================================
|
||||
# ── UNRAID SERVICE STATE ──────────────────────────────────────────────────────────────────────
|
||||
# ── PLATFORM SERVICE STATE ────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
# Reads Unraid config files to check whether Docker and VM Manager are enabled.
|
||||
# Use these guards before any script that manages containers or VMs.
|
||||
# Check whether Docker and VM Manager are enabled on this host.
|
||||
# Delegates to platform_is_service_enabled() from the platform adapter.
|
||||
|
||||
is_docker_enabled() {
|
||||
[[ "$(grep -oP '(?<=DOCKER_ENABLED=")[^"]+' /boot/config/docker.cfg 2>/dev/null)" == "yes" ]]
|
||||
platform_is_service_enabled docker
|
||||
}
|
||||
|
||||
is_vm_manager_enabled() {
|
||||
[[ "$(grep -oP '(?<=SERVICE=")[^"]+' /boot/config/domain.cfg 2>/dev/null)" == "enable" ]]
|
||||
platform_is_service_enabled libvirt
|
||||
}
|
||||
|
||||
# ==============================================================================================
|
||||
# ── LOCAL HEALTH CHECKS ───────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
|
||||
# Verifies local /mnt/user is mounted and has shares.
|
||||
# Verifies local storage is mounted and has shares.
|
||||
# Non-fatal — returns status for caller to decide.
|
||||
# Used by fallback before starting remote containers locally.
|
||||
check_local_array() {
|
||||
log "Checking local array..."
|
||||
if ! mountpoint -q /mnt/user 2>/dev/null; then
|
||||
error "$ICON_DISK Local array is not started — /mnt/user is not mounted"
|
||||
if ! platform_storage_healthy; then
|
||||
error "$ICON_DISK Local array is not started — storage not mounted"
|
||||
return 1
|
||||
fi
|
||||
local file_count
|
||||
file_count=$(ls /mnt/user 2>/dev/null | wc -l)
|
||||
local _storage_path file_count
|
||||
_storage_path=$(platform_storage_path)
|
||||
file_count=$(ls "$_storage_path" 2>/dev/null | wc -l)
|
||||
if [[ "$file_count" -eq 0 ]]; then
|
||||
error "$ICON_DISK Local array appears empty — shares may not be available"
|
||||
return 1
|
||||
@@ -767,14 +767,15 @@ check_local_array() {
|
||||
# ── REMOTE HEALTH CHECKS ─────────────────────────────────────────────────────────────────────
|
||||
# ==============================================================================================
|
||||
|
||||
# Verifies remote /mnt/user is mounted via SSH.
|
||||
# Verifies remote storage is mounted via SSH.
|
||||
# Non-fatal — returns status. Used before handback rsync.
|
||||
# Syncing to remote with no array fills rootfs rapidly.
|
||||
check_remote_array() {
|
||||
log "Checking remote array on $REMOTE_SERVER_NAME..."
|
||||
local result
|
||||
local _spath result
|
||||
_spath=$(platform_storage_path)
|
||||
result=$(ssh -i "$SSH_KEY" -o ConnectTimeout=10 root@"$REMOTE_SERVER" \
|
||||
"mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)
|
||||
"mountpoint -q '$_spath' && echo yes || echo no" 2>/dev/null)
|
||||
if [[ "$result" != "yes" ]]; then
|
||||
error "$ICON_DISK Remote array not started on $REMOTE_SERVER_NAME"
|
||||
return 1
|
||||
@@ -853,18 +854,11 @@ is_ssd() {
|
||||
[[ "$(cat "/sys/block/$base/queue/rotational" 2>/dev/null)" == "0" ]]
|
||||
}
|
||||
|
||||
# Reads disk temperature thresholds from unRAID's dynamix.cfg.
|
||||
# Sets globals: UNRAID_DISK_HOT UNRAID_DISK_MAX UNRAID_SSD_HOT UNRAID_SSD_MAX
|
||||
# Falls back to values in master.conf (SMART_TEMP_WARN/CRIT) if file not found.
|
||||
# Falls back to master.conf values (SMART_TEMP_WARN/CRIT) if platform returns defaults.
|
||||
get_unraid_temp_thresholds() {
|
||||
local cfg="/boot/config/plugins/dynamix/dynamix.cfg"
|
||||
if [[ -f "$cfg" ]]; then
|
||||
UNRAID_DISK_HOT=$(grep '^hot=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"')
|
||||
UNRAID_DISK_MAX=$(grep '^max=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"')
|
||||
UNRAID_SSD_HOT=$(grep '^hotssd=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"')
|
||||
UNRAID_SSD_MAX=$(grep '^maxssd=' "$cfg" 2>/dev/null | cut -d= -f2 | tr -d '"')
|
||||
fi
|
||||
# Fall back to master.conf values if dynamix.cfg not found or values empty
|
||||
read -r UNRAID_DISK_HOT UNRAID_DISK_MAX UNRAID_SSD_HOT UNRAID_SSD_MAX \
|
||||
<<< "$(platform_get_temp_thresholds)"
|
||||
UNRAID_DISK_HOT="${UNRAID_DISK_HOT:-${SMART_TEMP_WARN:-45}}"
|
||||
UNRAID_DISK_MAX="${UNRAID_DISK_MAX:-${SMART_TEMP_CRIT:-55}}"
|
||||
UNRAID_SSD_HOT="${UNRAID_SSD_HOT:-60}"
|
||||
@@ -884,10 +878,10 @@ get_unraid_temp_thresholds() {
|
||||
check_local_disk_temps() {
|
||||
get_unraid_temp_thresholds
|
||||
|
||||
local disks_ini="/var/local/emhttp/disks.ini"
|
||||
if [[ ! -f "$disks_ini" ]]; then
|
||||
warn "disks.ini not found — skipping temp check"
|
||||
TEMP_CHECK_RESULT="temp check skipped (disks.ini not found)"
|
||||
local disks_ini_content
|
||||
if ! disks_ini_content=$(platform_get_disk_states); then
|
||||
warn "disk state unavailable — skipping temp check"
|
||||
TEMP_CHECK_RESULT="temp check skipped (disk state unavailable)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -937,7 +931,7 @@ check_local_disk_temps() {
|
||||
current_temp="${current_temp//[^0-9]/}"
|
||||
current_temp="${current_temp:-0}"
|
||||
fi
|
||||
done < "$disks_ini"
|
||||
done <<< "$disks_ini_content"
|
||||
check_drive # process last drive
|
||||
|
||||
if [[ ${#crit_drives[@]} -gt 0 ]]; then
|
||||
@@ -980,13 +974,14 @@ check_remote_disks() {
|
||||
|
||||
info "$ICON_DISK Checking disks backing $share_name on $REMOTE_SERVER_NAME..."
|
||||
|
||||
# Read remote disks.ini for fsType mapping
|
||||
local disks_ini_content
|
||||
# Read remote disk state for fsType mapping
|
||||
local disks_ini_content _disk_states_path
|
||||
_disk_states_path=$(platform_disk_states_path)
|
||||
disks_ini_content=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
|
||||
"cat /var/local/emhttp/disks.ini 2>/dev/null" 2>/dev/null)
|
||||
"cat '$_disk_states_path' 2>/dev/null" 2>/dev/null)
|
||||
|
||||
if [[ -z "$disks_ini_content" ]]; then
|
||||
error "$ICON_DISK Cannot read disks.ini from $REMOTE_SERVER_NAME"
|
||||
error "$ICON_DISK Cannot read disk state from $REMOTE_SERVER_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -1004,11 +999,13 @@ check_remote_disks() {
|
||||
grep -q . && echo \"\$pool\"
|
||||
done | sort -u" 2>/dev/null)
|
||||
|
||||
# Tier 3 — shfs fallback via /mnt/user
|
||||
# Tier 3 — storage root fallback
|
||||
local _spath
|
||||
_spath=$(platform_storage_path)
|
||||
if [[ -z "$backing_disks" ]] && [[ -z "$zfs_pool_paths" ]]; then
|
||||
local on_user
|
||||
on_user=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
|
||||
"find /mnt/user -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \
|
||||
"find '$_spath' -maxdepth 3 -name '$share_name' -type d 2>/dev/null | head -1" \
|
||||
2>/dev/null)
|
||||
if [[ -n "$on_user" ]]; then
|
||||
local actual_path
|
||||
@@ -1020,12 +1017,12 @@ check_remote_disks() {
|
||||
zfs_pool_paths="$pool_name"
|
||||
info "$ICON_DISK $share_name found at $actual_path on $REMOTE_SERVER_NAME"
|
||||
else
|
||||
# Can't determine pool — verify shfs itself is mounted
|
||||
# Can't determine pool — verify storage root itself is mounted
|
||||
local shfs_ok
|
||||
shfs_ok=$(ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" \
|
||||
"mountpoint -q /mnt/user && echo yes || echo no" 2>/dev/null)
|
||||
"mountpoint -q '$_spath' && echo yes || echo no" 2>/dev/null)
|
||||
if [[ "$shfs_ok" == "yes" ]]; then
|
||||
success "All disks backing $share_name are online ✅ (via shfs)"
|
||||
success "All disks backing $share_name are online ✅ (via storage root)"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
+1
-1
@@ -255,7 +255,7 @@ else
|
||||
# ── Flash mode: sync Plugin/ to /boot/ so the webUI picks up updates ─────
|
||||
# In flash mode SCRIPTS_DIR is in appdata — Plugin/ lives in the repo there
|
||||
# but Unraid serves PHP from /boot/. Sync after every pull to keep them in step.
|
||||
_BOOT_DIR="/boot/config/plugins/varaverk"
|
||||
_BOOT_DIR=$(platform_webui_install_path)
|
||||
if [[ "$TARGET_DIR" != "$_BOOT_DIR" ]]; then
|
||||
echo ""
|
||||
echo "━━━ $ICON_SYNC Flash mode: sync Plugin/ → /boot/ ━━━"
|
||||
|
||||
Reference in New Issue
Block a user