Move interface counter state from global map to Interface struct

This commit is contained in:
Ian Gulliver
2026-02-02 10:16:37 -08:00
parent 2a8e376cbf
commit d4e7a8a9b7
2 changed files with 23 additions and 41 deletions

57
snmp.go
View File

@@ -6,30 +6,11 @@ import (
"net" "net"
"regexp" "regexp"
"strings" "strings"
"sync"
"time" "time"
"github.com/gosnmp/gosnmp" "github.com/gosnmp/gosnmp"
) )
type ifaceCounters struct {
inPkts uint64
outPkts uint64
inBytes uint64
outBytes uint64
uptime uint64
timestamp time.Time
}
type counterTracker struct {
mu sync.Mutex
counters map[string]*ifaceCounters
}
var ifaceTracker = &counterTracker{
counters: map[string]*ifaceCounters{},
}
var portNameRewrites = []struct { var portNameRewrites = []struct {
re *regexp.Regexp re *regexp.Regexp
repl string repl string
@@ -289,21 +270,19 @@ func (t *Tendrils) queryInterfaceStats(snmp *gosnmp.GoSNMP, node *Node, ifNames
inPkts := ifHCInUcastPkts[ifIndex] + ifHCInMcastPkts[ifIndex] + ifHCInBcastPkts[ifIndex] inPkts := ifHCInUcastPkts[ifIndex] + ifHCInMcastPkts[ifIndex] + ifHCInBcastPkts[ifIndex]
outPkts := ifHCOutUcastPkts[ifIndex] + ifHCOutMcastPkts[ifIndex] + ifHCOutBcastPkts[ifIndex] outPkts := ifHCOutUcastPkts[ifIndex] + ifHCOutMcastPkts[ifIndex] + ifHCOutBcastPkts[ifIndex]
key := node.ID + ":" + name hasPrev := !iface.prevTimestamp.IsZero()
ifaceTracker.mu.Lock()
prev, hasPrev := ifaceTracker.counters[key]
if hasPrev { if hasPrev {
if prev.uptime > 0 && stats.Uptime > 0 && stats.Uptime < prev.uptime { if iface.prevUptime > 0 && stats.Uptime > 0 && stats.Uptime < iface.prevUptime {
log.Printf("[ERROR] port flap on %s %s: uptime dropped from %d to %d seconds", node.DisplayName(), name, prev.uptime, stats.Uptime) log.Printf("[ERROR] port flap on %s %s: uptime dropped from %d to %d seconds", node.DisplayName(), name, iface.prevUptime, stats.Uptime)
t.errors.AddPortFlap(node, name) t.errors.AddPortFlap(node, name)
} }
if hasInBytes && hasOutBytes { if hasInBytes && hasOutBytes {
elapsed := now.Sub(prev.timestamp).Seconds() elapsed := now.Sub(iface.prevTimestamp).Seconds()
if elapsed > 0 { if elapsed > 0 {
stats.InPktsRate = float64(inPkts-prev.inPkts) / elapsed stats.InPktsRate = float64(inPkts-iface.prevInPkts) / elapsed
stats.OutPktsRate = float64(outPkts-prev.outPkts) / elapsed stats.OutPktsRate = float64(outPkts-iface.prevOutPkts) / elapsed
stats.InBytesRate = float64(inBytes-prev.inBytes) / elapsed stats.InBytesRate = float64(inBytes-iface.prevInBytes) / elapsed
stats.OutBytesRate = float64(outBytes-prev.outBytes) / elapsed stats.OutBytesRate = float64(outBytes-iface.prevOutBytes) / elapsed
maxBytesRate := float64(stats.Speed) / 8 * 2 maxBytesRate := float64(stats.Speed) / 8 * 2
if stats.InBytesRate < 0 || stats.InBytesRate > maxBytesRate { if stats.InBytesRate < 0 || stats.InBytesRate > maxBytesRate {
@@ -317,19 +296,15 @@ func (t *Tendrils) queryInterfaceStats(snmp *gosnmp.GoSNMP, node *Node, ifNames
} }
} }
} }
storedUptime := stats.Uptime
if storedUptime == 0 && hasPrev { iface.prevInPkts = inPkts
storedUptime = prev.uptime iface.prevOutPkts = outPkts
iface.prevInBytes = inBytes
iface.prevOutBytes = outBytes
if stats.Uptime > 0 {
iface.prevUptime = stats.Uptime
} }
ifaceTracker.counters[key] = &ifaceCounters{ iface.prevTimestamp = now
inPkts: inPkts,
outPkts: outPkts,
inBytes: inBytes,
outBytes: outBytes,
uptime: storedUptime,
timestamp: now,
}
ifaceTracker.mu.Unlock()
if poe, ok := poeStats[name]; ok { if poe, ok := poeStats[name]; ok {
stats.PoE = poe stats.PoE = poe

View File

@@ -362,6 +362,13 @@ type Interface struct {
IPs IPSet `json:"ips,omitempty"` IPs IPSet `json:"ips,omitempty"`
Up bool `json:"up,omitempty"` Up bool `json:"up,omitempty"`
Stats *InterfaceStats `json:"stats,omitempty"` Stats *InterfaceStats `json:"stats,omitempty"`
prevInPkts uint64
prevOutPkts uint64
prevInBytes uint64
prevOutBytes uint64
prevUptime uint64
prevTimestamp time.Time
} }
func (i *Interface) MarshalJSON() ([]byte, error) { func (i *Interface) MarshalJSON() ([]byte, error) {