feat(go.d/snmp): enable profile scalar metrics collection (#20426)
Ilya Mashchenko committed
Jun 5, 2025 at 20:56 UTC
3a787aa9e6a3b6b455c57b6e5a53cb286c561ced
7 files changed
+120
-4
src/go/plugin/go.d/collector/snmp/charts.go
+71
@@ -4,9 +4,12 @@ package snmp
4
5
import (
6
"fmt"
7
+ "maps"
8
"strings"
9
10
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
11
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/snmp/ddsnmp/ddprofiledefinition"
12
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/snmp/ddsnmp/ddsnmpcollector"
13
)
14
15
const (
@@ -19,6 +22,8 @@ const (
22
prioNetIfaceAdminStatus
23
prioNetIfaceOperStatus
24
prioSysUptime
25
+
26
+ prioProfileChart
27
)
28
29
var netIfaceChartsTmpl = module.Charts{
@@ -309,3 +314,69 @@ func newUserInputChart(cfg ChartConfig) (*module.Chart, error) {
314
315
return chart, nil
316
}
317
+
318
+func (c *Collector) addProfileScalarMetricChart(m ddsnmpcollector.Metric) {
319
+ if m.Name == "" {
320
+ return
321
+ }
322
+
323
+ r := strings.NewReplacer(".", "_", " ", "_")
324
+ chart := &module.Chart{
325
+ ID: fmt.Sprintf("snmp_device_prof_%s", r.Replace(m.Name)),
326
+ Title: m.Description,
327
+ Units: m.Unit,
328
+ Fam: m.Family,
329
+ Ctx: fmt.Sprintf("snmp.device_prof_%s", r.Replace(m.Name)),
330
+ Priority: prioProfileChart,
331
+ }
332
+ if chart.Title == "" {
333
+ chart.Title = fmt.Sprintf("SNMP metric %s", m.Name)
334
+ }
335
+ if chart.Units == "" {
336
+ chart.Units = "1"
337
+ }
338
+ if chart.Fam == "" {
339
+ chart.Fam = m.Name
340
+ }
341
+
342
+ tags := map[string]string{
343
+ "vendor": c.sysInfo.Organization,
344
+ "sysName": c.sysInfo.Name,
345
+ }
346
+ maps.Copy(tags, m.Tags)
347
+ for k, v := range tags {
348
+ chart.Labels = append(chart.Labels, module.Label{Key: k, Value: v})
349
+ }
350
+
351
+ if len(m.Mappings) > 0 {
352
+ for _, v := range m.Mappings {
353
+ id := fmt.Sprintf("snmp_device_prof_%s_%s", m.Name, v)
354
+ chart.Dims = append(chart.Dims, &module.Dim{ID: id, Name: v, Algo: module.Absolute})
355
+ }
356
+ } else {
357
+ id := fmt.Sprintf("snmp_device_prof_%s", m.Name)
358
+ chart.Dims = module.Dims{
359
+ {ID: id, Name: m.Name, Algo: dimAlgoFromDdSnmpType(m)},
360
+ }
361
+ }
362
+
363
+ if err := c.Charts().Add(chart); err != nil {
364
+ c.Warning(err)
365
+ }
366
+}
367
+
368
+func (c *Collector) removeProfileScalarMetricChart(metricName string) {
369
+ r := strings.NewReplacer(".", "_", " ", "_")
370
+ id := fmt.Sprintf("snmp_device_prof_%s", r.Replace(metricName))
371
+ if chart := c.Charts().Get(id); chart != nil {
372
+ chart.MarkRemove()
373
+ chart.MarkNotCreated()
374
+ }
375
+}
376
+
377
+func dimAlgoFromDdSnmpType(m ddsnmpcollector.Metric) module.DimAlgo {
378
+ if m.MetricType == ddprofiledefinition.ProfileMetricTypeGauge {
379
+ return module.Absolute
380
+ }
381
+ return module.Incremental
382
+}
src/go/plugin/go.d/collector/snmp/collect.go
+1
-1
@@ -37,7 +37,7 @@ func (c *Collector) collect() (map[string]int64, error) {
37
mx := make(map[string]int64)
38
39
if err := c.collectProfiles(mx); err != nil {
40
- return nil, err
40
+ c.Infof("failed to collect profiles: %v", err)
41
}
42
43
if err := c.collectSysUptime(mx); err != nil {
src/go/plugin/go.d/collector/snmp/collect_profiles.go
+38
-1
@@ -3,10 +3,13 @@
3
package snmp
4
5
import (
6
+ "fmt"
7
+
8
"github.com/netdata/netdata/go/plugins/plugin/go.d/collector/snmp/ddsnmp/ddsnmpcollector"
9
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/metrix"
10
)
11
9
-func (c *Collector) collectProfiles(_ map[string]int64) error {
12
+func (c *Collector) collectProfiles(mx map[string]int64) error {
13
if len(c.snmpProfiles) == 0 {
14
return nil
15
}
@@ -14,5 +17,39 @@ func (c *Collector) collectProfiles(_ map[string]int64) error {
17
c.ddSnmpColl = ddsnmpcollector.New(c.snmpClient, c.snmpProfiles, c.Logger)
18
}
19
20
+ profMetrics, err := c.ddSnmpColl.Collect()
21
+ if err != nil {
22
+ return err
23
+ }
24
+
25
+ seen := make(map[string]bool)
26
+
27
+ for _, pm := range profMetrics {
28
+ for _, m := range pm.Metrics {
29
+ seen[m.Name] = true
30
+ if !c.seenScalarMetrics[m.Name] {
31
+ c.seenScalarMetrics[m.Name] = true
32
+ c.addProfileScalarMetricChart(m)
33
+ }
34
+
35
+ if len(m.Mappings) > 0 {
36
+ for k, v := range m.Mappings {
37
+ id := fmt.Sprintf("snmp_device_prof_%s_%s", m.Name, v)
38
+ mx[id] = metrix.Bool(m.Value == k)
39
+ }
40
+ } else {
41
+ id := fmt.Sprintf("snmp_device_prof_%s", m.Name)
42
+ mx[id] = m.Value
43
+ }
44
+ }
45
+ }
46
+
47
+ for name := range c.seenScalarMetrics {
48
+ if !seen[name] {
49
+ delete(c.seenScalarMetrics, name)
50
+ c.removeProfileScalarMetricChart(name)
51
+ }
52
+ }
53
+
54
return nil
55
}
src/go/plugin/go.d/collector/snmp/collector.go
+4
@@ -61,6 +61,8 @@ func New() *Collector {
61
checkMaxReps: true,
62
collectIfMib: true,
63
netInterfaces: make(map[string]*netInterface),
64
+
65
+ seenScalarMetrics: make(map[string]bool),
66
}
67
}
68
@@ -90,6 +92,8 @@ type Collector struct {
92
customOids []string
93
94
snmpProfiles []*ddsnmp.Profile
95
+
96
+ seenScalarMetrics map[string]bool
97
}
98
99
func (c *Collector) Configuration() any {
src/go/plugin/go.d/collector/snmp/ddsnmp/ddprofiledefinition/metrics.go
+1
@@ -81,6 +81,7 @@ type SymbolConfig struct {
81
MetricType ProfileMetricType `yaml:"metric_type,omitempty" json:"metric_type,omitempty"`
82
Unit string `yaml:"unit,omitempty" json:"unit,omitempty"`
83
Description string `yaml:"description,omitempty" json:"description,omitempty"`
84
+ Family string `yaml:"family,omitempty" json:"family,omitempty"`
85
Mapping map[string]string `yaml:"mapping,omitempty" json:"mapping,omitempty"`
86
}
87
src/go/plugin/go.d/collector/snmp/ddsnmp/ddsnmpcollector/collect_scalar.go
+2
-1
@@ -102,8 +102,9 @@ func (c *Collector) collectScalarMetric(cfg ddprofiledefinition.MetricsConfig, p
102
Tags: tags,
103
Unit: cfg.Symbol.Unit,
104
Description: cfg.Symbol.Description,
105
+ Family: cfg.Symbol.Family,
106
Mappings: mappings,
106
- MetricType: string(getMetricType(cfg.Symbol, pdu)),
107
+ MetricType: getMetricType(cfg.Symbol, pdu),
108
}, nil
109
}
110
src/go/plugin/go.d/collector/snmp/ddsnmp/ddsnmpcollector/collector.go
+3
-1
@@ -13,6 +13,7 @@ import (
13
14
"github.com/netdata/netdata/go/plugins/logger"
15
"github.com/netdata/netdata/go/plugins/plugin/go.d/collector/snmp/ddsnmp"
16
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/snmp/ddsnmp/ddprofiledefinition"
17
)
18
19
type ProfileMetrics struct {
@@ -24,8 +25,9 @@ type ProfileMetrics struct {
25
type Metric struct {
26
Name string
27
Description string
28
+ Family string
29
Unit string
28
- MetricType string
30
+ MetricType ddprofiledefinition.ProfileMetricType
31
Tags map[string]string
32
Mappings map[int64]string
33
Value int64