go.d/snmp: add an option to automatically create vnode (#18520)
Ilya Mashchenko committed
Sep 11, 2024 at 11:12 UTC
2f6b810aa72cdc8a5cda0a855085996befef71a3
11 files changed
+125
-7
src/go/go.mod
+1
-1
@@ -23,6 +23,7 @@ require (
23
github.com/godbus/dbus/v5 v5.1.0
24
github.com/gofrs/flock v0.12.1
25
github.com/golang/mock v1.6.0
26
+ github.com/google/uuid v1.6.0
27
github.com/gosnmp/gosnmp v1.38.0
28
github.com/ilyam8/hashstructure v1.1.0
29
github.com/jackc/pgx/v4 v4.18.3
@@ -86,7 +87,6 @@ require (
87
github.com/google/gnostic-models v0.6.8 // indirect
88
github.com/google/go-cmp v0.6.0 // indirect
89
github.com/google/gofuzz v1.2.0 // indirect
89
- github.com/google/uuid v1.6.0 // indirect
90
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect
91
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
92
github.com/huandu/xstrings v1.5.0 // indirect
src/go/plugin/go.d/agent/module/job.go
+12
-3
@@ -394,9 +394,18 @@ func (j *Job) collect() (result map[string]int64) {
394
}
395
396
func (j *Job) processMetrics(metrics map[string]int64, startTime time.Time, sinceLastRun int) bool {
397
- if !j.vnodeCreated && j.vnodeGUID != "" {
398
- _ = j.api.HOSTINFO(j.vnodeGUID, j.vnodeHostname, j.vnodeLabels)
399
- j.vnodeCreated = true
397
+ if !j.vnodeCreated {
398
+ if j.vnodeGUID == "" {
399
+ if v := j.module.VirtualNode(); v != nil && v.GUID != "" && v.Hostname != "" {
400
+ j.vnodeGUID = v.GUID
401
+ j.vnodeHostname = v.Hostname
402
+ j.vnodeLabels = v.Labels
403
+ }
404
+ }
405
+ if j.vnodeGUID != "" {
406
+ _ = j.api.HOSTINFO(j.vnodeGUID, j.vnodeHostname, j.vnodeLabels)
407
+ j.vnodeCreated = true
408
+ }
409
}
410
411
_ = j.api.HOST(j.vnodeGUID)
src/go/plugin/go.d/agent/module/module.go
+5
@@ -7,6 +7,7 @@ import (
7
"testing"
8
9
"github.com/netdata/netdata/go/plugins/logger"
10
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/vnodes"
11
12
"github.com/stretchr/testify/assert"
13
"github.com/stretchr/testify/require"
@@ -35,6 +36,8 @@ type Module interface {
36
GetBase() *Base
37
38
Configuration() any
39
+
40
+ VirtualNode() *vnodes.VirtualNode
41
}
42
43
// Base is a helper struct. All modules should embed this struct.
@@ -44,6 +47,8 @@ type Base struct {
47
48
func (b *Base) GetBase() *Base { return b }
49
50
+func (b *Base) VirtualNode() *vnodes.VirtualNode { return nil }
51
+
52
func TestConfigurationSerialize(t *testing.T, mod Module, cfgJSON, cfgYAML []byte) {
53
t.Helper()
54
tests := map[string]struct {
src/go/plugin/go.d/agent/vnodes/vnodes.go
+3
-3
@@ -37,9 +37,9 @@ type (
37
vnodes map[string]*VirtualNode
38
}
39
VirtualNode struct {
40
- GUID string `yaml:"guid"`
41
- Hostname string `yaml:"hostname"`
42
- Labels map[string]string `yaml:"labels"`
40
+ GUID string `yaml:"guid" json:"guid"`
41
+ Hostname string `yaml:"hostname" json:"hostname"`
42
+ Labels map[string]string `yaml:"labels" json:"labels"`
43
}
44
)
45
src/go/plugin/go.d/modules/snmp/collect.go
+20
@@ -10,7 +10,9 @@ import (
10
"strings"
11
12
"github.com/netdata/netdata/go/plugins/logger"
13
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/vnodes"
14
15
+ "github.com/google/uuid"
16
"github.com/gosnmp/gosnmp"
17
)
18
@@ -29,6 +31,10 @@ func (s *SNMP) collect() (map[string]int64, error) {
31
}
32
s.sysName = sysName
33
s.addSysUptimeChart()
34
+
35
+ if s.CreateVnode {
36
+ s.vnode = s.setupVnode(sysName)
37
+ }
38
}
39
40
mx := make(map[string]int64)
@@ -52,6 +58,20 @@ func (s *SNMP) collect() (map[string]int64, error) {
58
return mx, nil
59
}
60
61
+func (s *SNMP) setupVnode(sysName string) *vnodes.VirtualNode {
62
+ if s.Vnode.GUID == "" {
63
+ s.Vnode.GUID = uuid.NewSHA1(uuid.NameSpaceDNS, []byte(s.Hostname)).String()
64
+ }
65
+ if s.Vnode.Hostname == "" {
66
+ s.Vnode.Hostname = fmt.Sprintf("%s(%s)", sysName, s.Hostname)
67
+ }
68
+ return &vnodes.VirtualNode{
69
+ GUID: s.Vnode.GUID,
70
+ Hostname: s.Vnode.Hostname,
71
+ Labels: s.Vnode.Labels,
72
+ }
73
+}
74
+
75
func (s *SNMP) getSysName() (string, error) {
76
resp, err := s.snmpClient.Get([]string{oidSysName})
77
if err != nil {
src/go/plugin/go.d/modules/snmp/config.go
+4
@@ -2,10 +2,14 @@
2
3
package snmp
4
5
+import "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/vnodes"
6
+
7
type (
8
Config struct {
9
UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
10
Hostname string `yaml:"hostname" json:"hostname"`
11
+ CreateVnode bool `yaml:"create_vnode,omitempty" json:"create_vnode"`
12
+ Vnode vnodes.VirtualNode `yaml:"vnode,omitempty" json:"vnode"`
13
Community string `yaml:"community,omitempty" json:"community"`
14
User User `yaml:"user,omitempty" json:"user"`
15
Options Options `yaml:"options,omitempty" json:"options"`
src/go/plugin/go.d/modules/snmp/config_schema.json
+43
@@ -21,6 +21,42 @@
21
"type": "string",
22
"default": "public"
23
},
24
+ "create_vnode": {
25
+ "title": "Create",
26
+ "description": "If set, the collector will create a [Virtual Node](https://learn.netdata.cloud/docs/netdata-agent/configuration/organize-systems-metrics-and-alerts#virtual-nodes) for this SNMP device, which will appear as a separate Node in Netdata.",
27
+ "type": "boolean"
28
+ },
29
+ "vnode": {
30
+ "title": "Configuration",
31
+ "description": "",
32
+ "type": [
33
+ "object",
34
+ "null"
35
+ ],
36
+ "properties": {
37
+ "guid": {
38
+ "title": "GUID",
39
+ "description": "A unique identifier for the Virtual Node. If not set, a GUID will be automatically generated from the device's IP address.",
40
+ "type": "string"
41
+ },
42
+ "hostname": {
43
+ "title": "Hostname",
44
+ "description": "The hostname that will be used for the Virtual Node. If not set, the device's hostname will be used.",
45
+ "type": "string"
46
+ },
47
+ "labels": {
48
+ "title": "Labels",
49
+ "description": "Additional key-value pairs to associate with the Virtual Node.",
50
+ "type": [
51
+ "object",
52
+ "null"
53
+ ],
54
+ "additionalProperties": {
55
+ "type": "string"
56
+ }
57
+ }
58
+ }
59
+ },
60
"network_interface_filter": {
61
"title": "Network interface filter",
62
"description": "Configuration for filtering specific network interfaces. If left empty, no interfaces will be filtered. You can filter interfaces by name or type using [simple patterns](/src/libnetdata/simple_pattern/README.md#simple-patterns).",
@@ -397,6 +433,13 @@
433
"community"
434
]
435
},
436
+ {
437
+ "title": "Vnode",
438
+ "fields": [
439
+ "create_vnode",
440
+ "vnode"
441
+ ]
442
+ },
443
{
444
"title": "Options",
445
"fields": [
src/go/plugin/go.d/modules/snmp/metadata.yaml
+16
@@ -79,6 +79,22 @@ modules:
79
description: Target ipv4 address.
80
default_value: ""
81
required: true
82
+ - name: create_vnode
83
+ description: If set, the collector will create a Netdata Virtual Node for this SNMP device, which will appear as a separate Node in Netdata.
84
+ default_value: "false"
85
+ required: false
86
+ - name: vnode.guid
87
+ description: A unique identifier for the Virtual Node. If not set, a GUID will be automatically generated from the device's IP address.
88
+ default_value: ""
89
+ required: false
90
+ - name: vnode.hostname
91
+ description: The hostname that will be used for the Virtual Node. If not set, the device's hostname will be used.
92
+ default_value: ""
93
+ required: false
94
+ - name: vnode.labels
95
+ description: Additional key-value pairs to associate with the Virtual Node.
96
+ default_value: ""
97
+ required: false
98
- name: community
99
description: SNMPv1/2 community string.
100
default_value: public
src/go/plugin/go.d/modules/snmp/snmp.go
+7
@@ -7,6 +7,7 @@ import (
7
"errors"
8
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
10
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/vnodes"
11
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/matcher"
12
13
"github.com/gosnmp/gosnmp"
@@ -57,6 +58,8 @@ type SNMP struct {
58
module.Base
59
Config `yaml:",inline" json:""`
60
61
+ vnode *vnodes.VirtualNode
62
+
63
charts *module.Charts
64
65
newSnmpClient func() gosnmp.Handler
@@ -153,3 +156,7 @@ func (s *SNMP) Cleanup() {
156
_ = s.snmpClient.Close()
157
}
158
}
159
+
160
+func (s *SNMP) VirtualNode() *vnodes.VirtualNode {
161
+ return s.vnode
162
+}
src/go/plugin/go.d/modules/snmp/testdata/config.json
+8
@@ -1,6 +1,14 @@
1
{
2
"update_every": 123,
3
"hostname": "ok",
4
+ "create_vnode": true,
5
+ "vnode": {
6
+ "guid": "ok",
7
+ "hostname": "ok",
8
+ "labels": {
9
+ "ok": "ok"
10
+ }
11
+ },
12
"community": "ok",
13
"network_interface_filter": {
14
"by_name": "ok",
src/go/plugin/go.d/modules/snmp/testdata/config.yaml
+6
@@ -1,5 +1,11 @@
1
update_every: 123
2
hostname: "ok"
3
+create_vnode: yes
4
+vnode:
5
+ guid: "ok"
6
+ hostname: "ok"
7
+ labels:
8
+ ok: "ok"
9
community: "ok"
10
network_interface_filter:
11
by_name: "ok"