master
go 345 lines 11.4 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 //go:build snmp_topology_fixtures
4
5 package parity
6
7 import (
8 "encoding/json"
9 "net/netip"
10 "os"
11 "path/filepath"
12 "sort"
13 "strings"
14 "testing"
15
16 "github.com/netdata/netdata/go/plugins/pkg/l2topology"
17 "github.com/stretchr/testify/require"
18 )
19
20 type nodeTopologyFixtureDocument struct {
21 SchemaVersion int `json:"schema_version"`
22 Scenarios map[string]nodeTopologyScenario `json:"scenarios"`
23 }
24
25 type nodeTopologyScenario struct {
26 BuilderClass string `json:"builder_class"`
27 Getters []string `json:"getters"`
28 Nodes []nodeTopologyFixtureNode `json:"nodes"`
29 IPs []nodeTopologyFixtureIP `json:"ips"`
30 SNMP []nodeTopologyFixtureSNMP `json:"snmp"`
31 Expect nodeTopologyFixtureExpectations `json:"expect"`
32 }
33
34 type nodeTopologyFixtureNode struct {
35 ID int `json:"id"`
36 Label string `json:"label"`
37 SysObject string `json:"sys_object"`
38 SysName string `json:"sys_name"`
39 Address string `json:"address"`
40 }
41
42 type nodeTopologyFixtureIP struct {
43 ID int `json:"id"`
44 NodeID int `json:"node_id"`
45 IPAddress string `json:"ip_address"`
46 Netmask string `json:"netmask"`
47 IsManaged bool `json:"is_managed"`
48 IsSnmpPrimary bool `json:"is_snmp_primary"`
49 IfIndex int `json:"if_index"`
50 SnmpInterfaceID int `json:"snmp_interface_id"`
51 }
52
53 type nodeTopologyFixtureSNMP struct {
54 ID int `json:"id"`
55 NodeID int `json:"node_id"`
56 IfIndex int `json:"if_index"`
57 IfName string `json:"if_name"`
58 IfDescr string `json:"if_descr"`
59 }
60
61 type nodeTopologyFixtureExpectations struct {
62 Nodes int `json:"nodes"`
63 IPs *int `json:"ips"`
64 Subnets *int `json:"subnets"`
65 LegalSubnets *int `json:"legal_subnets"`
66 PTPSubnets *int `json:"ptp_subnets"`
67 LegalPTPSubnets *int `json:"legal_ptp_subnets"`
68 Loopbacks *int `json:"loopbacks"`
69 LegalLoopbacks *int `json:"legal_loopbacks"`
70 MultiBet *int `json:"multibet"`
71 PrioritySize *int `json:"priority_size"`
72 PriorityRule nodeTopologyPriorityRule `json:"priority_rule"`
73 NLinksFinal *int `json:"nlinks_final"`
74 TopologyVertices *int `json:"topology_vertices"`
75 TopologyEdges *int `json:"topology_edges"`
76 CIDRNodeSizes []nodeTopologyCIDRExpectation `json:"cidr_node_sizes"`
77 }
78
79 type nodeTopologyPriorityRule struct {
80 Kind string `json:"kind"`
81 Value *int `json:"value"`
82 }
83
84 type nodeTopologyCIDRExpectation struct {
85 CIDR string `json:"cidr"`
86 NodeIDs int `json:"node_ids"`
87 }
88
89 func loadNodeTopologyFixtureDocument(t *testing.T) nodeTopologyFixtureDocument {
90 t.Helper()
91 path := filepath.Join("../../../testdata/snmp/enlinkd", "node_topology", "scenarios.json")
92 data, err := os.ReadFile(path)
93 require.NoError(t, err)
94
95 var doc nodeTopologyFixtureDocument
96 require.NoError(t, json.Unmarshal(data, &doc))
97 require.Equal(t, 1, doc.SchemaVersion)
98 require.NotEmpty(t, doc.Scenarios)
99 return doc
100 }
101
102 func scenarioToService(t *testing.T, scenario nodeTopologyScenario) *l2topology.NodeTopologyService {
103 t.Helper()
104
105 nodes := make([]l2topology.NodeTopologyEntity, 0, len(scenario.Nodes))
106 for _, node := range scenario.Nodes {
107 entry := l2topology.NodeTopologyEntity{
108 ID: node.ID,
109 Label: node.Label,
110 SysObject: node.SysObject,
111 SysName: node.SysName,
112 }
113 if ip, ok := parseOptionalAddr(node.Address); ok {
114 entry.Address = ip
115 }
116 nodes = append(nodes, entry)
117 }
118
119 ips := make([]l2topology.IPInterfaceTopologyEntity, 0, len(scenario.IPs))
120 for _, ip := range scenario.IPs {
121 entry := l2topology.IPInterfaceTopologyEntity{
122 ID: ip.ID,
123 NodeID: ip.NodeID,
124 IsManaged: ip.IsManaged,
125 IsSnmpPrimary: ip.IsSnmpPrimary,
126 IfIndex: ip.IfIndex,
127 SnmpInterfaceID: ip.SnmpInterfaceID,
128 }
129 parsedIP, ok := parseOptionalAddr(ip.IPAddress)
130 require.Truef(t, ok, "invalid fixture IP address %q", ip.IPAddress)
131 entry.IPAddress = parsedIP
132 if mask, ok := parseOptionalAddr(ip.Netmask); ok {
133 entry.NetMask = mask
134 }
135 ips = append(ips, entry)
136 }
137
138 snmp := make([]l2topology.SnmpInterfaceTopologyEntity, 0, len(scenario.SNMP))
139 for _, iface := range scenario.SNMP {
140 snmp = append(snmp, l2topology.SnmpInterfaceTopologyEntity{
141 ID: iface.ID,
142 NodeID: iface.NodeID,
143 IfIndex: iface.IfIndex,
144 IfName: iface.IfName,
145 IfDescr: iface.IfDescr,
146 })
147 }
148
149 return l2topology.NewNodeTopologyService(nodes, ips, snmp)
150 }
151
152 func parseOptionalAddr(value string) (netip.Addr, bool) {
153 value = stringsTrimSpace(value)
154 if value == "" {
155 return netip.Addr{}, false
156 }
157 addr, err := netip.ParseAddr(value)
158 if err != nil {
159 return netip.Addr{}, false
160 }
161 return addr.Unmap(), true
162 }
163
164 func runNodeTopologyScenarioParity(t *testing.T, scenarioName string) {
165 t.Helper()
166 doc := loadNodeTopologyFixtureDocument(t)
167 scenario, ok := doc.Scenarios[scenarioName]
168 require.Truef(t, ok, "missing scenario %q", scenarioName)
169
170 service := scenarioToService(t, scenario)
171 expect := scenario.Expect
172
173 require.Len(t, service.FindAllNode(), expect.Nodes)
174 if expect.IPs != nil {
175 require.Len(t, service.FindAllIP(), *expect.IPs)
176 }
177
178 subnets := service.FindAllSubNetwork()
179 if expect.Subnets != nil {
180 require.Len(t, subnets, *expect.Subnets)
181 }
182
183 legalSubnets := service.FindAllLegalSubNetwork()
184 if expect.LegalSubnets != nil {
185 require.Len(t, legalSubnets, *expect.LegalSubnets)
186 }
187
188 ptpSubnets := service.FindAllPointToPointSubNetwork()
189 if expect.PTPSubnets != nil {
190 require.Len(t, ptpSubnets, *expect.PTPSubnets)
191 }
192
193 legalPTPSubnets := service.FindAllLegalPointToPointSubNetwork()
194 if expect.LegalPTPSubnets != nil {
195 require.Len(t, legalPTPSubnets, *expect.LegalPTPSubnets)
196 }
197
198 loopbacks := service.FindAllLoopbacks()
199 if expect.Loopbacks != nil {
200 require.Len(t, loopbacks, *expect.Loopbacks)
201 }
202
203 legalLoopbacks := service.FindAllLegalLoopbacks()
204 if expect.LegalLoopbacks != nil {
205 require.Len(t, legalLoopbacks, *expect.LegalLoopbacks)
206 }
207
208 multibet := service.FindSubNetworkByNetworkPrefixLessThen(30, 126)
209 if expect.MultiBet != nil {
210 require.Len(t, multibet, *expect.MultiBet)
211 }
212
213 priority := service.GetNodeIDPriorityMap()
214 if expect.PrioritySize != nil {
215 require.Len(t, priority, *expect.PrioritySize)
216 }
217 switch expect.PriorityRule.Kind {
218 case "all_zero":
219 for _, value := range priority {
220 require.Equal(t, 0, value)
221 }
222 case "lt":
223 require.NotNil(t, expect.PriorityRule.Value)
224 for _, value := range priority {
225 require.Less(t, value, *expect.PriorityRule.Value)
226 }
227 }
228
229 if len(expect.CIDRNodeSizes) > 0 {
230 actual := make(map[string]int)
231 for _, subnet := range legalSubnets {
232 actual[subnet.CIDR()] = len(subnet.NodeIDs())
233 }
234 for _, expectedCIDR := range expect.CIDRNodeSizes {
235 require.Equalf(t, expectedCIDR.NodeIDs, actual[expectedCIDR.CIDR], "cidr %s", expectedCIDR.CIDR)
236 }
237 }
238
239 topology := l2topology.BuildNetworkRouterTopology(service, 30, 126)
240 if expect.TopologyVertices != nil {
241 require.Len(t, topology.Vertices, *expect.TopologyVertices)
242 } else if expect.MultiBet != nil {
243 require.Len(t, topology.Vertices, expect.Nodes+*expect.MultiBet)
244 }
245
246 if expect.TopologyEdges != nil {
247 require.Len(t, topology.Edges, *expect.TopologyEdges)
248 } else if expect.NLinksFinal != nil {
249 require.Len(t, topology.Edges, *expect.NLinksFinal)
250 }
251 }
252
253 func TestNodeTopologyServiceIT_nms0001SubnetworksTest(t *testing.T) {
254 runNodeTopologyScenarioParity(t, "nms0001SubnetworksTest")
255 }
256 func TestNodeTopologyServiceIT_nms0002SubnetworksTest(t *testing.T) {
257 runNodeTopologyScenarioParity(t, "nms0002SubnetworksTest")
258 }
259 func TestNodeTopologyServiceIT_nms003SubnetworkTests(t *testing.T) {
260 runNodeTopologyScenarioParity(t, "nms003SubnetworkTests")
261 }
262 func TestNodeTopologyServiceIT_nms007SubnetworkTest(t *testing.T) {
263 runNodeTopologyScenarioParity(t, "nms007SubnetworkTest")
264 }
265 func TestNodeTopologyServiceIT_nms101SubnetworksTest(t *testing.T) {
266 runNodeTopologyScenarioParity(t, "nms101SubnetworksTest")
267 }
268 func TestNodeTopologyServiceIT_nms102SubnetworksTest(t *testing.T) {
269 runNodeTopologyScenarioParity(t, "nms102SubnetworksTest")
270 }
271 func TestNodeTopologyServiceIT_nms0123SubnetworksTest(t *testing.T) {
272 runNodeTopologyScenarioParity(t, "nms0123SubnetworksTest")
273 }
274 func TestNodeTopologyServiceIT_nms1055SubnetworksTest(t *testing.T) {
275 runNodeTopologyScenarioParity(t, "nms1055SubnetworksTest")
276 }
277 func TestNodeTopologyServiceIT_nms4005SubnetworksTest(t *testing.T) {
278 runNodeTopologyScenarioParity(t, "nms4005SubnetworksTest")
279 }
280 func TestNodeTopologyServiceIT_nms4930SubnetworksTest(t *testing.T) {
281 runNodeTopologyScenarioParity(t, "nms4930SubnetworksTest")
282 }
283 func TestNodeTopologyServiceIT_nms6802SubnetworksTest(t *testing.T) {
284 runNodeTopologyScenarioParity(t, "nms6802SubnetworksTest")
285 }
286 func TestNodeTopologyServiceIT_nms7467SubnetworksTest(t *testing.T) {
287 runNodeTopologyScenarioParity(t, "nms7467SubnetworksTest")
288 }
289 func TestNodeTopologyServiceIT_nms7563SubnetworksTest(t *testing.T) {
290 runNodeTopologyScenarioParity(t, "nms7563SubnetworksTest")
291 }
292 func TestNodeTopologyServiceIT_nms7777DWSubnetworksTest(t *testing.T) {
293 runNodeTopologyScenarioParity(t, "nms7777DWSubnetworksTest")
294 }
295 func TestNodeTopologyServiceIT_nms7918SubnetworksTest(t *testing.T) {
296 runNodeTopologyScenarioParity(t, "nms7918SubnetworksTest")
297 }
298 func TestNodeTopologyServiceIT_nms8000SubnetworksTest(t *testing.T) {
299 runNodeTopologyScenarioParity(t, "nms8000SubnetworksTest")
300 }
301 func TestNodeTopologyServiceIT_nms10205aSubnetworksTest(t *testing.T) {
302 runNodeTopologyScenarioParity(t, "nms10205aSubnetworksTest")
303 }
304 func TestNodeTopologyServiceIT_nms10205bSubnetworksTest(t *testing.T) {
305 runNodeTopologyScenarioParity(t, "nms10205bSubnetworksTest")
306 }
307 func TestNodeTopologyServiceIT_nms13593SubnetworksTest(t *testing.T) {
308 runNodeTopologyScenarioParity(t, "nms13593SubnetworksTest")
309 }
310 func TestNodeTopologyServiceIT_nms13637SubnetworksTest(t *testing.T) {
311 runNodeTopologyScenarioParity(t, "nms13637SubnetworksTest")
312 }
313 func TestNodeTopologyServiceIT_nms13923SubnetworksTest(t *testing.T) {
314 runNodeTopologyScenarioParity(t, "nms13923SubnetworksTest")
315 }
316 func TestNodeTopologyServiceIT_nms17216SubnetworksTest(t *testing.T) {
317 runNodeTopologyScenarioParity(t, "nms17216SubnetworksTest")
318 }
319
320 func TestNms0001EnIT_testLinkdNetworkTopologyUpdater(t *testing.T) {
321 doc := loadNodeTopologyFixtureDocument(t)
322 scenario, ok := doc.Scenarios["nms0001EnIT_testLinkdNetworkTopologyUpdater"]
323 require.True(t, ok)
324
325 service := scenarioToService(t, scenario)
326 topology := l2topology.BuildNetworkRouterTopology(service, 30, 126)
327 require.NotEmpty(t, topology.Vertices)
328 require.Len(t, topology.Vertices, *scenario.Expect.TopologyVertices)
329 require.Len(t, topology.Edges, *scenario.Expect.TopologyEdges)
330 }
331
332 func TestNodeTopologyFixtureCoverage(t *testing.T) {
333 doc := loadNodeTopologyFixtureDocument(t)
334 scenarios := make([]string, 0, len(doc.Scenarios))
335 for name := range doc.Scenarios {
336 scenarios = append(scenarios, name)
337 }
338 sort.Strings(scenarios)
339
340 require.Contains(t, scenarios, "nms0001SubnetworksTest")
341 require.Contains(t, scenarios, "nms17216SubnetworksTest")
342 require.Contains(t, scenarios, "nms0001EnIT_testLinkdNetworkTopologyUpdater")
343 }
344
345 func stringsTrimSpace(value string) string { return strings.TrimSpace(value) }