@cryptotaxi247 / netdata-1 / commits / 9e0fa7635

chore(go.d/snmp/ddsnmp): use dd profile definition (#19886)

Ilya Mashchenko committed Mar 17, 2025 at 15:38 UTC 9e0fa763554486acc19b63f74e393070f552d737
6 files changed +178 -141
src/go/go.mod
+7 -2
@@ -6,6 +6,7 @@ replace github.com/prometheus/prometheus => github.com/prometheus/prometheus v0.
6
7 require (
8 github.com/DATA-DOG/go-sqlmock v1.5.2
9 + github.com/DataDog/datadog-agent/pkg/networkdevice/profile v0.65.0-devel.0.20250317105920-ce55f088ab29
10 github.com/Masterminds/sprig/v3 v3.3.0
11 github.com/Wing924/ltsv v0.4.0
12 github.com/apparentlymart/go-cidr v1.1.0
@@ -72,6 +73,8 @@ require (
73 github.com/Masterminds/goutils v1.1.1 // indirect
74 github.com/Masterminds/semver/v3 v3.3.0 // indirect
75 github.com/Microsoft/go-winio v0.6.1 // indirect
76 + github.com/bahlo/generic-list-go v0.2.0 // indirect
77 + github.com/buger/jsonparser v1.1.1 // indirect
78 github.com/cespare/xxhash/v2 v2.3.0 // indirect
79 github.com/containerd/log v0.1.0 // indirect
80 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
@@ -99,6 +102,7 @@ require (
102 github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
103 github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
104 github.com/huandu/xstrings v1.5.0 // indirect
105 + github.com/invopop/jsonschema v0.12.0 // indirect
106 github.com/jackc/chunkreader/v2 v2.0.1 // indirect
107 github.com/jackc/pgconn v1.14.3 // indirect
108 github.com/jackc/pgio v1.0.0 // indirect
@@ -113,7 +117,7 @@ require (
117 github.com/kamstrup/intmap v0.5.1 // indirect
118 github.com/klauspost/compress v1.17.11 // indirect
119 github.com/likexian/gokit v0.25.15 // indirect
116 - github.com/mailru/easyjson v0.7.7 // indirect
120 + github.com/mailru/easyjson v0.9.0 // indirect
121 github.com/mdlayher/genetlink v1.3.2 // indirect
122 github.com/mdlayher/netlink v1.7.2 // indirect
123 github.com/mdlayher/socket v0.4.1 // indirect
@@ -133,9 +137,10 @@ require (
137 github.com/shopspring/decimal v1.4.0 // indirect
138 github.com/sirupsen/logrus v1.9.3 // indirect
139 github.com/spf13/cast v1.7.0 // indirect
136 - github.com/spf13/pflag v1.0.5 // indirect
140 + github.com/spf13/pflag v1.0.6 // indirect
141 github.com/tidwall/match v1.1.1 // indirect
142 github.com/tidwall/pretty v1.2.0 // indirect
143 + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
144 github.com/x448/float16 v0.8.4 // indirect
145 github.com/xdg-go/pbkdf2 v1.0.0 // indirect
146 github.com/xdg-go/scram v1.1.2 // indirect
src/go/go.sum
+14 -4
@@ -24,6 +24,8 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83
24 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
25 github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
26 github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
27 +github.com/DataDog/datadog-agent/pkg/networkdevice/profile v0.65.0-devel.0.20250317105920-ce55f088ab29 h1:iaMpL007/uujrcE8kQyrmB4rfu+nW8Ugh82CI5Vwbos=
28 +github.com/DataDog/datadog-agent/pkg/networkdevice/profile v0.65.0-devel.0.20250317105920-ce55f088ab29/go.mod h1:BkwwzS9aVK+QQb9E+fF9q9ZYScH3Xvnt2dKcE5ZJbMw=
29 github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
30 github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
31 github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
@@ -47,6 +49,8 @@ github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk
49 github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
50 github.com/axiomhq/hyperloglog v0.2.5 h1:Hefy3i8nAs8zAI/tDp+wE7N+Ltr8JnwiW3875pvl0N8=
51 github.com/axiomhq/hyperloglog v0.2.5/go.mod h1:DLUK9yIzpU5B6YFLjxTIcbHu1g4Y1WQb1m5RH3radaM=
52 +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
53 +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
54 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
55 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
56 github.com/bitly/go-hostpool v0.1.0 h1:XKmsF6k5el6xHG3WPJ8U0Ku/ye7njX7W81Ng7O2ioR0=
@@ -61,6 +65,8 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
65 github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
66 github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
67 github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
68 +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
69 +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
70 github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
71 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
72 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
@@ -192,6 +198,8 @@ github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
198 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
199 github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
200 github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
201 +github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI=
202 +github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
203 github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
204 github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
205 github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
@@ -302,8 +310,8 @@ github.com/likexian/whois-parser v1.24.20 h1:oxEkRi0GxgqWQRLDMJpXU1EhgWmLmkqEFZ2
310 github.com/likexian/whois-parser v1.24.20/go.mod h1:rAtaofg2luol09H+ogDzGIfcG8ig1NtM5R16uQADDz4=
311 github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y=
312 github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
305 -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
306 -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
313 +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
314 +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
315 github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
316 github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
317 github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -413,8 +421,8 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS
421 github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
422 github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
423 github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
416 -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
417 -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
424 +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
425 +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
426 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
427 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
428 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -442,6 +450,8 @@ github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXV
450 github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
451 github.com/vmware/govmomi v0.49.0 h1:M80ExmFq3kOfeMvMJcHnXgA/4w5hUAFfYfc+Qm3lmPg=
452 github.com/vmware/govmomi v0.49.0/go.mod h1:+oZ0tYJw/pXKoeWHLR9Egq5KENVr2hLePRzisFhEWpA=
453 +github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
454 +github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
455 github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
456 github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
457 github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
src/go/plugin/go.d/collector/snmp/ddsnmp/load.go
+20 -24
@@ -9,8 +9,12 @@ import (
9 "strings"
10
11 "gopkg.in/yaml.v2"
12 +
13 + "github.com/netdata/netdata/go/plugins/logger"
14 )
15
16 +var log = logger.New().With("component", "snmp/ddsnmp")
17 +
18 func load(dirpath string) ([]*Profile, error) {
19 var profiles []*Profile
20
@@ -18,10 +22,18 @@ func load(dirpath string) ([]*Profile, error) {
22 if !(strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml")) {
23 return nil
24 }
21 - profile, err := loadYAML(path)
25 +
26 + profile, err := loadProfile(path)
27 if err != nil {
23 - return err
28 + log.Warningf("invalid profile '%s': %v", path, err)
29 + return nil
30 }
31 +
32 + if err := profile.validate(); err != nil {
33 + log.Warningf("invalid profile '%s': %v", path, err)
34 + return nil
35 + }
36 +
37 profiles = append(profiles, profile)
38 return nil
39 }); err != nil {
@@ -31,14 +43,14 @@ func load(dirpath string) ([]*Profile, error) {
43 return profiles, nil
44 }
45
34 -func loadYAML(filename string) (*Profile, error) {
46 +func loadProfile(filename string) (*Profile, error) {
47 content, err := os.ReadFile(filename)
48 if err != nil {
49 return nil, err
50 }
51
52 var prof Profile
41 - if err := yaml.Unmarshal(content, &prof); err != nil {
53 + if err := yaml.Unmarshal(content, &prof.Definition); err != nil {
54 return nil, err
55 }
56
@@ -48,30 +60,14 @@ func loadYAML(filename string) (*Profile, error) {
60
61 dir := filepath.Dir(filename)
62
51 - for _, name := range prof.Extends {
52 - baseProf, err := loadYAML(filepath.Join(dir, name))
63 + for _, name := range prof.Definition.Extends {
64 + baseProf, err := loadProfile(filepath.Join(dir, name))
65 if err != nil {
66 return nil, err
67 }
56 - mergeProfiles(&prof, baseProf)
68 +
69 + prof.merge(baseProf)
70 }
71
72 return &prof, nil
73 }
61 -
62 -func mergeProfiles(child, parent *Profile) {
63 - child.Metrics = append(parent.Metrics, child.Metrics...)
64 - //
65 - //if child.Metadata == nil || len(child.Metadata.Device) == 0 {
66 - // return
67 - //}
68 - //if child.Metadata.Device.Fields == nil {
69 - // child.Metadata.Device.Fields = make(map[string]Symbol)
70 - //}
71 - //
72 - //for key, value := range parent.Metadata.Device.Fields {
73 - // if _, exists := child.Metadata.Device.Fields[key]; !exists {
74 - // child.Metadata.Device.Fields[key] = value
75 - // }
76 - //}
77 -}
src/go/plugin/go.d/collector/snmp/ddsnmp/load_test.go
+3 -3
@@ -4,14 +4,14 @@ package ddsnmp
4
5 import (
6 "os"
7 + "path/filepath"
8 "testing"
9
9 - "github.com/stretchr/testify/assert"
10 "github.com/stretchr/testify/require"
11 )
12
13 func Test_loadDDSnmpProfiles(t *testing.T) {
14 - dir := "../../../config/go.d/snmp.profiles/default"
14 + dir, _ := filepath.Abs("../../../config/go.d/snmp.profiles/default")
15
16 f, err := os.Open(dir)
17 require.NoError(t, err)
@@ -23,5 +23,5 @@ func Test_loadDDSnmpProfiles(t *testing.T) {
23 names, err := f.Readdirnames(-1)
24 require.NoError(t, err)
25
26 - assert.Equal(t, len(names)-1 /*README.md*/, len(profiles))
26 + require.Equal(t, len(names)-1 /*README.md*/, len(profiles))
27 }
src/go/plugin/go.d/collector/snmp/ddsnmp/profile.go
+91 -108
@@ -3,131 +3,114 @@
3 package ddsnmp
4
5 import (
6 - "fmt"
7 -)
6 + "errors"
7 + "os"
8 + "path/filepath"
9 + "sync"
10 +
11 + "github.com/DataDog/datadog-agent/pkg/networkdevice/profile/profiledefinition"
12
9 -const (
10 - MetricTypeGauge = "gauge"
11 - MetricTypeRate = "rate"
12 - MetricTypePercent = "percent"
13 - MetricTypeMonotonicCount = "monotonic_count"
14 - MetricTypeMonotonicCountAndRate = "monotonic_count_and_rate"
15 - MetricTypeFlagStream = "flag_stream" // https://datadoghq.dev/integrations-core/tutorials/snmp/profile-format/#flag-stream
13 + "github.com/netdata/netdata/go/plugins/pkg/executable"
14 + "github.com/netdata/netdata/go/plugins/pkg/matcher"
15 )
16
18 -// Profile is Datadog SNMP profile (format: https://datadoghq.dev/integrations-core/tutorials/snmp/profile-format/)
19 -type Profile struct {
20 - SourceFile string
17 +var once sync.Once
18 +var ddProfiles []*Profile
19 +
20 +func Find(sysObjId string) []*Profile {
21 + once.Do(func() {
22 + dir := os.Getenv("NETDATA_STOCK_CONFIG_DIR")
23 + if dir == "" {
24 + dir = filepath.Join(executable.Directory, "../../../../usr/lib/netdata/conf.d/go.d/snmp.profiles/default/")
25 + }
26 +
27 + profiles, err := load(dir)
28 + if err != nil {
29 + log.Errorf("failed to load dd snmp profiles: %v", err)
30 + return
31 + }
32 + if len(profiles) == 0 {
33 + log.Warningf("no dd snmp profiles found in '%s'", dir)
34 + return
35 + }
36 +
37 + ddProfiles = profiles
38 + })
39 +
40 + var profiles []*Profile
41 +
42 + for _, prof := range ddProfiles {
43 + for _, id := range prof.Definition.SysObjectIDs {
44 + m, err := matcher.NewRegExpMatcher(id)
45 + if err != nil {
46 + log.Warningf("failed to compile regular expression from '%s': %v", id, err)
47 + continue
48 + }
49 + if m.MatchString(sysObjId) {
50 + profiles = append(profiles, prof.clone())
51 + }
52 + }
53 + }
54
22 - Extends []string `yaml:"extends"` // +done
23 - SysObjectID SysObjectIDs `yaml:"sysobjectid"` // +done
24 - Metrics []Metric `yaml:"metrics"`
25 - Metadata *Metadata `yaml:"metadata"` // +done
26 - MetricTags []GlobalMetricTag `yaml:"metric_tags"` // +done
55 + return profiles
56 }
57
29 -type SysObjectIDs []string
58 +type Profile struct {
59 + SourceFile string `yaml:"-"`
60 + Definition *profiledefinition.ProfileDefinition `yaml:",inline"`
61 +}
62
31 -type (
32 - // Metric defines which metrics will be collected by the profile.
33 - // Can reference either a single OID (a.k.a symbol), or an SNMP table.
34 - // https://datadoghq.dev/integrations-core/tutorials/snmp/profile-format/#metrics
35 - Metric struct {
36 - Name string `yaml:"name"`
37 - OID string `yaml:"OID"`
63 +func (p *Profile) clone() *Profile {
64 + return &Profile{
65 + SourceFile: p.SourceFile,
66 + Definition: p.Definition.Clone(),
67 + }
68 +}
69
39 - // Typically a symbol will be inferred from the SNMP type
40 - // https://datadoghq.dev/integrations-core/tutorials/snmp/profile-format/#metric-type-inference
41 - // Can be overwritten using "metric_type"
42 - // https://datadoghq.dev/integrations-core/tutorials/snmp/profile-format/#forced-metric-types
43 - MetricType string `yaml:"metric_type"`
70 +func (p *Profile) merge(base *Profile) {
71 + p.Definition.Metrics = append(p.Definition.Metrics, base.Definition.Metrics...)
72 + p.Definition.MetricTags = append(p.Definition.MetricTags, base.Definition.MetricTags...)
73 + p.Definition.StaticTags = append(p.Definition.StaticTags, base.Definition.StaticTags...)
74
45 - Options map[string]string
75 + if p.Definition.Metadata == nil {
76 + p.Definition.Metadata = make(profiledefinition.MetadataConfig)
77 + }
78
47 - MIB string `yaml:"MIB"`
79 + for resName, baseRes := range base.Definition.Metadata {
80 + targetRes, exists := p.Definition.Metadata[resName]
81 + if !exists {
82 + targetRes = profiledefinition.NewMetadataResourceConfig()
83 + }
84
49 - // Symbol metric
50 - // https://datadoghq.dev/integrations-core/tutorials/snmp/profile-format/#symbol-metrics
51 - Symbol *Symbol `yaml:"symbol"`
85 + targetRes.IDTags = append(targetRes.IDTags, baseRes.IDTags...)
86
53 - // Table metric
54 - // https://datadoghq.dev/integrations-core/tutorials/snmp/profile-format/#table-metrics
55 - Table *MetricTable `yaml:"table"`
56 - Symbols []Symbol `yaml:"symbols"`
87 + if targetRes.Fields == nil && len(baseRes.Fields) > 0 {
88 + targetRes.Fields = make(map[string]profiledefinition.MetadataField, len(baseRes.Fields))
89 + }
90
58 - MetricTags []MetricTag `yaml:"metric_tags"` //TODO check for only name existing in metric tag, as there is some case for that
59 - }
60 - MetricTable struct {
61 - OID string `yaml:"OID"`
62 - Name string `yaml:"name"`
63 - }
64 - // MetricTag used for Table metrics to identify each row's metric.
65 - // https://datadoghq.dev/integrations-core/tutorials/snmp/profile-format/#table-metrics-tagging
66 - MetricTag struct {
67 - MIB string `yaml:"mib"`
68 - Table string `yaml:"table"`
69 - Tag string `yaml:"tag"`
70 - Symbol Symbol `yaml:"symbol"`
71 - IndexTransform []IndexSlice `yaml:"index_transform"`
72 -
73 - Mapping map[int]string `yaml:"mapping"`
74 - Index int `yaml:"index"`
75 - }
76 - Symbol struct {
77 - OID string `yaml:"OID"`
78 - Name string `yaml:"name"`
79 - ExtractValue string `yaml:"extract_value"`
80 - MatchPattern string `yaml:"match_pattern"`
81 - MatchValue string `yaml:"match_value"`
82 - Format string `yaml:"format"`
83 - ScaleFactor float64 `yaml:"scale_factor"`
84 - }
85 - IndexSlice struct {
86 - Start int `yaml:"start"`
87 - End int `yaml:"end"`
88 - }
89 -)
91 + for field, symbol := range baseRes.Fields {
92 + if _, ok := targetRes.Fields[field]; !ok {
93 + targetRes.Fields[field] = symbol
94 + }
95 + }
96
91 -type (
92 - // Metadata used to declare where and how metadata should be collected
93 - // https://datadoghq.dev/integrations-core/tutorials/snmp/profile-format/#metadata
94 - Metadata struct {
95 - Device DeviceMetadata `yaml:"device"`
96 - }
97 - DeviceMetadata struct {
98 - Fields map[string]MetadataField `yaml:"fields"`
99 - }
100 - MetadataField struct {
101 - Value *string `yaml:"value"`
102 - Symbol *Symbol `yaml:"symbol"`
103 - Symbols []Symbol `yaml:"symbols"`
97 + p.Definition.Metadata[resName] = targetRes
98 }
105 -)
106 -
107 -// GlobalMetricTag used to apply tags to all metrics collected by the profile
108 -// https://datadoghq.dev/integrations-core/tutorials/snmp/profile-format/#metric_tags
109 -type GlobalMetricTag struct {
110 - OID string `yaml:"OID"`
111 - Symbol string `yaml:"symbol"`
112 - Tag string `yaml:"tag"`
113 -
114 - Match string `yaml:"match"`
115 - Tags map[string]string `yaml:"tags"`
116 - Mapping map[int]string `yaml:"mapping"`
99 }
100
119 -func (s *SysObjectIDs) UnmarshalYAML(unmarshal func(any) error) error {
120 - var single string
121 - if err := unmarshal(&single); err == nil {
122 - *s = []string{single}
123 - return nil
124 - }
125 -
126 - var multiple []string
127 - if err := unmarshal(&multiple); err == nil {
128 - *s = multiple
129 - return nil
101 +func (p *Profile) validate() error {
102 + profiledefinition.NormalizeMetrics(p.Definition.Metrics)
103 +
104 + errs := profiledefinition.ValidateEnrichMetadata(p.Definition.Metadata)
105 + errs = append(errs, profiledefinition.ValidateEnrichMetrics(p.Definition.Metrics)...)
106 + errs = append(errs, profiledefinition.ValidateEnrichMetricTags(p.Definition.MetricTags)...)
107 + if len(errs) > 0 {
108 + errList := make([]error, 0, len(errs))
109 + for _, s := range errs {
110 + errList = append(errList, errors.New(s))
111 + }
112 + return errors.Join(errList...)
113 }
114
132 - return fmt.Errorf("invalid sysobjectid format")
115 + return nil
116 }
src/go/plugin/go.d/collector/snmp/ddsnmp/profile_test.go new
+43
@@ -0,0 +1,43 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +package ddsnmp
4 +
5 +import (
6 + "os"
7 + "path/filepath"
8 + "testing"
9 +
10 + "github.com/stretchr/testify/require"
11 +)
12 +
13 +func TestMain(m *testing.M) {
14 + dir, _ := filepath.Abs("../../../config/go.d/snmp.profiles/default")
15 + _ = os.Setenv("NETDATA_STOCK_CONFIG_DIR", dir)
16 + code := m.Run()
17 + _ = os.Unsetenv("NETDATA_STOCK_CONFIG_DIR")
18 + os.Exit(code)
19 +}
20 +
21 +func Test_Find(t *testing.T) {
22 + test := map[string]struct {
23 + sysObjOId string
24 + wanProfiles int
25 + }{
26 + "mikrotik": {
27 + sysObjOId: "1.3.6.1.4.1.14988.1",
28 + wanProfiles: 2,
29 + },
30 + "no match": {
31 + sysObjOId: "0.1.2.3",
32 + wanProfiles: 0,
33 + },
34 + }
35 +
36 + for name, test := range test {
37 + t.Run(name, func(t *testing.T) {
38 + profiles := Find(test.sysObjOId)
39 +
40 + require.Len(t, profiles, test.wanProfiles)
41 + })
42 + }
43 +}