@cryptotaxi247 / netdata-1 / commits / 2f6b810aa

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"