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{