feat(go.d/snmp): enable ping by default (#21054)
Ilya Mashchenko committed
Sep 26, 2025 at 14:06 UTC
f76325f3f2686d0ee89767e041d62606694d93cb
7 files changed
+114
-73
src/go/plugin/go.d/collector/ping/prober.go
+1
@@ -48,6 +48,7 @@ func (p *pingProber) Ping(host string) (*probing.Statistics, error) {
48
}
49
50
pr.RecordRtts = false
51
+ pr.RecordTTLs = false
52
pr.Interval = p.conf.Interval.Duration()
53
pr.Count = p.conf.Packets
54
pr.Timeout = p.conf.Timeout
src/go/plugin/go.d/collector/snmp/charts.go
+37
-9
@@ -51,7 +51,17 @@ var (
51
)
52
53
func (c *Collector) addPingCharts() {
54
- if err := c.Charts().Add(*pingCharts.Copy()...); err != nil {
54
+ charts := pingCharts.Copy()
55
+
56
+ labels := c.chartBaseLabels()
57
+
58
+ for _, chart := range *charts {
59
+ for k, v := range labels {
60
+ chart.Labels = append(chart.Labels, module.Label{Key: k, Value: v})
61
+ }
62
+ }
63
+
64
+ if err := c.Charts().Add(*charts...); err != nil {
65
c.Warningf("failed to add ping charts: %v", err)
66
}
67
}
@@ -83,10 +93,7 @@ func (c *Collector) addProfileScalarMetricChart(m ddsnmp.Metric) {
93
chart.Type = module.Area
94
}
95
86
- tags := map[string]string{
87
- "vendor": c.sysInfo.Organization,
88
- "sysName": c.sysInfo.Name,
89
- }
96
+ tags := c.chartBaseLabels()
97
98
maps.Copy(tags, m.Profile.Tags)
99
for k, v := range tags {
@@ -142,10 +149,8 @@ func (c *Collector) addProfileTableMetricChart(m ddsnmp.Metric) {
149
chart.Type = module.Area
150
}
151
145
- tags := map[string]string{
146
- "vendor": c.sysInfo.Organization,
147
- "sysName": c.sysInfo.Name,
148
- }
152
+ tags := c.chartBaseLabels()
153
+
154
maps.Copy(tags, m.Profile.Tags)
155
for k, v := range m.Tags {
156
newKey := strings.TrimPrefix(k, "_")
@@ -185,6 +190,29 @@ func (c *Collector) removeProfileTableMetricChart(key string) {
190
}
191
}
192
193
+func (c *Collector) chartBaseLabels() map[string]string {
194
+ si := c.sysInfo
195
+
196
+ labels := map[string]string{
197
+ "sysName": si.Name,
198
+ "address": c.Hostname,
199
+ }
200
+
201
+ if si.Vendor != "" {
202
+ labels["vendor"] = si.Vendor
203
+ } else if si.Organization != "" {
204
+ labels["vendor"] = si.Organization
205
+ }
206
+ if si.Category != "" {
207
+ labels["device_type"] = si.Category
208
+ }
209
+ if si.Model != "" {
210
+ labels["model"] = si.Model
211
+ }
212
+
213
+ return labels
214
+}
215
+
216
func dimAlgoFromDdSnmpType(m ddsnmp.Metric) module.DimAlgo {
217
switch m.MetricType {
218
case ddprofiledefinition.ProfileMetricTypeGauge,
src/go/plugin/go.d/collector/snmp/collect.go
+66
-52
@@ -4,6 +4,7 @@ package snmp
4
5
import (
6
"context"
7
+ "errors"
8
"fmt"
9
"log/slog"
10
"maps"
@@ -11,6 +12,7 @@ import (
12
"slices"
13
"strconv"
14
"strings"
15
+ "syscall"
16
17
"github.com/google/uuid"
18
"github.com/gosnmp/gosnmp"
@@ -24,41 +26,19 @@ import (
26
)
27
28
func (c *Collector) collect() (map[string]int64, error) {
27
- if c.snmpClient == nil {
28
- snmpClient, err := c.initAndConnectSNMPClient()
29
- if err != nil {
30
- return nil, err
31
- }
32
- c.snmpClient = snmpClient
33
- if c.ddSnmpColl != nil {
34
- c.ddSnmpColl.SetSNMPClient(snmpClient)
35
- }
29
+ if err := c.ensureInitialized(); err != nil {
30
+ return nil, err
31
}
32
38
- if c.sysInfo == nil {
39
- si, err := snmputils.GetSysInfo(c.snmpClient)
40
- if err != nil {
41
- return nil, err
42
- }
43
-
44
- if c.enableProfiles {
45
- c.snmpProfiles = c.setupProfiles(si)
46
- }
47
-
48
- if c.ddSnmpColl == nil {
49
- c.ddSnmpColl = ddsnmpcollector.New(c.snmpClient, c.snmpProfiles, c.Logger, si.SysObjectID)
50
- }
33
+ mx, err := c.collectMetrics()
34
+ if err != nil {
35
+ return nil, err
36
+ }
37
52
- if c.CreateVnode {
53
- deviceMeta, err := c.ddSnmpColl.CollectDeviceMetadata()
54
- if err != nil {
55
- return nil, err
56
- }
57
- c.vnode = c.setupVnode(si, deviceMeta)
58
- }
38
+ return mx, nil
39
+}
40
60
- c.sysInfo = si
61
- }
41
+func (c *Collector) collectMetrics() (map[string]int64, error) {
42
var (
43
snmpMx map[string]int64
44
pingMx map[string]int64
@@ -81,7 +61,10 @@ func (c *Collector) collect() (map[string]int64, error) {
61
g.Go(func() error {
62
m := make(map[string]int64)
63
if err := c.collectPing(m); err != nil {
84
- c.Debugf("ping: %v", err)
64
+ c.Errorf("ping: %v", err)
65
+ if isPingUnrecoverableError(err) {
66
+ c.prober = nil
67
+ }
68
return nil
69
}
70
pingMx = m
@@ -93,7 +76,7 @@ func (c *Collector) collect() (map[string]int64, error) {
76
return nil, err
77
}
78
96
- mx := make(map[string]int64)
79
+ mx := make(map[string]int64, len(snmpMx)+len(pingMx))
80
81
maps.Copy(mx, snmpMx)
82
maps.Copy(mx, pingMx)
@@ -101,6 +84,52 @@ func (c *Collector) collect() (map[string]int64, error) {
84
return mx, nil
85
}
86
87
+func (c *Collector) ensureInitialized() error {
88
+ if c.snmpClient == nil {
89
+ snmpClient, err := c.initAndConnectSNMPClient()
90
+ if err != nil {
91
+ return err
92
+ }
93
+ c.snmpClient = snmpClient
94
+ if c.ddSnmpColl != nil {
95
+ c.ddSnmpColl.SetSNMPClient(snmpClient)
96
+ }
97
+ }
98
+
99
+ if c.sysInfo != nil {
100
+ return nil
101
+ }
102
+
103
+ si, err := snmputils.GetSysInfo(c.snmpClient)
104
+ if err != nil {
105
+ return err
106
+ }
107
+
108
+ if c.enableProfiles {
109
+ c.snmpProfiles = c.setupProfiles(si)
110
+ }
111
+
112
+ if c.ddSnmpColl == nil {
113
+ c.ddSnmpColl = ddsnmpcollector.New(c.snmpClient, c.snmpProfiles, c.Logger, si.SysObjectID)
114
+ }
115
+
116
+ if c.CreateVnode {
117
+ deviceMeta, err := c.ddSnmpColl.CollectDeviceMetadata()
118
+ if err != nil {
119
+ return err
120
+ }
121
+ c.vnode = c.setupVnode(si, deviceMeta)
122
+ }
123
+
124
+ c.sysInfo = si
125
+
126
+ if c.Ping.Enabled {
127
+ c.addPingCharts()
128
+ }
129
+
130
+ return nil
131
+}
132
+
133
func (c *Collector) setupVnode(si *snmputils.SysInfo, deviceMeta map[string]ddsnmp.MetaTag) *vnodes.VirtualNode {
134
if c.Vnode.GUID == "" {
135
c.Vnode.GUID = uuid.NewSHA1(uuid.NameSpaceDNS, []byte(c.Hostname)).String()
@@ -203,6 +232,7 @@ func (c *Collector) initAndConnectSNMPClient() (gosnmp.Handler, error) {
232
233
return snmpClient, nil
234
}
235
+
236
func (c *Collector) adjustMaxRepetitions(snmpClient gosnmp.Handler) (bool, error) {
237
orig := c.Config.Options.MaxRepetitions
238
maxReps := c.Config.Options.MaxRepetitions
@@ -256,23 +286,7 @@ func walkAll(snmpClient gosnmp.Handler, rootOid string) ([]gosnmp.SnmpPDU, error
286
return snmpClient.BulkWalkAll(rootOid)
287
}
288
259
-func pduToInt(pdu gosnmp.SnmpPDU) (int64, error) {
260
- switch pdu.Type {
261
- case gosnmp.Counter32, gosnmp.Counter64, gosnmp.Integer, gosnmp.Gauge32, gosnmp.TimeTicks:
262
- return gosnmp.ToBigInt(pdu.Value).Int64(), nil
263
- default:
264
- return 0, fmt.Errorf("unsupported type: '%v'", pdu.Type)
265
- }
289
+func isPingUnrecoverableError(err error) bool {
290
+ var errno syscall.Errno
291
+ return errors.As(err, &errno) && (errors.Is(errno, syscall.EPERM) || errors.Is(errno, syscall.EACCES))
292
}
267
-
268
-//func physAddressToString(pdu gosnmp.SnmpPDU) (string, error) {
269
-// address, ok := pdu.Value.([]uint8)
270
-// if !ok {
271
-// return "", errors.New("physAddress is not a []uint8")
272
-// }
273
-// parts := make([]string, 0, 6)
274
-// for _, v := range address {
275
-// parts = append(parts, fmt.Sprintf("%02X", v))
276
-// }
277
-// return strings.Join(parts, ":"), nil
278
-//}
src/go/plugin/go.d/collector/snmp/collector.go
+1
-2
@@ -55,8 +55,8 @@ func New() *Collector {
55
PrivProto: "aes192c",
56
},
57
Ping: PingConfig{
58
+ Enabled: true,
59
ProberConfig: ping.ProberConfig{
59
- Network: "ip",
60
Privileged: true,
61
Packets: 3,
62
Interval: confopt.Duration(time.Millisecond * 100),
@@ -131,7 +131,6 @@ func (c *Collector) Init(context.Context) error {
131
return fmt.Errorf("failed to initialize ping prober: %v", err)
132
}
133
c.prober = pr
134
- c.addPingCharts()
134
}
135
136
c.customOids = c.initCustomOIDs()
src/go/plugin/go.d/collector/snmp/config_schema.json
+1
-1
@@ -359,7 +359,7 @@
359
"enabled": {
360
"title": "Enable ping",
361
"type": "boolean",
362
- "default": false,
362
+ "default": true,
363
"description": "Collect ICMP round-trip time using pro-bing alongside SNMP."
364
},
365
"network": {
src/go/plugin/go.d/collector/snmp/init.go
+7
-8
@@ -70,14 +70,13 @@ func (c *Collector) initSNMPClient() (gosnmp.Handler, error) {
70
}
71
72
func (c *Collector) initProber() (ping.Prober, error) {
73
- mul := 0.9
74
- if c.UpdateEvery > 1 {
75
- mul = 0.8
76
- }
77
- timeout := time.Millisecond * time.Duration(float64(c.UpdateEvery)*mul*1000)
78
- if timeout.Milliseconds() == 0 {
79
- return nil, errors.New("zero ping timeout")
80
- }
73
+ // base timeout = update_every seconds
74
+ timeout := time.Duration(c.UpdateEvery) * time.Second
75
+
76
+ // clamp between 1s and 3s
77
+ const minTimeout = time.Second
78
+ const maxTimeout = 3 * time.Second
79
+ timeout = max(min(timeout, maxTimeout), minTimeout)
80
81
conf := c.Ping.ProberConfig
82
conf.Timeout = timeout
src/go/plugin/go.d/collector/snmp/metadata.yaml
+1
-1
@@ -202,7 +202,7 @@ modules:
202
- name: ping.enabled
203
group: Ping
204
description: Enable ICMP round-trip measurements (runs alongside SNMP). When disabled, no ping metrics are collected.
205
- default_value: false
205
+ default_value: true
206
required: false
207
- name: ping.privileged
208
group: Ping