v0.169.0: disk-health card + degradation notification (Lemezek állapota)
Consumes the agent v0.94.0 smart payload (MinAgent floor unchanged; feature-detect by presence). One pure verdict fn agentapi.DiskVerdictFor shared by the dashboard card and the 6h check. Card via a 60s /disks TTL cache (anti-smartctl-storm); unreachable agent -> Nincs adat, page never blocks. disk-health-check (6h) emits disk_health_degraded on a degradation only vs an in-memory baseline (first run silent, recovery/UNKNOWN never notify, multi-attr -> one event). No global banner (deliberate). Pairs with the hub allowlist bump. Tests: verdict table (>=90 red-proof), notifier emit, check first-run-silent (red-proof), degradation-once, recovery-silent, UNKNOWN-excluded, FAILING-critical, nil-smart card, TTL cache.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package agentapi
|
||||
|
||||
// DiskVerdict is the customer-facing disk-health verdict derived from a SmartSummary (v0.169.0).
|
||||
// It is the SHARED source of truth for both the "Lemezek állapota" dashboard card and the 6-hourly
|
||||
// degradation check — one pure function so the chip and the alert can never disagree.
|
||||
type DiskVerdict int
|
||||
|
||||
const (
|
||||
// DiskVerdictUnknown — no SMART data (nil / UNKNOWN / old agent). Renders "Nincs adat"; NEVER
|
||||
// alarms and NEVER participates in degradation transitions (excluded both directions).
|
||||
DiskVerdictUnknown DiskVerdict = iota
|
||||
DiskVerdictOK // "Rendben" — PASSED, all counters clean
|
||||
DiskVerdictWarn // "Figyelmeztetés" — PASSED but a wear/relocation counter is non-zero (or NVMe ≥90%)
|
||||
DiskVerdictFail // "Hiba" — FAILING
|
||||
)
|
||||
|
||||
// percentageUsedWarn is the NVMe wear threshold (inclusive) at which a still-PASSED disk warns.
|
||||
const percentageUsedWarn = 90
|
||||
|
||||
// DiskVerdictFor maps a SmartSummary to a verdict per the v0.169.0 rules:
|
||||
//
|
||||
// FAILING → Hiba
|
||||
// PASSED + any(reallocated>0, pending>0, offline_unc>0,
|
||||
// critical_warning>0, media_errors>0,
|
||||
// percentage_used >= 90) → Figyelmeztetés
|
||||
// PASSED otherwise → Rendben
|
||||
// nil / UNKNOWN / empty → Nincs adat
|
||||
func DiskVerdictFor(s *SmartSummary) DiskVerdict {
|
||||
if s == nil || s.Health == "" || s.Health == SmartUnknown {
|
||||
return DiskVerdictUnknown
|
||||
}
|
||||
if s.Health == SmartFailing {
|
||||
return DiskVerdictFail
|
||||
}
|
||||
// Health == PASSED (or any non-empty non-FAILING value we treat as passing): inspect the counters.
|
||||
if positive(s.ReallocatedSectors) || positive(s.PendingSectors) || positive(s.OfflineUncorrectable) ||
|
||||
positive(s.CriticalWarning) || positive(s.MediaErrors) || atLeast(s.PercentageUsed, percentageUsedWarn) {
|
||||
return DiskVerdictWarn
|
||||
}
|
||||
return DiskVerdictOK
|
||||
}
|
||||
|
||||
// Label is the exact Hungarian customer copy for the verdict (shared by the card chip and the email).
|
||||
func (v DiskVerdict) Label() string {
|
||||
switch v {
|
||||
case DiskVerdictOK:
|
||||
return "Rendben"
|
||||
case DiskVerdictWarn:
|
||||
return "Figyelmeztetés"
|
||||
case DiskVerdictFail:
|
||||
return "Hiba"
|
||||
default:
|
||||
return "Nincs adat"
|
||||
}
|
||||
}
|
||||
|
||||
// DegradedAttributes returns the human-readable Hungarian names of the attribute(s) that pushed a
|
||||
// PASSED disk to Figyelmeztetés (empty for OK/Fail/Unknown) — for the alert body. FAILING is a
|
||||
// whole-disk verdict with no single triggering counter, so it returns nil there.
|
||||
func DegradedAttributes(s *SmartSummary) []string {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
var out []string
|
||||
if positive(s.ReallocatedSectors) {
|
||||
out = append(out, "áthelyezett szektorok")
|
||||
}
|
||||
if positive(s.PendingSectors) {
|
||||
out = append(out, "függőben lévő szektorok")
|
||||
}
|
||||
if positive(s.OfflineUncorrectable) {
|
||||
out = append(out, "javíthatatlan szektorok")
|
||||
}
|
||||
if positive(s.CriticalWarning) {
|
||||
out = append(out, "kritikus figyelmeztetés")
|
||||
}
|
||||
if positive(s.MediaErrors) {
|
||||
out = append(out, "adathordozó-hibák")
|
||||
}
|
||||
if atLeast(s.PercentageUsed, percentageUsedWarn) {
|
||||
out = append(out, "elhasználódás")
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func positive(p *int) bool { return p != nil && *p > 0 }
|
||||
func atLeast(p *int, n int) bool { return p != nil && *p >= n }
|
||||
Reference in New Issue
Block a user