@cryptotaxi247 / netdata-1 / commits / 27dc6b9c5

feat(go.d/snmp): add `ping_only` option (#21064)

Ilya Mashchenko committed Sep 28, 2025 at 17:28 UTC 27dc6b9c5342e6f04078d55d2ad905a0ed7665f2
9 files changed +99 -30
src/go/plugin/go.d/collector/ping/prober.go
+16 -4
@@ -47,14 +47,26 @@ func (p *pingProber) Ping(host string) (*probing.Statistics, error) {
47 return nil, fmt.Errorf("DNS lookup '%s' : %v", host, err)
48 }
49
50 + pr.SetLogger(nil)
51 pr.RecordRtts = false
52 pr.RecordTTLs = false
52 - pr.Interval = p.conf.Interval.Duration()
53 - pr.Count = p.conf.Packets
54 - pr.Timeout = p.conf.Timeout
53 +
54 pr.InterfaceName = p.conf.Interface
55 pr.SetPrivileged(p.conf.Privileged)
57 - pr.SetLogger(nil)
56 +
57 + pr.Interval = time.Millisecond * 100
58 + pr.Count = 3
59 + pr.Timeout = time.Second * 5
60 +
61 + if p.conf.Interval.Duration().Milliseconds() > 0 {
62 + pr.Interval = p.conf.Interval.Duration()
63 + }
64 + if p.conf.Packets > 0 {
65 + pr.Count = p.conf.Packets
66 + }
67 + if p.conf.Timeout.Milliseconds() > 0 {
68 + pr.Timeout = p.conf.Timeout
69 + }
70
71 if err := pr.Run(); err != nil {
72 return nil, fmt.Errorf("pinging host '%s' (ip '%s' iface '%s'): %v",
src/go/plugin/go.d/collector/snmp/collect.go
+12 -4
@@ -30,15 +30,23 @@ func (c *Collector) collect() (map[string]int64, error) {
30 return nil, err
31 }
32
33 - mx, err := c.collectMetrics()
34 - if err != nil {
33 + if c.PingOnly {
34 + return c.collectPingOnly()
35 + }
36 + return c.collectDeviceMetrics()
37 +}
38 +
39 +func (c *Collector) collectPingOnly() (map[string]int64, error) {
40 + mx := make(map[string]int64)
41 +
42 + if err := c.collectPing(mx); err != nil {
43 return nil, err
44 }
45
46 return mx, nil
47 }
48
41 -func (c *Collector) collectMetrics() (map[string]int64, error) {
49 +func (c *Collector) collectDeviceMetrics() (map[string]int64, error) {
50 var (
51 snmpMx map[string]int64
52 pingMx map[string]int64
@@ -121,7 +129,7 @@ func (c *Collector) ensureInitialized() error {
129
130 c.sysInfo = si
131
124 - if c.Ping.Enabled {
132 + if c.PingOnly || c.Ping.Enabled {
133 c.addPingCharts()
134 }
135
src/go/plugin/go.d/collector/snmp/collector.go
+1 -1
@@ -119,7 +119,7 @@ func (c *Collector) Init(context.Context) error {
119 return fmt.Errorf("failed to initialize SNMP client: %v", err)
120 }
121
122 - if c.Ping.Enabled {
122 + if c.PingOnly || c.Ping.Enabled {
123 pr, err := c.initProber()
124 if err != nil {
125 return fmt.Errorf("failed to initialize ping prober: %v", err)
src/go/plugin/go.d/collector/snmp/collector_test.go
+51
@@ -8,8 +8,13 @@ import (
8 "os"
9 "strings"
10 "testing"
11 + "time"
12
13 + probing "github.com/prometheus-community/pro-bing"
14 +
15 + "github.com/netdata/netdata/go/plugins/logger"
16 "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
17 + "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/ping"
18 "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/snmp/ddsnmp"
19 "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/snmp/ddsnmp/ddsnmpcollector"
20 "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/snmputils"
@@ -285,6 +290,27 @@ func TestCollector_Collect(t *testing.T) {
290 "snmp_device_prof_if_octets_eth0_out": 2,
291 },
292 },
293 + "collects ping only metrics": {
294 + prepare: func(m *snmpmock.MockHandler) *Collector {
295 + setMockClientInitExpect(m)
296 + setMockClientSysInfoExpect(m)
297 +
298 + collr := New()
299 + collr.Config = prepareV2Config()
300 + collr.PingOnly = true
301 + collr.CreateVnode = false
302 + collr.newSnmpClient = func() gosnmp.Handler { return m }
303 + collr.newProber = func(cfg ping.ProberConfig, log *logger.Logger) ping.Prober { return &mockProber{} }
304 +
305 + return collr
306 + },
307 + want: map[string]int64{
308 + "ping_rtt_min": (10 * time.Millisecond).Microseconds(),
309 + "ping_rtt_max": (20 * time.Millisecond).Microseconds(),
310 + "ping_rtt_avg": (15 * time.Millisecond).Microseconds(),
311 + "ping_rtt_stddev": (5 * time.Millisecond).Microseconds(),
312 + },
313 + },
314 }
315
316 for name, tc := range tests {
@@ -305,6 +331,31 @@ func TestCollector_Collect(t *testing.T) {
331 }
332 }
333
334 +type mockProber struct {
335 + errOnPing bool
336 +}
337 +
338 +func (m *mockProber) Ping(host string) (*probing.Statistics, error) {
339 + if m.errOnPing {
340 + return nil, errors.New("mock.Ping() error")
341 + }
342 +
343 + stats := probing.Statistics{
344 + PacketsRecv: 5,
345 + PacketsSent: 5,
346 + PacketsRecvDuplicates: 0,
347 + PacketLoss: 0,
348 + Addr: host,
349 + Rtts: nil,
350 + MinRtt: time.Millisecond * 10,
351 + MaxRtt: time.Millisecond * 20,
352 + AvgRtt: time.Millisecond * 15,
353 + StdDevRtt: time.Millisecond * 5,
354 + }
355 +
356 + return &stats, nil
357 +}
358 +
359 type mockDdSnmpCollector struct {
360 pms []*ddsnmp.ProfileMetrics
361 meta map[string]ddsnmp.MetaTag
src/go/plugin/go.d/collector/snmp/config.go
+2 -21
@@ -22,7 +22,8 @@ type (
22
23 ManualProfiles []string `yaml:"manual_profiles,omitempty" json:"manual_profiles"`
24
25 - Ping PingConfig `yaml:"ping,omitempty" json:"ping"`
25 + PingOnly bool `yaml:"ping_only,omitempty" json:"ping_only"`
26 + Ping PingConfig `yaml:"ping,omitempty" json:"ping"`
27 }
28
29 PingConfig struct {
@@ -47,23 +48,3 @@ type (
48 MaxRepetitions int `yaml:"max_repetitions,omitempty" json:"max_repetitions"`
49 }
50 )
50 -
51 -type (
52 - ChartConfig struct {
53 - ID string `yaml:"id" json:"id"`
54 - Title string `yaml:"title" json:"title"`
55 - Units string `yaml:"units" json:"units"`
56 - Family string `yaml:"family" json:"family"`
57 - Type string `yaml:"type" json:"type"`
58 - Priority int `yaml:"priority" json:"priority"`
59 - IndexRange []int `yaml:"multiply_range,omitempty" json:"multiply_range"`
60 - Dimensions []DimensionConfig `yaml:"dimensions" json:"dimensions"`
61 - }
62 - DimensionConfig struct {
63 - OID string `yaml:"oid" json:"oid"`
64 - Name string `yaml:"name" json:"name"`
65 - Algorithm string `yaml:"algorithm" json:"algorithm"`
66 - Multiplier int `yaml:"multiplier" json:"multiplier"`
67 - Divisor int `yaml:"divisor" json:"divisor"`
68 - }
69 -)
src/go/plugin/go.d/collector/snmp/config_schema.json
+10
@@ -201,6 +201,12 @@
201 },
202 "uniqueItems": true
203 },
204 + "ping_only": {
205 + "title": "Ping only",
206 + "description": "Collect only ICMP round-trip time and skip SNMP profile metrics.",
207 + "type": "boolean",
208 + "default": false
209 + },
210 "ping": {
211 "title": "Ping",
212 "type": [
@@ -311,6 +317,9 @@
317 "ui:widget": "password"
318 }
319 },
320 + "ping_only": {
321 + "ui:help": "A minimal SNMP sysInfo query still runs during vnode setup for identification/metadata."
322 + },
323 "ping": {
324 "interface": {
325 "ui:widget": "hidden"
@@ -334,6 +343,7 @@
343 {
344 "title": "Ping",
345 "fields": [
346 + "ping_only",
347 "ping"
348 ]
349 },
src/go/plugin/go.d/collector/snmp/metadata.yaml
+5
@@ -199,6 +199,11 @@ modules:
199 default_value: 60
200 required: false
201
202 + - name: ping_only
203 + group: Ping
204 + description: Collect only ICMP round-trip metrics and skip periodic SNMP metrics. A minimal SNMP sysInfo request is still performed when setting up the Virtual Node (for naming/labels and metadata).
205 + default_value: false
206 + required: false
207 - name: ping.enabled
208 group: Ping
209 description: Enable ICMP round-trip measurements (runs alongside SNMP). When disabled, no ping metrics are collected.
src/go/plugin/go.d/collector/snmp/testdata/config.json
+1
@@ -31,6 +31,7 @@
31 "manual_profiles": [
32 "ok"
33 ],
34 + "ping_only": true,
35 "ping": {
36 "enabled": true,
37 "network": "ip",
src/go/plugin/go.d/collector/snmp/testdata/config.yaml
+1
@@ -30,6 +30,7 @@ options:
30 max_request_size: 123
31 max_repetitions: 123
32
33 +ping_only: yes
34 ping:
35 enabled: yes
36 network: ip