fleet-manager: chrony tracking metrics relative to the NTP source
The agent only reported chrony as synced|unsynced — nothing about how well the
clock tracks. Parse `chronyc tracking` for the source-relative metrics.
- proto: HealthSnapshot.chrony_offset_seconds (16, signed system-time offset,
fast=+), chrony_source (17, reference name/IP), chrony_stratum (18); regenerated.
- agent: replace chronyState() with chronyTracking()/parseChrony() — one exec,
parses Leap status + Reference ID + Stratum + System time offset (+ test).
- manager: Health.{ChronyOffset,ChronySource,ChronyStratum}; snapshotToHealth maps
them; influx writes chrony_offset_seconds/_stratum/_source and whitelists the
offset + stratum as graphable fields.
- UI: chronySummary() + offsetMs() — the chrony row now shows
"synced · 192.168.50.10 · s3 · +0.01ms" instead of just "synced".
This commit is contained in:
@@ -36,6 +36,27 @@ export function shortSerial(s) {
|
||||
return s ? s.slice(0, 8) : '—'
|
||||
}
|
||||
|
||||
// offsetMs formats a signed seconds offset as milliseconds ("+0.02ms" / "-1.3ms").
|
||||
export function offsetMs(sec) {
|
||||
if (sec === undefined || sec === null || Number.isNaN(sec)) return null
|
||||
const ms = sec * 1000
|
||||
const digits = Math.abs(ms) < 1 ? 3 : 2
|
||||
return (ms >= 0 ? '+' : '') + ms.toFixed(digits) + 'ms'
|
||||
}
|
||||
|
||||
// chronySummary renders the chrony health as "synced · <source> · s<stratum> ·
|
||||
// <offset>" — the sync state plus how it's tracking relative to the NTP source.
|
||||
export function chronySummary(h) {
|
||||
if (!h) return '—'
|
||||
const extra = []
|
||||
if (h.chrony_source) extra.push(h.chrony_source)
|
||||
if (h.chrony_stratum) extra.push('s' + h.chrony_stratum)
|
||||
const off = offsetMs(h.chrony_offset_seconds)
|
||||
if (off && h.chrony) extra.push(off)
|
||||
const state = h.chrony || '—'
|
||||
return extra.length ? `${state} · ${extra.join(' · ')}` : state
|
||||
}
|
||||
|
||||
// dur formats a span of SECONDS as a compact "3d 4h" / "5h 12m" / "45s".
|
||||
export function dur(s) {
|
||||
s = Math.max(0, Math.floor(s || 0))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import Panel from '../components/Panel.svelte'
|
||||
import StatePill from '../components/StatePill.svelte'
|
||||
import Chart from '../components/Chart.svelte'
|
||||
import { relTime, pct, num, healthState, shortSerial, dur, rebootStatus } from '../lib/fmt.js'
|
||||
import { relTime, pct, num, healthState, shortSerial, dur, rebootStatus, chronySummary } from '../lib/fmt.js'
|
||||
|
||||
let hosts = $state([])
|
||||
let error = $state('')
|
||||
@@ -103,7 +103,7 @@
|
||||
<dt class="text-faint">auth</dt><dd class="text-muted">{selected.health?.auth || '—'}</dd>
|
||||
<dt class="text-faint">nvme root</dt><dd class="text-muted">{selected.health?.nvme_root || '—'}</dd>
|
||||
<dt class="text-faint">selinux</dt><dd class="text-muted">{selected.health?.selinux || '—'}</dd>
|
||||
<dt class="text-faint">chrony</dt><dd class="text-muted">{selected.health?.chrony || '—'}</dd>
|
||||
<dt class="text-faint">chrony</dt><dd class="text-muted">{chronySummary(selected.health)}</dd>
|
||||
<dt class="text-faint">kiosk</dt><dd class="text-muted">{selected.health?.kiosk_active ? 'active' : 'idle'} · {selected.usage?.session_uptime || '—'}</dd>
|
||||
<dt class="text-faint">uptime</dt><dd class="text-muted">{selected.health?.uptime_seconds ? dur(selected.health.uptime_seconds) : '—'}</dd>
|
||||
{#if rebootStatus(selected)}
|
||||
|
||||
Reference in New Issue
Block a user