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
64 lines
2.0 KiB
Go
64 lines
2.0 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/v10networks/nmon/internal/alertpolicy"
|
|
"github.com/v10networks/nmon/internal/store"
|
|
)
|
|
|
|
// recommendedRules is the starter alert set: reachability, port errors and the three resource limits. Interface
|
|
// down stays out on purpose (it is opt-in per port or group).
|
|
var recommendedRules = []string{"device_down", "device_warning", "interface_errors", "cpu_high", "memory_high", "disk_full"}
|
|
|
|
// recommendedName is the policy name for a rule.
|
|
func recommendedName(rule string) string { return "recommended: " + rule }
|
|
|
|
func (s *server) setupPolicies(ctx context.Context, _ *struct{ Body SetupPoliciesBody }) (*struct{ Body SetupPolicies }, error) {
|
|
a, err := s.setupAdmin()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !a.InstallMode() {
|
|
return nil, s.setupErr(ErrSetupClosed)
|
|
}
|
|
existing, err := s.Store.ListAlertPolicies(ctx)
|
|
if err != nil {
|
|
return nil, s.storeErr(err)
|
|
}
|
|
out := SetupPolicies{Created: []string{}, Existing: []string{}}
|
|
for _, rule := range recommendedRules {
|
|
name := recommendedName(rule)
|
|
if policyExists(existing, name, rule) {
|
|
out.Existing = append(out.Existing, name)
|
|
continue
|
|
}
|
|
row, err := s.policyRow(AlertPolicyBody{Name: name, Rule: rule, Action: alertpolicy.ActionAlert,
|
|
Scope: alertpolicy.Scope{All: true}}, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := s.Store.CreateAlertPolicy(ctx, &row); err != nil {
|
|
return nil, s.storeErr(err)
|
|
}
|
|
out.Created = append(out.Created, name)
|
|
}
|
|
if len(out.Created) > 0 {
|
|
s.AlertPolicy.Changed()
|
|
if err := s.Store.AppendAudit(ctx, actorOf(ctx), "setup.recommended-policies", "alert-policies", nil, out.Created); err != nil {
|
|
s.Logger.Warn("audit setup.recommended-policies", "err", err)
|
|
}
|
|
}
|
|
return &struct{ Body SetupPolicies }{out}, nil
|
|
}
|
|
|
|
// policyExists reports whether a policy has this name or already binds the rule.
|
|
func policyExists(all []store.AlertPolicy, name, rule string) bool {
|
|
for _, p := range all {
|
|
if p.Name == name || p.Rule == rule {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|