From 072c8b720debea45b6fb889aa0c4419a47dda934 Mon Sep 17 00:00:00 2001 From: Gmer4Lfe Date: Tue, 26 May 2026 18:59:19 -0400 Subject: [PATCH] Varaverk: monitor grid overhaul + Containers & VMs card Monitor layout (8-col grid): - Row 1: System | Power | CPU | Memory | Network - Row 2: Scripts | Partner & Fallback (span 3) | Containers & VMs (span 4) - Row 3: GPU (span 2) | Transcode (span 2) | Streams (span 4) - Row 4: Parity (cols 1-2) | Pools (cols 3-4) | Array (cols 5-8) New Containers & VMs card: - Reads FolderView3 folders from docker.json; expand/collapse per folder - VMs listed first (virsh); containers show start/stop/webui/edit actions - 2-column balanced layout; collapses to 1 column below 900px - Docker WebUI URLs resolved from dockerMan template XMLs New files: include/vms.php, include/docker_folders.php, api/docker_action.php, api/snapshot.php Responsive: explicit grid-column placements reset at 1024px breakpoint --- .../emhttp/plugins/varaverk/Varaverk.page | 2 +- .../plugins/varaverk/api/docker_action.php | 21 ++ .../emhttp/plugins/varaverk/api/monitor.php | 10 +- .../emhttp/plugins/varaverk/api/scheduler.php | 22 ++ .../emhttp/plugins/varaverk/api/snapshot.php | 72 +++++ .../emhttp/plugins/varaverk/css/varaverk.css | 84 +++++- .../plugins/varaverk/icons/varaverk.png | Bin 0 -> 103 bytes .../varaverk/include/docker_folders.php | 99 +++++++ .../plugins/varaverk/include/scheduler.php | 17 ++ .../emhttp/plugins/varaverk/include/vms.php | 46 +++ .../emhttp/plugins/varaverk/pages/monitor.php | 272 ++++++++++++++---- .../plugins/varaverk/pages/scheduler.php | 127 ++++++-- 12 files changed, 689 insertions(+), 83 deletions(-) create mode 100644 Plugin/usr/local/emhttp/plugins/varaverk/api/docker_action.php create mode 100644 Plugin/usr/local/emhttp/plugins/varaverk/api/snapshot.php create mode 100644 Plugin/usr/local/emhttp/plugins/varaverk/icons/varaverk.png create mode 100644 Plugin/usr/local/emhttp/plugins/varaverk/include/docker_folders.php create mode 100644 Plugin/usr/local/emhttp/plugins/varaverk/include/vms.php diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/Varaverk.page b/Plugin/usr/local/emhttp/plugins/varaverk/Varaverk.page index db266f1..668395d 100644 --- a/Plugin/usr/local/emhttp/plugins/varaverk/Varaverk.page +++ b/Plugin/usr/local/emhttp/plugins/varaverk/Varaverk.page @@ -1,4 +1,4 @@ -Menu="Utilities:85" +Menu="Tasks:95" Title="Varaverk" Icon="varaverk.png" --- diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/api/docker_action.php b/Plugin/usr/local/emhttp/plugins/varaverk/api/docker_action.php new file mode 100644 index 0000000..0cb47ca --- /dev/null +++ b/Plugin/usr/local/emhttp/plugins/varaverk/api/docker_action.php @@ -0,0 +1,21 @@ + false, 'error' => 'Invalid request']); + exit; +} + +// Confirm container exists +$check = trim(shell_exec('docker ps -a --filter ' . escapeshellarg('name=^' . $name . '$') . " --format '{{.Names}}' 2>/dev/null") ?? ''); +if ($check !== $name) { + echo json_encode(['ok' => false, 'error' => 'Container not found']); + exit; +} + +exec(($action === 'start' ? 'docker start' : 'docker stop') . ' ' . escapeshellarg($name) . ' 2>&1', $out, $rc); + +echo json_encode(['ok' => $rc === 0, 'output' => implode("\n", $out)]); diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/api/monitor.php b/Plugin/usr/local/emhttp/plugins/varaverk/api/monitor.php index 3c7afca..d832272 100644 --- a/Plugin/usr/local/emhttp/plugins/varaverk/api/monitor.php +++ b/Plugin/usr/local/emhttp/plugins/varaverk/api/monitor.php @@ -1,6 +1,8 @@ vv_system_info(), @@ -20,7 +22,9 @@ echo json_encode([ 'parity' => vv_parity_status(), 'storage' => vv_storage_pools(), 'array_disks' => vv_array_disks(), - 'scripts' => vv_scripts_status(), - 'thresholds' => vv_disk_thresholds(), - 'ts' => time(), + 'scripts' => vv_scripts_status(), + 'thresholds' => vv_disk_thresholds(), + 'vms' => vv_get_vms(), + 'docker_folders' => vv_get_docker_folders(), + 'ts' => time(), ]); diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/api/scheduler.php b/Plugin/usr/local/emhttp/plugins/varaverk/api/scheduler.php index 51159c1..d6bb8d8 100644 --- a/Plugin/usr/local/emhttp/plugins/varaverk/api/scheduler.php +++ b/Plugin/usr/local/emhttp/plugins/varaverk/api/scheduler.php @@ -2,6 +2,28 @@ header('Content-Type: application/json'); require_once dirname(__DIR__) . '/include/scheduler.php'; +// Batch save — all entries in one load/write/rebuild cycle +if (!empty($_POST['batch'])) { + $entries = json_decode($_POST['batch'], true) ?: []; + $clean = []; + foreach ($entries as $e) { + $id = trim($e['id'] ?? ''); + $cron = trim($e['cron'] ?? ''); + if (!$id) continue; + if ($cron && !in_array($cron, ['@array_start', '@array_stop'], true) + && !preg_match('/^(\S+\s+){4}\S+$/', $cron)) $cron = ''; + $clean[] = [ + 'id' => $id, + 'enabled' => ($e['enabled'] ?? '0') === '1', + 'cron' => $cron, + 'log_enabled' => ($e['log_enabled'] ?? '0') === '1', + ]; + } + $ok = vv_schedule_update_batch($clean); + echo json_encode(['ok' => $ok, 'error' => $ok ? null : 'Failed to write schedule']); + exit; +} + $id = trim($_POST['id'] ?? ''); $enabled = (bool)($_POST['enabled'] ?? false); $cron = trim($_POST['cron'] ?? ''); diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/api/snapshot.php b/Plugin/usr/local/emhttp/plugins/varaverk/api/snapshot.php new file mode 100644 index 0000000..85aebea --- /dev/null +++ b/Plugin/usr/local/emhttp/plugins/varaverk/api/snapshot.php @@ -0,0 +1,72 @@ + 0 ? max(0, min(100, (int)round((1 - $di / $dt) * 100))) : 0; + } +} + +// RAM% +$mem = []; +foreach (file('/proc/meminfo') ?: [] as $line) { + if (preg_match('/^(MemTotal|MemAvailable):\s+(\d+)/', $line, $m)) $mem[$m[1]] = (int)$m[2]; +} +$ramTotalMb = (int)(($mem['MemTotal'] ?? 0) / 1024); +$ramUsedMb = (int)((($mem['MemTotal'] ?? 0) - ($mem['MemAvailable'] ?? 0)) / 1024); +$ramPct = $ramTotalMb > 0 ? (int)round($ramUsedMb / $ramTotalMb * 100) : 0; + +// Fallback state (fast file read, no exec) +$fallbackState = 'UNKNOWN'; +foreach (@file('/tmp/fallback_state.db') ?: [] as $line) { + if (preg_match('/^state=(.+)/', trim($line), $m)) { $fallbackState = trim($m[1]); break; } +} + +// Partner +$partner = vv_partner_state(); +$peers = array_values(array_filter($partner['hosts'], fn($h) => !$h['is_me'])); + +// Media sessions — cached 30s so the HTTP calls don't hold up every snapshot poll +$streamCount = 0; +$transcodeCount = 0; +$mediaCacheFile = '/tmp/vv_snap_media.json'; +$cacheMaxAge = 30; +$cacheValid = file_exists($mediaCacheFile) && (time() - filemtime($mediaCacheFile)) < $cacheMaxAge; +if ($cacheValid) { + $cached = json_decode(file_get_contents($mediaCacheFile), true) ?: []; +} else { + $media = vv_media_sessions(); + $cached = [ + 'stream_count' => count($media['sessions']), + 'transcode_count' => count(array_filter($media['sessions'], fn($s) => !empty($s['is_tc']))), + ]; + file_put_contents($mediaCacheFile, json_encode($cached)); +} +$streamCount = (int)($cached['stream_count'] ?? 0); +$transcodeCount = (int)($cached['transcode_count'] ?? 0); + +echo json_encode([ + 'cpu_pct' => $cpuPct, + 'ram_pct' => $ramPct, + 'ram_used_mb' => $ramUsedMb, + 'ram_total_mb' => $ramTotalMb, + 'fallback' => $fallbackState, + 'partner_enabled' => $partner['enabled'], + 'peers' => $peers, + 'stream_count' => $streamCount, + 'transcode_count' => $transcodeCount, +]); diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/css/varaverk.css b/Plugin/usr/local/emhttp/plugins/varaverk/css/varaverk.css index 4a81fcb..44fa5e4 100644 --- a/Plugin/usr/local/emhttp/plugins/varaverk/css/varaverk.css +++ b/Plugin/usr/local/emhttp/plugins/varaverk/css/varaverk.css @@ -121,6 +121,17 @@ @media (max-width: 1024px) { #vv-monitor { grid-template-columns: repeat(4, 1fr) !important; } #vv-docker { grid-column: span 4 !important; } + /* Reset explicit placements so cards reflow in the 4-col grid */ + #vv-docker-folders { grid-column: span 4 !important; } + #vv-parity-card { grid-column: auto !important; } + #vv-storage-card { grid-column: auto !important; } + #vv-array-card { grid-column: auto !important; } +} + +/* Containers+VMs: single column when viewport is narrow */ +@media (max-width: 900px) { + .vv-df-cols { flex-direction: column; } + .vv-df-fname { flex: 0 1 auto; } } /* Phone layout — scheduler row/actions fixes + monitor single-column */ @@ -133,10 +144,30 @@ .vv-cron { flex: 1 1 80px; width: auto; min-width: 80px; } /* Slightly tighter label on narrow screens */ .vv-job-label { font-size: 14px; } + /* Footer buttons wrap instead of overflowing */ + .vv-sched-footer { flex-wrap: wrap; } + /* Log toolbar wraps so Stop/Clear stay inside the card */ + .vv-log-toolbar { flex-wrap: wrap; align-items: flex-start; } + .vv-log-toolbar > div { flex-wrap: wrap; } + /* Snapshot footer — smaller on mobile */ + .vv-snap-footer { gap: 10px !important; } + .vv-snap-item { gap: 4px; } + .vv-snap-label { font-size: 10px; } + .vv-snap-bar { width: 44px; height: 5px; } + .vv-snap-val { font-size: 11px; min-width: 26px; } + .vv-snap-div { font-size: 11px; } + .vv-snap-state { font-size: 11px; } + #vv-snap-partner { font-size: 11px; } + .vv-snap-media { font-size: 11px; } - /* Monitor single-column */ + /* CPU core bars — shrink gap and min-width so many cores don't overflow */ + .vv-cpu-cores { gap: 1px !important; } + .vv-cpu-core { min-width: 4px !important; } + + /* Monitor single-column — explicit placement cards need override too */ #vv-monitor { grid-template-columns: 1fr !important; } #vv-monitor > .vv-card { grid-column: 1 / -1 !important; } + #vv-docker-folders { grid-column: 1 / -1 !important; } } /* Shared footer (Save Schedule left, info right) — same min-height so log card ends level with script cards */ @@ -481,6 +512,8 @@ code.vv-unknown-var { color: #ff9800; background: #1f130d; } .vv-arrange-btn-active { background: #1a3a1e !important; color: #4caf50 !important; border-color: #2d5c33 !important; } .vv-arrange-save-btn { background: #1a3a1e; border-color: #2d5c33; color: #4caf50; } .vv-arrange-save-btn:hover { background: #22502a; } +#vv-arrange-btn { background: #7b1fa2; border-color: #7b1fa2; } +#vv-arrange-btn:hover { background: #4a148c; border-color: #4a148c; } /* ── Arrange workspace panel ─────────────────────────────────────────────── */ .vv-arrange-ws-hdr { font-size: 11px; font-weight: bold; color: #888; text-transform: uppercase; @@ -518,8 +551,53 @@ code.vv-unknown-var { color: #ff9800; background: #1f130d; } .vv-folder-children.vv-drop-target { background: rgba(76,175,80,0.07); border-radius: 4px; outline: 1px dashed #3a6a3e; } .vv-folder-new-row { display: flex; align-items: center; gap: 6px; padding: 4px 6px; } -.vv-new-folder-btn { background: #1c1e1c; border-color: #2e3a2e; color: #6a9e6a; } -.vv-new-folder-btn:hover { background: #222e22; } +.vv-new-folder-btn { background: #0277bd !important; border: none !important; color: #fff !important; } +.vv-new-folder-btn:hover { background: #01579b !important; } + +/* ── Snapshot footer (right panel status bar) ────────────────────────────── */ +.vv-snap-footer { display: flex !important; flex-direction: row !important; + align-items: center !important; justify-content: center; gap: 20px; flex-wrap: wrap; } +.vv-snap-item { display: flex; align-items: center; gap: 7px; } +.vv-snap-label { font-size: 12px; color: #555; text-transform: uppercase; letter-spacing: 0.05em; } +.vv-snap-bar { width: 90px; height: 8px; background: #222; border-radius: 4px; overflow: hidden; flex-shrink: 0; } +.vv-snap-bar span { display: block; height: 100%; border-radius: 4px; width: 0; + transition: width 0.5s, background-color 0.5s; } +.vv-snap-val { font-size: 15px; color: #aaa; font-family: monospace; min-width: 36px; } +.vv-snap-div { color: #333; font-size: 15px; } +.vv-snap-state { font-size: 15px; font-weight: bold; } +#vv-snap-partner { font-size: 15px; color: #666; } +.vv-snap-media { font-size: 15px; color: #666; } + +/* ── Containers and VMs card ──────────────────────────────────────────────── */ +.vv-df-section-hdr { font-size: 10px; font-weight: bold; color: #555; text-transform: uppercase; + letter-spacing: 0.08em; padding: 4px 2px 5px; border-bottom: 1px solid #222; + margin-bottom: 4px; } +.vv-df-empty { font-size: 12px; color: #555; font-style: italic; padding: 4px 2px 8px; } +.vv-df-vm-row { display: flex; align-items: center; gap: 8px; padding: 5px 2px; + border-bottom: 1px solid #1a1a1a; } +.vv-df-vm-row:last-of-type { border-bottom: none; } +.vv-df-vm-icon { font-size: 15px; line-height: 1; flex-shrink: 0; } +.vv-df-vm-meta { font-size: 10px; color: #555; } +.vv-df-cols { display: flex; gap: 10px; align-items: flex-start; } +.vv-df-col { flex: 1; min-width: 0; } +.vv-df-folder { border-bottom: 1px solid #1a1a1a; } +.vv-df-folder:last-child { border-bottom: none; } +.vv-df-folder-hdr { display: flex; align-items: center; gap: 6px; padding: 5px 4px; + cursor: pointer; user-select: none; border-radius: 3px; } +.vv-df-folder-hdr:hover { background: rgba(255,255,255,0.03); } +.vv-df-chevron { color: #555; font-size: 10px; width: 10px; flex-shrink: 0; } +.vv-df-fname { flex: 1; font-size: 12px; color: #aaa; font-weight: 500; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.vv-df-folder-body { padding-left: 10px; padding-bottom: 3px; } +.vv-df-container { display: flex; align-items: center; gap: 7px; padding: 3px 6px; + cursor: pointer; border-radius: 3px; user-select: none; } +.vv-df-container:hover { background: rgba(255,255,255,0.04); } +.vv-df-active { background: rgba(255,255,255,0.05) !important; outline: 1px solid #444; } +.vv-df-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; } +.vv-df-cname { flex: 1; font-size: 12px; color: #ccc; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.vv-df-status { font-size: 10px; color: #555; flex-shrink: 0; white-space: nowrap; } +.vv-df-actions { display: flex; gap: 6px; padding: 3px 6px 5px 24px; flex-wrap: wrap; } /* ── Rsync standalone controls ────────────────────────────────────────────── */ .vv-rsync-location { width: 120px; flex-shrink: 1; min-width: 60px; font-size: 11px; font-family: monospace; diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/icons/varaverk.png b/Plugin/usr/local/emhttp/plugins/varaverk/icons/varaverk.png new file mode 100644 index 0000000000000000000000000000000000000000..7b19c8ccaac3c7d2e16d075a61d954ceb44155bf GIT binary patch literal 103 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|oCO|{#S9F5M?jcysy3fAP*Bp- x#W6%/dev/null | awk '/src/{for(i=1;i<=NF;i++)if(\$i==\"src\")print \$(i+1)}'") ?? ''); + if (!$ip) $ip = gethostbyname(gethostname()); + return $ip; +} + +function vv_container_webui(string $name, array $portMap): string { + $template = '/boot/config/plugins/dockerMan/templates-user/my-' . $name . '.xml'; + if (!file_exists($template)) return ''; + + $xml = @file_get_contents($template) ?: ''; + if (!preg_match('/(.*?)<\/WebUI>/s', $xml, $m)) return ''; + + $url = trim($m[1]); + if (!$url) return ''; + + $url = str_replace('[IP]', vv_local_ip(), $url); + + // [PORT:XXXX] → mapped host port + $url = preg_replace_callback('/\[PORT:(\d+)\]/', function($pm) use ($name, $portMap) { + return $portMap[$name][$pm[1]] ?? $pm[1]; + }, $url); + + return $url; +} + +function vv_get_docker_folders(): array { + $folderFile = '/boot/config/plugins/folder.view3/docker.json'; + $folderData = file_exists($folderFile) + ? (json_decode(@file_get_contents($folderFile), true) ?: []) + : []; + + // One docker ps call: names, status, port mappings + $raw = shell_exec("docker ps -a --format '{{.Names}}\t{{.Status}}\t{{.Ports}}' 2>/dev/null") ?? ''; + $statusMap = []; + $portMap = []; + foreach (explode("\n", trim($raw)) as $line) { + $parts = explode("\t", $line, 3); + if (count($parts) < 2) continue; + [$cname, $status, $ports] = array_pad($parts, 3, ''); + $cname = trim($cname); + if ($cname === '') continue; + $statusMap[$cname] = trim($status); + foreach (explode(',', $ports) as $entry) { + if (preg_match('/(\d+)->(\d+)\/tcp/', trim($entry), $pm)) { + $portMap[$cname][$pm[2]] = $pm[1]; // containerPort => hostPort + } + } + } + + $folderContainerNames = []; + $folders = []; + + foreach ($folderData as $id => $f) { + $containers = []; + foreach ($f['containers'] ?? [] as $cname) { + $folderContainerNames[] = $cname; + $status = $statusMap[$cname] ?? ''; + $running = str_starts_with($status, 'Up'); + $containers[] = [ + 'name' => $cname, + 'running' => $running, + 'status' => $status, + 'webui' => vv_container_webui($cname, $portMap), + ]; + } + usort($containers, fn($a, $b) => $b['running'] <=> $a['running'] ?: strcmp($a['name'], $b['name'])); + + $folders[] = [ + 'id' => $id, + 'name' => $f['name'] ?? 'Unnamed', + 'icon' => $f['icon'] ?? '', + 'containers' => $containers, + ]; + } + usort($folders, fn($a, $b) => strcmp($a['name'], $b['name'])); + + $ungrouped = []; + foreach ($statusMap as $cname => $status) { + if (in_array($cname, $folderContainerNames, true)) continue; + $running = str_starts_with($status, 'Up'); + $ungrouped[] = [ + 'name' => $cname, + 'running' => $running, + 'status' => $status, + 'webui' => vv_container_webui($cname, $portMap), + ]; + } + usort($ungrouped, fn($a, $b) => strcmp($a['name'], $b['name'])); + + return [ + 'available' => true, + 'folders' => $folders, + 'ungrouped' => $ungrouped, + ]; +} diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/include/scheduler.php b/Plugin/usr/local/emhttp/plugins/varaverk/include/scheduler.php index 961f4cb..92af3e8 100644 --- a/Plugin/usr/local/emhttp/plugins/varaverk/include/scheduler.php +++ b/Plugin/usr/local/emhttp/plugins/varaverk/include/scheduler.php @@ -33,6 +33,23 @@ function vv_schedule_update(string $id, bool $enabled, string $cron, bool $log_e return vv_cron_rebuild($schedule); } +function vv_schedule_update_batch(array $entries): bool { + $schedule = vv_schedule_load(); + foreach ($entries as $e) { + $id = trim($e['id'] ?? ''); + if (!$id) continue; + $schedule[$id] = [ + 'id' => $id, + 'enabled' => (bool)($e['enabled'] ?? false), + 'cron' => trim($e['cron'] ?? ''), + 'log_enabled' => (bool)($e['log_enabled'] ?? false), + 'updated' => date('c'), + ]; + } + if (!vv_schedule_save($schedule)) return false; + return vv_cron_rebuild($schedule); +} + function vv_job_flags(string $id): string { $schedule = vv_schedule_load(); return !empty($schedule[$id]['log_enabled']) ? '--log' : ''; diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/include/vms.php b/Plugin/usr/local/emhttp/plugins/varaverk/include/vms.php new file mode 100644 index 0000000..94a772b --- /dev/null +++ b/Plugin/usr/local/emhttp/plugins/varaverk/include/vms.php @@ -0,0 +1,46 @@ + false, 'vms' => []]; + + exec('virsh list --all --name 2>/dev/null', $names, $rc); + if ($rc !== 0) return ['available' => false, 'vms' => []]; + + $vms = []; + foreach ($names as $raw) { + $name = trim($raw); + if ($name === '') continue; + + $state = trim(shell_exec('virsh domstate ' . escapeshellarg($name) . ' 2>/dev/null') ?? 'unknown'); + + $vcpus = null; + $memMb = null; + if ($state === 'running') { + $info = shell_exec('virsh dominfo ' . escapeshellarg($name) . ' 2>/dev/null') ?? ''; + if (preg_match('/CPU\(s\)\s*:\s*(\d+)/i', $info, $m)) $vcpus = (int)$m[1]; + if (preg_match('/Used memory\s*:\s*(\d+)/i', $info, $m)) $memMb = (int)round((int)$m[1] / 1024); + } + + // OS detection from libvirt XML + $os = 'linux'; + $xmlPath = '/etc/libvirt/qemu/' . $name . '.xml'; + if (file_exists($xmlPath)) { + $xml = @file_get_contents($xmlPath) ?: ''; + if (stripos($xml, 'windows') !== false || stripos($xml, 'win10') !== false || stripos($xml, 'win11') !== false) $os = 'windows'; + elseif (stripos($xml, 'darwin') !== false || stripos($xml, 'macos') !== false) $os = 'macos'; + } + $nl = strtolower($name); + if (str_contains($nl, 'win')) $os = 'windows'; + elseif (str_contains($nl, 'mac') || str_contains($nl, 'osx')) $os = 'macos'; + elseif (str_contains($nl, 'bsd') || str_contains($nl, 'freebsd')) $os = 'bsd'; + + $vms[] = [ + 'name' => $name, + 'state' => $state, + 'os' => $os, + 'vcpus' => $vcpus, + 'mem_mb' => $memMb, + ]; + } + + return ['available' => true, 'vms' => $vms]; +} diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/pages/monitor.php b/Plugin/usr/local/emhttp/plugins/varaverk/pages/monitor.php index 718545c..40c8b2c 100644 --- a/Plugin/usr/local/emhttp/plugins/varaverk/pages/monitor.php +++ b/Plugin/usr/local/emhttp/plugins/varaverk/pages/monitor.php @@ -2,11 +2,38 @@
+
Loading...
-
+
+

Power

+
Loading...
+
+ +
+

CPU

+
Loading...
+
+ +
+

Memory

+
Loading...
+
+ +
+

Network

+
Loading...
+
+ + +
+

Scripts

+
Loading...
+
+ +

Partner & Fallback @@ -33,47 +60,13 @@
Loading...

-
-

CPU

-
Loading...
+
+

Containers and VMs

+
Loading...
-
-

Memory

-
Loading...
-
- -
-

Network

-
Loading...
-
- -
-

Power

-
Loading...
-
- -
-

Parity

-
Loading...
-
- -
-

Pools

-
Loading...
-
- -
-

Array

-
Loading...
-
- -
-

Scripts

-
Loading...
-
- -
+ +

GPU

Loading...
@@ -88,14 +81,23 @@
Loading...
-
-

Containers

- - - -
NameStatusImage
Loading...
+ +
+

Parity

+
Loading...
+
+

Pools

+
Loading...
+
+ +
+

Array

+
Loading...
+
+ +