@cryptotaxi247 / netdata-1 / commits / 27fc3ddbc

go.d snmp: add sys descr, contact and loc as host labels for vnode (#18527)

Ilya Mashchenko committed Sep 11, 2024 at 16:03 UTC 27fc3ddbc5bdcd02dfb69be621a6f763d5319400
7 files changed +414 -331
src/go/plugin/go.d/modules/snmp/charts.go
+2 -2
@@ -162,7 +162,7 @@ func (s *SNMP) addNetIfaceCharts(iface *netInterface) {
162 for _, chart := range *charts {
163 chart.ID = fmt.Sprintf(chart.ID, cleanIfaceName(iface.ifName))
164 chart.Labels = []module.Label{
165 - {Key: "sysName", Value: s.sysName},
165 + {Key: "sysName", Value: s.sysInfo.name},
166 {Key: "ifDescr", Value: iface.ifDescr},
167 {Key: "ifName", Value: iface.ifName},
168 {Key: "ifType", Value: ifTypeMapping[iface.ifType]},
@@ -190,7 +190,7 @@ func (s *SNMP) removeNetIfaceCharts(iface *netInterface) {
190 func (s *SNMP) addSysUptimeChart() {
191 chart := uptimeChart.Copy()
192 chart.Labels = []module.Label{
193 - {Key: "sysName", Value: s.sysName},
193 + {Key: "sysName", Value: s.sysInfo.name},
194 }
195 if err := s.Charts().Add(chart); err != nil {
196 s.Warning(err)
src/go/plugin/go.d/modules/snmp/collect.go
+28 -320
@@ -3,37 +3,27 @@
3 package snmp
4
5 import (
6 - "errors"
6 "fmt"
8 - "log/slog"
9 - "sort"
7 "strings"
8
12 - "github.com/netdata/netdata/go/plugins/logger"
9 "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/vnodes"
10
11 "github.com/google/uuid"
12 "github.com/gosnmp/gosnmp"
13 )
14
19 -const (
20 - oidSysUptime = "1.3.6.1.2.1.1.3.0"
21 - oidSysName = "1.3.6.1.2.1.1.5.0"
22 - rootOidIfMibIfTable = "1.3.6.1.2.1.2.2"
23 - rootOidIfMibIfXTable = "1.3.6.1.2.1.31.1.1"
24 -)
25 -
15 func (s *SNMP) collect() (map[string]int64, error) {
27 - if s.sysName == "" {
28 - sysName, err := s.getSysName()
16 + if s.sysInfo == nil {
17 + si, err := s.getSysInfo()
18 if err != nil {
19 return nil, err
20 }
32 - s.sysName = sysName
21 +
22 + s.sysInfo = si
23 s.addSysUptimeChart()
24
25 if s.CreateVnode {
36 - s.vnode = s.setupVnode(sysName)
26 + s.vnode = s.setupVnode(si)
27 }
28 }
29
@@ -49,7 +39,7 @@ func (s *SNMP) collect() (map[string]int64, error) {
39 }
40 }
41
52 - if len(s.oids) > 0 {
42 + if len(s.customOids) > 0 {
43 if err := s.collectOIDs(mx); err != nil {
44 return nil, err
45 }
@@ -58,285 +48,41 @@ func (s *SNMP) collect() (map[string]int64, error) {
48 return mx, nil
49 }
50
61 -func (s *SNMP) setupVnode(sysName string) *vnodes.VirtualNode {
62 - if s.Vnode.GUID == "" {
63 - s.Vnode.GUID = uuid.NewSHA1(uuid.NameSpaceDNS, []byte(s.Hostname)).String()
64 - }
65 - if s.Vnode.Hostname == "" {
66 - s.Vnode.Hostname = fmt.Sprintf("%s(%s)", sysName, s.Hostname)
67 - }
68 - return &vnodes.VirtualNode{
69 - GUID: s.Vnode.GUID,
70 - Hostname: s.Vnode.Hostname,
71 - Labels: s.Vnode.Labels,
72 - }
73 -}
74 -
75 -func (s *SNMP) getSysName() (string, error) {
76 - resp, err := s.snmpClient.Get([]string{oidSysName})
77 - if err != nil {
78 - return "", err
79 - }
80 - if len(resp.Variables) == 0 {
81 - return "", errors.New("no system name")
82 - }
83 - return pduToString(resp.Variables[0])
84 -}
85 -
86 -func (s *SNMP) collectSysUptime(mx map[string]int64) error {
87 - resp, err := s.snmpClient.Get([]string{oidSysUptime})
88 - if err != nil {
89 - return err
90 - }
91 - if len(resp.Variables) == 0 {
92 - return errors.New("no system uptime")
93 - }
94 - v, err := pduToInt(resp.Variables[0])
95 - if err != nil {
96 - return err
51 +func (s *SNMP) walkAll(rootOid string) ([]gosnmp.SnmpPDU, error) {
52 + if s.snmpClient.Version() == gosnmp.Version1 {
53 + return s.snmpClient.WalkAll(rootOid)
54 }
98 -
99 - mx["uptime"] = v / 100 // the time is in hundredths of a second
100 -
101 - return nil
55 + return s.snmpClient.BulkWalkAll(rootOid)
56 }
57
104 -func (s *SNMP) collectNetworkInterfaces(mx map[string]int64) error {
105 - if s.checkMaxReps {
106 - ok, err := s.adjustMaxRepetitions()
107 - if err != nil {
108 - return err
109 - }
110 -
111 - s.checkMaxReps = false
112 -
113 - if !ok {
114 - s.collectIfMib = false
115 -
116 - if len(s.oids) == 0 {
117 - return errors.New("no IF-MIB data returned")
118 - }
119 -
120 - s.Warning("no IF-MIB data returned")
121 - return nil
122 - }
123 - }
124 -
125 - ifMibTable, err := s.walkAll(rootOidIfMibIfTable)
126 - if err != nil {
127 - return err
128 - }
129 -
130 - ifMibXTable, err := s.walkAll(rootOidIfMibIfXTable)
131 - if err != nil {
132 - return err
133 - }
134 -
135 - if len(ifMibTable) == 0 && len(ifMibXTable) == 0 {
136 - s.Warning("no IF-MIB data returned")
137 - s.collectIfMib = false
138 - return nil
58 +func (s *SNMP) setupVnode(si *sysInfo) *vnodes.VirtualNode {
59 + if s.Vnode.GUID == "" {
60 + s.Vnode.GUID = uuid.NewSHA1(uuid.NameSpaceDNS, []byte(s.Hostname)).String()
61 }
140 -
141 - for _, i := range s.netInterfaces {
142 - i.updated = false
62 + if s.Vnode.Hostname == "" {
63 + s.Vnode.Hostname = fmt.Sprintf("%s(%s)", si.name, s.Hostname)
64 }
65
145 - pdus := make([]gosnmp.SnmpPDU, 0, len(ifMibTable)+len(ifMibXTable))
146 - pdus = append(pdus, ifMibTable...)
147 - pdus = append(pdus, ifMibXTable...)
148 -
149 - for _, pdu := range pdus {
150 - i := strings.LastIndexByte(pdu.Name, '.')
151 - if i == -1 {
152 - continue
153 - }
154 -
155 - idx := pdu.Name[i+1:]
156 - oid := strings.TrimPrefix(pdu.Name[:i], ".")
157 -
158 - iface, ok := s.netInterfaces[idx]
159 - if !ok {
160 - iface = &netInterface{idx: idx}
161 - }
162 -
163 - switch oid {
164 - case oidIfIndex:
165 - iface.ifIndex, err = pduToInt(pdu)
166 - case oidIfDescr:
167 - iface.ifDescr, err = pduToString(pdu)
168 - case oidIfType:
169 - iface.ifType, err = pduToInt(pdu)
170 - case oidIfMtu:
171 - iface.ifMtu, err = pduToInt(pdu)
172 - case oidIfSpeed:
173 - iface.ifSpeed, err = pduToInt(pdu)
174 - case oidIfAdminStatus:
175 - iface.ifAdminStatus, err = pduToInt(pdu)
176 - case oidIfOperStatus:
177 - iface.ifOperStatus, err = pduToInt(pdu)
178 - case oidIfInOctets:
179 - iface.ifInOctets, err = pduToInt(pdu)
180 - case oidIfInUcastPkts:
181 - iface.ifInUcastPkts, err = pduToInt(pdu)
182 - case oidIfInNUcastPkts:
183 - iface.ifInNUcastPkts, err = pduToInt(pdu)
184 - case oidIfInDiscards:
185 - iface.ifInDiscards, err = pduToInt(pdu)
186 - case oidIfInErrors:
187 - iface.ifInErrors, err = pduToInt(pdu)
188 - case oidIfInUnknownProtos:
189 - iface.ifInUnknownProtos, err = pduToInt(pdu)
190 - case oidIfOutOctets:
191 - iface.ifOutOctets, err = pduToInt(pdu)
192 - case oidIfOutUcastPkts:
193 - iface.ifOutUcastPkts, err = pduToInt(pdu)
194 - case oidIfOutNUcastPkts:
195 - iface.ifOutNUcastPkts, err = pduToInt(pdu)
196 - case oidIfOutDiscards:
197 - iface.ifOutDiscards, err = pduToInt(pdu)
198 - case oidIfOutErrors:
199 - iface.ifOutErrors, err = pduToInt(pdu)
200 - case oidIfName:
201 - iface.ifName, err = pduToString(pdu)
202 - case oidIfInMulticastPkts:
203 - iface.ifInMulticastPkts, err = pduToInt(pdu)
204 - case oidIfInBroadcastPkts:
205 - iface.ifInBroadcastPkts, err = pduToInt(pdu)
206 - case oidIfOutMulticastPkts:
207 - iface.ifOutMulticastPkts, err = pduToInt(pdu)
208 - case oidIfOutBroadcastPkts:
209 - iface.ifOutBroadcastPkts, err = pduToInt(pdu)
210 - case oidIfHCInOctets:
211 - iface.ifHCInOctets, err = pduToInt(pdu)
212 - case oidIfHCInUcastPkts:
213 - iface.ifHCInUcastPkts, err = pduToInt(pdu)
214 - case oidIfHCInMulticastPkts:
215 - iface.ifHCInMulticastPkts, err = pduToInt(pdu)
216 - case oidIfHCInBroadcastPkts:
217 - iface.ifHCInBroadcastPkts, err = pduToInt(pdu)
218 - case oidIfHCOutOctets:
219 - iface.ifHCOutOctets, err = pduToInt(pdu)
220 - case oidIfHCOutUcastPkts:
221 - iface.ifHCOutUcastPkts, err = pduToInt(pdu)
222 - case oidIfHCOutMulticastPkts:
223 - iface.ifHCOutMulticastPkts, err = pduToInt(pdu)
224 - case oidIfHCOutBroadcastPkts:
225 - iface.ifHCOutMulticastPkts, err = pduToInt(pdu)
226 - case oidIfHighSpeed:
227 - iface.ifHighSpeed, err = pduToInt(pdu)
228 - case oidIfAlias:
229 - iface.ifAlias, err = pduToString(pdu)
230 - default:
231 - continue
232 - }
233 -
234 - if err != nil {
235 - return fmt.Errorf("OID '%s': %v", pdu.Name, err)
236 - }
237 -
238 - s.netInterfaces[idx] = iface
239 - iface.updated = true
66 + labels := make(map[string]string)
67 + for k, v := range s.Vnode.Labels {
68 + labels[k] = v
69 }
70
242 - for _, iface := range s.netInterfaces {
243 - if iface.ifName == "" {
244 - continue
245 - }
246 -
247 - typeStr := ifTypeMapping[iface.ifType]
248 - if s.netIfaceFilterByName.MatchString(iface.ifName) || s.netIfaceFilterByType.MatchString(typeStr) {
249 - continue
250 - }
251 -
252 - if !iface.updated {
253 - delete(s.netInterfaces, iface.idx)
254 - if iface.hasCharts {
255 - s.removeNetIfaceCharts(iface)
256 - }
257 - continue
258 - }
259 - if !iface.hasCharts {
260 - iface.hasCharts = true
261 - s.addNetIfaceCharts(iface)
262 - }
263 -
264 - px := fmt.Sprintf("net_iface_%s_", iface.ifName)
265 - mx[px+"traffic_in"] = iface.ifHCInOctets * 8 / 1000 // kilobits
266 - mx[px+"traffic_out"] = iface.ifHCOutOctets * 8 / 1000 // kilobits
267 - mx[px+"ucast_in"] = iface.ifHCInUcastPkts
268 - mx[px+"ucast_out"] = iface.ifHCOutUcastPkts
269 - mx[px+"mcast_in"] = iface.ifHCInMulticastPkts
270 - mx[px+"mcast_out"] = iface.ifHCOutMulticastPkts
271 - mx[px+"bcast_in"] = iface.ifHCInBroadcastPkts
272 - mx[px+"bcast_out"] = iface.ifHCOutBroadcastPkts
273 - mx[px+"errors_in"] = iface.ifInErrors
274 - mx[px+"errors_out"] = iface.ifOutErrors
275 - mx[px+"discards_in"] = iface.ifInDiscards
276 - mx[px+"discards_out"] = iface.ifOutDiscards
277 -
278 - for _, v := range ifAdminStatusMapping {
279 - mx[px+"admin_status_"+v] = 0
280 - }
281 - mx[px+"admin_status_"+ifAdminStatusMapping[iface.ifAdminStatus]] = 1
282 -
283 - for _, v := range ifOperStatusMapping {
284 - mx[px+"oper_status_"+v] = 0
285 - }
286 - mx[px+"oper_status_"+ifOperStatusMapping[iface.ifOperStatus]] = 1
71 + if si.descr != "" {
72 + labels["sysDescr"] = si.descr
73 }
288 -
289 - if logger.Level.Enabled(slog.LevelDebug) {
290 - ifaces := make([]*netInterface, 0, len(s.netInterfaces))
291 - for _, nif := range s.netInterfaces {
292 - ifaces = append(ifaces, nif)
293 - }
294 - sort.Slice(ifaces, func(i, j int) bool { return ifaces[i].ifIndex < ifaces[j].ifIndex })
295 - for _, iface := range ifaces {
296 - s.Debugf("found %s", iface)
297 - }
74 + if si.contact != "" {
75 + labels["sysContact"] = si.descr
76 }
299 -
300 - return nil
301 -}
302 -
303 -func (s *SNMP) adjustMaxRepetitions() (bool, error) {
304 - orig := s.Config.Options.MaxRepetitions
305 - maxReps := s.Config.Options.MaxRepetitions
306 -
307 - for {
308 - v, err := s.walkAll(oidIfIndex)
309 - if err != nil {
310 - return false, err
311 - }
312 -
313 - if len(v) > 0 {
314 - if orig != maxReps {
315 - s.Infof("changed 'max_repetitions' %d => %d", orig, maxReps)
316 - }
317 - return true, nil
318 - }
319 -
320 - if maxReps > 5 {
321 - maxReps = max(5, maxReps-5)
322 - } else {
323 - maxReps--
324 - }
325 -
326 - if maxReps <= 0 {
327 - return false, nil
328 - }
329 -
330 - s.Debugf("no IF-MIB data returned, trying to decrese 'max_repetitions' to %d", maxReps)
331 - s.snmpClient.SetMaxRepetitions(uint32(maxReps))
77 + if si.location != "" {
78 + labels["sysLocation"] = si.descr
79 }
333 -}
80
335 -func (s *SNMP) walkAll(rootOid string) ([]gosnmp.SnmpPDU, error) {
336 - if s.snmpClient.Version() == gosnmp.Version1 {
337 - return s.snmpClient.WalkAll(rootOid)
81 + return &vnodes.VirtualNode{
82 + GUID: s.Vnode.GUID,
83 + Hostname: s.Vnode.Hostname,
84 + Labels: labels,
85 }
339 - return s.snmpClient.BulkWalkAll(rootOid)
86 }
87
88 func pduToString(pdu gosnmp.SnmpPDU) (string, error) {
@@ -375,41 +121,3 @@ func pduToInt(pdu gosnmp.SnmpPDU) (int64, error) {
121 // }
122 // return strings.Join(parts, ":"), nil
123 //}
378 -
379 -func (s *SNMP) collectOIDs(mx map[string]int64) error {
380 - for i, end := 0, 0; i < len(s.oids); i += s.Options.MaxOIDs {
381 - if end = i + s.Options.MaxOIDs; end > len(s.oids) {
382 - end = len(s.oids)
383 - }
384 -
385 - oids := s.oids[i:end]
386 - resp, err := s.snmpClient.Get(oids)
387 - if err != nil {
388 - s.Errorf("cannot get SNMP data: %v", err)
389 - return err
390 - }
391 -
392 - for i, oid := range oids {
393 - if i >= len(resp.Variables) {
394 - continue
395 - }
396 -
397 - switch v := resp.Variables[i]; v.Type {
398 - case gosnmp.Boolean,
399 - gosnmp.Counter32,
400 - gosnmp.Counter64,
401 - gosnmp.Gauge32,
402 - gosnmp.TimeTicks,
403 - gosnmp.Uinteger32,
404 - gosnmp.OpaqueFloat,
405 - gosnmp.OpaqueDouble,
406 - gosnmp.Integer:
407 - mx[oid] = gosnmp.ToBigInt(v.Value).Int64()
408 - default:
409 - s.Debugf("skipping OID '%s' (unsupported type '%s')", oid, v.Type)
410 - }
411 - }
412 - }
413 -
414 - return nil
415 -}
src/go/plugin/go.d/modules/snmp/collect_custom_oids.go new
+45
@@ -0,0 +1,45 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +package snmp
4 +
5 +import (
6 + "github.com/gosnmp/gosnmp"
7 +)
8 +
9 +func (s *SNMP) collectOIDs(mx map[string]int64) error {
10 + for i, end := 0, 0; i < len(s.customOids); i += s.Options.MaxOIDs {
11 + if end = i + s.Options.MaxOIDs; end > len(s.customOids) {
12 + end = len(s.customOids)
13 + }
14 +
15 + oids := s.customOids[i:end]
16 + resp, err := s.snmpClient.Get(oids)
17 + if err != nil {
18 + s.Errorf("cannot get SNMP data: %v", err)
19 + return err
20 + }
21 +
22 + for i, oid := range oids {
23 + if i >= len(resp.Variables) {
24 + continue
25 + }
26 +
27 + switch v := resp.Variables[i]; v.Type {
28 + case gosnmp.Boolean,
29 + gosnmp.Counter32,
30 + gosnmp.Counter64,
31 + gosnmp.Gauge32,
32 + gosnmp.TimeTicks,
33 + gosnmp.Uinteger32,
34 + gosnmp.OpaqueFloat,
35 + gosnmp.OpaqueDouble,
36 + gosnmp.Integer:
37 + mx[oid] = gosnmp.ToBigInt(v.Value).Int64()
38 + default:
39 + s.Debugf("skipping OID '%s' (unsupported type '%s')", oid, v.Type)
40 + }
41 + }
42 + }
43 +
44 + return nil
45 +}
src/go/plugin/go.d/modules/snmp/collect_if_mib.go new
+251
@@ -0,0 +1,251 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +package snmp
4 +
5 +import (
6 + "errors"
7 + "fmt"
8 + "log/slog"
9 + "sort"
10 + "strings"
11 +
12 + "github.com/netdata/netdata/go/plugins/logger"
13 +
14 + "github.com/gosnmp/gosnmp"
15 +)
16 +
17 +const (
18 + rootOidIfMibIfTable = "1.3.6.1.2.1.2.2"
19 + rootOidIfMibIfXTable = "1.3.6.1.2.1.31.1.1"
20 +)
21 +
22 +func (s *SNMP) collectNetworkInterfaces(mx map[string]int64) error {
23 + if s.checkMaxReps {
24 + ok, err := s.adjustMaxRepetitions()
25 + if err != nil {
26 + return err
27 + }
28 +
29 + s.checkMaxReps = false
30 +
31 + if !ok {
32 + s.collectIfMib = false
33 +
34 + if len(s.customOids) == 0 {
35 + return errors.New("no IF-MIB data returned")
36 + }
37 +
38 + s.Warning("no IF-MIB data returned")
39 + return nil
40 + }
41 + }
42 +
43 + ifMibTable, err := s.walkAll(rootOidIfMibIfTable)
44 + if err != nil {
45 + return err
46 + }
47 +
48 + ifMibXTable, err := s.walkAll(rootOidIfMibIfXTable)
49 + if err != nil {
50 + return err
51 + }
52 +
53 + if len(ifMibTable) == 0 && len(ifMibXTable) == 0 {
54 + s.Warning("no IF-MIB data returned")
55 + s.collectIfMib = false
56 + return nil
57 + }
58 +
59 + for _, i := range s.netInterfaces {
60 + i.updated = false
61 + }
62 +
63 + pdus := make([]gosnmp.SnmpPDU, 0, len(ifMibTable)+len(ifMibXTable))
64 + pdus = append(pdus, ifMibTable...)
65 + pdus = append(pdus, ifMibXTable...)
66 +
67 + for _, pdu := range pdus {
68 + i := strings.LastIndexByte(pdu.Name, '.')
69 + if i == -1 {
70 + continue
71 + }
72 +
73 + idx := pdu.Name[i+1:]
74 + oid := strings.TrimPrefix(pdu.Name[:i], ".")
75 +
76 + iface, ok := s.netInterfaces[idx]
77 + if !ok {
78 + iface = &netInterface{idx: idx}
79 + }
80 +
81 + switch oid {
82 + case oidIfIndex:
83 + iface.ifIndex, err = pduToInt(pdu)
84 + case oidIfDescr:
85 + iface.ifDescr, err = pduToString(pdu)
86 + case oidIfType:
87 + iface.ifType, err = pduToInt(pdu)
88 + case oidIfMtu:
89 + iface.ifMtu, err = pduToInt(pdu)
90 + case oidIfSpeed:
91 + iface.ifSpeed, err = pduToInt(pdu)
92 + case oidIfAdminStatus:
93 + iface.ifAdminStatus, err = pduToInt(pdu)
94 + case oidIfOperStatus:
95 + iface.ifOperStatus, err = pduToInt(pdu)
96 + case oidIfInOctets:
97 + iface.ifInOctets, err = pduToInt(pdu)
98 + case oidIfInUcastPkts:
99 + iface.ifInUcastPkts, err = pduToInt(pdu)
100 + case oidIfInNUcastPkts:
101 + iface.ifInNUcastPkts, err = pduToInt(pdu)
102 + case oidIfInDiscards:
103 + iface.ifInDiscards, err = pduToInt(pdu)
104 + case oidIfInErrors:
105 + iface.ifInErrors, err = pduToInt(pdu)
106 + case oidIfInUnknownProtos:
107 + iface.ifInUnknownProtos, err = pduToInt(pdu)
108 + case oidIfOutOctets:
109 + iface.ifOutOctets, err = pduToInt(pdu)
110 + case oidIfOutUcastPkts:
111 + iface.ifOutUcastPkts, err = pduToInt(pdu)
112 + case oidIfOutNUcastPkts:
113 + iface.ifOutNUcastPkts, err = pduToInt(pdu)
114 + case oidIfOutDiscards:
115 + iface.ifOutDiscards, err = pduToInt(pdu)
116 + case oidIfOutErrors:
117 + iface.ifOutErrors, err = pduToInt(pdu)
118 + case oidIfName:
119 + iface.ifName, err = pduToString(pdu)
120 + case oidIfInMulticastPkts:
121 + iface.ifInMulticastPkts, err = pduToInt(pdu)
122 + case oidIfInBroadcastPkts:
123 + iface.ifInBroadcastPkts, err = pduToInt(pdu)
124 + case oidIfOutMulticastPkts:
125 + iface.ifOutMulticastPkts, err = pduToInt(pdu)
126 + case oidIfOutBroadcastPkts:
127 + iface.ifOutBroadcastPkts, err = pduToInt(pdu)
128 + case oidIfHCInOctets:
129 + iface.ifHCInOctets, err = pduToInt(pdu)
130 + case oidIfHCInUcastPkts:
131 + iface.ifHCInUcastPkts, err = pduToInt(pdu)
132 + case oidIfHCInMulticastPkts:
133 + iface.ifHCInMulticastPkts, err = pduToInt(pdu)
134 + case oidIfHCInBroadcastPkts:
135 + iface.ifHCInBroadcastPkts, err = pduToInt(pdu)
136 + case oidIfHCOutOctets:
137 + iface.ifHCOutOctets, err = pduToInt(pdu)
138 + case oidIfHCOutUcastPkts:
139 + iface.ifHCOutUcastPkts, err = pduToInt(pdu)
140 + case oidIfHCOutMulticastPkts:
141 + iface.ifHCOutMulticastPkts, err = pduToInt(pdu)
142 + case oidIfHCOutBroadcastPkts:
143 + iface.ifHCOutMulticastPkts, err = pduToInt(pdu)
144 + case oidIfHighSpeed:
145 + iface.ifHighSpeed, err = pduToInt(pdu)
146 + case oidIfAlias:
147 + iface.ifAlias, err = pduToString(pdu)
148 + default:
149 + continue
150 + }
151 +
152 + if err != nil {
153 + return fmt.Errorf("OID '%s': %v", pdu.Name, err)
154 + }
155 +
156 + s.netInterfaces[idx] = iface
157 + iface.updated = true
158 + }
159 +
160 + for _, iface := range s.netInterfaces {
161 + if iface.ifName == "" {
162 + continue
163 + }
164 +
165 + typeStr := ifTypeMapping[iface.ifType]
166 + if s.netIfaceFilterByName.MatchString(iface.ifName) || s.netIfaceFilterByType.MatchString(typeStr) {
167 + continue
168 + }
169 +
170 + if !iface.updated {
171 + delete(s.netInterfaces, iface.idx)
172 + if iface.hasCharts {
173 + s.removeNetIfaceCharts(iface)
174 + }
175 + continue
176 + }
177 + if !iface.hasCharts {
178 + iface.hasCharts = true
179 + s.addNetIfaceCharts(iface)
180 + }
181 +
182 + px := fmt.Sprintf("net_iface_%s_", iface.ifName)
183 + mx[px+"traffic_in"] = iface.ifHCInOctets * 8 / 1000 // kilobits
184 + mx[px+"traffic_out"] = iface.ifHCOutOctets * 8 / 1000 // kilobits
185 + mx[px+"ucast_in"] = iface.ifHCInUcastPkts
186 + mx[px+"ucast_out"] = iface.ifHCOutUcastPkts
187 + mx[px+"mcast_in"] = iface.ifHCInMulticastPkts
188 + mx[px+"mcast_out"] = iface.ifHCOutMulticastPkts
189 + mx[px+"bcast_in"] = iface.ifHCInBroadcastPkts
190 + mx[px+"bcast_out"] = iface.ifHCOutBroadcastPkts
191 + mx[px+"errors_in"] = iface.ifInErrors
192 + mx[px+"errors_out"] = iface.ifOutErrors
193 + mx[px+"discards_in"] = iface.ifInDiscards
194 + mx[px+"discards_out"] = iface.ifOutDiscards
195 +
196 + for _, v := range ifAdminStatusMapping {
197 + mx[px+"admin_status_"+v] = 0
198 + }
199 + mx[px+"admin_status_"+ifAdminStatusMapping[iface.ifAdminStatus]] = 1
200 +
201 + for _, v := range ifOperStatusMapping {
202 + mx[px+"oper_status_"+v] = 0
203 + }
204 + mx[px+"oper_status_"+ifOperStatusMapping[iface.ifOperStatus]] = 1
205 + }
206 +
207 + if logger.Level.Enabled(slog.LevelDebug) {
208 + ifaces := make([]*netInterface, 0, len(s.netInterfaces))
209 + for _, nif := range s.netInterfaces {
210 + ifaces = append(ifaces, nif)
211 + }
212 + sort.Slice(ifaces, func(i, j int) bool { return ifaces[i].ifIndex < ifaces[j].ifIndex })
213 + for _, iface := range ifaces {
214 + s.Debugf("found %s", iface)
215 + }
216 + }
217 +
218 + return nil
219 +}
220 +
221 +func (s *SNMP) adjustMaxRepetitions() (bool, error) {
222 + orig := s.Config.Options.MaxRepetitions
223 + maxReps := s.Config.Options.MaxRepetitions
224 +
225 + for {
226 + v, err := s.walkAll(oidIfIndex)
227 + if err != nil {
228 + return false, err
229 + }
230 +
231 + if len(v) > 0 {
232 + if orig != maxReps {
233 + s.Infof("changed 'max_repetitions' %d => %d", orig, maxReps)
234 + }
235 + return true, nil
236 + }
237 +
238 + if maxReps > 5 {
239 + maxReps = max(5, maxReps-5)
240 + } else {
241 + maxReps--
242 + }
243 +
244 + if maxReps <= 0 {
245 + return false, nil
246 + }
247 +
248 + s.Debugf("no IF-MIB data returned, trying to decrese 'max_repetitions' to %d", maxReps)
249 + s.snmpClient.SetMaxRepetitions(uint32(maxReps))
250 + }
251 +}
src/go/plugin/go.d/modules/snmp/collect_mib_system.go new
+76
@@ -0,0 +1,76 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +package snmp
4 +
5 +import (
6 + "errors"
7 + "fmt"
8 + "strings"
9 +)
10 +
11 +const (
12 + rootOidMibSystem = "1.3.6.1.2.1.1"
13 + oidSysDescr = "1.3.6.1.2.1.1.1.0"
14 + oidSysUptime = "1.3.6.1.2.1.1.3.0"
15 + oidSysContact = "1.3.6.1.2.1.1.4.0"
16 + oidSysName = "1.3.6.1.2.1.1.5.0"
17 + oidSysLocation = "1.3.6.1.2.1.1.6.0"
18 +)
19 +
20 +type sysInfo struct {
21 + descr string
22 + contact string
23 + name string
24 + location string
25 +}
26 +
27 +func (s *SNMP) getSysInfo() (*sysInfo, error) {
28 + pdus, err := s.snmpClient.WalkAll(rootOidMibSystem)
29 + if err != nil {
30 + return nil, err
31 + }
32 +
33 + var si sysInfo
34 +
35 + for _, pdu := range pdus {
36 + oid := strings.TrimPrefix(pdu.Name, ".")
37 +
38 + switch oid {
39 + case oidSysDescr:
40 + si.descr, err = pduToString(pdu)
41 + case oidSysContact:
42 + si.contact, err = pduToString(pdu)
43 + case oidSysName:
44 + si.name, err = pduToString(pdu)
45 + case oidSysLocation:
46 + si.location, err = pduToString(pdu)
47 + }
48 + if err != nil {
49 + return nil, fmt.Errorf("OID '%s': %v", pdu.Name, err)
50 + }
51 + }
52 +
53 + if si.name == "" {
54 + return nil, errors.New("no system name")
55 + }
56 +
57 + return &si, nil
58 +}
59 +
60 +func (s *SNMP) collectSysUptime(mx map[string]int64) error {
61 + resp, err := s.snmpClient.Get([]string{oidSysUptime})
62 + if err != nil {
63 + return err
64 + }
65 + if len(resp.Variables) == 0 {
66 + return errors.New("no system uptime")
67 + }
68 + v, err := pduToInt(resp.Variables[0])
69 + if err != nil {
70 + return err
71 + }
72 +
73 + mx["uptime"] = v / 100 // the time is in hundredths of a second
74 +
75 + return nil
76 +}
src/go/plugin/go.d/modules/snmp/snmp.go
+7 -5
@@ -68,12 +68,14 @@ type SNMP struct {
68 netIfaceFilterByName matcher.Matcher
69 netIfaceFilterByType matcher.Matcher
70
71 - checkMaxReps bool
72 - collectIfMib bool
71 + checkMaxReps bool
72 + collectIfMib bool
73 +
74 netInterfaces map[string]*netInterface
74 - sysName string
75
76 - oids []string
76 + sysInfo *sysInfo
77 +
78 + customOids []string
79 }
80
81 func (s *SNMP) Configuration() any {
@@ -115,7 +117,7 @@ func (s *SNMP) Init() error {
117 }
118 s.charts = charts
119
118 - s.oids = s.initOIDs()
120 + s.customOids = s.initOIDs()
121
122 return nil
123 }
src/go/plugin/go.d/modules/snmp/snmp_test.go
+5 -4
@@ -580,10 +580,11 @@ func setMockClientInitExpect(m *snmpmock.MockHandler) {
580 }
581
582 func setMockClientSysExpect(m *snmpmock.MockHandler) {
583 - m.EXPECT().Get([]string{oidSysName}).Return(&gosnmp.SnmpPacket{
584 - Variables: []gosnmp.SnmpPDU{
585 - {Value: []uint8("mock-host"), Type: gosnmp.OctetString},
586 - },
583 + m.EXPECT().WalkAll(rootOidMibSystem).Return([]gosnmp.SnmpPDU{
584 + {Name: oidSysDescr, Value: []uint8("mock sysDescr"), Type: gosnmp.OctetString},
585 + {Name: oidSysContact, Value: []uint8("mock sysContact"), Type: gosnmp.OctetString},
586 + {Name: oidSysName, Value: []uint8("mock sysName"), Type: gosnmp.OctetString},
587 + {Name: oidSysLocation, Value: []uint8("mock sysLocation"), Type: gosnmp.OctetString},
588 }, nil).MinTimes(1)
589
590 m.EXPECT().Get([]string{oidSysUptime}).Return(&gosnmp.SnmpPacket{