@cryptotaxi247 / netdata-1 / commits / f76325f3f

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