mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-24 12:42:49 +00:00
chore(monitoring): record agent-lifecycle contract and disk-life proof
Follow-up to the physical-disk endurance precedence fix. Records the dependent agent-lifecycle contract section and moves the regression into the accepted physical-disk proof file so later per-commit canonical guards find both the contract and the verification artifact. Change-source: pulse-maintainer
This commit is contained in:
@@ -8105,3 +8105,19 @@ install token clears the block and heals to the base identity, while a token
|
||||
already bound to its own derived identity (an established distinct host sharing
|
||||
a hostname or machine ID, #1753) is left untouched. Focused proof lives in
|
||||
`internal/monitoring/monitor_host_agents_test.go`.
|
||||
|
||||
### Agent endurance evidence cannot raise a disk's remaining life
|
||||
|
||||
The physical-disk SMART merge treats a linked host agent's `PercentageUsed` as
|
||||
more precise than the Proxmox inventory, but it must not make a disk look
|
||||
healthier than Proxmox already reports. An intermittent NVMe endurance reading
|
||||
of `PercentageUsed` 0 previously raised the merged remaining life to 100, which
|
||||
resolved a genuine low-life disk-wearout alert and let it re-fire on the next
|
||||
physical-disk poll with a fresh resolved notification every poll interval
|
||||
(#2112). The merge now takes the agent value only when the Proxmox value is
|
||||
unreported or more pessimistic, never raising a reported remaining-life value,
|
||||
while still recording the agent's SMART attributes. This does not change agent
|
||||
admission, token binding or removal-block behaviour. Focused proof lives in
|
||||
`internal/monitoring/physical_disk_roundtrip_test.go`
|
||||
(`TestMergeHostAgentSMARTIntoDisks_AgentWearoutDoesNotHideLowPVELife` and
|
||||
`TestMergeHostAgentSMARTIntoDisks_AgentWearoutFillsUnreportedPVELife`).
|
||||
|
||||
@@ -4116,7 +4116,7 @@ now takes the agent value when the Proxmox value is unreported (`-1`) or when
|
||||
the agent value is lower (more pessimistic); it never raises a reported
|
||||
remaining-life value. `PercentageUsed` and the other merged SMART attributes are
|
||||
still recorded. Focused proof lives in
|
||||
`internal/monitoring/monitor_integration_test.go`
|
||||
`internal/monitoring/physical_disk_roundtrip_test.go`
|
||||
(`TestMergeHostAgentSMARTIntoDisks_AgentWearoutDoesNotHideLowPVELife` and
|
||||
`TestMergeHostAgentSMARTIntoDisks_AgentWearoutFillsUnreportedPVELife`). This is
|
||||
synthetic merge evidence, not reporter acceptance or hardware confirmation.
|
||||
|
||||
@@ -217,73 +217,6 @@ func TestMergeHostAgentSMARTIntoDisks_SMARTFailureAndWearoutOverrideCoarsePVEVal
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeHostAgentSMARTIntoDisks_AgentWearoutDoesNotHideLowPVELife(t *testing.T) {
|
||||
// A worn drive whose NVMe endurance log intermittently reads
|
||||
// PercentageUsed 0 must not have its Proxmox-reported remaining life
|
||||
// raised to 100. That resolved the low-life alert and let it re-fire on the
|
||||
// next poll, producing repeated notifications (#2112).
|
||||
disks := []models.PhysicalDisk{{
|
||||
ID: "d1",
|
||||
Node: "pve1",
|
||||
Serial: "SER1",
|
||||
Type: "nvme",
|
||||
Health: "PASSED",
|
||||
Wearout: 0,
|
||||
}}
|
||||
nodes := []models.Node{{Name: "pve1", LinkedAgentID: "host-1"}}
|
||||
used := 0
|
||||
hosts := []models.Host{{
|
||||
ID: "host-1",
|
||||
Sensors: models.HostSensorSummary{
|
||||
SMART: []models.HostDiskSMART{{
|
||||
Device: "/dev/nvme0n1",
|
||||
Serial: "SER1",
|
||||
Health: "PASSED",
|
||||
Attributes: &models.SMARTAttributes{
|
||||
PercentageUsed: &used,
|
||||
},
|
||||
}},
|
||||
},
|
||||
}}
|
||||
|
||||
result := mergeHostAgentSMARTIntoDisks(disks, nodes, hosts)
|
||||
if result[0].Wearout != 0 {
|
||||
t.Fatalf("agent endurance reading raised the remaining life of a worn disk: wearout=%d", result[0].Wearout)
|
||||
}
|
||||
if result[0].SmartAttributes == nil || result[0].SmartAttributes.PercentageUsed == nil || *result[0].SmartAttributes.PercentageUsed != 0 {
|
||||
t.Fatalf("agent SMART attributes were not merged: %+v", result[0].SmartAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeHostAgentSMARTIntoDisks_AgentWearoutFillsUnreportedPVELife(t *testing.T) {
|
||||
disks := []models.PhysicalDisk{{
|
||||
ID: "d1",
|
||||
Node: "pve1",
|
||||
Serial: "SER1",
|
||||
Type: "nvme",
|
||||
Wearout: -1,
|
||||
}}
|
||||
nodes := []models.Node{{Name: "pve1", LinkedAgentID: "host-1"}}
|
||||
used := 40
|
||||
hosts := []models.Host{{
|
||||
ID: "host-1",
|
||||
Sensors: models.HostSensorSummary{
|
||||
SMART: []models.HostDiskSMART{{
|
||||
Device: "/dev/nvme0n1",
|
||||
Serial: "SER1",
|
||||
Attributes: &models.SMARTAttributes{
|
||||
PercentageUsed: &used,
|
||||
},
|
||||
}},
|
||||
},
|
||||
}}
|
||||
|
||||
result := mergeHostAgentSMARTIntoDisks(disks, nodes, hosts)
|
||||
if result[0].Wearout != 60 {
|
||||
t.Fatalf("agent endurance did not fill unreported PVE life: wearout=%d", result[0].Wearout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeHostAgentSMARTIntoDisks_DoesNotReplaceFailureWithPassed(t *testing.T) {
|
||||
disks := []models.PhysicalDisk{{ID: "d1", Node: "pve1", Serial: "SER1", Health: "FAILED"}}
|
||||
nodes := []models.Node{{Name: "pve1", LinkedAgentID: "host-1"}}
|
||||
|
||||
@@ -120,3 +120,72 @@ func TestPhysicalDiskReadbackSourceIDFallback(t *testing.T) {
|
||||
t.Fatal("nil resource has a source ID")
|
||||
}
|
||||
}
|
||||
|
||||
// TestMergeHostAgentSMARTIntoDisks_AgentWearoutDoesNotHideLowPVELife pins the
|
||||
// #2112 regression: a worn drive whose NVMe endurance log intermittently reads
|
||||
// PercentageUsed 0 must not have its Proxmox-reported remaining life raised to
|
||||
// 100, which resolved the low-life alert and let it re-fire on the next poll.
|
||||
func TestMergeHostAgentSMARTIntoDisks_AgentWearoutDoesNotHideLowPVELife(t *testing.T) {
|
||||
disks := []models.PhysicalDisk{{
|
||||
ID: "d1",
|
||||
Node: "pve1",
|
||||
Serial: "SER1",
|
||||
Type: "nvme",
|
||||
Health: "PASSED",
|
||||
Wearout: 0,
|
||||
}}
|
||||
nodes := []models.Node{{Name: "pve1", LinkedAgentID: "host-1"}}
|
||||
used := 0
|
||||
hosts := []models.Host{{
|
||||
ID: "host-1",
|
||||
Sensors: models.HostSensorSummary{
|
||||
SMART: []models.HostDiskSMART{{
|
||||
Device: "/dev/nvme0n1",
|
||||
Serial: "SER1",
|
||||
Health: "PASSED",
|
||||
Attributes: &models.SMARTAttributes{
|
||||
PercentageUsed: &used,
|
||||
},
|
||||
}},
|
||||
},
|
||||
}}
|
||||
|
||||
result := mergeHostAgentSMARTIntoDisks(disks, nodes, hosts)
|
||||
if result[0].Wearout != 0 {
|
||||
t.Fatalf("agent endurance reading raised the remaining life of a worn disk: wearout=%d", result[0].Wearout)
|
||||
}
|
||||
if result[0].SmartAttributes == nil || result[0].SmartAttributes.PercentageUsed == nil || *result[0].SmartAttributes.PercentageUsed != 0 {
|
||||
t.Fatalf("agent SMART attributes were not merged: %+v", result[0].SmartAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMergeHostAgentSMARTIntoDisks_AgentWearoutFillsUnreportedPVELife confirms
|
||||
// the agent still supplies endurance when the Proxmox inventory reports none.
|
||||
func TestMergeHostAgentSMARTIntoDisks_AgentWearoutFillsUnreportedPVELife(t *testing.T) {
|
||||
disks := []models.PhysicalDisk{{
|
||||
ID: "d1",
|
||||
Node: "pve1",
|
||||
Serial: "SER1",
|
||||
Type: "nvme",
|
||||
Wearout: -1,
|
||||
}}
|
||||
nodes := []models.Node{{Name: "pve1", LinkedAgentID: "host-1"}}
|
||||
used := 40
|
||||
hosts := []models.Host{{
|
||||
ID: "host-1",
|
||||
Sensors: models.HostSensorSummary{
|
||||
SMART: []models.HostDiskSMART{{
|
||||
Device: "/dev/nvme0n1",
|
||||
Serial: "SER1",
|
||||
Attributes: &models.SMARTAttributes{
|
||||
PercentageUsed: &used,
|
||||
},
|
||||
}},
|
||||
},
|
||||
}}
|
||||
|
||||
result := mergeHostAgentSMARTIntoDisks(disks, nodes, hosts)
|
||||
if result[0].Wearout != 60 {
|
||||
t.Fatalf("agent endurance did not fill unreported PVE life: wearout=%d", result[0].Wearout)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user