mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 18:45:53 +00:00
893aa0b2cd
The reset added in b45bd66b9 only fired when the temperature SSH key
file on disk changed (mtime/size). An operator who repairs SSH access
any other way — fixing authorized_keys on the host, repairing
known_hosts, restoring network reachability — still waited out a
backoff window that may have compounded toward fifteen minutes.
A system-settings save is the natural operator touchpoint after such a
repair, so the settings handler now fans ResetSSHFailureBackoff out to
every live tenant monitor after a successful save, clearing the
per-host temperature SSH backoff and the knownhosts keyscan backoff.
The reset touches in-memory retry timing only; nothing is persisted
and no request field controls it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
177 lines
5.5 KiB
Go
177 lines
5.5 KiB
Go
package monitoring
|
|
|
|
import "time"
|
|
|
|
// Runtime-tunable polling cadence settings.
|
|
//
|
|
// Each tenant monitor polls against a detached DeepCopy of the base config,
|
|
// so mutating the base config after a system-settings save never reaches a
|
|
// running monitor (#1619). These setters push the saved values into live
|
|
// monitors directly, shadowing the config fields. The overrides live behind
|
|
// runtimePollingMu because polling goroutines read them every cycle while
|
|
// the settings API writes them.
|
|
|
|
// runtimePollingOverrides holds the polling-cadence values the settings API
|
|
// has pushed into this live monitor, shadowing the corresponding fields of
|
|
// the monitor's detached config copy. nil means "use the config value".
|
|
type runtimePollingOverrides struct {
|
|
backupEnabled *bool
|
|
backupInterval *time.Duration
|
|
pbsInterval *time.Duration
|
|
pmgInterval *time.Duration
|
|
}
|
|
|
|
// backupPollingEnabledSetting returns the effective EnableBackupPolling value.
|
|
func (m *Monitor) backupPollingEnabledSetting() bool {
|
|
if m == nil || m.config == nil {
|
|
return false
|
|
}
|
|
m.runtimePollingMu.RLock()
|
|
override := m.runtimePollingOverride.backupEnabled
|
|
m.runtimePollingMu.RUnlock()
|
|
if override != nil {
|
|
return *override
|
|
}
|
|
return m.config.EnableBackupPolling
|
|
}
|
|
|
|
// backupPollingIntervalSetting returns the effective BackupPollingInterval.
|
|
// Zero means cycle-based scheduling (BackupPollingCycles).
|
|
func (m *Monitor) backupPollingIntervalSetting() time.Duration {
|
|
if m == nil || m.config == nil {
|
|
return 0
|
|
}
|
|
m.runtimePollingMu.RLock()
|
|
override := m.runtimePollingOverride.backupInterval
|
|
m.runtimePollingMu.RUnlock()
|
|
if override != nil {
|
|
return *override
|
|
}
|
|
return m.config.BackupPollingInterval
|
|
}
|
|
|
|
// pbsPollingIntervalSetting returns the effective PBSPollingInterval before
|
|
// clamping.
|
|
func (m *Monitor) pbsPollingIntervalSetting() time.Duration {
|
|
if m == nil || m.config == nil {
|
|
return 0
|
|
}
|
|
m.runtimePollingMu.RLock()
|
|
override := m.runtimePollingOverride.pbsInterval
|
|
m.runtimePollingMu.RUnlock()
|
|
if override != nil {
|
|
return *override
|
|
}
|
|
return m.config.PBSPollingInterval
|
|
}
|
|
|
|
// pmgPollingIntervalSetting returns the effective PMGPollingInterval before
|
|
// clamping.
|
|
func (m *Monitor) pmgPollingIntervalSetting() time.Duration {
|
|
if m == nil || m.config == nil {
|
|
return 0
|
|
}
|
|
m.runtimePollingMu.RLock()
|
|
override := m.runtimePollingOverride.pmgInterval
|
|
m.runtimePollingMu.RUnlock()
|
|
if override != nil {
|
|
return *override
|
|
}
|
|
return m.config.PMGPollingInterval
|
|
}
|
|
|
|
// SetBackupPollingEnabled toggles backup polling on the live monitor.
|
|
// Re-enabling clears the per-instance last-poll timestamps so the next
|
|
// polling cycle runs an immediate catch-up poll, matching the behavior of
|
|
// a full monitor reload.
|
|
func (m *Monitor) SetBackupPollingEnabled(enabled bool) {
|
|
if m == nil {
|
|
return
|
|
}
|
|
m.runtimePollingMu.Lock()
|
|
wasEnabled := m.config != nil && m.config.EnableBackupPolling
|
|
if m.runtimePollingOverride.backupEnabled != nil {
|
|
wasEnabled = *m.runtimePollingOverride.backupEnabled
|
|
}
|
|
m.runtimePollingOverride.backupEnabled = &enabled
|
|
m.runtimePollingMu.Unlock()
|
|
|
|
if enabled && !wasEnabled {
|
|
m.resetBackupPollTimestamps()
|
|
}
|
|
}
|
|
|
|
// SetBackupPollingInterval updates the backup polling cadence on the live
|
|
// monitor. Lowering the interval (or switching from cycle-based scheduling
|
|
// to an interval) clears the per-instance last-poll timestamps so the next
|
|
// polling cycle runs an immediate catch-up poll rather than waiting out the
|
|
// remainder of the old interval.
|
|
func (m *Monitor) SetBackupPollingInterval(interval time.Duration) {
|
|
if m == nil {
|
|
return
|
|
}
|
|
if interval < 0 {
|
|
interval = 0
|
|
}
|
|
m.runtimePollingMu.Lock()
|
|
previous := time.Duration(0)
|
|
if m.config != nil {
|
|
previous = m.config.BackupPollingInterval
|
|
}
|
|
if m.runtimePollingOverride.backupInterval != nil {
|
|
previous = *m.runtimePollingOverride.backupInterval
|
|
}
|
|
m.runtimePollingOverride.backupInterval = &interval
|
|
m.runtimePollingMu.Unlock()
|
|
|
|
if interval > 0 && (previous <= 0 || interval < previous) {
|
|
m.resetBackupPollTimestamps()
|
|
}
|
|
}
|
|
|
|
// SetPBSPollingInterval updates the PBS polling cadence on the live monitor.
|
|
// The scheduler re-reads the base interval every cycle, so no further action
|
|
// is needed.
|
|
func (m *Monitor) SetPBSPollingInterval(interval time.Duration) {
|
|
if m == nil || interval <= 0 {
|
|
return
|
|
}
|
|
m.runtimePollingMu.Lock()
|
|
m.runtimePollingOverride.pbsInterval = &interval
|
|
m.runtimePollingMu.Unlock()
|
|
}
|
|
|
|
// SetPMGPollingInterval updates the PMG polling cadence on the live monitor.
|
|
// The scheduler re-reads the base interval every cycle, so no further action
|
|
// is needed.
|
|
func (m *Monitor) SetPMGPollingInterval(interval time.Duration) {
|
|
if m == nil || interval <= 0 {
|
|
return
|
|
}
|
|
m.runtimePollingMu.Lock()
|
|
m.runtimePollingOverride.pmgInterval = &interval
|
|
m.runtimePollingMu.Unlock()
|
|
}
|
|
|
|
// ResetSSHFailureBackoff clears the temperature collector's per-host SSH
|
|
// backoff and the knownhosts keyscan backoff. A system-settings save is an
|
|
// operator touchpoint that often follows repairing SSH access, and the
|
|
// on-disk key-change check only notices key file replacement, so the save
|
|
// itself also drops the backoff windows rather than making the operator wait
|
|
// one out (#1638).
|
|
func (m *Monitor) ResetSSHFailureBackoff() {
|
|
if m == nil {
|
|
return
|
|
}
|
|
m.tempCollector.ResetSSHFailures()
|
|
}
|
|
|
|
// resetBackupPollTimestamps clears the per-instance backup poll timestamps
|
|
// so the next cycle polls immediately.
|
|
func (m *Monitor) resetBackupPollTimestamps() {
|
|
m.mu.Lock()
|
|
m.lastPVEBackupPoll = make(map[string]time.Time)
|
|
m.lastPBSBackupPoll = make(map[string]time.Time)
|
|
m.mu.Unlock()
|
|
}
|