mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-24 04:33:17 +00:00
Keep native TrueNAS Thermals in local and persisted host History
Finish the connected #2077 outcome: persist an observed TrueNAS host temperature through the existing canonical writer, so local chart coverage cannot suppress the Thermals panel. Extend the pinned REST-to-chart control to cover the write, persistent readback and full local-window fast path; preserve source and absence gates. Change-source: pulse-maintainer Contract-Neutral: Restores existing TrueNAS Thermals history through its canonical writer; no agent lifecycle, schema, route, resource identity or alert-policy delta.
This commit is contained in:
@@ -319,6 +319,11 @@ assuming a missing member was idle. Other graphs remain independently usable.
|
||||
Native CPU temperature maps into the existing canonical host temperature and
|
||||
`temperature` History key, using the same package-versus-core outlier safeguard
|
||||
as current readings. Disk-temperature History uses the same envelope decoder.
|
||||
The canonical writer also records each positive finite TrueNAS host temperature
|
||||
in local and persistent `temperature` History, under the existing source/dedup
|
||||
gates. A full local CPU window must not make the existing Thermals panel depend
|
||||
on native fallback or lose its observed temperature. Missing temperature is not
|
||||
written as zero, and no additional sensor writes are added to other sources.
|
||||
No frontend route, resource identity, alert suppression policy, credential scope
|
||||
or recognised-legacy transport boundary changes.
|
||||
|
||||
@@ -333,7 +338,8 @@ shortened controls, not complete native windows. Extended
|
||||
`TestTrueNASPartialReportingPipeline` in
|
||||
`internal/monitoring/truenas_poller_test.go` crosses pinned-certificate HTTP,
|
||||
poller, canonical registry, writer, in-memory/persistent chart readbacks and
|
||||
native fallback, including temperature. Existing CORE isolation, recognised
|
||||
native fallback, including temperature and the sufficiently covered local
|
||||
chart fast path. Existing CORE isolation, recognised
|
||||
transport/auth/TLS and modern SCALE aggregation tests remain required. Source
|
||||
proofs do not substitute for containing-release qualification or installed CORE
|
||||
live/collapsed/expanded/History acceptance.
|
||||
|
||||
@@ -5539,6 +5539,18 @@ func (m *Monitor) syncUnifiedAgentMetrics(store ResourceStoreInterface, sinks ..
|
||||
metricKey := fmt.Sprintf("agent:%s", targetID)
|
||||
observedAt := unifiedResourceObservedAt(resource, now)
|
||||
|
||||
// Native TrueNAS temperature must also survive the local History path.
|
||||
// A sufficiently covered ring/store read need not query native fallback.
|
||||
if monitorHasSource(resource.Sources, unifiedresources.SourceTrueNAS) && resource.Temperature != nil {
|
||||
value := *resource.Temperature
|
||||
if value > 0 && !math.IsNaN(value) && !math.IsInf(value, 0) {
|
||||
if m.metricsHistory != nil {
|
||||
m.metricsHistory.AddGuestMetric(metricKey, "temperature", value, observedAt)
|
||||
}
|
||||
appendStoreWrite("agent", targetID, "temperature", value, observedAt)
|
||||
}
|
||||
}
|
||||
|
||||
if metric := resource.Metrics.CPU; metric != nil {
|
||||
value := metric.Percent
|
||||
if value == 0 {
|
||||
|
||||
@@ -2234,7 +2234,7 @@ func TestTrueNASPartialReportingPipeline(t *testing.T) {
|
||||
{"name":"cputemp","legend":["cputemp0","cputemp1"],"data":[[41,42]]},
|
||||
{"name":"interface","identifier":"nic-a","legend":["rx","tx","overlap"],"data":[[8,16,8]]},
|
||||
{"name":"disk","identifier":"disk-a","legend":["disk_octets_read","disk_octets_write"],"data":[[0,32]]}
|
||||
]`, 200, map[string]float64{"cpu": 10, "memory": 62.5, "netin": 8, "netout": 16, "diskread": 0, "diskwrite": 32}, "", true},
|
||||
]`, 200, map[string]float64{"cpu": 10, "memory": 62.5, "netin": 8, "netout": 16, "diskread": 0, "diskwrite": 32, "temperature": 42}, "", true},
|
||||
}
|
||||
var current atomic.Int64
|
||||
var nativeEnd atomic.Int64
|
||||
@@ -2389,7 +2389,7 @@ func TestTrueNASPartialReportingPipeline(t *testing.T) {
|
||||
persistent.WriteBatchSync(writes)
|
||||
inMemory := monitor.GetGuestMetrics("agent:"+instance.ID, time.Hour)
|
||||
chart := monitor.GetGuestMetricsForChart("agent:"+instance.ID, "agent", instance.ID, time.Hour)
|
||||
for _, key := range []string{"cpu", "memory", "disk", "netin", "netout", "diskread", "diskwrite"} {
|
||||
for _, key := range []string{"cpu", "memory", "disk", "netin", "netout", "diskread", "diskwrite", "temperature"} {
|
||||
if len(inMemory[key]) != counts[key] || len(chart[key]) != counts[key] {
|
||||
t.Errorf("cycle %d %s chart contains fabricated/lost points: memory=%d chart=%d want=%d", index, key, len(inMemory[key]), len(chart[key]), counts[key])
|
||||
}
|
||||
@@ -2402,9 +2402,6 @@ func TestTrueNASPartialReportingPipeline(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
wantNative := len(cycle.want)
|
||||
if cycle.native {
|
||||
wantNative++
|
||||
}
|
||||
if err != nil || id != instance.ID || len(native) != wantNative {
|
||||
t.Fatalf("cycle %d native History=%+v id=%q err=%v", index, native, id, err)
|
||||
}
|
||||
@@ -2430,4 +2427,14 @@ func TestTrueNASPartialReportingPipeline(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Once local CPU History covers the window, the chart's fast path must
|
||||
// still retain Thermals without relying on another appliance request.
|
||||
now := time.Now()
|
||||
for i := 0; i <= 60; i++ {
|
||||
monitor.metricsHistory.AddGuestMetric("agent:"+instance.ID, "cpu", 10, now.Add(time.Duration(i-60)*time.Minute))
|
||||
}
|
||||
chart := monitor.GetGuestMetricsForChart("agent:"+instance.ID, "agent", instance.ID, time.Hour)
|
||||
if points := chart["temperature"]; len(points) != 1 || points[0].Value != 42 {
|
||||
t.Fatalf("sufficiently covered local History lost Thermals: %+v", points)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user