Preserve explicit filesystem disablement during deferred reads

Separate operator policy from retained numeric evidence. Apply a filesystem disable override without evaluating its old usage or treating absent deferred inventory as authoritative removal.

Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-10-04 21:41:32 +01:00
parent 5ce63f1400
commit 0fd7768e9d
3 changed files with 41 additions and 10 deletions
@@ -30,7 +30,8 @@ Existing incidents retain their last trusted value, occurrence, acknowledgement,
observation time and lifecycle/delivery evidence until a current observation
resumes evaluation. Successful current inventory still resolves removed
filesystems. Explicit disablement, suppression and stopped-guest behavior remain
effective independently of telemetry. Legacy unannotated memory and independent
effective independently of telemetry, including per-filesystem disable overrides
on retained identities. Legacy unannotated memory and independent
current PVE/Pulse-agent memory/disks keep their existing policy, even while QGA
is deferred; current CPU and I/O continue to evaluate. Producers own freshness;
no new age threshold, QGA request, payload, alert family or UI surface is added.
+17 -9
View File
@@ -300,11 +300,9 @@ func (m *Manager) CheckGuest(guest any, instanceName string) {
// Retained/failed filesystem evidence is useful to display, but neither
// its values nor an empty result can establish a breach, recovery or disk
// removal. CPU/I/O and independent current memory were evaluated above;
// explicit suppression/disablement and stopped-guest handling stay earlier.
if snapshot.DiskUnavailable {
return
}
// removal. CPU/I/O and independent current memory were evaluated above.
// Still apply explicit per-filesystem disablement to retained identities;
// this is operator policy, not recovery inferred from an old value.
if len(disks) > 0 {
seenDiskKeys := make(map[string]struct{})
@@ -342,6 +340,12 @@ func (m *Manager) CheckGuest(guest any, instanceName string) {
effectiveDiskThreshold = ensureHysteresisThreshold(diskOverride.Disk)
}
}
if snapshot.DiskUnavailable {
if effectiveDiskThreshold == nil || effectiveDiskThreshold.Trigger <= 0 {
m.clearAlert(canonicalMetricStateID(perDiskResourceID, "disk"))
}
continue
}
log.Debug().
Str("guest", name).
@@ -387,11 +391,15 @@ func (m *Manager) CheckGuest(guest any, instanceName string) {
MonitorOnly: monitorOnly,
})
}
if cleared := m.cleanupGuestDiskAlerts(guestID, seenDiskResources); cleared > 0 {
m.saveActiveAlertsAsync("guest-disk-set-changed")
if !snapshot.DiskUnavailable {
if cleared := m.cleanupGuestDiskAlerts(guestID, seenDiskResources); cleared > 0 {
m.saveActiveAlertsAsync("guest-disk-set-changed")
}
}
} else if !snapshot.DiskUnavailable {
if cleared := m.cleanupGuestDiskAlerts(guestID, nil); cleared > 0 {
m.saveActiveAlertsAsync("guest-disk-alerts-cleared")
}
} else if cleared := m.cleanupGuestDiskAlerts(guestID, nil); cleared > 0 {
m.saveActiveAlertsAsync("guest-disk-alerts-cleared")
}
}
+22
View File
@@ -63,6 +63,28 @@ func TestGuestSnapshotObservationAvailability(t *testing.T) {
}
}
func TestGuestAlertRetainedDiskStillHonoursExplicitOverride(t *testing.T) {
for _, override := range []ThresholdConfig{{Disabled: true}, {Disk: &HysteresisThreshold{Trigger: -1}}} {
m := guestObservationManager(t)
vm := guestObservationVM()
vm.Memory.Usage = 10
m.CheckGuest(vm, "site")
if len(m.GetActiveAlerts()) != 2 {
t.Fatal("expected two current filesystem incidents")
}
_, keySource, key := guestDiskIdentity(vm.Disks[0], 0)
cfg := m.GetConfig()
cfg.Overrides[guestDiskOverrideKey(vm, vm.ID, keySource)] = override
m.UpdateConfig(cfg)
vm.DiskStatusReason = "prev-vm-locked"
m.CheckGuest(vm, "site")
active := m.GetActiveAlerts()
if len(active) != 1 || active[0].ResourceID == vm.ID+"-disk-"+key {
t.Fatalf("explicit filesystem disablement was lost or crossed disk identity: %+v", active)
}
}
}
func TestGuestSnapshotUsesCanonicalProxmoxCPUPercent(t *testing.T) {
for _, tc := range []struct {
name string