master
go 206 lines 4.57 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package snmp
4
5 import (
6 "errors"
7 "fmt"
8 "slices"
9 "time"
10
11 "github.com/netdata/netdata/go/plugins/pkg/funcapi"
12 "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/snmp/ddsnmp"
13 )
14
15 type bgpIntegration struct {
16 peerCache *bgpPeerCache
17 collectError error
18 collectFailedSources map[string]bool
19 }
20
21 func newBGPIntegration() *bgpIntegration {
22 return &bgpIntegration{
23 peerCache: newBGPPeerCache(),
24 }
25 }
26
27 func (c *Collector) enableBGPIntegration() {
28 if c.bgp == nil {
29 c.bgp = newBGPIntegration()
30 }
31 c.bgp.setStaleAfter(c.bgpStaleAfter())
32 if c.funcRouter != nil {
33 for _, h := range c.bgp.functionHandlers() {
34 c.funcRouter.registerHandler(h.methodID, h.handler)
35 }
36 }
37 }
38
39 func (c *Collector) markBGPCollectFailed(err error) {
40 if c.bgp == nil {
41 return
42 }
43 c.bgp.markCollectFailed(err)
44 }
45
46 func (b *bgpIntegration) setStaleAfter(d time.Duration) {
47 if b == nil || b.peerCache == nil {
48 return
49 }
50 b.peerCache.setStaleAfter(d)
51 }
52
53 func (b *bgpIntegration) markCollectFailed(err error) {
54 if b == nil || b.peerCache == nil {
55 return
56 }
57 b.peerCache.markCollectFailed(err)
58 }
59
60 func (c *Collector) prepareProfileMetrics(pms []*ddsnmp.ProfileMetrics) []ddsnmp.Metric {
61 if c.bgp == nil {
62 return flattenProfileMetrics(pms)
63 }
64 return c.bgp.prepareProfileMetrics(pms)
65 }
66
67 func (b *bgpIntegration) prepareProfileMetrics(pms []*ddsnmp.ProfileMetrics) []ddsnmp.Metric {
68 b.collectError, b.collectFailedSources = bgpCollectErrorsBySource(pms)
69
70 metrics := flattenProfileMetrics(pms)
71 b.peerCache.resetExceptSources(b.collectFailedSources)
72 for _, pm := range pms {
73 if pm == nil || pm.BGPCollectError != nil {
74 continue
75 }
76 for _, row := range pm.BGPRows {
77 b.peerCache.updateRow(pm.Source, row)
78 }
79 }
80
81 metrics = append(metrics, typedBGPMetricsFromProfileMetrics(successfulBGPProfileMetrics(pms))...)
82
83 return filterChartMetrics(metrics)
84 }
85
86 func (c *Collector) finalizeProfileMetrics() {
87 if c.bgp == nil {
88 return
89 }
90 c.bgp.finalizeProfileMetrics()
91 }
92
93 func (b *bgpIntegration) finalizeProfileMetrics() {
94 b.peerCache.finalize()
95 if b.collectError != nil {
96 b.peerCache.markSourcesCollectFailed(b.collectFailedSources, b.collectError)
97 }
98 b.collectError = nil
99 b.collectFailedSources = nil
100 }
101
102 func bgpCollectErrorsBySource(pms []*ddsnmp.ProfileMetrics) (error, map[string]bool) {
103 var errs []error
104 sources := make(map[string]bool)
105 for _, pm := range pms {
106 if pm == nil || pm.BGPCollectError == nil {
107 continue
108 }
109 if pm.Source == "" {
110 errs = append(errs, pm.BGPCollectError)
111 continue
112 }
113 sources[pm.Source] = true
114 errs = append(errs, fmt.Errorf("%s: %w", pm.Source, pm.BGPCollectError))
115 }
116 if len(sources) == 0 {
117 sources = nil
118 }
119 return errors.Join(errs...), sources
120 }
121
122 func successfulBGPProfileMetrics(pms []*ddsnmp.ProfileMetrics) []*ddsnmp.ProfileMetrics {
123 result := make([]*ddsnmp.ProfileMetrics, 0, len(pms))
124 for _, pm := range pms {
125 if pm == nil || pm.BGPCollectError != nil {
126 continue
127 }
128 result = append(result, pm)
129 }
130 return result
131 }
132
133 func flattenProfileMetrics(pms []*ddsnmp.ProfileMetrics) []ddsnmp.Metric {
134 var metrics []ddsnmp.Metric
135 for _, pm := range pms {
136 for _, metric := range pm.Metrics {
137 if metric.Profile == nil {
138 metric.Profile = pm
139 }
140 metrics = append(metrics, metric)
141 }
142 }
143 return metrics
144 }
145
146 func (c *Collector) additionalFuncHandlers() []registeredSNMPFunction {
147 if c.bgp == nil {
148 return nil
149 }
150 return c.bgp.functionHandlers()
151 }
152
153 func (b *bgpIntegration) functionHandlers() []registeredSNMPFunction {
154 if b.peerCache == nil {
155 return nil
156 }
157 return []registeredSNMPFunction{{
158 methodID: bgpPeersMethodID,
159 handler: newFuncBGPPeers(b.peerCache),
160 }}
161 }
162
163 func collectorSpecificFunctionHandlers() []registeredSNMPFunction {
164 return []registeredSNMPFunction{{
165 methodID: bgpPeersMethodID,
166 handler: newFuncBGPPeers(nil),
167 }}
168 }
169
170 func collectorSpecificMethodConfigs() []funcapi.MethodConfig {
171 return []funcapi.MethodConfig{
172 bgpPeersMethodConfig(),
173 }
174 }
175
176 func profilesHaveBGP(profiles []*ddsnmp.Profile) bool {
177 return slices.ContainsFunc(profiles, profileHasBGP)
178 }
179
180 func profileHasBGP(prof *ddsnmp.Profile) bool {
181 if prof == nil || prof.Definition == nil {
182 return false
183 }
184 def := prof.Definition
185 if len(def.BGP) > 0 {
186 return true
187 }
188 return false
189 }
190
191 func profileMetricsHaveBGP(pms []*ddsnmp.ProfileMetrics) bool {
192 for _, pm := range pms {
193 if pm.BGPCollectError != nil || len(pm.BGPRows) > 0 {
194 return true
195 }
196 }
197 return false
198 }
199
200 func (c *Collector) bgpStaleAfter() time.Duration {
201 updateEvery := c.UpdateEvery
202 if updateEvery <= 0 {
203 updateEvery = 10
204 }
205 return time.Duration(updateEvery*3) * time.Second
206 }