diff --git a/.gitignore b/.gitignore index 6081c90..dc866f2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ log *~ cert.pem key.pem +status diff --git a/config.go b/config.go index 5afccdc..f308af3 100644 --- a/config.go +++ b/config.go @@ -8,7 +8,8 @@ import ( ) type Config struct { - Locations []*Location `yaml:"locations" json:"locations"` + Locations []*Location `yaml:"locations" json:"locations"` + SharedNames []string `yaml:"shared_names,omitempty" json:"shared_names,omitempty"` } type Location struct { diff --git a/nodes.go b/nodes.go index 30bd6b1..d7d0282 100644 --- a/nodes.go +++ b/nodes.go @@ -113,6 +113,9 @@ func (n *Nodes) findOrMergeByName(target *Node, nodeName string) *Node { if nodeName == "" { return target } + if n.isSharedName(nodeName) { + return target + } found := n.nameIndex[nodeName] if found == nil { return target @@ -126,6 +129,18 @@ func (n *Nodes) findOrMergeByName(target *Node, nodeName string) *Node { return target } +func (n *Nodes) isSharedName(name string) bool { + if n.t.config == nil { + return false + } + for _, shared := range n.t.config.SharedNames { + if shared == name { + return true + } + } + return false +} + func (n *Nodes) createNode() *Node { node := &Node{ ID: newID("node"), diff --git a/snmp.go b/snmp.go index 0851da8..0bc6313 100644 --- a/snmp.go +++ b/snmp.go @@ -73,10 +73,14 @@ func snmpToInt(val interface{}) (int, bool) { switch v := val.(type) { case int: return v, true - case uint: + case int32: return int(v), true case int64: return int(v), true + case uint: + return int(v), true + case uint32: + return int(v), true case uint64: return int(v), true default: @@ -126,12 +130,33 @@ func (t *Tendrils) querySNMPDevice(node *Node, ip net.IP) { t.querySysName(snmp, node) t.queryInterfaceMACs(snmp, node, ifNames) - t.queryInterfaceStats(snmp, node, ifNames) + sysUpTime := t.getSysUpTime(snmp) + t.queryInterfaceStats(snmp, node, ifNames, sysUpTime) t.queryPoEBudget(snmp, node) t.queryBridgeMIB(snmp, node, ifNames) t.queryDHCPBindings(snmp) } +func (t *Tendrils) getSysUpTime(snmp *gosnmp.GoSNMP) uint64 { + oid := "1.3.6.1.2.1.1.3.0" + + result, err := snmp.Get([]string{oid}) + if err != nil { + return 0 + } + + if len(result.Variables) == 0 { + return 0 + } + + v, ok := snmpToInt(result.Variables[0].Value) + if !ok { + log.Printf("[ERROR] failed to parse sysUpTime: type=%T value=%v", result.Variables[0].Value, result.Variables[0].Value) + return 0 + } + return uint64(v) +} + func (t *Tendrils) querySysName(snmp *gosnmp.GoSNMP, node *Node) { oid := "1.3.6.1.2.1.1.5.0" @@ -195,8 +220,9 @@ func (t *Tendrils) queryInterfaceMACs(snmp *gosnmp.GoSNMP, node *Node, ifNames m } } -func (t *Tendrils) queryInterfaceStats(snmp *gosnmp.GoSNMP, node *Node, ifNames map[int]string) { +func (t *Tendrils) queryInterfaceStats(snmp *gosnmp.GoSNMP, node *Node, ifNames map[int]string, sysUpTime uint64) { ifOperStatus := t.getInterfaceTable(snmp, "1.3.6.1.2.1.2.2.1.8") + ifLastChange := t.getInterfaceTable(snmp, "1.3.6.1.2.1.2.2.1.9") ifHighSpeed := t.getInterfaceTable(snmp, "1.3.6.1.2.1.31.1.1.1.15") ifInErrors := t.getInterfaceTable(snmp, "1.3.6.1.2.1.2.2.1.14") ifOutErrors := t.getInterfaceTable(snmp, "1.3.6.1.2.1.2.2.1.20") @@ -236,6 +262,12 @@ func (t *Tendrils) queryInterfaceStats(snmp *gosnmp.GoSNMP, node *Node, ifNames stats.Speed = uint64(speed) * 1000000 } + if lastChange, ok := ifLastChange[ifIndex]; ok && sysUpTime > 0 { + if uint64(lastChange) <= sysUpTime { + stats.Uptime = (sysUpTime - uint64(lastChange)) / 100 + } + } + if inErr, ok := ifInErrors[ifIndex]; ok { stats.InErrors = uint64(inErr) } diff --git a/types.go b/types.go index 4130813..11b4f93 100644 --- a/types.go +++ b/types.go @@ -384,6 +384,7 @@ func (i *Interface) MarshalJSON() ([]byte, error) { type InterfaceStats struct { Speed uint64 `json:"speed,omitempty"` + Uptime uint64 `json:"uptime,omitempty"` InErrors uint64 `json:"in_errors,omitempty"` OutErrors uint64 `json:"out_errors,omitempty"` InPktsRate float64 `json:"in_pkts_rate,omitempty"` @@ -400,6 +401,7 @@ func round2(v float64) float64 { func (s *InterfaceStats) MarshalJSON() ([]byte, error) { type statsJSON struct { Speed uint64 `json:"speed,omitempty"` + Uptime uint64 `json:"uptime,omitempty"` InErrors uint64 `json:"in_errors,omitempty"` OutErrors uint64 `json:"out_errors,omitempty"` InPktsRate float64 `json:"in_pkts_rate,omitempty"` @@ -410,6 +412,7 @@ func (s *InterfaceStats) MarshalJSON() ([]byte, error) { } return json.Marshal(statsJSON{ Speed: s.Speed, + Uptime: s.Uptime, InErrors: s.InErrors, OutErrors: s.OutErrors, InPktsRate: round2(s.InPktsRate),