/* * SysDeck - Fleet Compute Panel (v0.0.10) * Author: Jeremy Anderson (https://dcos.net) * * Subscribes to the bridge metrics tap for live CPU + memory samples. * The tap is a cockpit.metrics channel that emits derive samples at * the bridge's update interval; no polling required. * * Also surfaces the local host uptime via `uptime` and peer-host * guidance via the cockpit multi-host dashboard contract. */ export async function mount(panel, { bridge, EventBus }) { panel.innerHTML = renderSkeleton(); const [uptime] = await Promise.allSettled([bridge.fleet.uptime()]); panel.innerHTML = `

Fleet Compute

Local host (fleet-of-one)

Uptime

${escapeHtml(uptime.value ?? 'unavailable')}

Live CPU / Memory

CPU user —
Memory used —

Peer Hosts

Multi-host dashboard not enabled. Configure /etc/cockpit/machines.d/ to surface peer hosts.
`; // Subscribe to the live metrics tap. The unsubscribe function is // stashed on the panel so a re-mount can clean up. if (panel._unsubscribeMetrics) panel._unsubscribeMetrics(); try { panel._unsubscribeMetrics = bridge.fleet.subscribeLoadAvg((samples) => { if (!Array.isArray(samples)) return; const [cpuUser, _cpuSys, memUsed, memTotal] = samples; if (typeof cpuUser === 'number') { const cpuEl = panel.querySelector('#fleet-cpu-user'); const cpuBar = panel.querySelector('#fleet-cpu-bar'); if (cpuEl) cpuEl.textContent = `${cpuUser.toFixed(1)}%`; if (cpuBar) cpuBar.style.width = `${Math.min(cpuUser, 100)}%`; } if (typeof memUsed === 'number' && typeof memTotal === 'number' && memTotal > 0) { const pct = (memUsed / memTotal) * 100; const memEl = panel.querySelector('#fleet-mem-used'); const memBar = panel.querySelector('#fleet-mem-bar'); if (memEl) memEl.textContent = `${formatBytes(memUsed)} / ${formatBytes(memTotal)}`; if (memBar) { memBar.style.width = `${pct.toFixed(1)}%`; memBar.classList.toggle('warn', pct > 75); memBar.classList.toggle('danger', pct > 90); } } }); } catch { // Metrics channel unavailable — bars stay at 0%. } EventBus.emit('fleet.loaded'); } function formatBytes(bytes) { const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB']; let value = bytes; let unit = 0; while (value >= 1024 && unit < units.length - 1) { value /= 1024; unit += 1; } return `${value.toFixed(1)} ${units[unit]}`; } function escapeHtml(s) { return String(s).replace(/[&<>]/g, (c) => ({ '&': '&', '<': '<', '>': '>' }[c])); } function renderSkeleton() { return `
`; }