Platform adapter: rename System_Essentials, add Plugin/unraid/adapter.sh, wire call sites
- Rename unRAID_Essentials/ → System_Essentials/ (git detects as rename) - Add Plugin/unraid/adapter.sh: 13 platform_*() functions providing OS-agnostic API for storage health, service management, mover, user scripts, notifications, disk temps, and platform command validation - Update load_config.sh: detect PLATFORM (unraid/truenas/unknown), export SCRIPTS_DIR, auto-source Plugin/$PLATFORM/adapter.sh after common.sh - Wire all call sites: replace direct rc.d, pgrep/pkill, var.ini, dynamix.cfg, disks.ini, and validate_unraid_cmd calls with platform_*() functions across watchdogs, orchestrators, and System_Essentials scripts - Update all documentation: rename refs, update webgui escalation logic, add platform adapter section to Plugin README, update main README with portability vision and corrected self-healing stack description
This commit is contained in:
+37
-1
@@ -33,7 +33,8 @@ no separate monitoring stack, no third-party dashboards.
|
||||
Plugin/
|
||||
├── dev_install.sh # One-time developer setup: symlinks plugin into web server
|
||||
├── Icons/ # Source icon assets (1024px master files)
|
||||
└── unraid/ # The Unraid plugin application
|
||||
└── unraid/ # The Unraid platform adapter + plugin application
|
||||
├── adapter.sh # Platform adapter — provides platform_*() API to all scripts
|
||||
├── Varaverk.page # Main plugin entry point (Tasks menu)
|
||||
├── VaraverkSettings.page # Unraid Settings → Other Settings entry
|
||||
├── api/ # PHP API endpoints (called by JS via fetch)
|
||||
@@ -45,6 +46,10 @@ Plugin/
|
||||
├── js/ # Frontend JavaScript
|
||||
├── pages/ # Per-tab page includes (monitor, scheduler, docker, ...)
|
||||
└── run_job.sh # Script runner invoked by the Scheduler
|
||||
|
||||
# Future platform adapters follow the same structure:
|
||||
# Plugin/truenas/adapter.sh — TrueNAS adapter (future)
|
||||
# Plugin/ubuntu/adapter.sh — Ubuntu/Debian adapter (future)
|
||||
```
|
||||
|
||||
---
|
||||
@@ -74,6 +79,37 @@ so behaviour stays consistent without a shell dependency.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## ━━━ THE PLATFORM ADAPTER ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
`Plugin/unraid/adapter.sh` is the Unraid platform adapter. It is sourced automatically
|
||||
by `load_config.sh` whenever `PLATFORM=unraid` is detected (via `/etc/unraid-version`).
|
||||
|
||||
Every bash script in the ecosystem calls `platform_*()` functions instead of OS-specific
|
||||
commands directly. The adapter translates those calls into Unraid-specific implementations.
|
||||
|
||||
```
|
||||
platform_storage_healthy # is the array up and shfs mounted?
|
||||
platform_is_maintenance_running # parity check or sync in progress?
|
||||
platform_is_service_running # is a named service process alive?
|
||||
platform_restart_service # restart via rc.d (Unraid) or systemctl (future)
|
||||
platform_stop_service # stop a named service
|
||||
platform_is_mover_running # Unraid mover active?
|
||||
platform_get_mover_pid # PID of the mover process
|
||||
platform_stop_user_scripts # kill Unraid user.scripts background jobs
|
||||
platform_send_os_notification # dynamix notify (Unraid) or equivalent
|
||||
platform_get_disk_states # reads disks.ini (Unraid) or equivalent
|
||||
platform_get_temp_thresholds # reads dynamix.cfg (Unraid) or equivalent
|
||||
platform_is_service_enabled # docker.cfg / domain.cfg enabled check
|
||||
platform_require_cmd # verify a platform command exists
|
||||
```
|
||||
|
||||
**Adding a new platform:** Create `Plugin/<platform>/adapter.sh` implementing the same
|
||||
function names. `load_config.sh` detects the OS at runtime and sources the correct adapter.
|
||||
No other files need changing.
|
||||
|
||||
---
|
||||
|
||||
## ━━━ UNRAID INTEGRATION POINTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
| File | Where it appears in Unraid |
|
||||
|
||||
Executable
+211
@@ -0,0 +1,211 @@
|
||||
#!/bin/bash
|
||||
# ==============================================================================================
|
||||
# ================================= Unraid Platform Adapter ====================================
|
||||
# ==============================================================================================
|
||||
# Sourced by load_config.sh when PLATFORM=unraid.
|
||||
# Provides the platform_*() API — bash scripts call these instead of OS-specific commands.
|
||||
#
|
||||
# ── API CONTRACT ──────────────────────────────────────────────────────────────────────────────
|
||||
# Every function returns 0 on success / 1 on failure unless noted.
|
||||
# Functions that produce output write to stdout; callers capture with $().
|
||||
# No function calls exit — callers decide what failure means for their flow.
|
||||
#
|
||||
# ── ADDING A PLATFORM ─────────────────────────────────────────────────────────────────────────
|
||||
# Create Plugin/truenas/adapter.sh (or ubuntu/adapter.sh) implementing the same function names.
|
||||
# load_config.sh sources Plugin/$PLATFORM/adapter.sh — no other changes needed.
|
||||
#
|
||||
# ── 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_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
|
||||
# platform_stop_service — stop a named service via rc.d
|
||||
# platform_is_service_running — check if a named service process is alive
|
||||
# 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)
|
||||
# ==============================================================================================
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# Internal: map a logical service name → its rc.d script path
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
_platform_rc_script() {
|
||||
case "$1" in
|
||||
docker) echo "/etc/rc.d/rc.docker" ;;
|
||||
sshd) echo "/etc/rc.d/rc.sshd" ;;
|
||||
libvirt) echo "/etc/rc.d/rc.libvirt" ;;
|
||||
rsyslog) echo "/etc/rc.d/rc.rsyslogd" ;;
|
||||
nginx) echo "/etc/rc.d/rc.nginx" ;;
|
||||
php-fpm) echo "/etc/rc.d/rc.php-fpm" ;;
|
||||
emhttp) echo "/etc/rc.d/rc.emhttp" ;;
|
||||
*) echo "/etc/rc.d/rc.$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_require_cmd <path> [test_arg] [expected_output] [label]
|
||||
# Replaces platform_require_cmd. Returns 0 if the command exists and (optionally) its output
|
||||
# matches expected_output. Returns 1 and prints a warning if not.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_require_cmd() {
|
||||
local cmd="$1"
|
||||
local test_arg="${2:-}"
|
||||
local expected="${3:-}"
|
||||
local label="${4:-$cmd}"
|
||||
|
||||
if [[ ! -x "$cmd" ]]; then
|
||||
return 1
|
||||
fi
|
||||
if [[ -n "$test_arg" && -n "$expected" ]]; then
|
||||
"$cmd" $test_arg 2>&1 | grep -q "$expected" || return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_storage_healthy
|
||||
# Returns 0 if /mnt/user is mounted as shfs (array is up and healthy).
|
||||
# Returns 1 if the mount is absent or is not shfs (array stopped / degraded).
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_storage_healthy() {
|
||||
df --output=fstype /mnt/user 2>/dev/null | grep -q shfs
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_get_disk_states
|
||||
# Writes raw /var/local/emhttp/disks.ini to stdout.
|
||||
# Returns 1 if the file is absent (array not started or emhttp not running).
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_get_disk_states() {
|
||||
local disks_ini="/var/local/emhttp/disks.ini"
|
||||
[[ -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.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
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}"
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_is_maintenance_running
|
||||
# Returns 0 if a parity check or sync is currently in progress.
|
||||
# Checks var.ini (Unraid 7.3+) then falls back to parity-date.txt (older).
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_is_maintenance_running() {
|
||||
local resync
|
||||
resync=$(awk -F'"' '/^mdResync=/{print $2}' /var/local/emhttp/var.ini 2>/dev/null)
|
||||
if [[ -n "$resync" && "$resync" != "0" ]]; then
|
||||
return 0
|
||||
fi
|
||||
grep -q "progress" /var/local/emhttp/parity-date.txt 2>/dev/null
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_is_service_enabled <service>
|
||||
# service: docker | libvirt
|
||||
# Returns 0 if the service is enabled in the Unraid boot config.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_is_service_enabled() {
|
||||
case "$1" in
|
||||
docker) grep -q '^DOCKER_ENABLED=yes' /boot/config/docker.cfg 2>/dev/null ;;
|
||||
libvirt) grep -q '^DOMAIN_ENABLE=yes' /boot/config/domain.cfg 2>/dev/null ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_restart_service <service>
|
||||
# Restarts the named service via its rc.d script.
|
||||
# Returns 1 if the rc.d script does not exist or is not executable.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_restart_service() {
|
||||
local rc
|
||||
rc=$(_platform_rc_script "$1")
|
||||
[[ -x "$rc" ]] || return 1
|
||||
"$rc" restart >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_stop_service <service>
|
||||
# Stops the named service via its rc.d script.
|
||||
# Returns 1 if the rc.d script does not exist or is not executable.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_stop_service() {
|
||||
local rc
|
||||
rc=$(_platform_rc_script "$1")
|
||||
[[ -x "$rc" ]] || return 1
|
||||
"$rc" stop >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_is_service_running <service>
|
||||
# Returns 0 if the service's main process is alive.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_is_service_running() {
|
||||
case "$1" in
|
||||
docker) pgrep -x dockerd >/dev/null 2>&1 ;;
|
||||
emhttp) pgrep -x emhttpd >/dev/null 2>&1 ;;
|
||||
nginx) pgrep -x nginx >/dev/null 2>&1 ;;
|
||||
php-fpm) pgrep -x php-fpm >/dev/null 2>&1 ;;
|
||||
sshd) pgrep -x sshd >/dev/null 2>&1 ;;
|
||||
rsyslog) pgrep -x rsyslogd >/dev/null 2>&1 ;;
|
||||
libvirt) pgrep -x libvirtd >/dev/null 2>&1 ;;
|
||||
*) pgrep -x "$1" >/dev/null 2>&1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_is_mover_running
|
||||
# Returns 0 if the Unraid mover is currently active.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_is_mover_running() {
|
||||
pgrep -f "emhttp.*Mover" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_get_mover_pid
|
||||
# Writes the mover's PID to stdout. Returns 1 if the mover is not running.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_get_mover_pid() {
|
||||
local pid
|
||||
pid=$(pgrep -f "emhttp.*Mover" | head -1)
|
||||
[[ -n "$pid" ]] || return 1
|
||||
echo "$pid"
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_stop_user_scripts
|
||||
# Kills all Unraid user.scripts background processes.
|
||||
# Returns 0 whether or not any processes were found (pkill exits 1 on no match).
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_stop_user_scripts() {
|
||||
pkill -f "/tmp/user.scripts" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
# platform_send_os_notification <message> [subject] [severity]
|
||||
# Sends a native Unraid notification via the dynamix notify script.
|
||||
# severity: normal | warning | alert (default: normal)
|
||||
# Returns 1 if the notify script is absent.
|
||||
# ──────────────────────────────────────────────────────────────────────────────────────────────
|
||||
platform_send_os_notification() {
|
||||
local message="$1"
|
||||
local subject="${2:-Varaverk}"
|
||||
local severity="${3:-normal}"
|
||||
local notify_script="/usr/local/emhttp/plugins/dynamix/scripts/notify"
|
||||
|
||||
[[ -x "$notify_script" ]] || return 1
|
||||
"$notify_script" -e "Varaverk" -s "$subject" -d "$message" -i "$severity" 2>/dev/null
|
||||
}
|
||||
@@ -141,7 +141,7 @@ if ($action === 'api_status') {
|
||||
|
||||
// ── Setup/renew API keys (local + all partners via SSH) ───────────────────────
|
||||
if ($action === 'setup_apikeys' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$script = SCRIPTS_DIR . '/unRAID_Essentials/unraid_api_key_renew.sh';
|
||||
$script = SCRIPTS_DIR . '/System_Essentials/unraid_api_key_renew.sh';
|
||||
if (!file_exists($script)) {
|
||||
echo json_encode(['ok' => false, 'error' => 'unraid_api_key_renew.sh not found']); exit;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user