Make nmon easier to install and run without editing YAML by hand: - Settings: point-and-click forms over config.yaml (GET/PUT /api/v1/settings/form); the YAML editor moves to /settings/yaml. - First install: with no sign-in configured nmon starts in install mode, serves only the setup wizard (sign-in, basics, devices, alerting) and closes it for good once sign-in is saved. A self-signed certificate is generated when no TLS source is given. poller.interval becomes a config setting. - Reverse proxies: opt-in --http-listen plain-HTTP listener beside HTTPS; sign-in stays required. - Webhooks: Generic, Teams Adaptive Card, Slack and Discord presets, template preview and test send. - Help: the operations, coverage and profile docs served at /help. - Polling: per-device "Poll now", a dry-run test poll returning an SNMP trace, and time-limited in-memory SNMP debug recording, in the UI and as `nmon device poll`. - Ansible: deploys into install mode when no sign-in is set, and no longer overwrites the config.yaml the wizard wrote. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017TuKgyKve2XLFtrrXWMi2w
54 lines
1.9 KiB
Go
54 lines
1.9 KiB
Go
package notify
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
"time"
|
|
)
|
|
|
|
// States lists every Notification state a template can see, in the order the UI offers them.
|
|
func States() []string {
|
|
return []string{StateFiring, StateResolved, StateFlapping, StateStale, StateSuppressedSummary}
|
|
}
|
|
|
|
// ValidState reports whether s is a Notification state.
|
|
func ValidState(s string) bool { return slices.Contains(States(), s) }
|
|
|
|
// SampleNotification is a realistic notification in the given state for previews and test sends. It never
|
|
// reads the clock: now is the "cleared" instant and the fired instant is 12 minutes earlier.
|
|
func SampleNotification(state string, now time.Time) Notification {
|
|
fired := now.Add(-12 * time.Minute).UTC()
|
|
var n Notification
|
|
n.AlertID = 4242
|
|
n.IncidentID = IncidentID(n.AlertID, fired)
|
|
n.Rule = "interface_down"
|
|
n.State = state
|
|
n.Severity = "critical"
|
|
n.Device.Slug, n.Device.Name = "core-sw1", "Core switch 1"
|
|
n.Interface.ID, n.Interface.Name, n.Interface.Alias = "01J9ZXAMPLE0000000000000", "ge-0/0/1", "Uplink to Edge-A"
|
|
n.Summary = "Interface ge-0/0/1 on core-sw1 is down"
|
|
n.Description = "Operational status has been down for 3 consecutive polls."
|
|
n.FiredAt = fired
|
|
n.Labels = map[string]string{"site": "dc1", "role": "core"}
|
|
n.URL = "https://nmon.example.net/devices/core-sw1"
|
|
switch state {
|
|
case StateResolved:
|
|
c := now.UTC()
|
|
n.ClearedAt = &c
|
|
n.Summary = "Interface ge-0/0/1 on core-sw1 recovered"
|
|
n.Severity = "info"
|
|
case StateFlapping:
|
|
n.Severity = "warning"
|
|
n.Summary = "Interface ge-0/0/1 on core-sw1 is flapping"
|
|
case StateStale:
|
|
n.Severity = "warning"
|
|
n.Summary = "No data from core-sw1 for 15 minutes"
|
|
case StateSuppressedSummary:
|
|
n.Interface.ID, n.Interface.Name, n.Interface.Alias = "", "", ""
|
|
n.Parents = []string{"edge-a"}
|
|
n.SuppressedChildren = 12
|
|
n.Summary = fmt.Sprintf("%d alerts suppressed behind edge-a", n.SuppressedChildren)
|
|
}
|
|
return n
|
|
}
|