Files
Varaverk/Plugin/unraid/include/partnership.php
T
Gmer4Lfe a5755921ab Two-phase onboard + partner card enhancements
partnership_onboard.sh:
- --phase1-only (OWNER): SSH key exchange + conf push only; safe to run
  before HOST2 has Varaverk installed; writes HOST2_PHASE1_DONE to setup.db
- --phase2-only (OWNER): skips SSH, runs Steps 2-9 (containers, arr stack,
  onboard, arr sync, conf push); writes HOST2_PHASE2_DONE to setup.db
- Mirror path: after SSH setup, SSHes OWNER and fires --phase2-only in
  background (nohup); reads OWNER's SCRIPTS_DIR from varaverk.cfg first;
  falls back to manual instruction if SSH fails

partnership.php (include): vv_pt_nodes() reads setup.db and exposes
onboard_phase (0/1/2) per remote node

partnership.php (page): phase-aware Actions section per partner host;
node cards show phase badge (not provisioned / awaiting onboard / done);
Phase 1 and Phase 2 buttons call run.php with extra_args

monitor.php: partner card logo (top-right), CPU thread count, RAM total GB,
unRAID version row in remote stats grid
2026-05-30 18:12:49 -04:00

186 lines
7.7 KiB
PHP

<?php
// Partnership page data helpers
require_once __DIR__ . '/config.php';
require_once __DIR__ . '/arrs.php'; // vv_arr_known_hosts(), vv_arr_scalar()
// ── Config ────────────────────────────────────────────────────────────────────
function vv_pt_config(): array {
$v = vv_conf_vars();
return [
'enabled' => ($v['PARTNERSHIP_ENABLED'] ?? 'false') === 'true',
'owner_host' => $v['PARTNERSHIP_OWNER_HOST'] ?? '',
'sync_min' => (int)($v['PARTNERSHIP_SYNC_INTERVAL'] ?? 15),
'grace_hours' => (int)($v['PARTNERSHIP_GRACE_HOURS'] ?? 6),
'offline_threshold' => (int)($v['PARTNERSHIP_OFFLINE_THRESHOLD'] ?? 30),
'remove_tailscale' => ($v['PARTNERSHIP_REMOVE_TAILSCALE'] ?? 'true') === 'true',
'folderview3' => ($v['PARTNERSHIP_FOLDERVIEW3'] ?? 'false') === 'true',
'tailscale_configured' => !empty($v['TAILSCALE_API_KEY']) && !empty($v['TAILSCALE_TAILNET']),
];
}
// ── State file parser ─────────────────────────────────────────────────────────
function vv_pt_read_db(string $path): array {
if (!file_exists($path)) return [];
$out = [];
foreach (file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
[$k, $v] = array_pad(explode('=', trim($line), 2), 2, '');
if ($k) $out[trim($k)] = trim($v, '"\'');
}
return $out;
}
// ── Tailscale peers ───────────────────────────────────────────────────────────
function vv_pt_ts_peers(): array {
$raw = shell_exec('tailscale status --json 2>/dev/null') ?: '{}';
$data = json_decode($raw, true) ?: [];
$peers = [];
// Self
$self = $data['Self'] ?? [];
$selfLabel = strtolower(explode('.', $self['DNSName'] ?? '')[0]);
if ($selfLabel) {
$peers[$selfLabel] = [
'online' => true,
'active' => true,
'ip' => $self['TailscaleIPs'][0] ?? null,
];
}
// Peers
foreach ($data['Peer'] ?? [] as $peer) {
$label = strtolower(explode('.', $peer['DNSName'] ?? '')[0]);
if (!$label) continue;
$peers[$label] = [
'online' => (bool)($peer['Online'] ?? false),
'active' => (bool)($peer['Active'] ?? false),
'ip' => $peer['TailscaleIPs'][0] ?? null,
];
}
return $peers;
}
// ── SSH helper — run a single command on a remote host ────────────────────────
function vv_pt_ssh(string $ip, string $sshKey, string $cmd, int $timeout = 4): string {
if (!$ip || !$sshKey || !file_exists($sshKey)) return '';
$full = sprintf(
'ssh -i %s -o ConnectTimeout=%d -o StrictHostKeyChecking=no -o BatchMode=yes root@%s %s 2>/dev/null',
escapeshellarg($sshKey), $timeout, escapeshellarg($ip), escapeshellarg($cmd)
);
return shell_exec($full) ?: '';
}
// ── System info ───────────────────────────────────────────────────────────────
function vv_pt_local_system(): array {
$ver = '';
if (file_exists('/etc/unraid-version')) {
preg_match('/VERSION="([^"]+)"/', file_get_contents('/etc/unraid-version'), $m);
$ver = $m[1] ?? '';
}
$uptime = 0;
if (file_exists('/proc/uptime')) {
$uptime = (int)explode(' ', file_get_contents('/proc/uptime'))[0];
}
return ['unraid_version' => $ver, 'uptime_sec' => $uptime];
}
function vv_pt_remote_system(string $ip, string $sshKey): array {
$out = vv_pt_ssh($ip, $sshKey,
'printf "%s\nUPTIME:%s\n" "$(cat /etc/unraid-version 2>/dev/null)" "$(cat /proc/uptime 2>/dev/null)"');
$ver = '';
preg_match('/VERSION="([^"]+)"/', $out, $m);
if ($m) $ver = $m[1];
$uptime = 0;
if (preg_match('/UPTIME:([\d.]+)/', $out, $m)) $uptime = (int)$m[1];
return ['unraid_version' => $ver, 'uptime_sec' => $uptime];
}
// ── Per-node data ─────────────────────────────────────────────────────────────
function vv_pt_nodes(): array {
$currentHost = vv_detect_host();
$hosts = vv_arr_known_hosts(); // ['host1' => 'hostname', ...]
$vars = vv_conf_vars();
$tsPeers = vv_pt_ts_peers();
$ownerSlot = strtolower($vars['PARTNERSHIP_OWNER_HOST'] ?? '');
$setupDb = vv_setup_state_read();
// SSH key for this host
$myId = strtoupper($currentHost);
$myRaw = vv_read_conf_raw($currentHost . '.conf');
$mySshKey = vv_arr_scalar($myRaw, $myId . '_SSH_KEY');
$nodes = [];
foreach ($hosts as $slot => $hostname) {
$isMe = ($slot === $currentHost || $currentHost === 'unknown');
$isOwner = (strtolower($ownerSlot) === $slot);
// Tailscale
$tsLabel = strtolower($hostname);
$ts = $tsPeers[$tsLabel] ?? ['online' => null, 'active' => false, 'ip' => null];
// Fallback state
$fbState = 'UNKNOWN';
$fbPath = '/boot/config/fallback_state.db';
if ($isMe) {
$fb = vv_pt_read_db($fbPath);
$fbState = $fb['state'] ?? 'UNKNOWN';
} elseif ($ts['online'] && $ts['ip'] && $mySshKey) {
$out = vv_pt_ssh($ts['ip'], $mySshKey, 'cat /boot/config/fallback_state.db 2>/dev/null');
if ($out) {
$fb = [];
foreach (explode("\n", $out) as $line) {
[$k, $v] = array_pad(explode('=', trim($line), 2), 2, '');
if ($k) $fb[trim($k)] = trim($v, '"\'');
}
$fbState = $fb['state'] ?? 'UNKNOWN';
}
}
// Partnership DB — local only (each server writes its own)
$dbPath = "/boot/config/partnership_{$hostname}.db";
$ptDb = vv_pt_read_db($dbPath);
// System info
$system = $isMe
? vv_pt_local_system()
: ($ts['online'] && $ts['ip'] && $mySshKey ? vv_pt_remote_system($ts['ip'], $mySshKey) : []);
// Onboard phase from setup.db — 0=not started, 1=SSH+conf done, 2=fully onboarded
$nodeIdUpper = strtoupper($slot);
$onboardPhase = ($setupDb[$nodeIdUpper . '_PHASE2_DONE'] ?? '') === 'true' ? 2
: (($setupDb[$nodeIdUpper . '_PHASE1_DONE'] ?? '') === 'true' ? 1 : 0);
$nodes[] = [
'slot' => $slot,
'id' => $nodeIdUpper,
'hostname' => $hostname,
'is_me' => $isMe,
'is_owner' => $isOwner,
'ts_online' => $ts['online'],
'ts_active' => $ts['active'],
'ts_ip' => $ts['ip'],
'fallback' => $fbState,
'partnership' => $ptDb,
'system' => $system,
'onboard_phase' => $isMe ? null : $onboardPhase,
];
}
return $nodes;
}
// ── Entry point ───────────────────────────────────────────────────────────────
function vv_partnership_all(): array {
return [
'config' => vv_pt_config(),
'nodes' => vv_pt_nodes(),
'ts' => time(),
];
}