Initial commit with snmp profile code (#19813)
* initial commit with snmp profile code that does not affect functionality * remove old and possibly useless functions * move loading dd snmp profile to ddsnmp subpkg --------- Co-authored-by: Ilya Mashchenko <ilya@netdata.cloud>
Fotis Voutsas committed
Mar 14, 2025 at 15:18 UTC
a27bd240989b86cde5c31e23f760c38af84a4e25
9 files changed
+1501
src/go/plugin/go.d/collector/snmp/collect.go
+27
@@ -51,6 +51,33 @@ func (c *Collector) collect() (map[string]int64, error) {
51
return mx, nil
52
}
53
54
+func (c *Collector) getSysObjectID(oid string) (string, error) {
55
+ resp, err := c.snmpClient.Get([]string{oid})
56
+ if err != nil {
57
+ return "", err
58
+ }
59
+
60
+ return strings.Replace(resp.Variables[0].Value.(string), ".", "", 1), nil
61
+}
62
+
63
+func (c *Collector) makeChartsFromMetricMap(mx map[string]int64, metricMap map[string]processedMetric) error {
64
+
65
+ for _, metric := range metricMap {
66
+ if metric.tableName == "" {
67
+ switch s := metric.value.(type) {
68
+ case int:
69
+
70
+ // log.Println(metric)
71
+
72
+ // c.addSNMPChart(metric)
73
+ mx[metric.name] = int64(s)
74
+
75
+ }
76
+ }
77
+ }
78
+ return nil
79
+}
80
+
81
func (c *Collector) collectSysUptime(mx map[string]int64) error {
82
resp, err := c.snmpClient.Get([]string{snmpsd.OidSysUptime})
83
if err != nil {
src/go/plugin/go.d/collector/snmp/command_func.go
new
+43
@@ -0,0 +1,43 @@
1
+package snmp
2
+
3
+import (
4
+ "fmt"
5
+ "log"
6
+ "strings"
7
+)
8
+
9
+func (c *Collector) walkOIDTree(baseOID string) (map[string]processedMetric, error) {
10
+ tableRows := make(map[string]processedMetric)
11
+
12
+ currentOID := baseOID
13
+ for {
14
+ result, err := c.snmpClient.GetNext([]string{currentOID})
15
+ if err != nil {
16
+ return tableRows, fmt.Errorf("snmpgetnext failed: %v", err)
17
+ }
18
+ if len(result.Variables) == 0 {
19
+ log.Println("No OID returned, ending walk.")
20
+ return tableRows, nil
21
+ }
22
+ pdu := result.Variables[0]
23
+
24
+ nextOID := strings.Replace(pdu.Name, ".", "", 1) //remove dot at the start of the OID
25
+ // fmt.Println(nextOID, baseOID)
26
+
27
+ // If the next OID does not start with the base OID, we've reached the end of the subtree.
28
+ if !strings.HasPrefix(nextOID, baseOID) {
29
+ return tableRows, nil
30
+ }
31
+
32
+ metricType := pdu.Type
33
+ value := fmt.Sprintf("%v", pdu.Value)
34
+
35
+ tableRows[nextOID] = processedMetric{
36
+ oid: nextOID,
37
+ value: value,
38
+ metric_type: metricType,
39
+ }
40
+
41
+ currentOID = nextOID
42
+ }
43
+}
src/go/plugin/go.d/collector/snmp/ddsnmp/load.go
new
+77
@@ -0,0 +1,77 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package ddsnmp
4
+
5
+import (
6
+ "io/fs"
7
+ "os"
8
+ "path/filepath"
9
+ "strings"
10
+
11
+ "gopkg.in/yaml.v2"
12
+)
13
+
14
+func load(dirpath string) ([]*Profile, error) {
15
+ var profiles []*Profile
16
+
17
+ if err := filepath.WalkDir(dirpath, func(path string, d fs.DirEntry, err error) error {
18
+ if !(strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml")) {
19
+ return nil
20
+ }
21
+ profile, err := loadYAML(path)
22
+ if err != nil {
23
+ return err
24
+ }
25
+ profiles = append(profiles, profile)
26
+ return nil
27
+ }); err != nil {
28
+ return nil, err
29
+ }
30
+
31
+ return profiles, nil
32
+}
33
+
34
+func loadYAML(filename string) (*Profile, error) {
35
+ content, err := os.ReadFile(filename)
36
+ if err != nil {
37
+ return nil, err
38
+ }
39
+
40
+ var prof Profile
41
+ if err := yaml.Unmarshal(content, &prof); err != nil {
42
+ return nil, err
43
+ }
44
+
45
+ if prof.SourceFile == "" {
46
+ prof.SourceFile, _ = filepath.Abs(filename)
47
+ }
48
+
49
+ dir := filepath.Dir(filename)
50
+
51
+ for _, name := range prof.Extends {
52
+ baseProf, err := loadYAML(filepath.Join(dir, name))
53
+ if err != nil {
54
+ return nil, err
55
+ }
56
+ mergeProfiles(&prof, baseProf)
57
+ }
58
+
59
+ return &prof, nil
60
+}
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
new
+27
@@ -0,0 +1,27 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package ddsnmp
4
+
5
+import (
6
+ "os"
7
+ "testing"
8
+
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"
15
+
16
+ f, err := os.Open(dir)
17
+ require.NoError(t, err)
18
+ defer f.Close()
19
+
20
+ profiles, err := load(dir)
21
+ require.NoError(t, err)
22
+
23
+ names, err := f.Readdirnames(-1)
24
+ require.NoError(t, err)
25
+
26
+ assert.Equal(t, len(names)-1 /*README.md*/, len(profiles))
27
+}
src/go/plugin/go.d/collector/snmp/ddsnmp/profile.go
new
+133
@@ -0,0 +1,133 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package ddsnmp
4
+
5
+import (
6
+ "fmt"
7
+)
8
+
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
16
+)
17
+
18
+// Profile is Datadog SNMP profile (format: https://datadoghq.dev/integrations-core/tutorials/snmp/profile-format/)
19
+type Profile struct {
20
+ SourceFile string
21
+
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
27
+}
28
+
29
+type SysObjectIDs []string
30
+
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"`
38
+
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"`
44
+
45
+ Options map[string]string
46
+
47
+ MIB string `yaml:"MIB"`
48
+
49
+ // Symbol metric
50
+ // https://datadoghq.dev/integrations-core/tutorials/snmp/profile-format/#symbol-metrics
51
+ Symbol *Symbol `yaml:"symbol"`
52
+
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"`
57
+
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
+)
90
+
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"`
104
+ }
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"`
117
+}
118
+
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
130
+ }
131
+
132
+ return fmt.Errorf("invalid sysobjectid format")
133
+}
src/go/plugin/go.d/collector/snmp/helpers.go
new
+73
@@ -0,0 +1,73 @@
1
+package snmp
2
+
3
+func sliceToStrings(items []interface{}) []string {
4
+ var strs []string
5
+ for _, v := range items {
6
+ s, ok := v.(string)
7
+ if !ok {
8
+ // Handle error if an element is not a string.
9
+ continue
10
+ }
11
+ strs = append(strs, s)
12
+ }
13
+ return strs
14
+}
15
+
16
+func sliceToTableMetricTags(items []interface{}) []TableMetricTag {
17
+ var metricTag []TableMetricTag
18
+ for _, v := range items {
19
+ s, ok := v.(TableMetricTag)
20
+ if !ok {
21
+ // Handle error if an element is not a string.
22
+ continue
23
+ }
24
+ metricTag = append(metricTag, s)
25
+ }
26
+ return metricTag
27
+}
28
+
29
+func mergeTableBatches(target tableBatches, source tableBatches) tableBatches {
30
+ merged := tableBatches{}
31
+
32
+ // Extend batches in `target` with OIDs from `source` that share the same key.
33
+ for key, batch := range target {
34
+
35
+ if srcBatch, ok := source[key]; ok {
36
+ mergedOids := append(batch.oids, srcBatch.oids...)
37
+ merged[key] = tableBatch{
38
+ tableOID: batch.tableOID,
39
+ oids: mergedOids,
40
+ }
41
+ }
42
+ }
43
+
44
+ for key := range source {
45
+ if _, ok := target[key]; !ok {
46
+ merged[key] = source[key]
47
+ }
48
+ }
49
+
50
+ return merged
51
+}
52
+
53
+func mergeStringMaps(m1 map[string]string, m2 map[string]string) map[string]string {
54
+ merged := make(map[string]string)
55
+ for k, v := range m1 {
56
+ merged[k] = v
57
+ }
58
+ for key, value := range m2 {
59
+ merged[key] = value
60
+ }
61
+ return merged
62
+}
63
+
64
+func mergeProcessedMetricMaps(m1 map[string]processedMetric, m2 map[string]processedMetric) map[string]processedMetric {
65
+ merged := make(map[string]processedMetric)
66
+ for k, v := range m1 {
67
+ merged[k] = v
68
+ }
69
+ for key, value := range m2 {
70
+ merged[key] = value
71
+ }
72
+ return merged
73
+}
src/go/plugin/go.d/collector/snmp/parsing.go
new
+700
@@ -0,0 +1,700 @@
1
+package snmp
2
+
3
+import (
4
+ "errors"
5
+ "fmt"
6
+ "log"
7
+ "reflect"
8
+ "regexp"
9
+)
10
+
11
+func parseMetrics(metrics []Metric) (parsedResult, error) {
12
+ oids := []string{}
13
+ next_oids := []string{}
14
+ bulk_oids := []string{}
15
+ parsed_metrics := []parsedMetric{}
16
+ oids_to_resolve := []map[string]string{}
17
+ indexes_to_resolve := []indexMapping{}
18
+ bulk_threshold := 0
19
+ for _, metric := range metrics {
20
+ result, err := parseMetric(metric)
21
+
22
+ if err != nil {
23
+ return parsedResult{}, err
24
+ }
25
+
26
+ oids = append(oids, result.oidsToFetch...)
27
+
28
+ for name, oid := range result.oidsToResolve {
29
+ // here in the python implementation a registration happens to their OIDResolver. I will not support this atm
30
+ oids_to_resolve = append(oids_to_resolve, map[string]string{name: oid})
31
+ }
32
+
33
+ // here in the python implementation a registration happens to their OIDResolver. I will not support this atm
34
+ indexes_to_resolve = append(indexes_to_resolve, result.indexMappings...)
35
+
36
+ for _, batch := range result.tableBatches {
37
+ should_query_in_bulk := bulk_threshold > 0 && len(batch.oids) > bulk_threshold
38
+ if should_query_in_bulk {
39
+ bulk_oids = append(bulk_oids, batch.tableOID)
40
+ } else {
41
+ next_oids = append(next_oids, batch.oids...)
42
+ }
43
+ }
44
+
45
+ parsed_metrics = append(parsed_metrics, result.parsedMetrics...)
46
+
47
+ }
48
+ return parsedResult{oids: oids,
49
+ next_oids: next_oids, bulk_oids: bulk_oids, parsed_metrics: parsed_metrics}, nil
50
+}
51
+
52
+func parseMetric(metric Metric) (metricParseResult, error) {
53
+ /*Can either be:
54
+
55
+ * An OID metric:
56
+
57
+ ```
58
+ metrics:
59
+ - OID: 1.3.6.1.2.1.2.2.1.14
60
+ name: ifInErrors
61
+ ```
62
+
63
+ * A symbol metric:
64
+
65
+ ```
66
+ metrics:
67
+ - MIB: IF-MIB
68
+ symbol: ifInErrors
69
+ # OR:
70
+ symbol:
71
+ OID: 1.3.6.1.2.1.2.2.1.14
72
+ name: ifInErrors
73
+ ```
74
+
75
+ * A table metric (see parsing for table metrics for all possible options):
76
+
77
+ ```
78
+ metrics:
79
+ - MIB: IF-MIB
80
+ table: ifTable
81
+ symbols:
82
+ - OID: 1.3.6.1.2.1.2.2.1.14
83
+ name: ifInErrors
84
+ ```*/
85
+
86
+ // Cast the tags to either array
87
+ castedStringMetricTags := []string{}
88
+ castedTableMetricTags := []TableMetricTag{}
89
+
90
+ if len(metric.MetricTags) > 0 {
91
+ /*//TODO investigate if there are metric tags and not only table metric tags
92
+ fmt.Println("parseMetric MetricTags switch, have:")
93
+ spew.Dump(metric.MetricTags)
94
+ switch metric.MetricTags[0].(type) {
95
+ case string:
96
+ os.Exit(-40)
97
+ castedStringMetricTags = sliceToStrings(metric.MetricTags)
98
+
99
+ case MetricTag:
100
+ os.Exit(-40)
101
+ castedTableMetricTags = sliceToTableMetricTags(metric.MetricTags)
102
+ }*/
103
+ }
104
+
105
+ if len(metric.OID) > 0 {
106
+ // TODO investigate if this exists in the yamls
107
+ return (parseOIDMetric(oidMetric{name: metric.Name, oid: metric.OID, metricTags: castedStringMetricTags, forcedType: metric.MetricType, options: metric.Options})), nil
108
+ } else if len(metric.MIB) == 0 {
109
+ return metricParseResult{}, fmt.Errorf("unsupported metric {%v}", metric)
110
+ } else if metric.Symbol != (Symbol{}) {
111
+ // Single Metric
112
+ return (parseSymbolMetric(symbolMetric{mib: metric.MIB, symbol: metric.Symbol, forcedType: metric.MetricType, metricTags: castedStringMetricTags, options: metric.Options}))
113
+ } else if metric.Table != nil {
114
+ // Table
115
+ if len(metric.MetricTags) > 0 {
116
+
117
+ /*//This will be cleared out when tags are supported
118
+ // for _, rawItem := range metric.MetricTags {
119
+ // item, ok := rawItem.(map[string]interface{})
120
+ // if !ok {
121
+ // continue
122
+ // }
123
+ // fmt.Println("ITEM", item["symbol"])
124
+
125
+ // var index int
126
+ // if val, exists := item["Index"]; exists {
127
+ // if i, ok := val.(int); ok {
128
+ // index = i
129
+ // } else {
130
+ // index = -1
131
+ // }
132
+ // }
133
+
134
+ // var mapping map[int]string
135
+ // if val, exists := item["mapping"]; exists {
136
+ // if m, ok := val.(map[int]string); ok {
137
+ // fmt.Print("IN")
138
+ // os.Exit(-90)
139
+ // mapping = m
140
+ // }
141
+ // }
142
+
143
+ // var tag string
144
+ // if val, exists := item["Tag"]; exists {
145
+ // if s, ok := val.(string); ok {
146
+ // tag = s
147
+ // }
148
+ // }
149
+
150
+ // var symbol Symbol
151
+ // if rawSymbol, exists := item["symbol"]; exists {
152
+ // if symMap, ok := rawSymbol.(map[string]interface{}); ok {
153
+ // var oid, name string
154
+
155
+ // if v, exists := symMap["OID"]; exists {
156
+ // if oidStr, ok := v.(string); ok {
157
+ // oid = oidStr
158
+ // }
159
+ // }
160
+
161
+ // if v, exists := symMap["name"]; exists {
162
+ // if nameStr, ok := v.(string); ok {
163
+ // name = nameStr
164
+ // }
165
+ // }
166
+
167
+ // symbol = Symbol{OID: oid, Name: name}
168
+ // } else {
169
+ // fmt.Println("symbol is not a map[string]interface{}")
170
+ // }
171
+ // }
172
+
173
+ // var table string
174
+ // if val, exists := item["Table"]; exists {
175
+ // if s, ok := val.(string); ok {
176
+ // table = s
177
+ // } else {
178
+ // table = ""
179
+ // }
180
+ // }
181
+
182
+ // var mib string
183
+ // if val, exists := item["MIB"]; exists {
184
+ // if s, ok := val.(string); ok {
185
+ // mib = s
186
+ // } else {
187
+ // mib = ""
188
+ // }
189
+ // }
190
+
191
+ // var indexTransform []IndexSlice
192
+ // if val, exists := item["IndexTransform"]; exists {
193
+ // if xs, ok := val.([]IndexSlice); ok {
194
+ // indexTransform = xs
195
+ // }
196
+ // }*/
197
+
198
+ castedTableMetricTags = append(castedTableMetricTags, metric.MetricTags...)
199
+ }
200
+
201
+ if metric.Symbols == nil {
202
+ return metricParseResult{}, fmt.Errorf("when specifying a table, you must specify a list of symbols %v", metric)
203
+ }
204
+
205
+ return (parseTableMetric(tableMetric{
206
+ mib: metric.MIB,
207
+ table: metric.Table,
208
+ symbols: metric.Symbols,
209
+ forcedType: metric.MetricType,
210
+ metricTags: castedTableMetricTags,
211
+ options: metric.Options,
212
+ }))
213
+
214
+ }
215
+ return metricParseResult{}, fmt.Errorf("unsupported metric {%v}", metric)
216
+}
217
+
218
+// TODO error outs on functions
219
+func parseOIDMetric(metric oidMetric) metricParseResult {
220
+ /*Parse a fully resolved OID/name metric.
221
+
222
+ Note: This `OID/name` syntax is deprecated in favour of `symbol` syntax.
223
+
224
+ Example:
225
+
226
+ ```
227
+ metrics:
228
+ - OID: 1.3.6.1.2.1.2.1
229
+ name: ifNumber
230
+ ```
231
+ */
232
+ name := metric.name
233
+ oid := metric.oid
234
+
235
+ // TODO can't find a profile with this metric type
236
+
237
+ parsed_symbol_metric := parsedSymbolMetric{
238
+ name: name,
239
+ tags: metric.metricTags,
240
+ forcedType: metric.forcedType,
241
+ enforceScalar: true,
242
+ options: metric.options,
243
+ baseoid: oid,
244
+ }
245
+
246
+ return metricParseResult{
247
+ oidsToFetch: []string{oid},
248
+ oidsToResolve: map[string]string{name: oid},
249
+ parsedMetrics: []parsedMetric{parsed_symbol_metric},
250
+ tableBatches: nil,
251
+ indexMappings: nil,
252
+ }
253
+}
254
+
255
+// TODO error outs on functions
256
+func parseSymbolMetric(metric symbolMetric) (metricParseResult, error) {
257
+ /* Parse a symbol metric (= an OID in a MIB).
258
+ Example:
259
+
260
+ ```
261
+ metrics:
262
+ - MIB: IF-MIB
263
+ symbol: <string or OID/name object>
264
+ - MIB: IF-MIB
265
+ symbol: # MIB-less syntax
266
+ OID: 1.3.6.1.2.1.6.5.0
267
+ name: tcpActiveOpens
268
+ - MIB: IF-MIB
269
+ symbol: tcpActiveOpens # require MIB syntax
270
+ ```*/
271
+ mib := metric.mib
272
+ symbol := metric.symbol
273
+ parsed_symbol, err := parseSymbol(mib, symbol)
274
+ if err != nil {
275
+ return metricParseResult{}, err
276
+ }
277
+
278
+ parsed_symbol_metric := parsedSymbolMetric{
279
+ name: parsed_symbol.name,
280
+ tags: metric.metricTags,
281
+ forcedType: metric.forcedType,
282
+ enforceScalar: false,
283
+ options: metric.options,
284
+ extractValuePattern: parsed_symbol.extractValuePattern,
285
+ baseoid: parsed_symbol.oid,
286
+ }
287
+
288
+ return metricParseResult{
289
+ oidsToFetch: []string{parsed_symbol.oid},
290
+ oidsToResolve: parsed_symbol.oidsToResolve,
291
+ parsedMetrics: []parsedMetric{parsed_symbol_metric},
292
+ tableBatches: nil,
293
+ indexMappings: nil,
294
+ }, nil
295
+}
296
+
297
+// TODO error outs on functions
298
+func parseTableMetric(metric tableMetric) (metricParseResult, error) {
299
+
300
+ mib := metric.mib
301
+ parsed_table, err := parseSymbol(mib, metric.table)
302
+ if err != nil {
303
+ return metricParseResult{}, err
304
+ }
305
+
306
+ table_name := parsed_table.name
307
+ table_oid := parsed_table.oid
308
+
309
+ oids_to_resolve := parsed_table.oidsToResolve
310
+
311
+ var index_tags []indexTag
312
+ var column_tags []columnTag
313
+ var index_mappings []indexMapping
314
+ var table_batches map[tableBatchKey]tableBatch
315
+
316
+ if metric.metricTags != nil {
317
+ for _, metric_tag := range metric.metricTags {
318
+ parsed_table_metric_tag, err := parseTableMetricTag(mib, parsed_table, metric_tag)
319
+ if err != nil {
320
+ return metricParseResult{}, err
321
+ }
322
+
323
+ if parsed_table_metric_tag.oidsToResolve != nil {
324
+ oids_to_resolve = mergeStringMaps(oids_to_resolve, parsed_table_metric_tag.oidsToResolve)
325
+
326
+ column_tags = append(column_tags, parsed_table_metric_tag.columnTags...)
327
+
328
+ table_batches = mergeTableBatches(table_batches, parsed_table_metric_tag.tableBatches)
329
+ } else {
330
+
331
+ index_tags = append(index_tags, parsed_table_metric_tag.indexTags...)
332
+
333
+ for index, mapping := range parsed_table_metric_tag.indexMappings {
334
+ for _, symbol := range metric.symbols {
335
+ index_mappings = append(index_mappings, indexMapping{tag: symbol.Name, index: index, mapping: mapping})
336
+ }
337
+
338
+ for _, tag := range metric.metricTags {
339
+ if reflect.DeepEqual(tag.Symbol, Symbol{}) {
340
+ tag = TableMetricTag{
341
+ Tag: tag.Tag,
342
+ Symbol: tag.Symbol,
343
+ }
344
+ index_mappings = append(index_mappings, indexMapping{
345
+ tag: tag.Symbol.Name,
346
+ index: index,
347
+ mapping: mapping,
348
+ })
349
+ }
350
+ }
351
+ }
352
+ }
353
+ }
354
+ }
355
+
356
+ table_oids := []string{}
357
+ parsed_metrics := []parsedMetric{}
358
+
359
+ for _, symbol := range metric.symbols {
360
+ parsed_symbol, err := parseSymbol(mib, symbol)
361
+ if err != nil {
362
+ return metricParseResult{}, nil
363
+ }
364
+
365
+ for key, value := range parsed_symbol.oidsToResolve {
366
+ oids_to_resolve[key] = value
367
+ }
368
+
369
+ table_oids = append(table_oids, parsed_symbol.oid)
370
+
371
+ parsed_table_metric := parsedTableMetric{
372
+ name: parsed_symbol.name,
373
+ indexTags: index_tags,
374
+ columnTags: column_tags,
375
+ forcedType: metric.forcedType,
376
+ options: metric.options,
377
+ extractValuePattern: parsed_symbol.extractValuePattern,
378
+ rowOID: parsed_symbol.oid,
379
+ tableName: table_name,
380
+ tableOID: table_oid,
381
+ }
382
+
383
+ parsed_metrics = append(parsed_metrics, parsed_table_metric)
384
+ }
385
+
386
+ table_batches = mergeTableBatches(table_batches, map[tableBatchKey]tableBatch{{mib: mib, table: parsed_table.name}: {tableOID: parsed_table.oid, oids: table_oids}})
387
+
388
+ return metricParseResult{
389
+ oidsToFetch: []string{},
390
+ oidsToResolve: oids_to_resolve,
391
+ tableBatches: table_batches,
392
+ indexMappings: index_mappings,
393
+ parsedMetrics: parsed_metrics,
394
+ }, nil
395
+}
396
+
397
+func parseTableMetricTag(mib string, parsed_table parsedSymbol, metric_tag TableMetricTag) (parsedTableMetricTag, error) {
398
+ /*
399
+ Parse an item of the `metric_tags` section of a table metric.
400
+
401
+ Items can be:
402
+
403
+ * A reference to a column in the same table.
404
+
405
+ Example using entPhySensorTable in ENTITY-SENSOR-MIB:
406
+
407
+ ```
408
+ metric_tags:
409
+ - tag: sensor_type
410
+ column: entPhySensorType
411
+ # OR
412
+ column:
413
+ OID: 1.3.6.1.2.1.99.1.1.1.1
414
+ name: entPhySensorType
415
+
416
+ ```
417
+
418
+ * A reference to a column in a different table.
419
+
420
+ Example:
421
+
422
+ ```
423
+ metric_tags:
424
+ - tag: adapter
425
+ table: genericAdaptersAttrTable
426
+ column: adapterName
427
+ # OR
428
+ column:
429
+ OID: 1.3.6.1.4.1.343.2.7.2.2.1.1.1.2
430
+ name: adapterName
431
+
432
+ ```
433
+
434
+ * A reference to an OID by its index in the table entry.
435
+
436
+ An optional `mapping` can be used to map index values to human-readable strings.
437
+
438
+ Example using ipIfStatsTable in IP-MIB:
439
+
440
+ ```
441
+ metric_tags:
442
+ - # ipIfStatsIPVersion (1.3.6.1.2.1.4.21.3.1.1)
443
+ tag: ip_version
444
+ index: 1
445
+ mapping:
446
+ 0: unknown
447
+ 1: ipv4
448
+ 2: ipv6
449
+ 3: ipv4z
450
+ 4: ipv6z
451
+ 16: dns
452
+ - # ipIfStatsIfIndex (1.3.6.1.2.1.4.21.3.1.2)
453
+ tag: interface
454
+ index: 2
455
+ ```
456
+ */
457
+ if metric_tag.Symbol != (Symbol{}) {
458
+ metric_tag_mib := metric_tag.MIB
459
+
460
+ if metric_tag.Table != "" {
461
+ return parseOtherTableColumnMetricTag(metric_tag_mib, metric_tag.Table, metric_tag)
462
+ }
463
+
464
+ if mib != metric_tag_mib && metric_tag_mib != "" {
465
+ return parsedTableMetricTag{}, fmt.Errorf("when tagging from a different MIB, the table must be specified, TABLE_MAME: %s MIB: %s METRIC_TAG_MIB: %s", parsed_table.name, mib, metric_tag_mib)
466
+ }
467
+ return parseColumnMetricTag(mib, parsed_table, metric_tag)
468
+ } else if &metric_tag.Index != nil { //TODO, this is tautological condition, need to return a "-1" on index if it does not exist or something similar
469
+ return parseIndexMetricTag(metric_tag)
470
+ } else {
471
+ return parsedTableMetricTag{}, errors.New("symbol is empty")
472
+ }
473
+
474
+}
475
+
476
+func parseIndexMetricTag(metric_tag TableMetricTag) (parsedTableMetricTag, error) {
477
+ parsed_metric_tag, err := parseMetricTag(
478
+ MetricTag{
479
+ Tag: metric_tag.Tag,
480
+ })
481
+ if err != nil {
482
+ return parsedTableMetricTag{}, err
483
+ }
484
+
485
+ index_tags := []indexTag{{
486
+ parsedMetricTag: parsed_metric_tag,
487
+ index: metric_tag.Index,
488
+ }}
489
+
490
+ index_mappings := map[int]map[int]string{}
491
+
492
+ if metric_tag.Mapping != nil {
493
+ index_mappings = map[int]map[int]string{metric_tag.Index: metric_tag.Mapping}
494
+ }
495
+
496
+ return parsedTableMetricTag{
497
+ indexTags: index_tags,
498
+ indexMappings: index_mappings,
499
+ }, nil
500
+}
501
+
502
+func parseOtherTableColumnMetricTag(mib string, table string, metric_tag TableMetricTag) (parsedTableMetricTag, error) {
503
+ parsed_table, err := parseSymbol(mib, &table)
504
+ if err != nil {
505
+ return parsedTableMetricTag{}, err
506
+ }
507
+ parsed_metric_tag, err := parseColumnMetricTag(mib, parsed_table, metric_tag)
508
+ if err != nil {
509
+ return parsedTableMetricTag{}, err
510
+ }
511
+
512
+ oids_to_resolve := parsed_metric_tag.oidsToResolve
513
+ oids_to_resolve = mergeStringMaps(oids_to_resolve, parsed_table.oidsToResolve)
514
+
515
+ return parsedTableMetricTag{
516
+ oidsToResolve: oids_to_resolve,
517
+ tableBatches: parsed_metric_tag.tableBatches,
518
+ columnTags: parsed_metric_tag.columnTags,
519
+ }, nil
520
+}
521
+
522
+func parseColumnMetricTag(mib string, parsed_table parsedSymbol, metric_tag TableMetricTag) (parsedTableMetricTag, error) {
523
+ parsed_column, err := parseSymbol(mib, metric_tag.Symbol)
524
+ if err != nil {
525
+ return parsedTableMetricTag{}, err
526
+ }
527
+
528
+ batches := map[tableBatchKey]tableBatch{
529
+ {mib: mib, table: parsed_table.name}: {tableOID: parsed_table.oid, oids: []string{parsed_column.oid}},
530
+ }
531
+
532
+ parsed_metric_tag, err := parseMetricTag(MetricTag{MIB: metric_tag.MIB, OID: "", Tag: metric_tag.Tag, Symbol: metric_tag.Symbol})
533
+ if err != nil {
534
+ return parsedTableMetricTag{}, err
535
+ }
536
+
537
+ return parsedTableMetricTag{
538
+ oidsToResolve: parsed_column.oidsToResolve,
539
+ columnTags: []columnTag{{
540
+ parsedMetricTag: parsed_metric_tag,
541
+ column: parsed_column.name,
542
+ indexSlices: parseIndexSlices(metric_tag),
543
+ },
544
+ },
545
+ tableBatches: batches,
546
+ }, nil
547
+}
548
+
549
+func parseIndexSlices(metric_tag TableMetricTag) []IndexSlice {
550
+ /*
551
+ // Transform index_transform into list of index slices.
552
+
553
+ // `index_transform` is needed to support tagging using another table with different indexes.
554
+
555
+ // Example: TableB have two indexes indexX (1 digit) and indexY (3 digits).
556
+ // We want to tag by an external TableA that have indexY (3 digits).
557
+
558
+ // For example TableB has a row with full index `1.2.3.4`, indexX is `1` and indexY is `2.3.4`.
559
+ // TableA has a row with full index `2.3.4`, indexY is `2.3.4` (matches indexY of TableB).
560
+
561
+ // SNMP integration doesn't know how to compare the full indexes from TableB and TableA.
562
+ // We need to extract a subset of the full index of TableB to match with TableA full index.
563
+
564
+ // Using the below `index_transform` we provide enough info to extract a subset of index that
565
+ // will be used to match TableA's full index.
566
+
567
+ // ```yaml
568
+ // index_transform:
569
+ // - start: 1
570
+ // - end: 3
571
+ //
572
+ // ```
573
+ */
574
+ raw_index_slices := metric_tag.IndexTransform
575
+ index_slices := []IndexSlice{}
576
+
577
+ for _, rule := range raw_index_slices {
578
+ start, end := rule.Start, rule.End
579
+ if start > end {
580
+ log.Println("start bigger than end")
581
+ return nil
582
+ }
583
+ if start < 0 {
584
+ log.Println("start is negative")
585
+ return nil
586
+ }
587
+ index_slices = append(index_slices, IndexSlice{start, end + 1})
588
+
589
+ }
590
+
591
+ return index_slices
592
+}
593
+
594
+func parseMetricTag(metric_tag MetricTag) (parsedMetricTag, error) {
595
+ parsed_metric_tag := parsedMetricTag{}
596
+
597
+ if metric_tag.Tag != "" {
598
+ parsed_metric_tag = parseSimpleMetricTag(metric_tag)
599
+ } else if metric_tag.Match != "" && metric_tag.Tags != nil {
600
+ tmp, err := parseRegexMetricTag(metric_tag)
601
+ if err != nil {
602
+ return parsedMetricTag{}, err
603
+ } else {
604
+ parsed_metric_tag = tmp
605
+ }
606
+ } else {
607
+ return parsedMetricTag{}, fmt.Errorf("a metric tag must specify either a tag, or a mapping of tags and a regular expression %v", metric_tag)
608
+ }
609
+ return parsed_metric_tag, nil
610
+}
611
+
612
+func parseRegexMetricTag(metric_tag MetricTag) (parsedMetricTag, error) {
613
+ match := metric_tag.Match
614
+ tags := metric_tag.Tags
615
+
616
+ // To be supported once tags are supported
617
+ // if reflect.TypeOf(tags) != reflect.TypeOf(map[string]string{}) {
618
+ // }
619
+
620
+ pattern, err := regexp.Compile(match)
621
+ if err != nil {
622
+ return parsedMetricTag{}, err
623
+ }
624
+
625
+ return parsedMetricTag{tags: tags, pattern: pattern}, nil
626
+}
627
+
628
+func parseSimpleMetricTag(metric_tag MetricTag) parsedMetricTag {
629
+ return parsedMetricTag{name: metric_tag.Tag}
630
+}
631
+
632
+func parseSymbol(mib string, symbol interface{}) (parsedSymbol, error) {
633
+ /*
634
+ Parse an OID symbol.
635
+
636
+ This can either be the unresolved name of a symbol:
637
+
638
+ ```
639
+ symbol: ifNumber
640
+ ```
641
+
642
+ Or a resolved OID/name object:
643
+
644
+ ```
645
+ symbol:
646
+ OID: 1.3.6.1.2.1.2.1
647
+ name: ifNumber
648
+ ```
649
+ */
650
+ // if reflect.TypeOf(symbol) == reflect.TypeOf(string) {
651
+ // // TODO, here they use ObjectIdentity(mib,symbol) to resolve the symbol. this is not straightfowrard in Go, it is a pysnmp function.
652
+ // // oid:=
653
+
654
+ switch s := symbol.(type) {
655
+ case Symbol:
656
+ oid := s.OID
657
+ name := s.Name
658
+ if s.ExtractValue != "" {
659
+ extractValuePattern, err := regexp.Compile(s.ExtractValue)
660
+ if err != nil {
661
+
662
+ return parsedSymbol{}, err
663
+ }
664
+ return parsedSymbol{
665
+ name,
666
+ oid,
667
+ extractValuePattern,
668
+ map[string]string{name: oid},
669
+ }, nil
670
+ } else {
671
+ return parsedSymbol{
672
+ name,
673
+ oid,
674
+ nil,
675
+ map[string]string{name: oid},
676
+ }, nil
677
+ }
678
+ case string:
679
+ return parsedSymbol{}, errors.New("string only symbol, can't support yet")
680
+ case map[string]interface{}:
681
+ oid, okOID := s["OID"].(string)
682
+ name, okName := s["name"].(string)
683
+
684
+ if !okOID || !okName {
685
+
686
+ return parsedSymbol{}, fmt.Errorf("invalid symbol format: %+v", s)
687
+ }
688
+
689
+ return parsedSymbol{
690
+ name: name,
691
+ oid: oid,
692
+ extractValuePattern: nil,
693
+ oidsToResolve: map[string]string{name: oid},
694
+ }, nil
695
+
696
+ default:
697
+ return parsedSymbol{}, fmt.Errorf("unsupported symbol type: %T", symbol)
698
+ }
699
+
700
+}
src/go/plugin/go.d/collector/snmp/profile.go
new
+183
@@ -0,0 +1,183 @@
1
+package snmp
2
+
3
+import (
4
+ "fmt"
5
+ "log"
6
+ "os"
7
+ "path/filepath"
8
+ "strings"
9
+
10
+ "github.com/gosnmp/gosnmp"
11
+ "gopkg.in/yaml.v3"
12
+)
13
+
14
+func (s *SysObjectIDs) UnmarshalYAML(unmarshal func(any) error) error {
15
+ var single string
16
+ if err := unmarshal(&single); err == nil {
17
+ *s = []string{single}
18
+ return nil
19
+ }
20
+
21
+ var multiple []string
22
+ if err := unmarshal(&multiple); err == nil {
23
+ *s = multiple
24
+ return nil
25
+ }
26
+
27
+ return fmt.Errorf("invalid sysobjectid format")
28
+}
29
+
30
+func (c *Collector) parseMetricsFromProfiles(matchingProfiles []*Profile) (map[string]processedMetric, error) {
31
+ metricMap := map[string]processedMetric{}
32
+ for _, profile := range matchingProfiles {
33
+ results, err := parseMetrics(profile.Metrics)
34
+ if err != nil {
35
+ return nil, err
36
+ }
37
+
38
+ for _, oid := range results.oids {
39
+ response, err := c.snmpClient.Get([]string{oid})
40
+ if err != nil {
41
+ return nil, err
42
+ }
43
+ if (response != &gosnmp.SnmpPacket{}) {
44
+ for _, metric := range results.parsed_metrics {
45
+ switch s := metric.(type) {
46
+ case parsedSymbolMetric:
47
+ // find a matching metric
48
+ if s.baseoid == oid {
49
+ metricName := s.name
50
+ metricType := response.Variables[0].Type
51
+ metricValue := response.Variables[0].Value
52
+
53
+ metricMap[oid] = processedMetric{oid: oid, name: metricName, value: metricValue, metric_type: metricType}
54
+ }
55
+ }
56
+ }
57
+
58
+ }
59
+ }
60
+
61
+ for _, oid := range results.next_oids {
62
+ if len(oid) < 1 {
63
+ continue
64
+ }
65
+ if tableRows, err := c.walkOIDTree(oid); err != nil {
66
+ log.Fatalf("Error walking OID tree: %v, oid %s", err, oid)
67
+ } else {
68
+ for _, metric := range results.parsed_metrics {
69
+ switch s := metric.(type) {
70
+ case parsedTableMetric:
71
+ // find a matching metric
72
+ if s.rowOID == oid {
73
+ for key, value := range tableRows {
74
+ value.name = s.name
75
+ value.tableName = s.tableName
76
+ tableRows[key] = value
77
+ }
78
+ metricMap = mergeProcessedMetricMaps(metricMap, tableRows)
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ }
85
+
86
+ }
87
+ return metricMap, nil
88
+}
89
+
90
+func (s *Symbol) UnmarshalYAML(node *yaml.Node) error {
91
+ // If scalar node, assume the value is the name.
92
+ if node.Kind == yaml.ScalarNode {
93
+ s.Name = node.Value
94
+ return nil
95
+ }
96
+
97
+ // Otherwise, decode normally
98
+ type plainSymbol Symbol
99
+ var ps plainSymbol
100
+ if err := node.Decode(&ps); err != nil {
101
+ return err
102
+ }
103
+ *s = Symbol(ps)
104
+ return nil
105
+}
106
+
107
+func LoadAllProfiles(profileDir string) (map[string]*Profile, error) {
108
+ profiles := make(map[string]*Profile)
109
+ err := filepath.Walk(profileDir, func(path string, info os.FileInfo, err error) error {
110
+ if err != nil {
111
+ return err
112
+ }
113
+ if strings.HasSuffix(info.Name(), ".yaml") {
114
+ profile, err := LoadYAML(path, profileDir)
115
+ if err == nil {
116
+ profiles[path] = profile
117
+ } else {
118
+ log.Printf("Skipping invalid YAML: %s (%v)\n", path, err)
119
+ }
120
+ }
121
+ return nil
122
+ })
123
+
124
+ if err != nil {
125
+ return nil, err
126
+ }
127
+
128
+ return profiles, nil
129
+}
130
+
131
+func LoadYAML(filename string, basePath string) (*Profile, error) {
132
+ data, err := os.ReadFile(filename)
133
+ if err != nil {
134
+ return nil, err
135
+ }
136
+
137
+ var profile Profile
138
+ err = yaml.Unmarshal(data, &profile)
139
+ if err != nil {
140
+ return nil, err
141
+ }
142
+
143
+ // If the profile extends other files, load and merge them
144
+ for _, parentFile := range profile.Extends {
145
+ parentProfile, err := LoadYAML(filepath.Join(basePath, parentFile), basePath)
146
+ if err != nil {
147
+ return nil, err
148
+ }
149
+ MergeProfiles(&profile, parentProfile)
150
+ }
151
+
152
+ return &profile, nil
153
+}
154
+
155
+// Merge two profiles, giving priority to the child profile
156
+func MergeProfiles(child, parent *Profile) {
157
+ if child.Metadata.Device.Fields == nil {
158
+ child.Metadata.Device.Fields = make(map[string]Symbol)
159
+ }
160
+
161
+ for key, value := range parent.Metadata.Device.Fields {
162
+ if _, exists := child.Metadata.Device.Fields[key]; !exists {
163
+ child.Metadata.Device.Fields[key] = value
164
+ }
165
+ }
166
+ child.Metrics = append(parent.Metrics, child.Metrics...)
167
+}
168
+
169
+// Find the matching profile based on sysObjectID
170
+func FindMatchingProfiles(profiles map[string]*Profile, deviceOID string) []*Profile {
171
+ var matchedProfiles []*Profile
172
+
173
+ for _, profile := range profiles {
174
+ for _, oidPattern := range profile.SysObjectID {
175
+ if strings.HasPrefix(deviceOID, strings.Split(oidPattern, "*")[0]) {
176
+ matchedProfiles = append(matchedProfiles, profile)
177
+ break
178
+ }
179
+ }
180
+ }
181
+
182
+ return matchedProfiles
183
+}
src/go/plugin/go.d/collector/snmp/types.go
new
+238
@@ -0,0 +1,238 @@
1
+package snmp
2
+
3
+import (
4
+ "regexp"
5
+
6
+ "github.com/gosnmp/gosnmp"
7
+)
8
+
9
+type snmpPDU struct {
10
+ value interface{}
11
+ oid string
12
+ metric_type gosnmp.Asn1BER
13
+}
14
+
15
+type Profile struct {
16
+ Extends []string `yaml:"extends"`
17
+ SysObjectID SysObjectIDs `yaml:"sysobjectid"`
18
+ Metadata Metadata `yaml:"metadata"`
19
+ Metrics []Metric `yaml:"metrics"`
20
+}
21
+
22
+type SysObjectIDs []string
23
+
24
+type Metadata struct {
25
+ Device DeviceMetadata `yaml:"device"`
26
+}
27
+
28
+type DeviceMetadata struct {
29
+ Fields map[string]Symbol `yaml:"fields"`
30
+}
31
+
32
+type Symbol struct {
33
+ OID string `yaml:"OID,omitempty"`
34
+ Name string `yaml:"name,omitempty"`
35
+ MatchPattern string `yaml:"match_pattern,omitempty"`
36
+ MatchValue string `yaml:"match_value,omitempty"`
37
+ ExtractValue string `yaml:"extract_value,omitempty"`
38
+}
39
+
40
+// superset of OIDMetric, SymbolMetric and TableMetric
41
+type Metric struct {
42
+ Name string `yaml:"name,omitempty"`
43
+ OID string `yaml:"OID,omitempty"`
44
+ //TODO check for only name existing in metric tag, as there is some case for that
45
+ MetricTags []TableMetricTag `yaml:"metric_tags,omitempty"`
46
+ MetricType string `yaml:"metric_type,omitempty"`
47
+ Options map[string]string
48
+
49
+ MIB string `yaml:"MIB,omitempty"`
50
+ Symbol Symbol `yaml:"symbol,omitempty"` //can be either string or Symbol
51
+
52
+ Table interface{} `yaml:"table,omitempty"` // can be either a string or Symbol
53
+ Symbols []Symbol `yaml:"symbols,omitempty"`
54
+}
55
+
56
+type TableMetricTag struct {
57
+ Index int `yaml:"index"`
58
+ Mapping map[int]string `yaml:"mapping"`
59
+
60
+ Tag string `yaml:"tag"`
61
+
62
+ MIB string `yaml:"mib"`
63
+ Symbol Symbol `yaml:"symbol"`
64
+ Table string `yaml:"table"`
65
+ IndexTransform []IndexSlice `yaml:"index_transform"`
66
+}
67
+
68
+type oidMetric struct {
69
+ name string
70
+ oid string
71
+ metricTags []string
72
+ forcedType string
73
+ options map[string]string
74
+}
75
+
76
+type symbolMetric struct {
77
+ mib string
78
+ symbol interface{} //can be either string or Symbol
79
+ forcedType string
80
+ metricTags []string
81
+ options map[string]string
82
+}
83
+
84
+type tableMetric struct {
85
+ mib string
86
+ table interface{} // can be either a string or Symbol
87
+ symbols []Symbol
88
+ forcedType string
89
+ metricTags []TableMetricTag
90
+ options map[string]string
91
+}
92
+
93
+type parsedResult struct {
94
+ oids []string
95
+ next_oids []string
96
+ bulk_oids []string
97
+ parsed_metrics []parsedMetric
98
+}
99
+
100
+type tableBatchKey struct {
101
+ mib string
102
+ table string
103
+}
104
+
105
+type tableBatch struct {
106
+ tableOID string
107
+ oids []string
108
+}
109
+
110
+type tableBatches map[tableBatchKey]tableBatch
111
+
112
+type indexTag struct {
113
+ parsedMetricTag parsedMetricTag
114
+ index int
115
+}
116
+
117
+type columnTag struct {
118
+ parsedMetricTag parsedMetricTag
119
+ column string
120
+ indexSlices []IndexSlice
121
+}
122
+
123
+type indexMapping struct {
124
+ tag string
125
+ index int
126
+ mapping map[int]string
127
+}
128
+
129
+type parsedSymbol struct {
130
+ name string
131
+ oid string
132
+ extractValuePattern *regexp.Regexp
133
+ oidsToResolve map[string]string
134
+}
135
+
136
+type parsedColumnMetricTag struct {
137
+ oidsToResolve map[string]string
138
+ tableBatches tableBatches
139
+ columnTags []columnTag
140
+}
141
+type parsedIndexMetricTag struct {
142
+ indexTags []indexTag
143
+ indexMappings map[int]map[string]string
144
+}
145
+
146
+type parsedTableMetricTag struct {
147
+ oidsToResolve map[string]string
148
+ tableBatches tableBatches
149
+ columnTags []columnTag
150
+ indexTags []indexTag
151
+ indexMappings map[int]map[int]string
152
+}
153
+
154
+type parsedSymbolMetric struct {
155
+ name string
156
+ tags []string
157
+ forcedType string
158
+ enforceScalar bool
159
+ options map[string]string
160
+ extractValuePattern *regexp.Regexp
161
+ baseoid string //TODO consider changing this to OID, it will not have nested OIDs as it is a symbol
162
+}
163
+
164
+type parsedTableMetric struct {
165
+ name string
166
+ indexTags []indexTag
167
+ columnTags []columnTag
168
+ forcedType string
169
+ options map[string]string
170
+ extractValuePattern *regexp.Regexp
171
+ rowOID string
172
+ tableName string
173
+ tableOID string
174
+}
175
+
176
+// union of two above
177
+type parsedMetric any
178
+
179
+// Not supported yet
180
+/*type parsedSimpleMetricTag struct {
181
+ name string
182
+}
183
+
184
+type parsedMatchMetricTag struct {
185
+tags []string
186
+symbol Symbol
187
+pattern *regexp.Regexp
188
+}
189
+
190
+type symbolTag struct {
191
+ parsedMetricTag parsedMetricTag
192
+ symbol string
193
+}
194
+
195
+type parsedSymbolTagsResult struct {
196
+ oids []string
197
+ parsedSymbolTags []symbolTag
198
+}
199
+*/
200
+type parsedMetricTag struct {
201
+ name string
202
+
203
+ tags []string
204
+ pattern *regexp.Regexp
205
+ // symbol Symbol not used yet
206
+}
207
+
208
+type metricParseResult struct {
209
+ oidsToFetch []string
210
+ oidsToResolve map[string]string
211
+ indexMappings []indexMapping
212
+ tableBatches tableBatches
213
+ parsedMetrics []parsedMetric
214
+}
215
+
216
+type MetricTag struct {
217
+ OID string
218
+ MIB string
219
+ Symbol Symbol
220
+ // simple tag
221
+ Tag string
222
+ // regex matching
223
+ Match string
224
+ Tags []string
225
+}
226
+
227
+type IndexSlice struct {
228
+ Start int
229
+ End int
230
+}
231
+
232
+type processedMetric struct {
233
+ oid string
234
+ name string
235
+ value interface{}
236
+ metric_type gosnmp.Asn1BER
237
+ tableName string
238
+}