From b2c0dc7a087c34e559cef743954c223d448d251c Mon Sep 17 00:00:00 2001 From: Jason Staack Date: Sat, 21 Mar 2026 19:34:23 -0500 Subject: [PATCH] feat(18-05): wire SNMPCollector into Scheduler and main.go - Add RegisterCollector method to Scheduler for external collector registration - Initialize ProfileCache with 5-min refresh in main.go - Initialize CounterCache using existing Redis client - Create and register SNMPCollector as "snmp" in scheduler - Start DiscoveryResponder for NATS SNMP device auto-detection - Existing RouterOS polling path completely unaffected Co-Authored-By: Claude Opus 4.6 (1M context) --- poller/cmd/poller/main.go | 28 ++++++++++++++++++++++++++++ poller/internal/poller/scheduler.go | 9 ++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/poller/cmd/poller/main.go b/poller/cmd/poller/main.go index ae36dd0..cd696f4 100644 --- a/poller/cmd/poller/main.go +++ b/poller/cmd/poller/main.go @@ -22,6 +22,7 @@ import ( "github.com/staack/the-other-dude/poller/internal/config" "github.com/staack/the-other-dude/poller/internal/observability" "github.com/staack/the-other-dude/poller/internal/poller" + "github.com/staack/the-other-dude/poller/internal/snmp" "github.com/staack/the-other-dude/poller/internal/sshrelay" "github.com/staack/the-other-dude/poller/internal/store" "github.com/staack/the-other-dude/poller/internal/tunnel" @@ -267,6 +268,33 @@ func main() { maxBackoff, ) + // ----------------------------------------------------------------------- + // Initialize SNMP components (profile cache, counter cache, collector) + // ----------------------------------------------------------------------- + profileCache := snmp.NewProfileCache(deviceStore.Pool(), 5*time.Minute) + if err := profileCache.Load(ctx); err != nil { + slog.Warn("initial SNMP profile cache load failed (SNMP polling will start after first refresh)", "error", err) + } + go profileCache.StartRefresh(ctx) + + counterCache := snmp.NewCounterCache(redisClient) + + snmpCollector := snmp.NewSNMPCollector(profileCache, credentialCache, counterCache, snmp.DefaultSNMPConfig()) + scheduler.RegisterCollector("snmp", snmpCollector) + + slog.Info("SNMP collector registered", + "profile_refresh_interval", "5m", + ) + + // ----------------------------------------------------------------------- + // Initialize SNMP discovery responder (NATS request-reply) + // ----------------------------------------------------------------------- + discoveryResponder := bus.NewDiscoveryResponder(publisher.Conn()) + if err := discoveryResponder.Start(); err != nil { + slog.Error("failed to start SNMP discovery responder", "error", err) + } + defer discoveryResponder.Stop() + slog.Info("starting device scheduler", "poll_interval", pollInterval, "refresh_period", refreshPeriod, diff --git a/poller/internal/poller/scheduler.go b/poller/internal/poller/scheduler.go index 38edaf0..5356688 100644 --- a/poller/internal/poller/scheduler.go +++ b/poller/internal/poller/scheduler.go @@ -87,12 +87,19 @@ func NewScheduler( activeDevices: make(map[string]*deviceState), } - // Register built-in collectors. Future device types (SNMP) register here. + // Register built-in collectors. s.collectors["routeros"] = NewRouterOSCollector(locker, credentialCache, connTimeout, cmdTimeout, lockTTL) return s } +// RegisterCollector adds a named Collector to the scheduler's dispatch map. +// This allows external packages (e.g., SNMP) to register collectors without +// modifying NewScheduler's parameter list. +func (s *Scheduler) RegisterCollector(name string, c Collector) { + s.collectors[name] = c +} + // Run is the main scheduler loop. It: // 1. Fetches devices from the database. // 2. Starts goroutines for newly-discovered devices.