Let explicit disk temperature overrides beat per-type defaults

DiskTempByType unconditionally replaced the resolved host threshold, so a
host-level (or inherited linked-resource) diskTemperature override never
applied to nvme/sas/sata disks. Explicit overrides now win; the per-type
map still beats the global agent default.

Refs #1540
This commit is contained in:
rcourtman 2026-07-07 23:15:59 +01:00
parent f8e5642ae7
commit b6fd06c63d
2 changed files with 67 additions and 6 deletions

View file

@ -69,31 +69,42 @@ func hostInstanceName(host models.Host) string {
func (m *Manager) resolveHostThresholdsNoLock(hostID, linkedNodeID, linkedVMID, linkedContainerID string) ThresholdConfig {
base := m.defaultThresholdsForResourceType("agent")
if override, exists := m.hostThresholdOverrideNoLock(hostID, linkedNodeID, linkedVMID, linkedContainerID); exists {
return m.applyThresholdOverride(base, override)
}
return base
}
// hostThresholdOverrideNoLock returns the first override in the host-agent
// resolution chain (host, linked node, linked guest), mirroring
// resolveHostThresholdsNoLock's precedence. Callers must hold m.mu.
func (m *Manager) hostThresholdOverrideNoLock(hostID, linkedNodeID, linkedVMID, linkedContainerID string) (ThresholdConfig, bool) {
if hostID = strings.TrimSpace(hostID); hostID != "" {
if override, exists := m.config.Overrides[hostID]; exists {
return m.applyThresholdOverride(base, override)
return override, true
}
}
if linkedNodeID = strings.TrimSpace(linkedNodeID); linkedNodeID != "" {
if override, exists := m.config.Overrides[linkedNodeID]; exists {
return m.applyThresholdOverride(base, override)
return override, true
}
}
if linkedVMID = strings.TrimSpace(linkedVMID); linkedVMID != "" {
if override, exists := lookupGuestOverride(m.config.Overrides, nil, linkedVMID); exists {
return m.applyThresholdOverride(base, override)
return override, true
}
}
if linkedContainerID = strings.TrimSpace(linkedContainerID); linkedContainerID != "" {
if override, exists := lookupGuestOverride(m.config.Overrides, nil, linkedContainerID); exists {
return m.applyThresholdOverride(base, override)
return override, true
}
}
return base
return ThresholdConfig{}, false
}
// resolveHostAlertThresholdsNoLock resolves thresholds for persisted host-agent alerts.
@ -217,6 +228,12 @@ func (m *Manager) CheckHost(host models.Host) {
alertsEnabled := m.config.Enabled
disableAllAgents := m.config.DisableAllAgents
thresholds := m.resolveHostThresholdsNoLock(host.ID, host.LinkedNodeID, host.LinkedVMID, host.LinkedContainerID)
// An explicit disk temperature override (host or inherited linked-resource)
// beats the per-type defaults in DiskTempByType.
diskTempOverridden := false
if override, exists := m.hostThresholdOverrideNoLock(host.ID, host.LinkedNodeID, host.LinkedVMID, host.LinkedContainerID); exists && override.DiskTemperature != nil {
diskTempOverridden = true
}
m.mu.RUnlock()
if !alertsEnabled {
@ -318,7 +335,7 @@ func (m *Manager) CheckHost(host models.Host) {
for _, disk := range host.Sensors.SMART {
if disk.Temperature > 0 && !disk.Standby {
effectiveTempThreshold := thresholds.DiskTemperature
if diskType := strings.ToLower(strings.TrimSpace(disk.Type)); diskType != "" {
if diskType := strings.ToLower(strings.TrimSpace(disk.Type)); diskType != "" && !diskTempOverridden {
m.mu.RLock()
if th, ok := m.config.DiskTempByType[diskType]; ok {
t := th

View file

@ -314,3 +314,47 @@ func TestHostDiskTempPerTypeThresholdDoesNotOverrideDisabledGlobalDefault(t *tes
}
m.mu.RUnlock()
}
func TestHostDiskTempExplicitOverrideBeatsPerTypeThreshold(t *testing.T) {
m := configureDiskTempTypeHostManager(t)
hostID := "host-temp-override-nvme"
m.mu.Lock()
m.config.Overrides[hostID] = ThresholdConfig{
DiskTemperature: &HysteresisThreshold{Trigger: 60, Clear: 55},
}
m.mu.Unlock()
// 62C is below the nvme per-type trigger (70) but above the explicit
// override (60): the override must win and fire the alert.
hostAbove := hostWithSMARTDiskTemp(hostID, "nvme", 62)
m.CheckHost(hostAbove)
trackingKey := hostDiskTempAlertID(hostAbove)
if _, exists := testLookupActiveAlert(t, m, trackingKey); !exists {
t.Fatalf("expected alert for nvme disk at 62C with explicit override trigger 60, active: %v", alertKeys(m))
}
m.ClearActiveAlerts()
m.mu.Lock()
m.config.Overrides[hostID] = ThresholdConfig{
DiskTemperature: &HysteresisThreshold{Trigger: 80, Clear: 75},
}
m.mu.Unlock()
// 75C is above the nvme per-type trigger (70) but below the explicit
// override (80): the override must win and stay quiet.
hostBelow := hostWithSMARTDiskTemp(hostID, "nvme", 75)
m.CheckHost(hostBelow)
if _, exists := testLookupActiveAlert(t, m, trackingKey); exists {
t.Fatalf("expected no alert for nvme disk at 75C with explicit override trigger 80, active: %v", alertKeys(m))
}
m.mu.RLock()
if _, pending := m.pendingAlerts[trackingKey]; pending {
m.mu.RUnlock()
t.Fatalf("expected no pending alert for nvme disk at 75C with explicit override trigger 80, but pendingAlerts has %q", trackingKey)
}
m.mu.RUnlock()
}