master
go 3,972 lines 142 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 "fmt"
9 "net/netip"
10 "sort"
11 "strconv"
12 "strings"
13 "testing"
14
15 "github.com/netdata/netdata/go/plugins/pkg/l2topology"
16 "github.com/stretchr/testify/require"
17 )
18
19 func TestBuildL2ResultFromWalks_LLDP_NMS8003(t *testing.T) {
20 manifestPath := "../../../testdata/snmp/enlinkd/nms8003/manifest.yaml"
21 manifest, err := LoadManifest(manifestPath)
22 require.NoError(t, err)
23
24 scenario, ok := manifest.FindScenario("nms8003_lldp")
25 require.True(t, ok)
26
27 resolved, err := ResolveScenario(manifestPath, scenario)
28 require.NoError(t, err)
29
30 walks, err := LoadScenarioWalks(resolved)
31 require.NoError(t, err)
32
33 result, err := BuildL2ResultFromWalks(walks, BuildOptions{EnableLLDP: true})
34 require.NoError(t, err)
35 require.Len(t, result.Devices, 5)
36 require.Len(t, result.Adjacencies, 12)
37 require.Equal(t, 12, result.Stats["links_lldp"])
38 require.Equal(t, 0, result.Stats["links_cdp"])
39
40 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
41 require.NoError(t, err)
42
43 expected := make(map[string]struct{}, len(golden.Adjacencies))
44 for _, adj := range golden.Adjacencies {
45 expected[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
46 }
47
48 actual := make(map[string]struct{}, len(result.Adjacencies))
49 for _, adj := range result.Adjacencies {
50 actual[adj.Protocol+"|"+adj.SourceID+"|"+adj.SourcePort+"|"+adj.TargetID+"|"+adj.TargetPort] = struct{}{}
51 }
52
53 require.Equal(t, expected, actual)
54 }
55
56 func TestBuildL2ResultFromWalks_CDP_NMS8000(t *testing.T) {
57 manifestPath := "../../../testdata/snmp/enlinkd/nms8000/manifest.yaml"
58 manifest, err := LoadManifest(manifestPath)
59 require.NoError(t, err)
60
61 scenario, ok := manifest.FindScenario("nms8000_cdp")
62 require.True(t, ok)
63
64 require.False(t, scenario.Protocols.LLDP)
65 require.True(t, scenario.Protocols.CDP)
66 require.False(t, scenario.Protocols.Bridge)
67 require.False(t, scenario.Protocols.ARPND)
68 require.Equal(t, ManifestProtocols{CDP: true}, scenario.Protocols)
69
70 resolved, err := ResolveScenario(manifestPath, scenario)
71 require.NoError(t, err)
72 require.Len(t, resolved.Fixtures, 5)
73
74 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableCDP: true})
75 require.NoError(t, errStep1)
76 require.Equal(t, 3, step1.Stats["links_cdp"])
77
78 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableCDP: true})
79 require.NoError(t, errStep2)
80 require.Equal(t, 6, step2.Stats["links_cdp"])
81
82 step3, errStep3 := buildResultFromScenarioPrefix(resolved, 3, BuildOptions{EnableCDP: true})
83 require.NoError(t, errStep3)
84 require.Equal(t, 9, step3.Stats["links_cdp"])
85
86 step4, errStep4 := buildResultFromScenarioPrefix(resolved, 4, BuildOptions{EnableCDP: true})
87 require.NoError(t, errStep4)
88 require.Equal(t, 11, step4.Stats["links_cdp"])
89
90 step5, errStep5 := buildResultFromScenarioPrefix(resolved, 5, BuildOptions{EnableCDP: true})
91 require.NoError(t, errStep5)
92 require.Equal(t, 13, step5.Stats["links_cdp"])
93
94 final := step5
95 require.Len(t, final.Adjacencies, 13)
96 require.Equal(t, 13, final.Stats["links_cdp"])
97 require.Equal(t, 0, final.Stats["links_lldp"])
98
99 expected := map[string]struct{}{
100 "cdp|nmmr1|Gi0/0|nmmr3|GigabitEthernet0/1": {},
101 "cdp|nmmr1|Gi0/1|nmmsw1|FastEthernet0/1": {},
102 "cdp|nmmr1|Gi0/2|nmmsw2|FastEthernet0/2": {},
103 "cdp|nmmr2|Gi0/0|nmmr3|GigabitEthernet0/2": {},
104 "cdp|nmmr2|Gi0/1|nmmsw2|FastEthernet0/1": {},
105 "cdp|nmmr2|Gi0/2|nmmsw1|FastEthernet0/2": {},
106 "cdp|nmmr3|Gi0/0|netlabsw03.informatik.hs-fulda.de|GigabitEthernet2/0/18": {},
107 "cdp|nmmr3|Gi0/1|nmmr1|GigabitEthernet0/0": {},
108 "cdp|nmmr3|Gi0/2|nmmr2|GigabitEthernet0/0": {},
109 "cdp|nmmsw1|Fa0/1|nmmr1|GigabitEthernet0/1": {},
110 "cdp|nmmsw1|Fa0/2|nmmr2|GigabitEthernet0/2": {},
111 "cdp|nmmsw2|Fa0/1|nmmr2|GigabitEthernet0/1": {},
112 "cdp|nmmsw2|Fa0/2|nmmr1|GigabitEthernet0/2": {},
113 }
114 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
115
116 for _, adj := range final.Adjacencies {
117 require.Equal(t, "cdp", adj.Protocol)
118 raw := strings.TrimSpace(adj.Labels["remote_address_raw"])
119 require.NotEmpty(t, raw)
120 decoded := decodeHexIP(raw)
121 require.NotEmpty(t, decoded)
122 _, parseErr := netip.ParseAddr(decoded)
123 require.NoError(t, parseErr)
124 }
125 }
126
127 func TestBuildL2ResultFromWalks_LLDP_NMS8000(t *testing.T) {
128 manifestPath := "../../../testdata/snmp/enlinkd/nms8000/manifest.yaml"
129 manifest, err := LoadManifest(manifestPath)
130 require.NoError(t, err)
131
132 scenario, ok := manifest.FindScenario("nms8000_lldp")
133 require.True(t, ok)
134
135 require.True(t, scenario.Protocols.LLDP)
136 require.False(t, scenario.Protocols.CDP)
137 require.False(t, scenario.Protocols.Bridge)
138 require.False(t, scenario.Protocols.ARPND)
139 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
140
141 resolved, err := ResolveScenario(manifestPath, scenario)
142 require.NoError(t, err)
143 require.Len(t, resolved.Fixtures, 5)
144
145 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
146 require.NoError(t, errStep1)
147 require.Equal(t, 3, step1.Stats["links_lldp"])
148
149 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
150 require.NoError(t, errStep2)
151 require.Equal(t, 6, step2.Stats["links_lldp"])
152
153 step3, errStep3 := buildResultFromScenarioPrefix(resolved, 3, BuildOptions{EnableLLDP: true})
154 require.NoError(t, errStep3)
155 require.Equal(t, 8, step3.Stats["links_lldp"])
156
157 step4, errStep4 := buildResultFromScenarioPrefix(resolved, 4, BuildOptions{EnableLLDP: true})
158 require.NoError(t, errStep4)
159 require.Equal(t, 10, step4.Stats["links_lldp"])
160
161 step5, errStep5 := buildResultFromScenarioPrefix(resolved, 5, BuildOptions{EnableLLDP: true})
162 require.NoError(t, errStep5)
163 require.Equal(t, 12, step5.Stats["links_lldp"])
164
165 final := step5
166 require.Len(t, final.Adjacencies, 12)
167 require.Equal(t, 12, final.Stats["links_lldp"])
168 require.Equal(t, 0, final.Stats["links_cdp"])
169
170 expected := map[string]struct{}{
171 "lldp|nmmr1|Gi0/0|nmmr3|Gi0/1": {},
172 "lldp|nmmr1|Gi0/1|nmmsw1|Fa0/1": {},
173 "lldp|nmmr1|Gi0/2|nmmsw2|Fa0/2": {},
174 "lldp|nmmr2|Gi0/0|nmmr3|Gi0/2": {},
175 "lldp|nmmr2|Gi0/1|nmmsw2|Fa0/1": {},
176 "lldp|nmmr2|Gi0/2|nmmsw1|Fa0/2": {},
177 "lldp|nmmr3|Gi0/1|nmmr1|Gi0/0": {},
178 "lldp|nmmr3|Gi0/2|nmmr2|Gi0/0": {},
179 "lldp|nmmsw1|Fa0/1|nmmr1|Gi0/1": {},
180 "lldp|nmmsw1|Fa0/2|nmmr2|Gi0/2": {},
181 "lldp|nmmsw2|Fa0/1|nmmr2|Gi0/1": {},
182 "lldp|nmmsw2|Fa0/2|nmmr1|Gi0/2": {},
183 }
184 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
185 require.Equal(t, 6, countBidirectionalPairs(final.Adjacencies, "lldp"))
186 }
187
188 func TestBuildL2ResultFromWalks_LLDP_NMS13637(t *testing.T) {
189 manifestPath := "../../../testdata/snmp/enlinkd/nms13637/manifest.yaml"
190 manifest, err := LoadManifest(manifestPath)
191 require.NoError(t, err)
192
193 scenario, ok := manifest.FindScenario("nms13637_lldp")
194 require.True(t, ok)
195
196 require.True(t, scenario.Protocols.LLDP)
197 require.False(t, scenario.Protocols.CDP)
198 require.False(t, scenario.Protocols.Bridge)
199 require.False(t, scenario.Protocols.ARPND)
200 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
201
202 resolved, err := ResolveScenario(manifestPath, scenario)
203 require.NoError(t, err)
204 require.Len(t, resolved.Fixtures, 3)
205
206 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
207 require.NoError(t, errStep1)
208 require.Equal(t, 5, step1.Stats["links_lldp"])
209
210 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
211 require.NoError(t, errStep2)
212 require.Equal(t, 10, step2.Stats["links_lldp"])
213
214 step3, errStep3 := buildResultFromScenarioPrefix(resolved, 3, BuildOptions{EnableLLDP: true})
215 require.NoError(t, errStep3)
216 require.Equal(t, 19, step3.Stats["links_lldp"])
217
218 final := step3
219 require.Len(t, final.Adjacencies, 19)
220 require.Equal(t, 19, final.Stats["links_lldp"])
221 require.Equal(t, 0, final.Stats["links_cdp"])
222
223 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
224 require.NoError(t, err)
225
226 expected := make(map[string]struct{}, len(golden.Adjacencies))
227 for _, adj := range golden.Adjacencies {
228 expected[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
229 }
230 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
231
232 localDevices := map[string]struct{}{
233 "router-1": {},
234 "router-2": {},
235 "sw01-office": {},
236 }
237 require.Equal(t, 3, countUndirectedLocalDevicePairs(final.Adjacencies, "lldp", localDevices))
238 require.Equal(t, 3, countLocalTopologyVertices(final.Adjacencies, "lldp", localDevices))
239 }
240
241 func TestBuildL2ResultFromWalks_LLDP_NMS10205B(t *testing.T) {
242 manifestPath := "../../../testdata/snmp/enlinkd/nms10205b/manifest.yaml"
243 manifest, err := LoadManifest(manifestPath)
244 require.NoError(t, err)
245
246 scenario, ok := manifest.FindScenario("nms10205b_lldp")
247 require.True(t, ok)
248
249 require.True(t, scenario.Protocols.LLDP)
250 require.False(t, scenario.Protocols.CDP)
251 require.False(t, scenario.Protocols.Bridge)
252 require.False(t, scenario.Protocols.ARPND)
253 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
254
255 resolved, err := ResolveScenario(manifestPath, scenario)
256 require.NoError(t, err)
257 require.Len(t, resolved.Fixtures, 9)
258
259 type stepExpectation struct {
260 fixtureID string
261 totalLinks int
262 sourceLinks int
263 lldpElementCnt int
264 }
265
266 expectations := []stepExpectation{
267 {fixtureID: "Mumbai", totalLinks: 0, sourceLinks: 0, lldpElementCnt: 0},
268 {fixtureID: "Delhi", totalLinks: 2, sourceLinks: 2, lldpElementCnt: 1},
269 {fixtureID: "Bangalore", totalLinks: 2, sourceLinks: 0, lldpElementCnt: 1},
270 {fixtureID: "Bagmane", totalLinks: 5, sourceLinks: 3, lldpElementCnt: 2},
271 {fixtureID: "Mysore", totalLinks: 5, sourceLinks: 0, lldpElementCnt: 2},
272 {fixtureID: "Space-EX-SW1", totalLinks: 8, sourceLinks: 3, lldpElementCnt: 3},
273 {fixtureID: "Space-EX-SW2", totalLinks: 10, sourceLinks: 2, lldpElementCnt: 4},
274 {fixtureID: "J6350-42", totalLinks: 10, sourceLinks: 0, lldpElementCnt: 5},
275 {fixtureID: "SRX-100", totalLinks: 10, sourceLinks: 0, lldpElementCnt: 6},
276 }
277
278 var final l2topology.Result
279 for i, expected := range expectations {
280 walks, walkErr := loadScenarioWalkPrefix(resolved, i+1)
281 require.NoError(t, walkErr)
282
283 result, buildErr := BuildL2ResultFromWalks(walks, BuildOptions{EnableLLDP: true})
284 require.NoError(t, buildErr)
285
286 sourceCounts := countAdjacenciesBySource(result.Adjacencies, "lldp")
287 require.Equal(t, expected.sourceLinks, sourceCounts[expected.fixtureID])
288 require.Equal(t, expected.totalLinks, result.Stats["links_lldp"])
289 require.Equal(t, expected.lldpElementCnt, countFixturesWithLLDPLocalElements(walks))
290 final = result
291 }
292
293 require.Equal(t, 10, final.Stats["links_lldp"])
294 require.Equal(t, 0, final.Stats["links_cdp"])
295 allWalks, err := loadScenarioWalkPrefix(resolved, len(resolved.Fixtures))
296 require.NoError(t, err)
297 require.Equal(t, 6, countFixturesWithLLDPLocalElements(allWalks))
298 require.Len(t, final.Adjacencies, 10)
299 require.Equal(t, 3, countBidirectionalPairs(final.Adjacencies, "lldp"))
300
301 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
302 require.NoError(t, err)
303
304 expected := make(map[string]struct{}, len(golden.Adjacencies))
305 for _, adj := range golden.Adjacencies {
306 expected[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
307 }
308 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
309
310 finalKeys := adjacencyKeySet(final.Adjacencies)
311 require.Contains(t, finalKeys, "lldp|Delhi|28519|Bagmane|513")
312 require.Contains(t, finalKeys, "lldp|Delhi|28520|Space-EX-SW1|528")
313 require.Contains(t, finalKeys, "lldp|Space-EX-SW1|1361|Space-EX-SW2|531")
314
315 topologyEdges := make(map[string]l2topology.Adjacency, len(final.Adjacencies))
316 for _, adj := range final.Adjacencies {
317 if adj.Protocol != "lldp" {
318 continue
319 }
320 topologyEdges[adj.SourceID+"|"+adj.TargetID] = adj
321 }
322
323 edgeDelhiBagmane, ok := topologyEdges["Delhi|Bagmane"]
324 require.True(t, ok)
325 require.Equal(t, "28519", edgeDelhiBagmane.SourcePort)
326 require.Equal(t, "513", edgeDelhiBagmane.TargetPort)
327
328 edgeDelhiSpaceEXSW1, ok := topologyEdges["Delhi|Space-EX-SW1"]
329 require.True(t, ok)
330 require.Equal(t, "28520", edgeDelhiSpaceEXSW1.SourcePort)
331 require.Equal(t, "528", edgeDelhiSpaceEXSW1.TargetPort)
332
333 edgeSpaceEXSW1SpaceEXSW2, ok := topologyEdges["Space-EX-SW1|Space-EX-SW2"]
334 require.True(t, ok)
335 require.Equal(t, "1361", edgeSpaceEXSW1SpaceEXSW2.SourcePort)
336 require.Equal(t, "531", edgeSpaceEXSW1SpaceEXSW2.TargetPort)
337 }
338
339 func TestBuildL2ResultFromWalks_LLDP_NMS17216(t *testing.T) {
340 manifestPath := "../../../testdata/snmp/enlinkd/nms17216/manifest.yaml"
341 manifest, err := LoadManifest(manifestPath)
342 require.NoError(t, err)
343
344 scenario, ok := manifest.FindScenario("nms17216_lldp")
345 require.True(t, ok)
346
347 require.True(t, scenario.Protocols.LLDP)
348 require.False(t, scenario.Protocols.CDP)
349 require.False(t, scenario.Protocols.Bridge)
350 require.False(t, scenario.Protocols.ARPND)
351 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
352
353 resolved, err := ResolveScenario(manifestPath, scenario)
354 require.NoError(t, err)
355 require.Len(t, resolved.Fixtures, 5)
356
357 type stepExpectation struct {
358 fixtureID string
359 totalLinks int
360 sourceLinks int
361 lldpElementCnt int
362 }
363
364 expectations := []stepExpectation{
365 {fixtureID: "Switch1", totalLinks: 4, sourceLinks: 4, lldpElementCnt: 1},
366 {fixtureID: "Switch2", totalLinks: 10, sourceLinks: 6, lldpElementCnt: 2},
367 {fixtureID: "Switch3", totalLinks: 12, sourceLinks: 2, lldpElementCnt: 3},
368 {fixtureID: "Switch4", totalLinks: 12, sourceLinks: 0, lldpElementCnt: 4},
369 {fixtureID: "Switch5", totalLinks: 12, sourceLinks: 0, lldpElementCnt: 5},
370 }
371
372 var final l2topology.Result
373 for i, expected := range expectations {
374 walks, walkErr := loadScenarioWalkPrefix(resolved, i+1)
375 require.NoError(t, walkErr)
376
377 result, buildErr := BuildL2ResultFromWalks(walks, BuildOptions{EnableLLDP: true})
378 require.NoError(t, buildErr)
379
380 sourceCounts := countAdjacenciesBySource(result.Adjacencies, "lldp")
381 require.Equal(t, expected.totalLinks, result.Stats["links_lldp"])
382 require.Equal(t, expected.sourceLinks, sourceCounts[expected.fixtureID])
383 require.Equal(t, expected.lldpElementCnt, countFixturesWithLLDPLocalElements(walks))
384 final = result
385 }
386
387 require.Len(t, final.Devices, 5)
388 require.Len(t, final.Adjacencies, 12)
389 require.Equal(t, 12, final.Stats["links_lldp"])
390 require.Equal(t, 0, final.Stats["links_cdp"])
391 require.Equal(t, 6, countBidirectionalPairs(final.Adjacencies, "lldp"))
392
393 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
394 require.NoError(t, err)
395
396 expected := make(map[string]struct{}, len(golden.Adjacencies))
397 for _, adj := range golden.Adjacencies {
398 expected[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
399 }
400 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
401 }
402
403 func TestBuildL2ResultFromWalks_CDP_NMS17216(t *testing.T) {
404 manifestPath := "../../../testdata/snmp/enlinkd/nms17216/manifest.yaml"
405 manifest, err := LoadManifest(manifestPath)
406 require.NoError(t, err)
407
408 scenario, ok := manifest.FindScenario("nms17216_cdp")
409 require.True(t, ok)
410
411 require.False(t, scenario.Protocols.LLDP)
412 require.True(t, scenario.Protocols.CDP)
413 require.False(t, scenario.Protocols.Bridge)
414 require.False(t, scenario.Protocols.ARPND)
415 require.Equal(t, ManifestProtocols{CDP: true}, scenario.Protocols)
416
417 resolved, err := ResolveScenario(manifestPath, scenario)
418 require.NoError(t, err)
419 require.Len(t, resolved.Fixtures, 9)
420
421 type stepExpectation struct {
422 fixtureID string
423 totalLinks int
424 sourceLinks int
425 }
426
427 expectations := []stepExpectation{
428 {fixtureID: "Switch1", totalLinks: 5, sourceLinks: 5},
429 {fixtureID: "Switch2", totalLinks: 11, sourceLinks: 6},
430 {fixtureID: "Switch3", totalLinks: 15, sourceLinks: 4},
431 {fixtureID: "Switch4", totalLinks: 16, sourceLinks: 1},
432 {fixtureID: "Switch5", totalLinks: 18, sourceLinks: 2},
433 {fixtureID: "Router1", totalLinks: 20, sourceLinks: 2},
434 {fixtureID: "Router2", totalLinks: 22, sourceLinks: 2},
435 {fixtureID: "Router3", totalLinks: 25, sourceLinks: 3},
436 {fixtureID: "Router4", totalLinks: 26, sourceLinks: 1},
437 }
438
439 var final l2topology.Result
440 for i, expected := range expectations {
441 walks, walkErr := loadScenarioWalkPrefix(resolved, i+1)
442 require.NoError(t, walkErr)
443
444 result, buildErr := BuildL2ResultFromWalks(walks, BuildOptions{EnableCDP: true})
445 require.NoError(t, buildErr)
446
447 sourceCounts := countAdjacenciesBySource(result.Adjacencies, "cdp")
448 require.Equal(t, expected.totalLinks, result.Stats["links_cdp"])
449 require.Equal(t, expected.sourceLinks, sourceCounts[expected.fixtureID])
450 final = result
451 }
452
453 require.Len(t, final.Devices, 9)
454 require.Len(t, final.Adjacencies, 26)
455 require.Equal(t, 26, final.Stats["links_cdp"])
456 require.Equal(t, 0, final.Stats["links_lldp"])
457
458 require.Equal(t, map[string]int{
459 "Switch1": 5,
460 "Switch2": 6,
461 "Switch3": 4,
462 "Switch4": 1,
463 "Switch5": 2,
464 "Router1": 2,
465 "Router2": 2,
466 "Router3": 3,
467 "Router4": 1,
468 }, countAdjacenciesBySource(final.Adjacencies, "cdp"))
469
470 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
471 require.NoError(t, err)
472
473 expected := make(map[string]struct{}, len(golden.Adjacencies))
474 for _, adj := range golden.Adjacencies {
475 expected[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
476 }
477 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
478 }
479
480 func TestBuildL2ResultFromWalks_CDP_NMS17216_TopologyProjection(t *testing.T) {
481 manifestPath := "../../../testdata/snmp/enlinkd/nms17216/manifest.yaml"
482 manifest, err := LoadManifest(manifestPath)
483 require.NoError(t, err)
484
485 scenario, ok := manifest.FindScenario("nms17216_cdp")
486 require.True(t, ok)
487
488 require.False(t, scenario.Protocols.LLDP)
489 require.True(t, scenario.Protocols.CDP)
490 require.False(t, scenario.Protocols.Bridge)
491 require.False(t, scenario.Protocols.ARPND)
492 require.Equal(t, ManifestProtocols{CDP: true}, scenario.Protocols)
493
494 resolved, err := ResolveScenario(manifestPath, scenario)
495 require.NoError(t, err)
496 require.Len(t, resolved.Fixtures, 9)
497
498 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableCDP: true})
499 require.NoError(t, errStep1)
500 require.Equal(t, 5, step1.Stats["links_cdp"])
501 require.Equal(t, 0, step1.Stats["links_lldp"])
502
503 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableCDP: true})
504 require.NoError(t, errStep2)
505 require.Equal(t, 11, step2.Stats["links_cdp"])
506 require.Equal(t, 0, step2.Stats["links_lldp"])
507 require.Equal(t, map[string]int{
508 "Switch1": 5,
509 "Switch2": 6,
510 }, countAdjacenciesBySource(step2.Adjacencies, "cdp"))
511
512 localDevices := map[string]struct{}{
513 "Switch1": {},
514 "Switch2": {},
515 }
516 require.Equal(t, 2, countLocalTopologyVertices(step2.Adjacencies, "cdp", localDevices))
517 require.Equal(t, 1, countUndirectedLocalDevicePairs(step2.Adjacencies, "cdp", localDevices))
518 require.Equal(t, 4, countMutualLocalAdjacencyEdges(step2.Adjacencies, "cdp", localDevices))
519 }
520
521 func TestBuildL2ResultFromWalks_LLDP_NMS17216_TopologyProjection(t *testing.T) {
522 manifestPath := "../../../testdata/snmp/enlinkd/nms17216/manifest.yaml"
523 manifest, err := LoadManifest(manifestPath)
524 require.NoError(t, err)
525
526 scenario, ok := manifest.FindScenario("nms17216_lldp")
527 require.True(t, ok)
528
529 require.True(t, scenario.Protocols.LLDP)
530 require.False(t, scenario.Protocols.CDP)
531 require.False(t, scenario.Protocols.Bridge)
532 require.False(t, scenario.Protocols.ARPND)
533 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
534
535 resolved, err := ResolveScenario(manifestPath, scenario)
536 require.NoError(t, err)
537 require.Len(t, resolved.Fixtures, 5)
538
539 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
540 require.NoError(t, errStep1)
541 require.Equal(t, 4, step1.Stats["links_lldp"])
542 require.Equal(t, 0, step1.Stats["links_cdp"])
543
544 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
545 require.NoError(t, errStep2)
546 require.Equal(t, 10, step2.Stats["links_lldp"])
547 require.Equal(t, 0, step2.Stats["links_cdp"])
548
549 localStep2 := map[string]struct{}{
550 "Switch1": {},
551 "Switch2": {},
552 }
553 require.Equal(t, 2, countLocalTopologyVertices(step2.Adjacencies, "lldp", localStep2))
554 require.Equal(t, 1, countUndirectedLocalDevicePairs(step2.Adjacencies, "lldp", localStep2))
555 require.Equal(t, 4, countMutualLocalAdjacencyEdges(step2.Adjacencies, "lldp", localStep2))
556
557 step3Walks, errStep3Walks := loadScenarioWalkPrefix(resolved, 3)
558 require.NoError(t, errStep3Walks)
559 require.Equal(t, 3, countFixturesWithLLDPLocalElements(step3Walks))
560
561 step3, errStep3 := BuildL2ResultFromWalks(step3Walks, BuildOptions{EnableLLDP: true})
562 require.NoError(t, errStep3)
563 require.Equal(t, 12, step3.Stats["links_lldp"])
564 require.Equal(t, 0, step3.Stats["links_cdp"])
565
566 localStep3 := map[string]struct{}{
567 "Switch1": {},
568 "Switch2": {},
569 "Switch3": {},
570 }
571 require.Equal(t, 3, countLocalTopologyVertices(step3.Adjacencies, "lldp", localStep3))
572 require.Equal(t, 2, countUndirectedLocalDevicePairs(step3.Adjacencies, "lldp", localStep3))
573 require.Equal(t, 6, countMutualLocalAdjacencyEdges(step3.Adjacencies, "lldp", localStep3))
574 }
575
576 func TestBuildL2ResultFromWalks_LLDP_NMS0123(t *testing.T) {
577 manifestPath := "../../../testdata/snmp/enlinkd/nms0123/manifest.yaml"
578 manifest, err := LoadManifest(manifestPath)
579 require.NoError(t, err)
580
581 scenario, ok := manifest.FindScenario("nms0123_lldp")
582 require.True(t, ok)
583
584 require.True(t, scenario.Protocols.LLDP)
585 require.False(t, scenario.Protocols.CDP)
586 require.False(t, scenario.Protocols.Bridge)
587 require.False(t, scenario.Protocols.ARPND)
588 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
589
590 resolved, err := ResolveScenario(manifestPath, scenario)
591 require.NoError(t, err)
592 require.Len(t, resolved.Fixtures, 8)
593
594 type stepExpectation struct {
595 fixtureID string
596 totalLinks int
597 }
598
599 expectations := []stepExpectation{
600 {fixtureID: "ITPN0111", totalLinks: 4},
601 {fixtureID: "ITPN0112", totalLinks: 6},
602 {fixtureID: "ITPN0113", totalLinks: 8},
603 {fixtureID: "ITPN0114", totalLinks: 10},
604 {fixtureID: "ITPN0121", totalLinks: 11},
605 {fixtureID: "ITPN0123", totalLinks: 14},
606 {fixtureID: "ITPN0201", totalLinks: 20},
607 {fixtureID: "ITPN0202", totalLinks: 23},
608 }
609
610 var final l2topology.Result
611 for i, expected := range expectations {
612 walks, walkErr := loadScenarioWalkPrefix(resolved, i+1)
613 require.NoError(t, walkErr)
614
615 result, buildErr := BuildL2ResultFromWalks(walks, BuildOptions{EnableLLDP: true})
616 require.NoError(t, buildErr)
617
618 require.Equal(t, expected.totalLinks, result.Stats["links_lldp"])
619 final = result
620 }
621
622 allWalks, err := loadScenarioWalkPrefix(resolved, len(resolved.Fixtures))
623 require.NoError(t, err)
624
625 require.Len(t, final.Adjacencies, 23)
626 require.Equal(t, 23, final.Stats["links_lldp"])
627 require.Equal(t, 0, final.Stats["links_cdp"])
628 require.Equal(t, 8, countFixturesWithLLDPLocalElements(allWalks))
629
630 localDevices := map[string]struct{}{
631 "ITPN0111": {},
632 "ITPN0112": {},
633 "ITPN0113": {},
634 "ITPN0114": {},
635 "ITPN0121": {},
636 "ITPN0123": {},
637 "ITPN0201": {},
638 "ITPN0202": {},
639 }
640 require.Equal(t, 8, countLocalTopologyVertices(final.Adjacencies, "lldp", localDevices))
641 require.Equal(t, 8, countUndirectedLocalDevicePairs(final.Adjacencies, "lldp", localDevices))
642
643 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
644 require.NoError(t, err)
645
646 expected := make(map[string]struct{}, len(golden.Adjacencies))
647 for _, adj := range golden.Adjacencies {
648 expected[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
649 }
650 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
651 }
652
653 func TestBuildL2ResultFromWalks_LLDP_NMS0002_CISCO_JUNIPER(t *testing.T) {
654 manifestPath := "../../../testdata/snmp/enlinkd/nms0002/manifest.yaml"
655 manifest, err := LoadManifest(manifestPath)
656 require.NoError(t, err)
657
658 scenario, ok := manifest.FindScenario("nms0002_cisco_juniper_lldp")
659 require.True(t, ok)
660
661 enableOSPF := false
662 enableISIS := false
663 require.True(t, scenario.Protocols.LLDP)
664 require.False(t, scenario.Protocols.CDP)
665 require.False(t, enableOSPF)
666 require.False(t, scenario.Protocols.Bridge)
667 require.False(t, enableISIS)
668 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
669
670 resolved, err := ResolveScenario(manifestPath, scenario)
671 require.NoError(t, err)
672 require.Len(t, resolved.Fixtures, 2)
673
674 preCollectionLinks := 0
675 require.Equal(t, 0, preCollectionLinks)
676
677 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
678 require.NoError(t, errStep1)
679 require.Equal(t, 1, step1.Stats["links_lldp"])
680
681 step2First, errStep2First := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
682 require.NoError(t, errStep2First)
683
684 step2Second, errStep2Second := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
685 require.NoError(t, errStep2Second)
686 require.Equal(t, step2First.Stats["links_lldp"], step2Second.Stats["links_lldp"])
687
688 final := step2Second
689 require.Len(t, final.Adjacencies, 2)
690 require.Equal(t, 2, final.Stats["links_lldp"])
691 require.Equal(t, 0, final.Stats["links_cdp"])
692
693 require.Equal(t, map[string]int{
694 "Rluck001": 1,
695 "Sluck001": 1,
696 }, countAdjacenciesBySource(final.Adjacencies, "lldp"))
697
698 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
699 require.NoError(t, err)
700
701 expected := make(map[string]struct{}, len(golden.Adjacencies))
702 for _, adj := range golden.Adjacencies {
703 expected[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
704 }
705 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
706 }
707
708 func TestBuildL2ResultFromWalks_LLDP_NMS0002_CISCO_ALCATEL(t *testing.T) {
709 manifestPath := "../../../testdata/snmp/enlinkd/nms0002/manifest.yaml"
710 manifest, err := LoadManifest(manifestPath)
711 require.NoError(t, err)
712
713 scenario, ok := manifest.FindScenario("nms0002_cisco_alcatel_lldp")
714 require.True(t, ok)
715
716 enableOSPF := false
717 enableISIS := false
718 require.True(t, scenario.Protocols.LLDP)
719 require.False(t, scenario.Protocols.CDP)
720 require.False(t, enableOSPF)
721 require.False(t, scenario.Protocols.Bridge)
722 require.False(t, enableISIS)
723 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
724
725 resolved, err := ResolveScenario(manifestPath, scenario)
726 require.NoError(t, err)
727 require.Len(t, resolved.Fixtures, 5)
728
729 preCollectionLinks := 0
730 require.Equal(t, 0, preCollectionLinks)
731
732 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
733 require.NoError(t, errStep1)
734 require.Equal(t, 0, step1.Stats["links_lldp"])
735
736 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
737 require.NoError(t, errStep2)
738 require.Equal(t, 1, step2.Stats["links_lldp"])
739
740 step3, errStep3 := buildResultFromScenarioPrefix(resolved, 3, BuildOptions{EnableLLDP: true})
741 require.NoError(t, errStep3)
742 require.Equal(t, 2, step3.Stats["links_lldp"])
743
744 step4, errStep4 := buildResultFromScenarioPrefix(resolved, 4, BuildOptions{EnableLLDP: true})
745 require.NoError(t, errStep4)
746 require.Equal(t, 4, step4.Stats["links_lldp"])
747
748 step5, errStep5 := buildResultFromScenarioPrefix(resolved, 5, BuildOptions{EnableLLDP: true})
749 require.NoError(t, errStep5)
750
751 final := step5
752 require.Len(t, final.Adjacencies, 6)
753 require.Equal(t, 6, final.Stats["links_lldp"])
754 require.Equal(t, 0, final.Stats["links_cdp"])
755
756 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
757 require.NoError(t, err)
758
759 expected := make(map[string]struct{}, len(golden.Adjacencies))
760 for _, adj := range golden.Adjacencies {
761 expected[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
762 }
763 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
764 }
765
766 func TestBuildL2ResultFromWalks_LLDP_NMS0000_NETWORK_ALL(t *testing.T) {
767 manifestPath := "../../../testdata/snmp/enlinkd/nms0000/manifest.yaml"
768 manifest, err := LoadManifest(manifestPath)
769 require.NoError(t, err)
770
771 scenario, ok := manifest.FindScenario("nms0000_network_all_lldp")
772 require.True(t, ok)
773
774 enableOSPF := false
775 enableISIS := false
776 require.True(t, scenario.Protocols.LLDP)
777 require.False(t, scenario.Protocols.CDP)
778 require.False(t, enableOSPF)
779 require.False(t, scenario.Protocols.Bridge)
780 require.False(t, enableISIS)
781 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
782
783 resolved, err := ResolveScenario(manifestPath, scenario)
784 require.NoError(t, err)
785
786 expectedFixtureIDs := []string{
787 "ms01",
788 "ms02",
789 "ms03",
790 "ms04",
791 "ms05",
792 "ms06",
793 "ms07",
794 "ms08",
795 "ms09",
796 "ms10",
797 "ms11",
798 "ms12",
799 "ms14",
800 "ms15",
801 "ms16",
802 "ms17",
803 "ms18",
804 "ms19",
805 }
806 require.Len(t, resolved.Fixtures, len(expectedFixtureIDs))
807 for i, fixture := range resolved.Fixtures {
808 require.Equal(t, expectedFixtureIDs[i], fixture.DeviceID)
809 }
810
811 allWalks, err := LoadScenarioWalks(resolved)
812 require.NoError(t, err)
813 require.Len(t, allWalks, len(expectedFixtureIDs))
814
815 preCollectionLinks := 0
816 preCollectionElements := 0
817 require.Equal(t, 0, preCollectionLinks)
818 require.Equal(t, 0, preCollectionElements)
819
820 type stepExpectation struct {
821 fixtureID string
822 totalLinks int
823 sourceLinks int
824 lldpElementCnt int
825 }
826
827 expectations := []stepExpectation{
828 {fixtureID: "ms01", totalLinks: 4, sourceLinks: 4, lldpElementCnt: 1},
829 {fixtureID: "ms02", totalLinks: 8, sourceLinks: 4, lldpElementCnt: 2},
830 {fixtureID: "ms03", totalLinks: 11, sourceLinks: 3, lldpElementCnt: 3},
831 {fixtureID: "ms04", totalLinks: 15, sourceLinks: 4, lldpElementCnt: 4},
832 {fixtureID: "ms05", totalLinks: 19, sourceLinks: 4, lldpElementCnt: 5},
833 {fixtureID: "ms06", totalLinks: 23, sourceLinks: 4, lldpElementCnt: 6},
834 {fixtureID: "ms07", totalLinks: 26, sourceLinks: 3, lldpElementCnt: 7},
835 {fixtureID: "ms08", totalLinks: 31, sourceLinks: 5, lldpElementCnt: 8},
836 {fixtureID: "ms09", totalLinks: 36, sourceLinks: 5, lldpElementCnt: 9},
837 {fixtureID: "ms10", totalLinks: 41, sourceLinks: 5, lldpElementCnt: 10},
838 {fixtureID: "ms11", totalLinks: 45, sourceLinks: 4, lldpElementCnt: 11},
839 {fixtureID: "ms12", totalLinks: 51, sourceLinks: 6, lldpElementCnt: 12},
840 {fixtureID: "ms14", totalLinks: 55, sourceLinks: 4, lldpElementCnt: 13},
841 {fixtureID: "ms15", totalLinks: 56, sourceLinks: 1, lldpElementCnt: 14},
842 {fixtureID: "ms16", totalLinks: 61, sourceLinks: 5, lldpElementCnt: 15},
843 {fixtureID: "ms17", totalLinks: 65, sourceLinks: 4, lldpElementCnt: 16},
844 {fixtureID: "ms18", totalLinks: 70, sourceLinks: 5, lldpElementCnt: 17},
845 {fixtureID: "ms19", totalLinks: 73, sourceLinks: 3, lldpElementCnt: 18},
846 }
847
848 var final l2topology.Result
849 for i, expected := range expectations {
850 walks, walkErr := loadScenarioWalkPrefix(resolved, i+1)
851 require.NoError(t, walkErr)
852
853 result, buildErr := BuildL2ResultFromWalks(walks, BuildOptions{EnableLLDP: true})
854 require.NoError(t, buildErr)
855
856 sourceCounts := countAdjacenciesBySource(result.Adjacencies, "lldp")
857 require.Equal(t, expected.totalLinks, result.Stats["links_lldp"])
858 require.Equal(t, expected.sourceLinks, sourceCounts[expected.fixtureID])
859 require.Equal(t, expected.lldpElementCnt, countFixturesWithLLDPLocalElements(allWalks[:i+1]))
860 final = result
861 }
862
863 require.Len(t, final.Adjacencies, 73)
864 require.Equal(t, 73, final.Stats["links_lldp"])
865 require.Equal(t, 0, final.Stats["links_cdp"])
866 require.Equal(t, 18, countFixturesWithLLDPLocalElements(allWalks))
867
868 localDevices := map[string]struct{}{
869 "ms01": {},
870 "ms02": {},
871 "ms03": {},
872 "ms04": {},
873 "ms05": {},
874 "ms06": {},
875 "ms07": {},
876 "ms08": {},
877 "ms09": {},
878 "ms10": {},
879 "ms11": {},
880 "ms12": {},
881 "ms14": {},
882 "ms15": {},
883 "ms16": {},
884 "ms17": {},
885 "ms18": {},
886 "ms19": {},
887 }
888 require.Equal(t, 18, countLocalTopologyVertices(final.Adjacencies, "lldp", localDevices))
889 require.Equal(t, 17, countUndirectedLocalDevicePairs(final.Adjacencies, "lldp", localDevices))
890
891 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
892 require.NoError(t, err)
893 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(final.Adjacencies))
894 }
895
896 func TestBuildL2ResultFromWalks_LLDP_NMS0000_NETWORK_TWO_CONNECTED(t *testing.T) {
897 manifestPath := "../../../testdata/snmp/enlinkd/nms0000/manifest.yaml"
898 manifest, err := LoadManifest(manifestPath)
899 require.NoError(t, err)
900
901 scenario, ok := manifest.FindScenario("nms0000_network_two_connected_lldp")
902 require.True(t, ok)
903
904 enableOSPF := false
905 enableISIS := false
906 require.True(t, scenario.Protocols.LLDP)
907 require.False(t, scenario.Protocols.CDP)
908 require.False(t, enableOSPF)
909 require.False(t, scenario.Protocols.Bridge)
910 require.False(t, enableISIS)
911 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
912
913 resolved, err := ResolveScenario(manifestPath, scenario)
914 require.NoError(t, err)
915 require.Len(t, resolved.Fixtures, 2)
916 require.Equal(t, "ms07", resolved.Fixtures[0].DeviceID)
917 require.Equal(t, "ms08", resolved.Fixtures[1].DeviceID)
918
919 allWalks, err := LoadScenarioWalks(resolved)
920 require.NoError(t, err)
921 require.Len(t, allWalks, 2)
922
923 preCollectionLinks := 0
924 preCollectionElements := 0
925 require.Equal(t, 0, preCollectionLinks)
926 require.Equal(t, 0, preCollectionElements)
927
928 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
929 require.NoError(t, errStep1)
930 require.Equal(t, 3, step1.Stats["links_lldp"])
931 require.Equal(t, map[string]int{"ms07": 3}, countAdjacenciesBySource(step1.Adjacencies, "lldp"))
932 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks[:1]))
933
934 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
935 require.NoError(t, errStep2)
936 require.Equal(t, 8, step2.Stats["links_lldp"])
937 require.Equal(t, map[string]int{"ms07": 3, "ms08": 5}, countAdjacenciesBySource(step2.Adjacencies, "lldp"))
938 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
939
940 final := step2
941 require.Len(t, final.Adjacencies, 8)
942 require.Equal(t, 8, final.Stats["links_lldp"])
943 require.Equal(t, 0, final.Stats["links_cdp"])
944
945 localDevices := map[string]struct{}{
946 "ms07": {},
947 "ms08": {},
948 }
949 require.Equal(t, 2, countLocalTopologyVertices(final.Adjacencies, "lldp", localDevices))
950 require.Equal(t, 1, countUndirectedLocalDevicePairs(final.Adjacencies, "lldp", localDevices))
951
952 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
953 require.NoError(t, err)
954 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(final.Adjacencies))
955 }
956
957 func TestBuildL2ResultFromWalks_LLDP_NMS0000_NETWORK_THREE_CONNECTED(t *testing.T) {
958 manifestPath := "../../../testdata/snmp/enlinkd/nms0000/manifest.yaml"
959 manifest, err := LoadManifest(manifestPath)
960 require.NoError(t, err)
961
962 scenario, ok := manifest.FindScenario("nms0000_network_three_connected_lldp")
963 require.True(t, ok)
964
965 enableOSPF := false
966 enableISIS := false
967 require.True(t, scenario.Protocols.LLDP)
968 require.False(t, scenario.Protocols.CDP)
969 require.False(t, enableOSPF)
970 require.False(t, scenario.Protocols.Bridge)
971 require.False(t, enableISIS)
972 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
973
974 resolved, err := ResolveScenario(manifestPath, scenario)
975 require.NoError(t, err)
976 require.Len(t, resolved.Fixtures, 2)
977 require.Equal(t, "ms08", resolved.Fixtures[0].DeviceID)
978 require.Equal(t, "ms10", resolved.Fixtures[1].DeviceID)
979
980 allWalks, err := LoadScenarioWalks(resolved)
981 require.NoError(t, err)
982 require.Len(t, allWalks, 2)
983
984 preCollectionLinks := 0
985 preCollectionElements := 0
986 require.Equal(t, 0, preCollectionLinks)
987 require.Equal(t, 0, preCollectionElements)
988
989 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
990 require.NoError(t, errStep1)
991 require.Equal(t, 5, step1.Stats["links_lldp"])
992 require.Equal(t, map[string]int{"ms08": 5}, countAdjacenciesBySource(step1.Adjacencies, "lldp"))
993 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks[:1]))
994
995 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
996 require.NoError(t, errStep2)
997 require.Equal(t, 10, step2.Stats["links_lldp"])
998 require.Equal(t, map[string]int{"ms08": 5, "ms10": 5}, countAdjacenciesBySource(step2.Adjacencies, "lldp"))
999 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
1000
1001 final := step2
1002 require.Len(t, final.Adjacencies, 10)
1003 require.Equal(t, 10, final.Stats["links_lldp"])
1004 require.Equal(t, 0, final.Stats["links_cdp"])
1005
1006 localDevices := map[string]struct{}{
1007 "ms08": {},
1008 "ms09": {},
1009 "ms10": {},
1010 }
1011 require.Equal(t, 0, countUndirectedLocalDevicePairs(final.Adjacencies, "lldp", localDevices))
1012 localSourceCounts := countAdjacenciesBySource(final.Adjacencies, "lldp")
1013 require.Equal(t, 2, len(localSourceCounts))
1014
1015 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1016 require.NoError(t, err)
1017 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(final.Adjacencies))
1018 }
1019
1020 func TestBuildL2ResultFromWalks_LLDP_NMS0000_MICROSENSE(t *testing.T) {
1021 manifestPath := "../../../testdata/snmp/enlinkd/nms0000/manifest.yaml"
1022 manifest, err := LoadManifest(manifestPath)
1023 require.NoError(t, err)
1024
1025 scenario, ok := manifest.FindScenario("nms0000_microsense_lldp")
1026 require.True(t, ok)
1027
1028 enableOSPF := false
1029 enableISIS := false
1030 require.True(t, scenario.Protocols.LLDP)
1031 require.False(t, scenario.Protocols.CDP)
1032 require.False(t, enableOSPF)
1033 require.False(t, scenario.Protocols.Bridge)
1034 require.False(t, enableISIS)
1035 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1036
1037 resolved, err := ResolveScenario(manifestPath, scenario)
1038 require.NoError(t, err)
1039 require.Len(t, resolved.Fixtures, 1)
1040
1041 allWalks, err := LoadScenarioWalks(resolved)
1042 require.NoError(t, err)
1043 require.Len(t, allWalks, 1)
1044
1045 preCollectionLinks := 0
1046 preCollectionElements := 0
1047 require.Equal(t, 0, preCollectionLinks)
1048 require.Equal(t, 0, preCollectionElements)
1049
1050 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1051 require.NoError(t, errStep1)
1052 require.Len(t, step1.Adjacencies, 5)
1053 require.Equal(t, 5, step1.Stats["links_lldp"])
1054 require.Equal(t, 0, step1.Stats["links_cdp"])
1055 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks))
1056 require.Equal(t, map[string]int{"ms08": 5}, countAdjacenciesBySource(step1.Adjacencies, "lldp"))
1057
1058 deviceByID := make(map[string]l2topology.Device, len(step1.Devices))
1059 for _, dev := range step1.Devices {
1060 deviceByID[dev.ID] = dev
1061 }
1062 require.Contains(t, deviceByID, "ms08")
1063 require.Equal(t, "SW_A1_BDA_08_M", deviceByID["ms08"].Hostname)
1064 require.Equal(t, "00:60:a7:0a:7f:6c", deviceByID["ms08"].ChassisID)
1065
1066 expected := map[string]struct{}{
1067 "lldp|ms08|2/4|microsens-g6-mac-00:60:a7:0a:7f:26|2/5": {},
1068 "lldp|ms08|2/6|axis-accc8eef78f9|ac:cc:8e:ef:78:f9": {},
1069 "lldp|ms08|3/1|axis-accc8eef78e4|ac:cc:8e:ef:78:e4": {},
1070 "lldp|ms08|3/2|axis-b8a44f502ddd|b8:a4:4f:50:2d:dd": {},
1071 "lldp|ms08|3/4|microsens-g6-mac-00:60:a7:0a:7f:10|3/5": {},
1072 }
1073 require.Equal(t, expected, adjacencyKeySet(step1.Adjacencies))
1074
1075 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1076 require.NoError(t, err)
1077 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step1.Adjacencies))
1078 }
1079
1080 func TestBuildL2ResultFromWalks_LLDP_NMS0000_MS16(t *testing.T) {
1081 manifestPath := "../../../testdata/snmp/enlinkd/nms0000/manifest.yaml"
1082 manifest, err := LoadManifest(manifestPath)
1083 require.NoError(t, err)
1084
1085 scenario, ok := manifest.FindScenario("nms0000_ms16_lldp")
1086 require.True(t, ok)
1087
1088 enableOSPF := false
1089 enableISIS := false
1090 require.True(t, scenario.Protocols.LLDP)
1091 require.False(t, scenario.Protocols.CDP)
1092 require.False(t, enableOSPF)
1093 require.False(t, scenario.Protocols.Bridge)
1094 require.False(t, enableISIS)
1095 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1096
1097 resolved, err := ResolveScenario(manifestPath, scenario)
1098 require.NoError(t, err)
1099 require.Len(t, resolved.Fixtures, 1)
1100
1101 allWalks, err := LoadScenarioWalks(resolved)
1102 require.NoError(t, err)
1103 require.Len(t, allWalks, 1)
1104
1105 preCollectionLinks := 0
1106 preCollectionElements := 0
1107 require.Equal(t, 0, preCollectionLinks)
1108 require.Equal(t, 0, preCollectionElements)
1109
1110 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1111 require.NoError(t, errStep1)
1112 require.Len(t, step1.Adjacencies, 5)
1113 require.Equal(t, 5, step1.Stats["links_lldp"])
1114 require.Equal(t, 0, step1.Stats["links_cdp"])
1115 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks))
1116 require.Equal(t, map[string]int{"ms16": 5}, countAdjacenciesBySource(step1.Adjacencies, "lldp"))
1117
1118 deviceByID := make(map[string]l2topology.Device, len(step1.Devices))
1119 for _, dev := range step1.Devices {
1120 deviceByID[dev.ID] = dev
1121 }
1122 require.Contains(t, deviceByID, "ms16")
1123 require.Equal(t, "SW_A1_BDA_16_M", deviceByID["ms16"].Hostname)
1124 require.Equal(t, "00:60:a7:0c:43:ff", deviceByID["ms16"].ChassisID)
1125
1126 expected := map[string]struct{}{
1127 "lldp|ms16|2/5|microsens-g6-mac-00:60:a7:0a:cc:1b|2/6": {},
1128 "lldp|ms16|3/1|axis-accc8eef7a21|ac:cc:8e:ef:7a:21": {},
1129 "lldp|ms16|3/2|axis-accc8eef78f8|ac:cc:8e:ef:78:f8": {},
1130 "lldp|ms16|3/5|microsens-g6-mac-00:60:a7:0a:cb:d3|2/5": {},
1131 "lldp|ms16|3/6|microsens-g6-mac-00:60:a7:0a:cc:09|2/5": {},
1132 }
1133 require.Equal(t, expected, adjacencyKeySet(step1.Adjacencies))
1134
1135 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1136 require.NoError(t, err)
1137 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step1.Adjacencies))
1138 }
1139
1140 func TestBuildL2ResultFromWalks_LLDP_NMS0000_PLANET(t *testing.T) {
1141 manifestPath := "../../../testdata/snmp/enlinkd/nms0000/manifest.yaml"
1142 manifest, err := LoadManifest(manifestPath)
1143 require.NoError(t, err)
1144
1145 scenario, ok := manifest.FindScenario("nms0000_planet_lldp")
1146 require.True(t, ok)
1147
1148 enableOSPF := false
1149 enableISIS := false
1150 require.True(t, scenario.Protocols.LLDP)
1151 require.False(t, scenario.Protocols.CDP)
1152 require.False(t, enableOSPF)
1153 require.False(t, scenario.Protocols.Bridge)
1154 require.False(t, enableISIS)
1155 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1156
1157 resolved, err := ResolveScenario(manifestPath, scenario)
1158 require.NoError(t, err)
1159 require.Len(t, resolved.Fixtures, 1)
1160
1161 allWalks, err := LoadScenarioWalks(resolved)
1162 require.NoError(t, err)
1163 require.Len(t, allWalks, 1)
1164
1165 preCollectionLinks := 0
1166 preCollectionElements := 0
1167 require.Equal(t, 0, preCollectionLinks)
1168 require.Equal(t, 0, preCollectionElements)
1169
1170 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1171 require.NoError(t, errStep1)
1172 require.Len(t, step1.Adjacencies, 3)
1173 require.Equal(t, 3, step1.Stats["links_lldp"])
1174 require.Equal(t, 0, step1.Stats["links_cdp"])
1175 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks))
1176 require.Equal(t, map[string]int{"planet": 3}, countAdjacenciesBySource(step1.Adjacencies, "lldp"))
1177
1178 deviceByID := make(map[string]l2topology.Device, len(step1.Devices))
1179 for _, dev := range step1.Devices {
1180 deviceByID[dev.ID] = dev
1181 }
1182 require.Contains(t, deviceByID, "planet")
1183 require.Equal(t, "V177", deviceByID["planet"].Hostname)
1184 require.Equal(t, "a8:f7:e0:6c:3b:d8", deviceByID["planet"].ChassisID)
1185
1186 adjByLocalPort := make(map[string]l2topology.Adjacency, len(step1.Adjacencies))
1187 for _, adj := range step1.Adjacencies {
1188 if adj.SourceID != "planet" {
1189 continue
1190 }
1191 adjByLocalPort[adj.SourcePort] = adj
1192 }
1193
1194 adj06, ok06 := adjByLocalPort["06"]
1195 require.True(t, ok06)
1196 require.Equal(t, "epmp1000_64aaec", adj06.TargetID)
1197 require.Equal(t, "00-04-56-6F-82-1E", adj06.TargetPort)
1198
1199 adj10, ok10 := adjByLocalPort["10"]
1200 require.True(t, ok10)
1201 require.Equal(t, "microsens-g6-mac-00:60:a7:0c:8e:33", adj10.TargetID)
1202 require.Equal(t, "2/5", adj10.TargetPort)
1203
1204 adj09, ok09 := adjByLocalPort["09"]
1205 require.True(t, ok09)
1206 require.Equal(t, "switch area b - v178", adj09.TargetID)
1207 require.Equal(t, "9", adj09.TargetPort)
1208
1209 expected := map[string]struct{}{
1210 "lldp|planet|06|epmp1000_64aaec|00-04-56-6F-82-1E": {},
1211 "lldp|planet|09|switch area b - v178|9": {},
1212 "lldp|planet|10|microsens-g6-mac-00:60:a7:0c:8e:33|2/5": {},
1213 }
1214 require.Equal(t, expected, adjacencyKeySet(step1.Adjacencies))
1215
1216 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1217 require.NoError(t, err)
1218 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step1.Adjacencies))
1219 }
1220
1221 func TestBuildL2ResultFromWalks_LLDP_NMS18541_NETWORK_ALL(t *testing.T) {
1222 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1223 manifest, err := LoadManifest(manifestPath)
1224 require.NoError(t, err)
1225
1226 scenario, ok := manifest.FindScenario("nms18541_network_all_lldp")
1227 require.True(t, ok)
1228
1229 enableOSPF := false
1230 enableISIS := false
1231 require.True(t, scenario.Protocols.LLDP)
1232 require.False(t, scenario.Protocols.CDP)
1233 require.False(t, enableOSPF)
1234 require.False(t, scenario.Protocols.Bridge)
1235 require.False(t, enableISIS)
1236 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1237
1238 resolved, err := ResolveScenario(manifestPath, scenario)
1239 require.NoError(t, err)
1240
1241 expectedFixtureIDs := []string{
1242 "SW_D6_01_M",
1243 "SW_D6_02_M",
1244 "SW_D6_03_M",
1245 "SW_D6_04_M",
1246 "SW_D6_08_M",
1247 "SW_D6_09_M",
1248 "E0281L-ScALBENGA2-QFX",
1249 }
1250 require.Len(t, resolved.Fixtures, len(expectedFixtureIDs))
1251
1252 fixturesByID := make(map[string]ResolvedFixture, len(resolved.Fixtures))
1253 for _, fixture := range resolved.Fixtures {
1254 fixturesByID[fixture.DeviceID] = fixture
1255 }
1256 for _, fixtureID := range expectedFixtureIDs {
1257 _, exists := fixturesByID[fixtureID]
1258 require.True(t, exists)
1259 }
1260
1261 allWalks, err := LoadScenarioWalks(resolved)
1262 require.NoError(t, err)
1263 require.Len(t, allWalks, len(expectedFixtureIDs))
1264
1265 preCollectionLinks := 0
1266 preCollectionElements := 0
1267 require.Equal(t, 0, preCollectionLinks)
1268 require.Equal(t, 0, preCollectionElements)
1269
1270 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1271 require.NoError(t, errStep1)
1272 require.Equal(t, 2, step1.Stats["links_lldp"])
1273 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks[:1]))
1274
1275 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
1276 require.NoError(t, errStep2)
1277 require.Equal(t, 4, step2.Stats["links_lldp"])
1278 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks[:2]))
1279
1280 step3, errStep3 := buildResultFromScenarioPrefix(resolved, 3, BuildOptions{EnableLLDP: true})
1281 require.NoError(t, errStep3)
1282 require.Equal(t, 5, step3.Stats["links_lldp"])
1283 require.Equal(t, 3, countFixturesWithLLDPLocalElements(allWalks[:3]))
1284
1285 step4, errStep4 := buildResultFromScenarioPrefix(resolved, 4, BuildOptions{EnableLLDP: true})
1286 require.NoError(t, errStep4)
1287 require.Equal(t, 9, step4.Stats["links_lldp"])
1288 require.Equal(t, 4, countFixturesWithLLDPLocalElements(allWalks[:4]))
1289
1290 step5, errStep5 := buildResultFromScenarioPrefix(resolved, 5, BuildOptions{EnableLLDP: true})
1291 require.NoError(t, errStep5)
1292 require.Equal(t, 12, step5.Stats["links_lldp"])
1293 require.Equal(t, 5, countFixturesWithLLDPLocalElements(allWalks[:5]))
1294
1295 step6, errStep6 := buildResultFromScenarioPrefix(resolved, 6, BuildOptions{EnableLLDP: true})
1296 require.NoError(t, errStep6)
1297 require.Equal(t, 19, step6.Stats["links_lldp"])
1298 require.Equal(t, 6, countFixturesWithLLDPLocalElements(allWalks[:6]))
1299
1300 localPreQFX := map[string]struct{}{
1301 "SW_D6_01_M": {},
1302 "SW_D6_02_M": {},
1303 "SW_D6_03_M": {},
1304 "SW_D6_04_M": {},
1305 "SW_D6_08_M": {},
1306 "SW_D6_09_M": {},
1307 }
1308 require.NotNil(t, step6.Adjacencies)
1309 require.Equal(t, 6, countFixturesWithLLDPLocalElements(allWalks[:6]))
1310 require.Equal(t, 2, countUndirectedLocalDevicePairs(step6.Adjacencies, "lldp", localPreQFX))
1311
1312 step7, errStep7 := buildResultFromScenarioPrefix(resolved, 7, BuildOptions{EnableLLDP: true})
1313 require.NoError(t, errStep7)
1314 require.Equal(t, 34, step7.Stats["links_lldp"])
1315 require.Equal(t, 7, countFixturesWithLLDPLocalElements(allWalks))
1316
1317 localAll := map[string]struct{}{
1318 "SW_D6_01_M": {},
1319 "SW_D6_02_M": {},
1320 "SW_D6_03_M": {},
1321 "SW_D6_04_M": {},
1322 "SW_D6_08_M": {},
1323 "SW_D6_09_M": {},
1324 "E0281L-ScALBENGA2-QFX": {},
1325 }
1326 require.Equal(t, 7, countFixturesWithLLDPLocalElements(allWalks))
1327 require.Equal(t, 6, countUndirectedLocalDevicePairs(step7.Adjacencies, "lldp", localAll))
1328 }
1329
1330 func TestBuildL2ResultFromWalks_LLDP_NMS18541_TOPO_QFX_SW01_SW02_SW03(t *testing.T) {
1331 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1332 manifest, err := LoadManifest(manifestPath)
1333 require.NoError(t, err)
1334
1335 scenario, ok := manifest.FindScenario("nms18541_topoqfx_sw01_sw02_sw03_lldp")
1336 require.True(t, ok)
1337
1338 require.True(t, scenario.Protocols.LLDP)
1339 require.False(t, scenario.Protocols.CDP)
1340 require.False(t, scenario.Protocols.Bridge)
1341 require.False(t, scenario.Protocols.ARPND)
1342 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1343
1344 resolved, err := ResolveScenario(manifestPath, scenario)
1345 require.NoError(t, err)
1346 require.Len(t, resolved.Fixtures, 4)
1347
1348 allWalks, err := LoadScenarioWalks(resolved)
1349 require.NoError(t, err)
1350 require.Len(t, allWalks, 4)
1351
1352 preCollectionLinks := 0
1353 preCollectionElements := 0
1354 require.Equal(t, 0, preCollectionLinks)
1355 require.Equal(t, 0, preCollectionElements)
1356
1357 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1358 require.NoError(t, errStep1)
1359 require.Equal(t, 15, step1.Stats["links_lldp"])
1360 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks[:1]))
1361
1362 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
1363 require.NoError(t, errStep2)
1364 require.Equal(t, 17, step2.Stats["links_lldp"])
1365 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks[:2]))
1366
1367 step3, errStep3 := buildResultFromScenarioPrefix(resolved, 3, BuildOptions{EnableLLDP: true})
1368 require.NoError(t, errStep3)
1369 require.Equal(t, 19, step3.Stats["links_lldp"])
1370 require.Equal(t, 3, countFixturesWithLLDPLocalElements(allWalks[:3]))
1371
1372 step4, errStep4 := buildResultFromScenarioPrefix(resolved, 4, BuildOptions{EnableLLDP: true})
1373 require.NoError(t, errStep4)
1374 require.Equal(t, 20, step4.Stats["links_lldp"])
1375 require.Equal(t, 4, countFixturesWithLLDPLocalElements(allWalks))
1376
1377 localDevices := map[string]struct{}{
1378 "E0281L-ScALBENGA2-QFX": {},
1379 "SW_D6_01_M": {},
1380 "SW_D6_02_M": {},
1381 "SW_D6_03_M": {},
1382 }
1383 require.NotNil(t, step4.Adjacencies)
1384 require.Equal(t, 4, countFixturesWithLLDPLocalElements(allWalks))
1385 require.Equal(t, 3, countUndirectedLocalDevicePairs(step4.Adjacencies, "lldp", localDevices))
1386
1387 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1388 require.NoError(t, err)
1389 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step4.Adjacencies))
1390 }
1391
1392 func TestBuildL2ResultFromWalks_LLDP_NMS18541_TOPO_QFX_SW01(t *testing.T) {
1393 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1394 manifest, err := LoadManifest(manifestPath)
1395 require.NoError(t, err)
1396
1397 scenario, ok := manifest.FindScenario("nms18541_topoqfx_sw01_lldp")
1398 require.True(t, ok)
1399
1400 require.True(t, scenario.Protocols.LLDP)
1401 require.False(t, scenario.Protocols.CDP)
1402 require.False(t, scenario.Protocols.Bridge)
1403 require.False(t, scenario.Protocols.ARPND)
1404 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1405
1406 resolved, err := ResolveScenario(manifestPath, scenario)
1407 require.NoError(t, err)
1408 require.Len(t, resolved.Fixtures, 2)
1409
1410 allWalks, err := LoadScenarioWalks(resolved)
1411 require.NoError(t, err)
1412 require.Len(t, allWalks, 2)
1413
1414 preCollectionLinks := 0
1415 preCollectionElements := 0
1416 require.Equal(t, 0, preCollectionLinks)
1417 require.Equal(t, 0, preCollectionElements)
1418
1419 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1420 require.NoError(t, errStep1)
1421 require.Equal(t, 15, step1.Stats["links_lldp"])
1422 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks[:1]))
1423
1424 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
1425 require.NoError(t, errStep2)
1426 require.Equal(t, 17, step2.Stats["links_lldp"])
1427 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
1428
1429 localDevices := map[string]struct{}{
1430 "E0281L-ScALBENGA2-QFX": {},
1431 "SW_D6_01_M": {},
1432 }
1433 require.NotNil(t, step2.Adjacencies)
1434 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
1435 require.Equal(t, 0, countUndirectedLocalDevicePairs(step2.Adjacencies, "lldp", localDevices))
1436
1437 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1438 require.NoError(t, err)
1439 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step2.Adjacencies))
1440 }
1441
1442 func TestBuildL2ResultFromWalks_LLDP_NMS18541_TOPO_QFX_SW02(t *testing.T) {
1443 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1444 manifest, err := LoadManifest(manifestPath)
1445 require.NoError(t, err)
1446
1447 scenario, ok := manifest.FindScenario("nms18541_topoqfx_sw02_lldp")
1448 require.True(t, ok)
1449
1450 require.True(t, scenario.Protocols.LLDP)
1451 require.False(t, scenario.Protocols.CDP)
1452 require.False(t, scenario.Protocols.Bridge)
1453 require.False(t, scenario.Protocols.ARPND)
1454 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1455
1456 resolved, err := ResolveScenario(manifestPath, scenario)
1457 require.NoError(t, err)
1458 require.Len(t, resolved.Fixtures, 2)
1459
1460 allWalks, err := LoadScenarioWalks(resolved)
1461 require.NoError(t, err)
1462 require.Len(t, allWalks, 2)
1463
1464 preCollectionLinks := 0
1465 preCollectionElements := 0
1466 require.Equal(t, 0, preCollectionLinks)
1467 require.Equal(t, 0, preCollectionElements)
1468
1469 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1470 require.NoError(t, errStep1)
1471 require.Equal(t, 15, step1.Stats["links_lldp"])
1472 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks[:1]))
1473
1474 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
1475 require.NoError(t, errStep2)
1476 require.Equal(t, 17, step2.Stats["links_lldp"])
1477 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
1478
1479 localDevices := map[string]struct{}{
1480 "E0281L-ScALBENGA2-QFX": {},
1481 "SW_D6_02_M": {},
1482 }
1483 require.NotNil(t, step2.Adjacencies)
1484 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
1485 require.Equal(t, 1, countUndirectedLocalDevicePairs(step2.Adjacencies, "lldp", localDevices))
1486
1487 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1488 require.NoError(t, err)
1489 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step2.Adjacencies))
1490 }
1491
1492 func TestBuildL2ResultFromWalks_LLDP_NMS18541_TOPO_QFX_SW03(t *testing.T) {
1493 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1494 manifest, err := LoadManifest(manifestPath)
1495 require.NoError(t, err)
1496
1497 scenario, ok := manifest.FindScenario("nms18541_topoqfx_sw03_lldp")
1498 require.True(t, ok)
1499
1500 require.True(t, scenario.Protocols.LLDP)
1501 require.False(t, scenario.Protocols.CDP)
1502 require.False(t, scenario.Protocols.Bridge)
1503 require.False(t, scenario.Protocols.ARPND)
1504 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1505
1506 resolved, err := ResolveScenario(manifestPath, scenario)
1507 require.NoError(t, err)
1508 require.Len(t, resolved.Fixtures, 2)
1509
1510 allWalks, err := LoadScenarioWalks(resolved)
1511 require.NoError(t, err)
1512 require.Len(t, allWalks, 2)
1513
1514 preCollectionLinks := 0
1515 preCollectionElements := 0
1516 require.Equal(t, 0, preCollectionLinks)
1517 require.Equal(t, 0, preCollectionElements)
1518
1519 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1520 require.NoError(t, errStep1)
1521 require.Equal(t, 15, step1.Stats["links_lldp"])
1522 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks[:1]))
1523
1524 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
1525 require.NoError(t, errStep2)
1526 require.Equal(t, 16, step2.Stats["links_lldp"])
1527 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
1528
1529 localDevices := map[string]struct{}{
1530 "E0281L-ScALBENGA2-QFX": {},
1531 "SW_D6_03_M": {},
1532 }
1533 require.NotNil(t, step2.Adjacencies)
1534 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
1535 require.Equal(t, 0, countUndirectedLocalDevicePairs(step2.Adjacencies, "lldp", localDevices))
1536
1537 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1538 require.NoError(t, err)
1539 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step2.Adjacencies))
1540 }
1541
1542 func TestBuildL2ResultFromWalks_LLDP_NMS18541_TOPO_QFX_SW04(t *testing.T) {
1543 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1544 manifest, err := LoadManifest(manifestPath)
1545 require.NoError(t, err)
1546
1547 scenario, ok := manifest.FindScenario("nms18541_topoqfx_sw04_lldp")
1548 require.True(t, ok)
1549
1550 require.True(t, scenario.Protocols.LLDP)
1551 require.False(t, scenario.Protocols.CDP)
1552 require.False(t, scenario.Protocols.Bridge)
1553 require.False(t, scenario.Protocols.ARPND)
1554 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1555
1556 resolved, err := ResolveScenario(manifestPath, scenario)
1557 require.NoError(t, err)
1558 require.Len(t, resolved.Fixtures, 2)
1559
1560 allWalks, err := LoadScenarioWalks(resolved)
1561 require.NoError(t, err)
1562 require.Len(t, allWalks, 2)
1563
1564 preCollectionLinks := 0
1565 preCollectionElements := 0
1566 require.Equal(t, 0, preCollectionLinks)
1567 require.Equal(t, 0, preCollectionElements)
1568
1569 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1570 require.NoError(t, errStep1)
1571 require.Equal(t, 15, step1.Stats["links_lldp"])
1572 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks[:1]))
1573
1574 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
1575 require.NoError(t, errStep2)
1576 require.Equal(t, 19, step2.Stats["links_lldp"])
1577 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
1578
1579 localDevices := map[string]struct{}{
1580 "E0281L-ScALBENGA2-QFX": {},
1581 "SW_D6_04_M": {},
1582 }
1583 require.NotNil(t, step2.Adjacencies)
1584 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
1585 require.Equal(t, 1, countUndirectedLocalDevicePairs(step2.Adjacencies, "lldp", localDevices))
1586
1587 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1588 require.NoError(t, err)
1589 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step2.Adjacencies))
1590 }
1591
1592 func TestBuildL2ResultFromWalks_LLDP_NMS18541_TOPO_QFX_SW08(t *testing.T) {
1593 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1594 manifest, err := LoadManifest(manifestPath)
1595 require.NoError(t, err)
1596
1597 scenario, ok := manifest.FindScenario("nms18541_topoqfx_sw08_lldp")
1598 require.True(t, ok)
1599
1600 require.True(t, scenario.Protocols.LLDP)
1601 require.False(t, scenario.Protocols.CDP)
1602 require.False(t, scenario.Protocols.Bridge)
1603 require.False(t, scenario.Protocols.ARPND)
1604 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1605
1606 resolved, err := ResolveScenario(manifestPath, scenario)
1607 require.NoError(t, err)
1608 require.Len(t, resolved.Fixtures, 2)
1609
1610 allWalks, err := LoadScenarioWalks(resolved)
1611 require.NoError(t, err)
1612 require.Len(t, allWalks, 2)
1613
1614 preCollectionLinks := 0
1615 preCollectionElements := 0
1616 require.Equal(t, 0, preCollectionLinks)
1617 require.Equal(t, 0, preCollectionElements)
1618
1619 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1620 require.NoError(t, errStep1)
1621 require.Equal(t, 15, step1.Stats["links_lldp"])
1622 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks[:1]))
1623
1624 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
1625 require.NoError(t, errStep2)
1626 require.Equal(t, 18, step2.Stats["links_lldp"])
1627 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
1628
1629 localDevices := map[string]struct{}{
1630 "E0281L-ScALBENGA2-QFX": {},
1631 "SW_D6_08_M": {},
1632 }
1633 require.NotNil(t, step2.Adjacencies)
1634 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
1635 require.Equal(t, 1, countUndirectedLocalDevicePairs(step2.Adjacencies, "lldp", localDevices))
1636
1637 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1638 require.NoError(t, err)
1639 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step2.Adjacencies))
1640 }
1641
1642 func TestBuildL2ResultFromWalks_LLDP_NMS18541_TOPO_QFX_SW09(t *testing.T) {
1643 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1644 manifest, err := LoadManifest(manifestPath)
1645 require.NoError(t, err)
1646
1647 scenario, ok := manifest.FindScenario("nms18541_topoqfx_sw09_lldp")
1648 require.True(t, ok)
1649
1650 require.True(t, scenario.Protocols.LLDP)
1651 require.False(t, scenario.Protocols.CDP)
1652 require.False(t, scenario.Protocols.Bridge)
1653 require.False(t, scenario.Protocols.ARPND)
1654 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1655
1656 resolved, err := ResolveScenario(manifestPath, scenario)
1657 require.NoError(t, err)
1658 require.Len(t, resolved.Fixtures, 2)
1659
1660 allWalks, err := LoadScenarioWalks(resolved)
1661 require.NoError(t, err)
1662 require.Len(t, allWalks, 2)
1663
1664 require.Equal(t, 0, 0)
1665 require.Equal(t, 0, 0)
1666
1667 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1668 require.NoError(t, errStep1)
1669 require.Equal(t, 15, step1.Stats["links_lldp"])
1670 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks[:1]))
1671
1672 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
1673 require.NoError(t, errStep2)
1674 require.Equal(t, 22, step2.Stats["links_lldp"])
1675 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
1676
1677 localDevices := map[string]struct{}{
1678 "E0281L-ScALBENGA2-QFX": {},
1679 "SW_D6_09_M": {},
1680 }
1681 require.NotNil(t, step2.Adjacencies)
1682 require.Equal(t, 2, countFixturesWithLLDPLocalElements(allWalks))
1683 require.Equal(t, 1, countUndirectedLocalDevicePairs(step2.Adjacencies, "lldp", localDevices))
1684
1685 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1686 require.NoError(t, err)
1687 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step2.Adjacencies))
1688 }
1689
1690 func TestBuildL2ResultFromWalks_LLDP_NMS18541_QFX(t *testing.T) {
1691 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1692 manifest, err := LoadManifest(manifestPath)
1693 require.NoError(t, err)
1694
1695 scenario, ok := manifest.FindScenario("nms18541_qfx_lldp")
1696 require.True(t, ok)
1697
1698 require.True(t, scenario.Protocols.LLDP)
1699 require.False(t, scenario.Protocols.CDP)
1700 require.False(t, scenario.Protocols.Bridge)
1701 require.False(t, scenario.Protocols.ARPND)
1702 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1703
1704 resolved, err := ResolveScenario(manifestPath, scenario)
1705 require.NoError(t, err)
1706 require.Len(t, resolved.Fixtures, 1)
1707
1708 allWalks, err := LoadScenarioWalks(resolved)
1709 require.NoError(t, err)
1710 require.Len(t, allWalks, 1)
1711
1712 preCollectionLinks := 0
1713 preCollectionElements := 0
1714 require.Equal(t, 0, preCollectionLinks)
1715 require.Equal(t, 0, preCollectionElements)
1716
1717 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1718 require.NoError(t, errStep1)
1719 require.Len(t, step1.Adjacencies, 15)
1720 require.Equal(t, 15, step1.Stats["links_lldp"])
1721 require.Equal(t, 0, step1.Stats["links_cdp"])
1722 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks))
1723 require.Equal(t, map[string]int{"E0281L-ScALBENGA2-QFX": 15}, countAdjacenciesBySource(step1.Adjacencies, "lldp"))
1724
1725 expected := map[string]struct{}{
1726 "lldp|E0281L-ScALBENGA2-QFX|et-0/0/48|e0281l-scalbenga2-r1|581": {},
1727 "lldp|E0281L-ScALBENGA2-QFX|et-0/0/49|e0281l-scalbenga2-r2|581": {},
1728 "lldp|E0281L-ScALBENGA2-QFX|ge-0/0/0|kwe0095p0i_1sgiusto65|28": {},
1729 "lldp|E0281L-ScALBENGA2-QFX|ge-0/0/1|kje0406p0i_1mbaldo11|28": {},
1730 "lldp|E0281L-ScALBENGA2-QFX|ge-0/0/10|arunarcangeli01|EC FC C6 C8 3B 80": {},
1731 "lldp|E0281L-ScALBENGA2-QFX|ge-0/0/13|microsens-g6-mac-00-60-a7-0a-81-13|2/5": {},
1732 "lldp|E0281L-ScALBENGA2-QFX|ge-0/0/15|microsens-g6-mac-00-60-a7-0a-7f-16|2/5": {},
1733 "lldp|E0281L-ScALBENGA2-QFX|ge-0/0/16|microsens-g6-mac-00:60:a7:0d:9e:15|3/6": {},
1734 "lldp|E0281L-ScALBENGA2-QFX|ge-0/0/2|kke0482p0i_1fleming15|28": {},
1735 "lldp|E0281L-ScALBENGA2-QFX|ge-0/0/3|wke0564p0i_1gnocchi8|26": {},
1736 "lldp|E0281L-ScALBENGA2-QFX|ge-0/0/4|wye0596p0i_1mbaldo15|28": {},
1737 "lldp|E0281L-ScALBENGA2-QFX|ge-0/0/5|wxe0729p0t_1pioii3|28": {},
1738 "lldp|E0281L-ScALBENGA2-QFX|ge-0/0/6|wje0588p0i_1marx2|27": {},
1739 "lldp|E0281L-ScALBENGA2-QFX|ge-0/0/7|microsens-g6-mac-00:60:a7:0c:27:fd|3/5": {},
1740 "lldp|E0281L-ScALBENGA2-QFX|ge-0/0/9|arunbellaria01|50 E4 E0 CE 3B 8E": {},
1741 }
1742 require.Equal(t, expected, adjacencyKeySet(step1.Adjacencies))
1743
1744 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1745 require.NoError(t, err)
1746 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step1.Adjacencies))
1747 }
1748
1749 func TestBuildL2ResultFromWalks_LLDP_NMS18541_MICROSENS_SW01(t *testing.T) {
1750 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1751 manifest, err := LoadManifest(manifestPath)
1752 require.NoError(t, err)
1753
1754 scenario, ok := manifest.FindScenario("nms18541_microsens_sw01_lldp")
1755 require.True(t, ok)
1756
1757 require.True(t, scenario.Protocols.LLDP)
1758 require.False(t, scenario.Protocols.CDP)
1759 require.False(t, scenario.Protocols.Bridge)
1760 require.False(t, scenario.Protocols.ARPND)
1761 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1762
1763 resolved, err := ResolveScenario(manifestPath, scenario)
1764 require.NoError(t, err)
1765 require.Len(t, resolved.Fixtures, 1)
1766
1767 allWalks, err := LoadScenarioWalks(resolved)
1768 require.NoError(t, err)
1769 require.Len(t, allWalks, 1)
1770
1771 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1772 require.NoError(t, errStep1)
1773 require.Len(t, step1.Adjacencies, 2)
1774 require.Equal(t, 2, step1.Stats["links_lldp"])
1775 require.Equal(t, 0, step1.Stats["links_cdp"])
1776 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks))
1777 require.Equal(t, map[string]int{"SW_D6_01_M": 2}, countAdjacenciesBySource(step1.Adjacencies, "lldp"))
1778
1779 expected := map[string]struct{}{
1780 "lldp|SW_D6_01_M|2/4|microsens-g6-mac-00:60:a7:0c:27:fd|2/5": {},
1781 "lldp|SW_D6_01_M|3/4|microsens-g6-mac-00:60:a7:0a:80:5e|2/5": {},
1782 }
1783 require.Equal(t, expected, adjacencyKeySet(step1.Adjacencies))
1784
1785 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1786 require.NoError(t, err)
1787 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step1.Adjacencies))
1788 }
1789
1790 func TestBuildL2ResultFromWalks_LLDP_NMS18541_MICROSENS_SW02(t *testing.T) {
1791 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1792 manifest, err := LoadManifest(manifestPath)
1793 require.NoError(t, err)
1794
1795 scenario, ok := manifest.FindScenario("nms18541_microsens_sw02_lldp")
1796 require.True(t, ok)
1797
1798 require.True(t, scenario.Protocols.LLDP)
1799 require.False(t, scenario.Protocols.CDP)
1800 require.False(t, scenario.Protocols.Bridge)
1801 require.False(t, scenario.Protocols.ARPND)
1802 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1803
1804 resolved, err := ResolveScenario(manifestPath, scenario)
1805 require.NoError(t, err)
1806 require.Len(t, resolved.Fixtures, 1)
1807
1808 allWalks, err := LoadScenarioWalks(resolved)
1809 require.NoError(t, err)
1810 require.Len(t, allWalks, 1)
1811
1812 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1813 require.NoError(t, errStep1)
1814 require.Len(t, step1.Adjacencies, 2)
1815 require.Equal(t, 2, step1.Stats["links_lldp"])
1816 require.Equal(t, 0, step1.Stats["links_cdp"])
1817 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks))
1818 require.Equal(t, map[string]int{"SW_D6_02_M": 2}, countAdjacenciesBySource(step1.Adjacencies, "lldp"))
1819
1820 expected := map[string]struct{}{
1821 "lldp|SW_D6_02_M|2/4|microsens-g6-mac-00:60:a7:0a:80:4e|2/5": {},
1822 "lldp|SW_D6_02_M|3/4|e0281l-scalbenga2-qfx|ge-0/0/7": {},
1823 }
1824 require.Equal(t, expected, adjacencyKeySet(step1.Adjacencies))
1825
1826 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1827 require.NoError(t, err)
1828 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step1.Adjacencies))
1829 }
1830
1831 func TestBuildL2ResultFromWalks_LLDP_NMS18541_MICROSENS_SW03(t *testing.T) {
1832 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1833 manifest, err := LoadManifest(manifestPath)
1834 require.NoError(t, err)
1835
1836 scenario, ok := manifest.FindScenario("nms18541_microsens_sw03_lldp")
1837 require.True(t, ok)
1838
1839 require.True(t, scenario.Protocols.LLDP)
1840 require.False(t, scenario.Protocols.CDP)
1841 require.False(t, scenario.Protocols.Bridge)
1842 require.False(t, scenario.Protocols.ARPND)
1843 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1844
1845 resolved, err := ResolveScenario(manifestPath, scenario)
1846 require.NoError(t, err)
1847 require.Len(t, resolved.Fixtures, 1)
1848
1849 allWalks, err := LoadScenarioWalks(resolved)
1850 require.NoError(t, err)
1851 require.Len(t, allWalks, 1)
1852
1853 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1854 require.NoError(t, errStep1)
1855 require.Len(t, step1.Adjacencies, 1)
1856 require.Equal(t, 1, step1.Stats["links_lldp"])
1857 require.Equal(t, 0, step1.Stats["links_cdp"])
1858 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks))
1859 require.Equal(t, map[string]int{"SW_D6_03_M": 1}, countAdjacenciesBySource(step1.Adjacencies, "lldp"))
1860
1861 expected := map[string]struct{}{
1862 "lldp|SW_D6_03_M|2/4|microsens-g6-mac-00:60:a7:0a:80:4e|3/5": {},
1863 }
1864 require.Equal(t, expected, adjacencyKeySet(step1.Adjacencies))
1865
1866 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1867 require.NoError(t, err)
1868 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step1.Adjacencies))
1869 }
1870
1871 func TestBuildL2ResultFromWalks_LLDP_NMS18541_MICROSENS_SW04(t *testing.T) {
1872 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1873 manifest, err := LoadManifest(manifestPath)
1874 require.NoError(t, err)
1875
1876 scenario, ok := manifest.FindScenario("nms18541_microsens_sw04_lldp")
1877 require.True(t, ok)
1878
1879 require.True(t, scenario.Protocols.LLDP)
1880 require.False(t, scenario.Protocols.CDP)
1881 require.False(t, scenario.Protocols.Bridge)
1882 require.False(t, scenario.Protocols.ARPND)
1883 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1884
1885 resolved, err := ResolveScenario(manifestPath, scenario)
1886 require.NoError(t, err)
1887 require.Len(t, resolved.Fixtures, 1)
1888
1889 allWalks, err := LoadScenarioWalks(resolved)
1890 require.NoError(t, err)
1891 require.Len(t, allWalks, 1)
1892
1893 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1894 require.NoError(t, errStep1)
1895 require.Len(t, step1.Adjacencies, 4)
1896 require.Equal(t, 4, step1.Stats["links_lldp"])
1897 require.Equal(t, 0, step1.Stats["links_cdp"])
1898 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks))
1899 require.Equal(t, map[string]int{"SW_D6_04_M": 4}, countAdjacenciesBySource(step1.Adjacencies, "lldp"))
1900
1901 expected := map[string]struct{}{
1902 "lldp|SW_D6_04_M|0|e0281l-scalbenga2-qfx|ge-0/0/13": {},
1903 "lldp|SW_D6_04_M|0|i0504-su-tlc01|e8:27:25:07:83:43": {},
1904 "lldp|SW_D6_04_M|0|i0504-su-tlc02|b8:a4:4f:b2:bb:2b": {},
1905 "lldp|SW_D6_04_M|0|i0504-su-tlc03|e8:27:25:07:96:9a": {},
1906 }
1907 require.Equal(t, expected, adjacencyKeySet(step1.Adjacencies))
1908
1909 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1910 require.NoError(t, err)
1911 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step1.Adjacencies))
1912 }
1913
1914 func TestBuildL2ResultFromWalks_LLDP_NMS18541_MICROSENS_SW08(t *testing.T) {
1915 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1916 manifest, err := LoadManifest(manifestPath)
1917 require.NoError(t, err)
1918
1919 scenario, ok := manifest.FindScenario("nms18541_microsens_sw08_lldp")
1920 require.True(t, ok)
1921
1922 require.True(t, scenario.Protocols.LLDP)
1923 require.False(t, scenario.Protocols.CDP)
1924 require.False(t, scenario.Protocols.Bridge)
1925 require.False(t, scenario.Protocols.ARPND)
1926 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1927
1928 resolved, err := ResolveScenario(manifestPath, scenario)
1929 require.NoError(t, err)
1930 require.Len(t, resolved.Fixtures, 1)
1931
1932 allWalks, err := LoadScenarioWalks(resolved)
1933 require.NoError(t, err)
1934 require.Len(t, allWalks, 1)
1935
1936 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1937 require.NoError(t, errStep1)
1938 require.Len(t, step1.Adjacencies, 3)
1939 require.Equal(t, 3, step1.Stats["links_lldp"])
1940 require.Equal(t, 0, step1.Stats["links_cdp"])
1941 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks))
1942 require.Equal(t, map[string]int{"SW_D6_08_M": 3}, countAdjacenciesBySource(step1.Adjacencies, "lldp"))
1943
1944 expected := map[string]struct{}{
1945 "lldp|SW_D6_08_M|0|e0281l-scalbenga2-qfx|ge-0/0/15": {},
1946 "lldp|SW_D6_08_M|0|i0506-su-tlc01|e8:27:25:07:96:63": {},
1947 "lldp|SW_D6_08_M|0|i0506-su-tlc02|e8:27:25:07:83:27": {},
1948 }
1949 require.Equal(t, expected, adjacencyKeySet(step1.Adjacencies))
1950
1951 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1952 require.NoError(t, err)
1953 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step1.Adjacencies))
1954 }
1955
1956 func TestBuildL2ResultFromWalks_LLDP_NMS18541_MICROSENS_SW09(t *testing.T) {
1957 manifestPath := "../../../testdata/snmp/enlinkd/nms18541/manifest.yaml"
1958 manifest, err := LoadManifest(manifestPath)
1959 require.NoError(t, err)
1960
1961 scenario, ok := manifest.FindScenario("nms18541_microsens_sw09_lldp")
1962 require.True(t, ok)
1963
1964 require.True(t, scenario.Protocols.LLDP)
1965 require.False(t, scenario.Protocols.CDP)
1966 require.False(t, scenario.Protocols.Bridge)
1967 require.False(t, scenario.Protocols.ARPND)
1968 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
1969
1970 resolved, err := ResolveScenario(manifestPath, scenario)
1971 require.NoError(t, err)
1972 require.Len(t, resolved.Fixtures, 1)
1973
1974 allWalks, err := LoadScenarioWalks(resolved)
1975 require.NoError(t, err)
1976 require.Len(t, allWalks, 1)
1977
1978 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
1979 require.NoError(t, errStep1)
1980 require.Len(t, step1.Adjacencies, 7)
1981 require.Equal(t, 7, step1.Stats["links_lldp"])
1982 require.Equal(t, 0, step1.Stats["links_cdp"])
1983 require.Equal(t, 1, countFixturesWithLLDPLocalElements(allWalks))
1984 require.Equal(t, map[string]int{"SW_D6_09_M": 7}, countAdjacenciesBySource(step1.Adjacencies, "lldp"))
1985
1986 expected := map[string]struct{}{
1987 "lldp|SW_D6_09_M|1/1|axis-accc8ef9c0a3|ac:cc:8e:f9:c0:a3": {},
1988 "lldp|SW_D6_09_M|2/1|axis-accc8e53f5fb|ac:cc:8e:53:f5:fb": {},
1989 "lldp|SW_D6_09_M|2/2|axis-accc8e536f3c|ac:cc:8e:53:6f:3c": {},
1990 "lldp|SW_D6_09_M|2/6|axis-accc8eaaeb7f|ac:cc:8e:aa:eb:7f": {},
1991 "lldp|SW_D6_09_M|3/1|axis-accc8ef9c09e|ac:cc:8e:f9:c0:9e": {},
1992 "lldp|SW_D6_09_M|3/2|axis camera|eth0": {},
1993 "lldp|SW_D6_09_M|3/5|e0281l-scalbenga2-qfx|ge-0/0/16": {},
1994 }
1995 require.Equal(t, expected, adjacencyKeySet(step1.Adjacencies))
1996
1997 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
1998 require.NoError(t, err)
1999 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step1.Adjacencies))
2000 }
2001
2002 func TestBuildL2ResultFromWalks_CDP_NMS7467(t *testing.T) {
2003 manifestPath := "../../../testdata/snmp/enlinkd/nms7467/manifest.yaml"
2004 manifest, err := LoadManifest(manifestPath)
2005 require.NoError(t, err)
2006
2007 scenario, ok := manifest.FindScenario("nms7467_cdp")
2008 require.True(t, ok)
2009
2010 require.False(t, scenario.Protocols.LLDP)
2011 require.True(t, scenario.Protocols.CDP)
2012 require.False(t, scenario.Protocols.Bridge)
2013 require.False(t, scenario.Protocols.ARPND)
2014 require.Equal(t, ManifestProtocols{CDP: true}, scenario.Protocols)
2015
2016 resolved, err := ResolveScenario(manifestPath, scenario)
2017 require.NoError(t, err)
2018 require.Len(t, resolved.Fixtures, 1)
2019
2020 ds, err := LoadWalkFile(resolved.Fixtures[0].WalkFile)
2021 require.NoError(t, err)
2022 require.Equal(t, 1, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.3.1.0")))
2023 require.Equal(t, "JAB043408B7", strings.TrimSpace(mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.3.4.0")))
2024 require.Equal(t, 3, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.3.7.0")))
2025
2026 walks, walkErr := loadScenarioWalkPrefix(resolved, 1)
2027 require.NoError(t, walkErr)
2028
2029 final, buildErr := BuildL2ResultFromWalks(walks, BuildOptions{EnableCDP: true})
2030 require.NoError(t, buildErr)
2031 require.Len(t, final.Adjacencies, 5)
2032 require.Equal(t, 5, final.Stats["links_cdp"])
2033 require.Equal(t, 0, final.Stats["links_lldp"])
2034 require.Equal(t, map[string]int{"ciscoswitch": 5}, countAdjacenciesBySource(final.Adjacencies, "cdp"))
2035
2036 for _, adj := range final.Adjacencies {
2037 require.NotEmpty(t, adj.TargetID)
2038 require.NotEmpty(t, adj.SourcePort)
2039 }
2040
2041 expected := map[string]struct{}{
2042 "cdp|ciscoswitch|2/1|sep0004f22ad83e|Port 1": {},
2043 "cdp|ciscoswitch|2/39|mrgarrison.internal.opennms.com|GigabitEthernet0": {},
2044 "cdp|ciscoswitch|2/4|sip000628f0fb0a|Port 1": {},
2045 "cdp|ciscoswitch|2/44|mrmakay.internal.opennms.com|FastEthernet2": {},
2046 "cdp|ciscoswitch|2/46|sip000ccea217a7|Port 1": {},
2047 }
2048 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
2049
2050 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
2051 require.NoError(t, err)
2052
2053 expectedFromGolden := make(map[string]struct{}, len(golden.Adjacencies))
2054 for _, adj := range golden.Adjacencies {
2055 expectedFromGolden[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
2056 }
2057 require.Equal(t, expectedFromGolden, adjacencyKeySet(final.Adjacencies))
2058 }
2059
2060 func TestBuildL2ResultFromWalks_MIXED_NMS7563_CISCO01(t *testing.T) {
2061 manifestPath := "../../../testdata/snmp/enlinkd/nms7563/manifest.yaml"
2062 manifest, err := LoadManifest(manifestPath)
2063 require.NoError(t, err)
2064
2065 scenario, ok := manifest.FindScenario("nms7563_cisco01")
2066 require.True(t, ok)
2067
2068 require.True(t, scenario.Protocols.LLDP)
2069 require.True(t, scenario.Protocols.CDP)
2070 require.False(t, scenario.Protocols.Bridge)
2071 require.False(t, scenario.Protocols.ARPND)
2072 require.Equal(t, ManifestProtocols{LLDP: true, CDP: true}, scenario.Protocols)
2073
2074 resolved, err := ResolveScenario(manifestPath, scenario)
2075 require.NoError(t, err)
2076 require.Len(t, resolved.Fixtures, 1)
2077
2078 ds, err := LoadWalkFile(resolved.Fixtures[0].WalkFile)
2079 require.NoError(t, err)
2080 require.Equal(t, 1, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.3.1.0")))
2081 require.Equal(t, "cisco01", strings.ToLower(strings.TrimSpace(mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.3.4.0"))))
2082
2083 walks, walkErr := loadScenarioWalkPrefix(resolved, 1)
2084 require.NoError(t, walkErr)
2085
2086 final, buildErr := BuildL2ResultFromWalks(walks, BuildOptions{EnableLLDP: true, EnableCDP: true})
2087 require.NoError(t, buildErr)
2088 require.Len(t, final.Devices, 2)
2089 require.Len(t, final.Adjacencies, 1)
2090 require.Equal(t, 1, final.Stats["links_lldp"])
2091 require.Equal(t, 0, final.Stats["links_cdp"])
2092 require.Equal(t, 1, countFixturesWithLLDPLocalElements(walks))
2093 require.Equal(t, 0, countBidirectionalPairs(final.Adjacencies, "lldp"))
2094
2095 deviceByID := make(map[string]l2topology.Device, len(final.Devices))
2096 for _, dev := range final.Devices {
2097 deviceByID[dev.ID] = dev
2098 }
2099 require.Contains(t, deviceByID, "cisco01")
2100 require.Contains(t, deviceByID, "procurve switch 2510b-24")
2101 require.Equal(t, "ac:a0:16:bf:02:00", deviceByID["cisco01"].ChassisID)
2102 require.Equal(t, "00:1d:b3:c5:09:60", deviceByID["procurve switch 2510b-24"].ChassisID)
2103 require.Equal(t, "ProCurve Switch 2510B-24", deviceByID["procurve switch 2510b-24"].Hostname)
2104
2105 expected := map[string]struct{}{
2106 "lldp|cisco01|Fa0/8|procurve switch 2510b-24|24": {},
2107 }
2108 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
2109
2110 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
2111 require.NoError(t, err)
2112
2113 expectedFromGolden := make(map[string]struct{}, len(golden.Adjacencies))
2114 for _, adj := range golden.Adjacencies {
2115 expectedFromGolden[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
2116 }
2117 require.Equal(t, expectedFromGolden, adjacencyKeySet(final.Adjacencies))
2118 }
2119
2120 func TestBuildL2ResultFromWalks_LLDP_NMS7563_HOMESERVER(t *testing.T) {
2121 manifestPath := "../../../testdata/snmp/enlinkd/nms7563/manifest.yaml"
2122 manifest, err := LoadManifest(manifestPath)
2123 require.NoError(t, err)
2124
2125 scenario, ok := manifest.FindScenario("nms7563_homeserver_lldp")
2126 require.True(t, ok)
2127
2128 require.True(t, scenario.Protocols.LLDP)
2129 require.False(t, scenario.Protocols.CDP)
2130 require.False(t, scenario.Protocols.Bridge)
2131 require.False(t, scenario.Protocols.ARPND)
2132 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
2133
2134 resolved, err := ResolveScenario(manifestPath, scenario)
2135 require.NoError(t, err)
2136 require.Len(t, resolved.Fixtures, 1)
2137
2138 walks, walkErr := loadScenarioWalkPrefix(resolved, 1)
2139 require.NoError(t, walkErr)
2140
2141 final, buildErr := BuildL2ResultFromWalks(walks, BuildOptions{EnableLLDP: true})
2142 require.NoError(t, buildErr)
2143 require.Len(t, final.Devices, 2)
2144 require.Len(t, final.Adjacencies, 1)
2145 require.Equal(t, 1, final.Stats["links_lldp"])
2146 require.Equal(t, 0, final.Stats["links_cdp"])
2147 require.Equal(t, 1, countFixturesWithLLDPLocalElements(walks))
2148
2149 deviceByID := make(map[string]l2topology.Device, len(final.Devices))
2150 for _, dev := range final.Devices {
2151 deviceByID[dev.ID] = dev
2152 }
2153 require.Contains(t, deviceByID, "homeserver")
2154 require.Contains(t, deviceByID, "procurve switch 2510b-24")
2155 require.Equal(t, "00:1f:f2:07:99:4f", deviceByID["homeserver"].ChassisID)
2156 require.Equal(t, "server.home.schwartzkopff.org", deviceByID["homeserver"].Hostname)
2157 require.Equal(t, "00:1d:b3:c5:09:60", deviceByID["procurve switch 2510b-24"].ChassisID)
2158 require.Equal(t, "ProCurve Switch 2510B-24", deviceByID["procurve switch 2510b-24"].Hostname)
2159
2160 expected := map[string]struct{}{
2161 "lldp|homeserver|00 1F F2 07 99 4F|procurve switch 2510b-24|7": {},
2162 }
2163 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
2164
2165 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
2166 require.NoError(t, err)
2167
2168 expectedFromGolden := make(map[string]struct{}, len(golden.Adjacencies))
2169 for _, adj := range golden.Adjacencies {
2170 expectedFromGolden[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
2171 }
2172 require.Equal(t, expectedFromGolden, adjacencyKeySet(final.Adjacencies))
2173 }
2174
2175 func TestBuildL2ResultFromWalks_CDP_NMS7563_SWITCH02(t *testing.T) {
2176 manifestPath := "../../../testdata/snmp/enlinkd/nms7563/manifest.yaml"
2177 manifest, err := LoadManifest(manifestPath)
2178 require.NoError(t, err)
2179
2180 scenario, ok := manifest.FindScenario("nms7563_switch02_cdp")
2181 require.True(t, ok)
2182
2183 require.False(t, scenario.Protocols.LLDP)
2184 require.True(t, scenario.Protocols.CDP)
2185 require.False(t, scenario.Protocols.Bridge)
2186 require.False(t, scenario.Protocols.ARPND)
2187 require.Equal(t, ManifestProtocols{CDP: true}, scenario.Protocols)
2188
2189 resolved, err := ResolveScenario(manifestPath, scenario)
2190 require.NoError(t, err)
2191 require.Len(t, resolved.Fixtures, 1)
2192
2193 ds, err := LoadWalkFile(resolved.Fixtures[0].WalkFile)
2194 require.NoError(t, err)
2195 require.Equal(t, 1, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.3.1.0")))
2196 require.Equal(t, "ProCurve Switch 2510B-24(001db3-c50960)", strings.TrimSpace(mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.3.4.0")))
2197
2198 walks, walkErr := loadScenarioWalkPrefix(resolved, 1)
2199 require.NoError(t, walkErr)
2200
2201 final, buildErr := BuildL2ResultFromWalks(walks, BuildOptions{EnableCDP: true})
2202 require.NoError(t, buildErr)
2203 require.Len(t, final.Devices, 3)
2204 require.Len(t, final.Adjacencies, 3)
2205 require.Equal(t, 3, final.Stats["links_cdp"])
2206 require.Equal(t, 0, final.Stats["links_lldp"])
2207 require.Equal(t, map[string]int{"switch02": 3}, countAdjacenciesBySource(final.Adjacencies, "cdp"))
2208
2209 deviceByID := make(map[string]l2topology.Device, len(final.Devices))
2210 for _, dev := range final.Devices {
2211 deviceByID[dev.ID] = dev
2212 }
2213 require.Contains(t, deviceByID, "switch02")
2214 require.Contains(t, deviceByID, "cisco01")
2215 require.Contains(t, deviceByID, "00 1f f2 07 99 4f")
2216 require.Equal(t, "cisco01", deviceByID["cisco01"].Hostname)
2217 require.Equal(t, "00 1F F2 07 99 4F", deviceByID["00 1f f2 07 99 4f"].Hostname)
2218
2219 for _, adj := range final.Adjacencies {
2220 require.Equal(t, "cdp", adj.Protocol)
2221 raw := strings.TrimSpace(adj.Labels["remote_address_raw"])
2222 require.NotEmpty(t, raw)
2223 decoded := decodeHexIP(raw)
2224 require.NotEmpty(t, decoded)
2225 _, parseErr := netip.ParseAddr(decoded)
2226 require.NoError(t, parseErr)
2227 }
2228
2229 ipByAdjacency := make(map[string]string, len(final.Adjacencies))
2230 for _, adj := range final.Adjacencies {
2231 raw := strings.TrimSpace(adj.Labels["remote_address_raw"])
2232 ipByAdjacency[adj.SourcePort+"|"+adj.TargetID+"|"+adj.TargetPort] = decodeHexIP(raw)
2233 }
2234 require.Equal(t, "192.168.88.240", ipByAdjacency["24|cisco01|Fa0/8"])
2235 require.Equal(t, "192.168.88.240", ipByAdjacency["24|cisco01|FastEthernet0/8"])
2236 require.Equal(t, "192.168.87.16", ipByAdjacency["7|00 1f f2 07 99 4f|00 1F F2 07 99 4F"])
2237
2238 expected := map[string]struct{}{
2239 "cdp|switch02|24|cisco01|Fa0/8": {},
2240 "cdp|switch02|24|cisco01|FastEthernet0/8": {},
2241 "cdp|switch02|7|00 1f f2 07 99 4f|00 1F F2 07 99 4F": {},
2242 }
2243 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
2244
2245 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
2246 require.NoError(t, err)
2247
2248 expectedFromGolden := make(map[string]struct{}, len(golden.Adjacencies))
2249 for _, adj := range golden.Adjacencies {
2250 expectedFromGolden[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
2251 }
2252 require.Equal(t, expectedFromGolden, adjacencyKeySet(final.Adjacencies))
2253 }
2254
2255 func TestBuildL2ResultFromWalks_LLDP_NMS7777DW_NO_LINKS(t *testing.T) {
2256 manifestPath := "../../../testdata/snmp/enlinkd/nms7777dw/manifest.yaml"
2257 manifest, err := LoadManifest(manifestPath)
2258 require.NoError(t, err)
2259
2260 scenario, ok := manifest.FindScenario("nms7777dw_lldp_no_links")
2261 require.True(t, ok)
2262
2263 enableOSPF := false
2264 enableISIS := false
2265 require.True(t, scenario.Protocols.LLDP)
2266 require.False(t, scenario.Protocols.CDP)
2267 require.False(t, enableOSPF)
2268 require.False(t, scenario.Protocols.Bridge)
2269 require.False(t, enableISIS)
2270 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
2271
2272 resolved, err := ResolveScenario(manifestPath, scenario)
2273 require.NoError(t, err)
2274 require.Len(t, resolved.Fixtures, 1)
2275
2276 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
2277 require.NoError(t, errStep1)
2278
2279 final := step1
2280 require.Len(t, final.Devices, 1)
2281 require.Len(t, final.Adjacencies, 0)
2282 require.Equal(t, 0, final.Stats["links_lldp"])
2283 require.Equal(t, 0, final.Stats["links_cdp"])
2284 require.Equal(t, 0, countBidirectionalPairs(final.Adjacencies, "lldp"))
2285
2286 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
2287 require.NoError(t, err)
2288
2289 expected := make(map[string]struct{}, len(golden.Adjacencies))
2290 for _, adj := range golden.Adjacencies {
2291 expected[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
2292 }
2293 require.Equal(t, expected, adjacencyKeySet(final.Adjacencies))
2294
2295 expectedDevices := make(map[string]string, len(golden.Devices))
2296 for _, dev := range golden.Devices {
2297 expectedDevices[dev.ID] = dev.Hostname
2298 }
2299 actualDevices := make(map[string]string, len(final.Devices))
2300 for _, dev := range final.Devices {
2301 actualDevices[dev.ID] = dev.Hostname
2302 }
2303 require.Equal(t, expectedDevices, actualDevices)
2304 }
2305
2306 func TestBuildL2ResultFromWalks_LLDP_NMS13923(t *testing.T) {
2307 manifestPath := "../../../testdata/snmp/enlinkd/nms13923/manifest.yaml"
2308 manifest, err := LoadManifest(manifestPath)
2309 require.NoError(t, err)
2310
2311 scenario, ok := manifest.FindScenario("nms13923_lldp")
2312 require.True(t, ok)
2313
2314 enableOSPF := false
2315 enableISIS := false
2316 require.True(t, scenario.Protocols.LLDP)
2317 require.False(t, scenario.Protocols.CDP)
2318 require.False(t, enableOSPF)
2319 require.False(t, scenario.Protocols.Bridge)
2320 require.False(t, enableISIS)
2321 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
2322
2323 resolved, err := ResolveScenario(manifestPath, scenario)
2324 require.NoError(t, err)
2325 require.Len(t, resolved.Fixtures, 1)
2326 require.Equal(t, "srv005", resolved.Fixtures[0].DeviceID)
2327
2328 walks, err := loadScenarioWalkPrefix(resolved, 1)
2329 require.NoError(t, err)
2330 require.Equal(t, 1, countFixturesWithLLDPLocalElements(walks))
2331
2332 remoteChassisRows := 0
2333 for _, rec := range walks[0].Records {
2334 if !strings.HasPrefix(normalizeOID(rec.OID), "1.3.6.1.4.1.6527.3.1.2.59.4.1.1.5.") {
2335 continue
2336 }
2337 remoteChassisRows++
2338 require.Len(t, decodeHexBytes(rec.Value), 6)
2339 }
2340 require.Equal(t, 49, remoteChassisRows)
2341
2342 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
2343 require.NoError(t, errStep1)
2344 require.Equal(t, 49, step1.Stats["links_lldp"])
2345 require.Len(t, step1.Adjacencies, 49)
2346 require.Equal(t, 0, countBidirectionalPairs(step1.Adjacencies, "lldp"))
2347
2348 sourceCounts := countAdjacenciesBySource(step1.Adjacencies, "lldp")
2349 require.Equal(t, 1, len(sourceCounts))
2350 require.Equal(t, 49, sourceCounts["srv005"])
2351
2352 for _, adj := range step1.Adjacencies {
2353 require.Equal(t, "lldp", adj.Protocol)
2354 require.Equal(t, "srv005", adj.SourceID)
2355 require.NotEmpty(t, adj.SourcePort)
2356 require.NotEmpty(t, adj.TargetID)
2357 }
2358
2359 localDevices := map[string]struct{}{
2360 "srv005": {},
2361 }
2362 require.Equal(t, 0, countUndirectedLocalDevicePairs(step1.Adjacencies, "lldp", localDevices))
2363
2364 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
2365 require.NoError(t, err)
2366 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step1.Adjacencies))
2367 }
2368
2369 func TestBuildL2ResultFromWalks_LLDP_NMS13593(t *testing.T) {
2370 manifestPath := "../../../testdata/snmp/enlinkd/nms13593/manifest.yaml"
2371 manifest, err := LoadManifest(manifestPath)
2372 require.NoError(t, err)
2373
2374 scenario, ok := manifest.FindScenario("nms13593_lldp")
2375 require.True(t, ok)
2376
2377 enableOSPF := false
2378 enableISIS := false
2379 require.True(t, scenario.Protocols.LLDP)
2380 require.False(t, scenario.Protocols.CDP)
2381 require.False(t, enableOSPF)
2382 require.False(t, scenario.Protocols.Bridge)
2383 require.False(t, enableISIS)
2384 require.Equal(t, ManifestProtocols{LLDP: true}, scenario.Protocols)
2385
2386 resolved, err := ResolveScenario(manifestPath, scenario)
2387 require.NoError(t, err)
2388 require.Len(t, resolved.Fixtures, 2)
2389 require.Equal(t, "ZHBGO1Zsr001", resolved.Fixtures[0].DeviceID)
2390 require.Equal(t, "ZHBGO1Zsr002", resolved.Fixtures[1].DeviceID)
2391
2392 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableLLDP: true})
2393 require.NoError(t, errStep1)
2394 require.Equal(t, 3, step1.Stats["links_lldp"])
2395 require.Len(t, step1.Adjacencies, 3)
2396
2397 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 2, BuildOptions{EnableLLDP: true})
2398 require.NoError(t, errStep2)
2399 require.Equal(t, 7, step2.Stats["links_lldp"])
2400 require.Len(t, step2.Adjacencies, 7)
2401 require.Equal(t, 0, countBidirectionalPairs(step2.Adjacencies, "lldp"))
2402
2403 localByID := make(map[string]l2topology.Device)
2404 for _, dev := range step2.Devices {
2405 if dev.ID != "ZHBGO1Zsr001" && dev.ID != "ZHBGO1Zsr002" {
2406 continue
2407 }
2408 localByID[dev.ID] = dev
2409 }
2410 require.Len(t, localByID, 2)
2411 require.Equal(t, "ZHBGO1Zsr001", localByID["ZHBGO1Zsr001"].Hostname)
2412 require.Equal(t, "24:21:24:ec:e2:3f", localByID["ZHBGO1Zsr001"].ChassisID)
2413 require.Equal(t, "ZHBGO1Zsr002", localByID["ZHBGO1Zsr002"].Hostname)
2414 require.Equal(t, "24:21:24:da:f6:3f", localByID["ZHBGO1Zsr002"].ChassisID)
2415
2416 expectedAdjacencies := map[string]struct{}{
2417 "lldp|ZHBGO1Zsr001|104906753|esat-1|35700737": {},
2418 "lldp|ZHBGO1Zsr001|105037825|ZHBGO1Zsr002|3/2/c5/1": {},
2419 "lldp|ZHBGO1Zsr001|105070593|ZHBGO1Zsr002|3/2/c6/1": {},
2420 "lldp|ZHBGO1Zsr002|3/2/c1/1|chassis-50e0ef005000|35700737": {},
2421 "lldp|ZHBGO1Zsr002|3/2/c5/1|ZHBGO1Zsr001|3/2/c5/1": {},
2422 "lldp|ZHBGO1Zsr002|3/2/c6/1|ZHBGO1Zsr001|3/2/c6/1": {},
2423 "lldp|ZHBGO1Zsr002|esat-1/1/27|chassis-e48184acbf34|1610901763": {},
2424 }
2425 require.Equal(t, expectedAdjacencies, adjacencyKeySet(step2.Adjacencies))
2426
2427 sourceCounts := countAdjacenciesBySource(step2.Adjacencies, "lldp")
2428 require.Equal(t, 3, sourceCounts["ZHBGO1Zsr001"])
2429 require.Equal(t, 4, sourceCounts["ZHBGO1Zsr002"])
2430
2431 localDevices := map[string]struct{}{
2432 "ZHBGO1Zsr001": {},
2433 "ZHBGO1Zsr002": {},
2434 }
2435 require.Equal(t, 2, countLocalTopologyVertices(step2.Adjacencies, "lldp", localDevices))
2436 require.Equal(t, 1, countUndirectedLocalDevicePairs(step2.Adjacencies, "lldp", localDevices))
2437
2438 ifNamesByDevice := make(map[string]map[int]string)
2439 for _, iface := range step2.Interfaces {
2440 name := strings.TrimSpace(iface.IfName)
2441 if idx := strings.Index(name, ","); idx >= 0 {
2442 name = strings.TrimSpace(name[:idx])
2443 }
2444 if name == "" {
2445 continue
2446 }
2447 byIndex := ifNamesByDevice[iface.DeviceID]
2448 if byIndex == nil {
2449 byIndex = make(map[int]string)
2450 ifNamesByDevice[iface.DeviceID] = byIndex
2451 }
2452 byIndex[iface.IfIndex] = name
2453 }
2454
2455 normalizePort := func(deviceID, port string) string {
2456 port = strings.TrimSpace(port)
2457 if port == "" {
2458 return ""
2459 }
2460 ifIndex, convErr := strconv.Atoi(port)
2461 if convErr != nil {
2462 return port
2463 }
2464 if byIndex, ok := ifNamesByDevice[deviceID]; ok {
2465 if ifName, found := byIndex[ifIndex]; found && strings.TrimSpace(ifName) != "" {
2466 return strings.TrimSpace(ifName)
2467 }
2468 }
2469 return port
2470 }
2471
2472 type projectedLocalEdge struct {
2473 SourceID string
2474 TargetID string
2475 SourcePort string
2476 TargetPort string
2477 }
2478
2479 projectedEdges := make([]projectedLocalEdge, 0, 2)
2480 for _, adj := range step2.Adjacencies {
2481 if adj.Protocol != "lldp" {
2482 continue
2483 }
2484 if adj.SourceID != "ZHBGO1Zsr001" || adj.TargetID != "ZHBGO1Zsr002" {
2485 continue
2486 }
2487 projectedEdges = append(projectedEdges, projectedLocalEdge{
2488 SourceID: adj.SourceID,
2489 TargetID: adj.TargetID,
2490 SourcePort: normalizePort(adj.SourceID, adj.SourcePort),
2491 TargetPort: normalizePort(adj.TargetID, adj.TargetPort),
2492 })
2493 }
2494
2495 sort.Slice(projectedEdges, func(i, j int) bool {
2496 return projectedEdges[i].SourcePort < projectedEdges[j].SourcePort
2497 })
2498
2499 require.Len(t, projectedEdges, 2)
2500 for _, edge := range projectedEdges {
2501 require.Equal(t, "ZHBGO1Zsr001", edge.SourceID)
2502 require.Equal(t, "ZHBGO1Zsr002", edge.TargetID)
2503 require.Equal(t, edge.SourcePort, edge.TargetPort)
2504 }
2505 require.Equal(t, "3/2/c5/1", projectedEdges[0].SourcePort)
2506 require.Equal(t, "3/2/c5/1", projectedEdges[0].TargetPort)
2507 require.Equal(t, "3/2/c6/1", projectedEdges[1].SourcePort)
2508 require.Equal(t, "3/2/c6/1", projectedEdges[1].TargetPort)
2509
2510 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
2511 require.NoError(t, err)
2512 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step2.Adjacencies))
2513 }
2514
2515 func TestBuildL2ResultFromWalks_CDP(t *testing.T) {
2516 walks := []FixtureWalk{
2517 {
2518 DeviceID: "switch-a",
2519 Hostname: "switch-a.example.net",
2520 Records: []WalkRecord{
2521 {OID: "1.3.6.1.2.1.1.5.0", Type: "STRING", Value: "switch-a.example.net"},
2522 {OID: "1.3.6.1.2.1.31.1.1.1.1.8", Type: "STRING", Value: "Gi0/0"},
2523 {OID: "1.3.6.1.4.1.9.9.23.1.2.1.1.6.8.1", Type: "STRING", Value: "switch-b.example.net"},
2524 {OID: "1.3.6.1.4.1.9.9.23.1.2.1.1.7.8.1", Type: "STRING", Value: "Gi0/1"},
2525 },
2526 },
2527 {
2528 DeviceID: "switch-b",
2529 Hostname: "switch-b.example.net",
2530 Records: []WalkRecord{
2531 {OID: "1.3.6.1.2.1.1.5.0", Type: "STRING", Value: "switch-b.example.net"},
2532 },
2533 },
2534 }
2535
2536 result, err := BuildL2ResultFromWalks(walks, BuildOptions{EnableCDP: true})
2537 require.NoError(t, err)
2538 require.Len(t, result.Adjacencies, 1)
2539
2540 adj := result.Adjacencies[0]
2541 require.Equal(t, "cdp", adj.Protocol)
2542 require.Equal(t, "switch-a", adj.SourceID)
2543 require.Equal(t, "Gi0/0", adj.SourcePort)
2544 require.Equal(t, "switch-b", adj.TargetID)
2545 require.Equal(t, "Gi0/1", adj.TargetPort)
2546 }
2547
2548 func TestBuildL2ResultFromWalks_FDB(t *testing.T) {
2549 walks := []FixtureWalk{
2550 {
2551 DeviceID: "switch-a",
2552 Hostname: "switch-a.example.net",
2553 Records: []WalkRecord{
2554 {OID: "1.3.6.1.2.1.1.5.0", Type: "STRING", Value: "switch-a.example.net"},
2555 {OID: "1.3.6.1.2.1.31.1.1.1.1.3", Type: "STRING", Value: "Port3"},
2556 {OID: "1.3.6.1.2.1.17.1.4.1.2.7", Type: "INTEGER", Value: "3"},
2557 {OID: "1.3.6.1.2.1.17.4.3.1.2.112.73.162.101.114.205", Type: "INTEGER", Value: "7"},
2558 {OID: "1.3.6.1.2.1.17.4.3.1.3.112.73.162.101.114.205", Type: "INTEGER", Value: "learned"},
2559 },
2560 },
2561 }
2562
2563 result, err := BuildL2ResultFromWalks(walks, BuildOptions{EnableBridge: true})
2564 require.NoError(t, err)
2565 require.Len(t, result.Attachments, 1)
2566
2567 attachment := result.Attachments[0]
2568 require.Equal(t, "switch-a", attachment.DeviceID)
2569 require.Equal(t, 3, attachment.IfIndex)
2570 require.Equal(t, "mac:70:49:a2:65:72:cd", attachment.EndpointID)
2571 require.Equal(t, "fdb", attachment.Method)
2572 require.Equal(t, "bridge-domain:switch-a:if:3", attachment.Labels["bridge_domain"])
2573 require.Equal(t, "7", attachment.Labels["bridge_port"])
2574 require.Equal(t, "learned", attachment.Labels["fdb_status"])
2575 require.Equal(t, "Port3", attachment.Labels["if_name"])
2576 require.Equal(t, 1, result.Stats["attachments_fdb"])
2577 }
2578
2579 func TestBuildL2ResultFromWalks_ARPEnrichment(t *testing.T) {
2580 walks := []FixtureWalk{
2581 {
2582 DeviceID: "switch-a",
2583 Hostname: "switch-a.example.net",
2584 Records: []WalkRecord{
2585 {OID: "1.3.6.1.2.1.31.1.1.1.1.3", Type: "STRING", Value: "Port3"},
2586 {OID: "1.3.6.1.2.1.4.22.1.2.3.10.20.4.84", Type: "Hex-STRING", Value: "7049a26572cd"},
2587 {OID: "1.3.6.1.2.1.4.22.1.3.3.10.20.4.84", Type: "IpAddress", Value: "10.20.4.84"},
2588 {OID: "1.3.6.1.2.1.4.22.1.4.3.10.20.4.84", Type: "INTEGER", Value: "dynamic"},
2589 },
2590 },
2591 }
2592
2593 result, err := BuildL2ResultFromWalks(walks, BuildOptions{EnableARP: true})
2594 require.NoError(t, err)
2595 require.Empty(t, result.Adjacencies)
2596 require.Empty(t, result.Attachments)
2597 require.Len(t, result.Enrichments, 1)
2598
2599 enrichment := result.Enrichments[0]
2600 require.Equal(t, "mac:70:49:a2:65:72:cd", enrichment.EndpointID)
2601 require.Equal(t, "70:49:a2:65:72:cd", enrichment.MAC)
2602 require.Len(t, enrichment.IPs, 1)
2603 require.Equal(t, "10.20.4.84", enrichment.IPs[0].String())
2604 require.Equal(t, "arp", enrichment.Labels["sources"])
2605 require.Equal(t, "switch-a", enrichment.Labels["device_ids"])
2606 require.Equal(t, "3", enrichment.Labels["if_indexes"])
2607 require.Equal(t, "Port3", enrichment.Labels["if_names"])
2608 require.Equal(t, "dynamic", enrichment.Labels["states"])
2609 require.Equal(t, "ipv4", enrichment.Labels["addr_types"])
2610 require.Equal(t, 1, result.Stats["enrichments_arp_nd"])
2611 }
2612
2613 func TestParseCDPInterfaceGetter_NMS0002_RPICT001(t *testing.T) {
2614 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms0002UkRoFakeLink/r-ro-suce-pict-001.txt")
2615 require.NoError(t, err)
2616
2617 require.Equal(t, "FastEthernet0", mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.1.1.1.6.1"))
2618 require.Equal(t, "FastEthernet1", mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.1.1.1.6.2"))
2619 require.Equal(t, "FastEthernet2", mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.1.1.1.6.3"))
2620 require.Equal(t, "FastEthernet3", mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.1.1.1.6.4"))
2621 require.Equal(t, "FastEthernet4", mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.1.1.1.6.5"))
2622 require.Equal(t, "Tunnel0", mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.1.1.1.6.9"))
2623 require.Equal(t, "Tunnel3", mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.1.1.1.6.10"))
2624
2625 require.Equal(t, "FastEthernet0", mustLookupWalkValue(t, ds, "1.3.6.1.2.1.2.2.1.2.1"))
2626 require.Equal(t, "FastEthernet1", mustLookupWalkValue(t, ds, "1.3.6.1.2.1.2.2.1.2.2"))
2627 require.Equal(t, "FastEthernet2", mustLookupWalkValue(t, ds, "1.3.6.1.2.1.2.2.1.2.3"))
2628 require.Equal(t, "FastEthernet3", mustLookupWalkValue(t, ds, "1.3.6.1.2.1.2.2.1.2.4"))
2629 require.Equal(t, "FastEthernet4", mustLookupWalkValue(t, ds, "1.3.6.1.2.1.2.2.1.2.5"))
2630 require.Equal(t, "Tunnel0", mustLookupWalkValue(t, ds, "1.3.6.1.2.1.2.2.1.2.9"))
2631 require.Equal(t, "Tunnel3", mustLookupWalkValue(t, ds, "1.3.6.1.2.1.2.2.1.2.10"))
2632 }
2633
2634 func TestParseCDPGlobalGroup_NMS0002_RPICT001(t *testing.T) {
2635 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms0002UkRoFakeLink/r-ro-suce-pict-001.txt")
2636 require.NoError(t, err)
2637
2638 require.Equal(t, "r-ro-suce-pict-001.infra.u-ssi.net", mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.3.4.0"))
2639 require.Equal(t, 1, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.3.1.0")))
2640 _, hasDeviceFormat := ds.Lookup("1.3.6.1.4.1.9.9.23.1.3.7.0")
2641 require.False(t, hasDeviceFormat)
2642 }
2643
2644 func TestParseCDPGlobalGroupWithDeviceFormat_NMS7467_CISCO_SWITCH(t *testing.T) {
2645 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms7467/192.0.2.7-walk.txt")
2646 require.NoError(t, err)
2647
2648 require.Equal(t, "JAB043408B7", mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.3.4.0"))
2649 require.Equal(t, 1, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.3.1.0")))
2650 require.Equal(t, 3, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.4.1.9.9.23.1.3.7.0")))
2651 }
2652
2653 func TestParseCDPCacheTable_NMS0002_RPICT001(t *testing.T) {
2654 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms0002UkRoFakeLink/r-ro-suce-pict-001.txt")
2655 require.NoError(t, err)
2656
2657 require.Equal(t, 14, countCDPCacheRows(ds))
2658 }
2659
2660 func TestParseLLDPLocalGroup_NMS17216_SWITCH1(t *testing.T) {
2661 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms17216/switch1-walk.txt")
2662 require.NoError(t, err)
2663
2664 require.Equal(t, "0016c8bd4d80", compactHexToken(mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.2.0")))
2665 require.Equal(t, 4, mustAtoi(t, mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.1.0")))
2666 require.Equal(t, "Switch1", mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.3.0"))
2667 }
2668
2669 func TestParseLLDPLocGetter_NMS17216_SWITCH1(t *testing.T) {
2670 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms17216/switch1-walk.txt")
2671 require.NoError(t, err)
2672
2673 val9 := lldpLocPortTriplet(t, ds, "9")
2674 require.Len(t, val9, 3)
2675 require.Equal(t, 5, mustAtoi(t, val9[0]))
2676 require.Equal(t, "Gi0/9", val9[1])
2677 require.Equal(t, "GigabitEthernet0/9", val9[2])
2678
2679 val10 := lldpLocPortTriplet(t, ds, "10")
2680 require.Len(t, val10, 3)
2681 require.Equal(t, 5, mustAtoi(t, val10[0]))
2682 require.Equal(t, "Gi0/10", val10[1])
2683 require.Equal(t, "GigabitEthernet0/10", val10[2])
2684 }
2685
2686 func TestParseLLDPLocGetter_NMS17216_SWITCH2(t *testing.T) {
2687 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms17216/switch2-walk.txt")
2688 require.NoError(t, err)
2689
2690 val1 := lldpLocPortTriplet(t, ds, "1")
2691 require.Len(t, val1, 3)
2692 require.Equal(t, 5, mustAtoi(t, val1[0]))
2693 require.Equal(t, "Gi0/1", val1[1])
2694 require.Equal(t, "GigabitEthernet0/1", val1[2])
2695
2696 val2 := lldpLocPortTriplet(t, ds, "2")
2697 require.Len(t, val2, 3)
2698 require.Equal(t, 5, mustAtoi(t, val2[0]))
2699 require.Equal(t, "Gi0/2", val2[1])
2700 require.Equal(t, "GigabitEthernet0/2", val2[2])
2701 }
2702
2703 func TestParseLLDPRemTable_NMS17216_SWITCH1(t *testing.T) {
2704 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms17216/switch1-walk.txt")
2705 require.NoError(t, err)
2706
2707 rows := collectLLDPRemoteRows(ds)
2708 require.NotEmpty(t, rows)
2709
2710 for _, row := range rows {
2711 require.Len(t, row, 6)
2712 require.Equal(t, 4, mustAtoi(t, row[4]))
2713 require.Equal(t, 5, mustAtoi(t, row[6]))
2714 }
2715 }
2716
2717 func TestParseLLDPRemoteTableWithLocLookup_NMS17216_SWITCH2(t *testing.T) {
2718 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms17216/switch2-walk.txt")
2719 require.NoError(t, err)
2720
2721 rows := collectLLDPRemoteRows(ds)
2722 require.NotEmpty(t, rows)
2723
2724 for key := range rows {
2725 parts := strings.SplitN(key, "|", 2)
2726 localPortNum := strings.TrimSpace(parts[0])
2727 remIndex := strings.TrimSpace(parts[1])
2728
2729 require.NotEmpty(t, key)
2730 require.NotEmpty(t, remIndex)
2731 require.NotEmpty(t, localPortNum)
2732
2733 localPortID := ""
2734 localPortIDSubtype := ""
2735 localPortDescr := ""
2736 require.Empty(t, localPortID)
2737 require.Empty(t, localPortIDSubtype)
2738 require.Empty(t, localPortDescr)
2739
2740 updatedPortIDSubtype := mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.7.1.2."+localPortNum)
2741 updatedPortID := mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.7.1.3."+localPortNum)
2742 updatedPortDescr := mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.7.1.4."+localPortNum)
2743 require.NotEmpty(t, updatedPortID)
2744 require.Equal(t, 5, mustAtoi(t, updatedPortIDSubtype))
2745 require.NotEmpty(t, updatedPortDescr)
2746 }
2747 }
2748
2749 func TestParseTimeTetraLLDPVendorRows_NMS13593(t *testing.T) {
2750 ds1, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms13593/srv001-walk.txt")
2751 require.NoError(t, err)
2752
2753 ds2, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms13593/srv002-walk.txt")
2754 require.NoError(t, err)
2755
2756 require.Equal(t, "24:21:24:ec:e2:3f", normalizeHexToken(mustLookupWalkValue(t, ds1, "1.0.8802.1.1.2.1.3.2.0")))
2757 require.Equal(t, 4, mustAtoi(t, mustLookupWalkValue(t, ds1, "1.0.8802.1.1.2.1.3.1.0")))
2758 require.Equal(t, "ZHBGO1Zsr001", mustLookupWalkValue(t, ds1, "1.0.8802.1.1.2.1.3.3.0"))
2759 require.Equal(t, "24:21:24:da:f6:3f", normalizeHexToken(mustLookupWalkValue(t, ds2, "1.0.8802.1.1.2.1.3.2.0")))
2760 require.Equal(t, 4, mustAtoi(t, mustLookupWalkValue(t, ds2, "1.0.8802.1.1.2.1.3.1.0")))
2761 require.Equal(t, "ZHBGO1Zsr002", mustLookupWalkValue(t, ds2, "1.0.8802.1.1.2.1.3.3.0"))
2762
2763 require.Len(t, ds1.Prefix("1.0.8802.1.1.2.1.4.1.1."), 0)
2764 require.Len(t, ds2.Prefix("1.0.8802.1.1.2.1.4.1.1."), 0)
2765
2766 rows1 := collectTimeTetraRemoteRows(t, ds1)
2767 rows2 := collectTimeTetraRemoteRows(t, ds2)
2768 require.Equal(t, 3, len(rows1))
2769 require.Equal(t, 4, len(rows2))
2770
2771 for _, row := range rows1 {
2772 require.NotZero(t, row.IfIndex)
2773 require.NotZero(t, row.LocalPortNum)
2774 require.NotZero(t, row.RemIndex)
2775 require.NotEmpty(t, row.ChassisID)
2776 require.NotEmpty(t, row.PortID)
2777 require.NotEmpty(t, row.PortDescr)
2778 require.Equal(t, 4, row.ChassisSubtype)
2779 require.Equal(t, 1, row.LocalDestMACAddress)
2780 }
2781
2782 for _, row := range rows2 {
2783 require.NotZero(t, row.IfIndex)
2784 require.NotZero(t, row.LocalPortNum)
2785 require.NotZero(t, row.RemIndex)
2786 require.NotEmpty(t, row.ChassisID)
2787 require.Equal(t, 4, row.ChassisSubtype)
2788 require.Equal(t, 1, row.LocalDestMACAddress)
2789
2790 localRows := timeTetraLocalPortRowsByIfIndex(t, ds2, row.IfIndex)
2791 require.NotEmpty(t, localRows)
2792 require.Equal(t, 7, localRows[0].PortSubtype)
2793 require.NotEmpty(t, localRows[0].PortDescr)
2794 }
2795 }
2796
2797 func TestParseTimeTetraLLDPVendorRows_NMS13923_SRV005(t *testing.T) {
2798 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms13923/srv005.txt")
2799 require.NoError(t, err)
2800
2801 require.Equal(t, "00:16:4d:dd:d5:5b", normalizeHexToken(mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.2.0")))
2802 require.Equal(t, 4, mustAtoi(t, mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.1.0")))
2803 require.Equal(t, "srv005", mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.3.0"))
2804 require.Equal(t, "00:16:4d:dd:d5:5b", normalizeHexToken(mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.2.0")))
2805 require.Equal(t, 4, mustAtoi(t, mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.1.0")))
2806 require.Equal(t, "srv005", mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.3.0"))
2807
2808 rows := collectTimeTetraRemoteRows(t, ds)
2809 require.Equal(t, 49, len(rows))
2810
2811 seen := make(map[string]bool, len(rows))
2812 for _, row := range rows {
2813 require.False(t, seen[row.IndexKey])
2814 seen[row.IndexKey] = true
2815
2816 require.NotZero(t, row.IfIndex)
2817 require.NotZero(t, row.LocalPortNum)
2818 require.NotZero(t, row.RemIndex)
2819 require.NotEmpty(t, row.ChassisID)
2820 require.NotEmpty(t, row.PortID)
2821 require.NotEmpty(t, row.PortDescr)
2822 require.Equal(t, 4, row.ChassisSubtype)
2823 require.Equal(t, 7, row.PortSubtype)
2824 }
2825 }
2826
2827 func TestBuildL2ResultFromWalks_ARP_NMS102(t *testing.T) {
2828 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms102/mikrotik-192.168.0.1-walk.txt")
2829 require.NoError(t, err)
2830
2831 result, err := BuildL2ResultFromWalks([]FixtureWalk{
2832 {
2833 DeviceID: "mikrotik",
2834 Hostname: "ARS-AP",
2835 Address: "192.168.0.1",
2836 Records: ds.Records,
2837 },
2838 }, BuildOptions{EnableARP: true})
2839 require.NoError(t, err)
2840 require.Empty(t, result.Adjacencies)
2841 require.Empty(t, result.Attachments)
2842 require.Len(t, result.Enrichments, 6)
2843 require.Equal(t, 6, result.Stats["enrichments_arp_nd"])
2844
2845 expectedByIP := map[string]struct {
2846 endpointID string
2847 mac string
2848 ifIndexes string
2849 ifNames string
2850 }{
2851 "10.129.16.1": {
2852 endpointID: "mac:00:90:1a:42:22:f8",
2853 mac: "00:90:1a:42:22:f8",
2854 ifIndexes: "1",
2855 ifNames: "ether1",
2856 },
2857 "10.129.16.164": {
2858 endpointID: "mac:00:13:c8:f1:d2:42",
2859 mac: "00:13:c8:f1:d2:42",
2860 ifIndexes: "1",
2861 ifNames: "ether1",
2862 },
2863 "192.168.0.13": {
2864 endpointID: "mac:f0:72:8c:99:99:4d",
2865 mac: "f0:72:8c:99:99:4d",
2866 ifIndexes: "2",
2867 ifNames: "wlan1",
2868 },
2869 "192.168.0.14": {
2870 endpointID: "mac:00:15:99:9f:07:ef",
2871 mac: "00:15:99:9f:07:ef",
2872 ifIndexes: "2",
2873 ifNames: "wlan1",
2874 },
2875 "192.168.0.16": {
2876 endpointID: "mac:60:33:4b:08:17:a8",
2877 mac: "60:33:4b:08:17:a8",
2878 ifIndexes: "2",
2879 ifNames: "wlan1",
2880 },
2881 "192.168.0.17": {
2882 endpointID: "mac:00:1b:63:cd:a9:fd",
2883 mac: "00:1b:63:cd:a9:fd",
2884 ifIndexes: "2",
2885 ifNames: "wlan1",
2886 },
2887 }
2888
2889 actualByIP := make(map[string]l2topology.Enrichment, len(result.Enrichments))
2890 for _, enrichment := range result.Enrichments {
2891 require.Len(t, enrichment.IPs, 1)
2892 actualByIP[enrichment.IPs[0].String()] = enrichment
2893 }
2894 require.Len(t, actualByIP, 6)
2895
2896 for ip, expected := range expectedByIP {
2897 enrichment, ok := actualByIP[ip]
2898 require.True(t, ok, "missing enrichment for %s", ip)
2899 require.Equal(t, expected.endpointID, enrichment.EndpointID)
2900 require.Equal(t, expected.mac, enrichment.MAC)
2901 require.Equal(t, "arp", enrichment.Labels["sources"])
2902 require.Equal(t, "mikrotik", enrichment.Labels["device_ids"])
2903 require.Equal(t, expected.ifIndexes, enrichment.Labels["if_indexes"])
2904 require.Equal(t, expected.ifNames, enrichment.Labels["if_names"])
2905 require.Equal(t, "3", enrichment.Labels["states"])
2906 require.Equal(t, "ipv4", enrichment.Labels["addr_types"])
2907 }
2908 }
2909
2910 func TestParseBridgeBaseWalk_NMS4930(t *testing.T) {
2911 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms4930/dlink_DES-3026.properties")
2912 require.NoError(t, err)
2913
2914 require.Equal(t, "00:1e:58:a3:2f:cd", normalizeHexToken(mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.1.1.0")))
2915 require.Equal(t, 26, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.1.2.0")))
2916 require.Equal(t, 2, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.1.3.0")))
2917 require.Equal(t, 3, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.1.0")))
2918 require.Equal(t, 32768, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.2.0")))
2919 require.Equal(t, "0000000000000000", compactHexToken(mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.5.0")))
2920 require.Equal(t, 0, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.6.0")))
2921 require.Equal(t, 0, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.7.0")))
2922 }
2923
2924 func TestParseBridgeBasePortTable_NMS4930(t *testing.T) {
2925 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms4930/dlink_DES-3026.properties")
2926 require.NoError(t, err)
2927
2928 rows := ds.Prefix("1.3.6.1.2.1.17.1.4.1.2.")
2929 require.Len(t, rows, 26)
2930
2931 const prefix = "1.3.6.1.2.1.17.1.4.1.2."
2932 for _, row := range rows {
2933 basePort, convErr := strconv.Atoi(strings.TrimPrefix(row.OID, prefix))
2934 require.NoError(t, convErr)
2935 ifIndex, parseErr := strconv.Atoi(strings.TrimSpace(row.Value))
2936 require.NoError(t, parseErr)
2937 require.Equal(t, basePort, ifIndex)
2938 }
2939 }
2940
2941 func TestParseBridgeStpPortTable_NMS4930(t *testing.T) {
2942 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms4930/dlink_DES-3026.properties")
2943 require.NoError(t, err)
2944 require.Len(t, ds.Prefix("1.3.6.1.2.1.17.2.15.1.3."), 26)
2945
2946 stateByPort := map[int]int{
2947 1: 5, 2: 5, 3: 5, 4: 5, 5: 5, 6: 5, 7: 1, 8: 1, 9: 1, 10: 1, 11: 1, 12: 1, 13: 1,
2948 14: 1, 15: 1, 16: 1, 17: 1, 18: 1, 19: 1, 20: 1, 21: 1, 22: 1, 23: 1, 24: 5, 25: 1, 26: 1,
2949 }
2950
2951 for i := 1; i <= 26; i++ {
2952 idx := strconv.Itoa(i)
2953 require.Equal(t, i, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.15.1.1."+idx)))
2954 require.Equal(t, 128, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.15.1.2."+idx)))
2955 require.Equal(t, stateByPort[i], mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.15.1.3."+idx)))
2956 require.Equal(t, 1, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.15.1.4."+idx)))
2957 require.Equal(t, 2000000, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.15.1.5."+idx)))
2958 require.Equal(t, "0000000000000000", compactHexToken(mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.15.1.6."+idx)))
2959 require.Equal(t, 0, mustAtoi(t, mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.15.1.7."+idx)))
2960 require.Equal(t, "0000000000000000", compactHexToken(mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.15.1.8."+idx)))
2961 require.Equal(t, "0000", compactHexToken(mustLookupWalkValue(t, ds, "1.3.6.1.2.1.17.2.15.1.9."+idx)))
2962 }
2963 }
2964
2965 func TestBuildL2ResultFromWalks_FDB_NMS4930(t *testing.T) {
2966 ds, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms4930/dlink_DES-3026.properties")
2967 require.NoError(t, err)
2968
2969 result, err := BuildL2ResultFromWalks([]FixtureWalk{
2970 {
2971 DeviceID: "dlink1",
2972 Hostname: "dlink1",
2973 Address: "10.1.1.2",
2974 Records: ds.Records,
2975 },
2976 }, BuildOptions{EnableBridge: true})
2977 require.NoError(t, err)
2978 require.Empty(t, result.Adjacencies)
2979 require.Len(t, result.Attachments, 17)
2980 require.Equal(t, 17, result.Stats["attachments_fdb"])
2981
2982 expectedPortByEndpoint := map[string]int{
2983 "mac:00:0c:29:dc:c0:76": 24,
2984 "mac:f0:7d:68:71:1f:89": 24,
2985 "mac:f0:7d:68:76:c5:65": 24,
2986 "mac:00:0f:fe:b1:0d:1e": 6,
2987 "mac:00:0f:fe:b1:0e:26": 6,
2988 "mac:00:1a:4b:80:27:90": 6,
2989 "mac:00:1d:60:04:ac:bc": 6,
2990 "mac:00:1e:58:86:5d:0f": 6,
2991 "mac:00:21:91:3b:51:08": 6,
2992 "mac:00:24:01:ad:34:16": 6,
2993 "mac:00:24:8c:4c:8b:d0": 6,
2994 "mac:00:24:d6:08:69:3e": 6,
2995 "mac:1c:af:f7:37:cc:33": 6,
2996 "mac:1c:af:f7:44:33:39": 6,
2997 "mac:1c:bd:b9:b5:61:60": 6,
2998 "mac:5c:d9:98:66:7a:bb": 6,
2999 "mac:e0:cb:4e:3e:7f:c0": 6,
3000 }
3001
3002 actualPortByEndpoint := make(map[string]int, len(result.Attachments))
3003 for _, attachment := range result.Attachments {
3004 require.Equal(t, "dlink1", attachment.DeviceID)
3005 require.Equal(t, "fdb", attachment.Method)
3006 require.Equal(t, "3", attachment.Labels["fdb_status"])
3007 bridgePort := mustAtoi(t, attachment.Labels["bridge_port"])
3008 require.Equal(t, bridgePort, attachment.IfIndex)
3009 actualPortByEndpoint[attachment.EndpointID] = bridgePort
3010 }
3011
3012 require.Equal(t, expectedPortByEndpoint, actualPortByEndpoint)
3013 }
3014
3015 func TestBuildL2ResultFromWalks_BRIDGE_NMS4930_DLINK1(t *testing.T) {
3016 manifestPath := "../../../testdata/snmp/enlinkd/nms4930/manifest.yaml"
3017 manifest, err := LoadManifest(manifestPath)
3018 require.NoError(t, err)
3019
3020 scenario, ok := manifest.FindScenario("nms4930_dlink1_bridge_fdb")
3021 require.True(t, ok)
3022
3023 enableOSPF := false
3024 enableISIS := false
3025 require.False(t, scenario.Protocols.LLDP)
3026 require.False(t, scenario.Protocols.CDP)
3027 require.False(t, enableOSPF)
3028 require.True(t, scenario.Protocols.Bridge)
3029 require.False(t, enableISIS)
3030 require.Equal(t, ManifestProtocols{Bridge: true}, scenario.Protocols)
3031
3032 resolved, err := ResolveScenario(manifestPath, scenario)
3033 require.NoError(t, err)
3034 require.Len(t, resolved.Fixtures, 1)
3035 require.Equal(t, "dlink1", resolved.Fixtures[0].DeviceID)
3036
3037 preCollectionBridgeLinks := 0
3038 preCollectionBridgeMacLinks := 0
3039 require.Equal(t, 0, preCollectionBridgeLinks)
3040 require.Equal(t, 0, preCollectionBridgeMacLinks)
3041
3042 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableBridge: true})
3043 require.NoError(t, errStep1)
3044 require.Empty(t, step1.Adjacencies)
3045 require.Equal(t, 17, step1.Stats["attachments_fdb"])
3046 require.Len(t, step1.Attachments, 17)
3047 }
3048
3049 func TestBuildL2ResultFromWalks_BRIDGE_NMS4930_DLINK2(t *testing.T) {
3050 manifestPath := "../../../testdata/snmp/enlinkd/nms4930/manifest.yaml"
3051 manifest, err := LoadManifest(manifestPath)
3052 require.NoError(t, err)
3053
3054 scenario, ok := manifest.FindScenario("nms4930_dlink2_bridge_fdb")
3055 require.True(t, ok)
3056
3057 enableOSPF := false
3058 enableISIS := false
3059 require.False(t, scenario.Protocols.LLDP)
3060 require.False(t, scenario.Protocols.CDP)
3061 require.False(t, enableOSPF)
3062 require.True(t, scenario.Protocols.Bridge)
3063 require.False(t, enableISIS)
3064 require.Equal(t, ManifestProtocols{Bridge: true}, scenario.Protocols)
3065
3066 resolved, err := ResolveScenario(manifestPath, scenario)
3067 require.NoError(t, err)
3068 require.Len(t, resolved.Fixtures, 1)
3069 require.Equal(t, "dlink2", resolved.Fixtures[0].DeviceID)
3070
3071 preCollectionBridgeLinks := 0
3072 preCollectionBridgeMacLinks := 0
3073 require.Equal(t, 0, preCollectionBridgeLinks)
3074 require.Equal(t, 0, preCollectionBridgeMacLinks)
3075
3076 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableBridge: true})
3077 require.NoError(t, errStep1)
3078 require.Empty(t, step1.Adjacencies)
3079 require.Equal(t, 11, step1.Stats["attachments_fdb"])
3080 require.Len(t, step1.Attachments, 11)
3081 }
3082
3083 func TestParseBridgeDot1qTpFdbTable_NMS4930(t *testing.T) {
3084 ds1, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms4930/dlink_DES-3026.properties")
3085 require.NoError(t, err)
3086
3087 ds2, err := LoadWalkFile("../../../testdata/snmp/enlinkd/upstream/linkd/nms4930/dlink_DGS-3612G.properties")
3088 require.NoError(t, err)
3089
3090 macs1 := buildDot1qMacPortMap(ds1)
3091 macs2 := buildDot1qMacPortMap(ds2)
3092
3093 require.Equal(t, 59, len(macs1))
3094 require.Equal(t, 979, len(macs2))
3095 require.Equal(t, 0, macs2["000c6e3f9f3e"])
3096 require.Equal(t, 0, macs2["a00bba158c8c"])
3097 }
3098
3099 func TestBuildL2ResultFromWalks_BRIDGE_NMS7918_ASW01(t *testing.T) {
3100 manifestPath := "../../../testdata/snmp/enlinkd/nms7918/manifest.yaml"
3101 manifest, err := LoadManifest(manifestPath)
3102 require.NoError(t, err)
3103
3104 scenario, ok := manifest.FindScenario("nms7918_asw01_bridge_fdb")
3105 require.True(t, ok)
3106
3107 enableOSPF := false
3108 enableISIS := false
3109 require.False(t, scenario.Protocols.LLDP)
3110 require.False(t, scenario.Protocols.CDP)
3111 require.False(t, enableOSPF)
3112 require.True(t, scenario.Protocols.Bridge)
3113 require.False(t, enableISIS)
3114 require.Equal(t, ManifestProtocols{Bridge: true}, scenario.Protocols)
3115
3116 resolved, err := ResolveScenario(manifestPath, scenario)
3117 require.NoError(t, err)
3118 require.Len(t, resolved.Fixtures, 1)
3119 require.Equal(t, "asw01", resolved.Fixtures[0].DeviceID)
3120
3121 preCollectionBridgeLinks := 0
3122 preCollectionBridgeMacLinks := 0
3123 require.Equal(t, 0, preCollectionBridgeLinks)
3124 require.Equal(t, 0, preCollectionBridgeMacLinks)
3125
3126 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableBridge: true})
3127 require.NoError(t, errStep1)
3128 require.Len(t, step1.Devices, 1)
3129 require.Empty(t, step1.Adjacencies)
3130 require.Equal(t, 0, step1.Stats["links_lldp"])
3131 require.Len(t, step1.Attachments, 40)
3132 require.Equal(t, 40, step1.Stats["attachments_fdb"])
3133
3134 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableBridge: true})
3135 require.NoError(t, errStep2)
3136 require.Len(t, step2.Devices, 1)
3137 require.Empty(t, step2.Adjacencies)
3138 require.Equal(t, 0, step2.Stats["links_lldp"])
3139 require.Len(t, step2.Attachments, 40)
3140 require.Equal(t, 40, step2.Stats["attachments_fdb"])
3141
3142 step3, errStep3 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableBridge: true})
3143 require.NoError(t, errStep3)
3144 require.Len(t, step3.Devices, 1)
3145 require.Empty(t, step3.Adjacencies)
3146 require.Equal(t, 0, step3.Stats["links_lldp"])
3147 require.Len(t, step3.Attachments, 40)
3148 require.Equal(t, 40, step3.Stats["attachments_fdb"])
3149
3150 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
3151 require.NoError(t, err)
3152 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step3.Adjacencies))
3153 }
3154
3155 func TestBuildL2ResultFromWalks_BRIDGE_NMS7918_SAMASW01(t *testing.T) {
3156 manifestPath := "../../../testdata/snmp/enlinkd/nms7918/manifest.yaml"
3157 manifest, err := LoadManifest(manifestPath)
3158 require.NoError(t, err)
3159
3160 scenario, ok := manifest.FindScenario("nms7918_samasw01_bridge_fdb")
3161 require.True(t, ok)
3162
3163 enableOSPF := false
3164 enableISIS := false
3165 require.False(t, scenario.Protocols.LLDP)
3166 require.False(t, scenario.Protocols.CDP)
3167 require.False(t, enableOSPF)
3168 require.True(t, scenario.Protocols.Bridge)
3169 require.False(t, enableISIS)
3170 require.Equal(t, ManifestProtocols{Bridge: true}, scenario.Protocols)
3171
3172 resolved, err := ResolveScenario(manifestPath, scenario)
3173 require.NoError(t, err)
3174 require.Len(t, resolved.Fixtures, 1)
3175 require.Equal(t, "samasw01", resolved.Fixtures[0].DeviceID)
3176
3177 preCollectionBridgeLinks := 0
3178 preCollectionBridgeMacLinks := 0
3179 require.Equal(t, 0, preCollectionBridgeLinks)
3180 require.Equal(t, 0, preCollectionBridgeMacLinks)
3181
3182 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableBridge: true})
3183 require.NoError(t, errStep1)
3184 require.Len(t, step1.Devices, 1)
3185 require.Empty(t, step1.Adjacencies)
3186 require.Equal(t, 0, step1.Stats["links_lldp"])
3187 require.Len(t, step1.Attachments, 22)
3188 require.Equal(t, 22, step1.Stats["attachments_fdb"])
3189
3190 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableBridge: true})
3191 require.NoError(t, errStep2)
3192 require.Len(t, step2.Devices, 1)
3193 require.Empty(t, step2.Adjacencies)
3194 require.Equal(t, 0, step2.Stats["links_lldp"])
3195 require.Len(t, step2.Attachments, 22)
3196 require.Equal(t, 22, step2.Stats["attachments_fdb"])
3197
3198 step3, errStep3 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableBridge: true})
3199 require.NoError(t, errStep3)
3200 require.Len(t, step3.Devices, 1)
3201 require.Empty(t, step3.Adjacencies)
3202 require.Equal(t, 0, step3.Stats["links_lldp"])
3203 require.Len(t, step3.Attachments, 22)
3204 require.Equal(t, 22, step3.Stats["attachments_fdb"])
3205
3206 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
3207 require.NoError(t, err)
3208 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step3.Adjacencies))
3209 }
3210
3211 func TestBuildL2ResultFromWalks_BRIDGE_NMS7918_STCASW01(t *testing.T) {
3212 manifestPath := "../../../testdata/snmp/enlinkd/nms7918/manifest.yaml"
3213 manifest, err := LoadManifest(manifestPath)
3214 require.NoError(t, err)
3215
3216 scenario, ok := manifest.FindScenario("nms7918_stcasw01_bridge_fdb")
3217 require.True(t, ok)
3218
3219 enableOSPF := false
3220 enableISIS := false
3221 require.False(t, scenario.Protocols.LLDP)
3222 require.False(t, scenario.Protocols.CDP)
3223 require.False(t, enableOSPF)
3224 require.True(t, scenario.Protocols.Bridge)
3225 require.False(t, enableISIS)
3226 require.Equal(t, ManifestProtocols{Bridge: true}, scenario.Protocols)
3227
3228 resolved, err := ResolveScenario(manifestPath, scenario)
3229 require.NoError(t, err)
3230 require.Len(t, resolved.Fixtures, 1)
3231 require.Equal(t, "stcasw01", resolved.Fixtures[0].DeviceID)
3232
3233 preCollectionBridgeLinks := 0
3234 preCollectionBridgeMacLinks := 0
3235 require.Equal(t, 0, preCollectionBridgeLinks)
3236 require.Equal(t, 0, preCollectionBridgeMacLinks)
3237
3238 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableBridge: true})
3239 require.NoError(t, errStep1)
3240 require.Len(t, step1.Devices, 1)
3241 require.Empty(t, step1.Adjacencies)
3242 require.Equal(t, 0, step1.Stats["links_lldp"])
3243 require.Len(t, step1.Attachments, 34)
3244 require.Equal(t, 34, step1.Stats["attachments_fdb"])
3245
3246 step2, errStep2 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableBridge: true})
3247 require.NoError(t, errStep2)
3248 require.Len(t, step2.Devices, 1)
3249 require.Empty(t, step2.Adjacencies)
3250 require.Equal(t, 0, step2.Stats["links_lldp"])
3251 require.Len(t, step2.Attachments, 34)
3252 require.Equal(t, 34, step2.Stats["attachments_fdb"])
3253
3254 step3, errStep3 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableBridge: true})
3255 require.NoError(t, errStep3)
3256 require.Len(t, step3.Devices, 1)
3257 require.Empty(t, step3.Adjacencies)
3258 require.Equal(t, 0, step3.Stats["links_lldp"])
3259 require.Len(t, step3.Attachments, 34)
3260 require.Equal(t, 34, step3.Stats["attachments_fdb"])
3261
3262 golden, err := LoadGoldenYAML(resolved.GoldenYAML)
3263 require.NoError(t, err)
3264 require.Equal(t, goldenAdjacencyKeySet(golden.Adjacencies), adjacencyKeySet(step3.Adjacencies))
3265 }
3266
3267 func TestBuildL2ResultFromWalks_ARP_NMS7918_OSPWL01(t *testing.T) {
3268 manifestPath := "../../../testdata/snmp/enlinkd/nms7918/manifest.yaml"
3269 manifest, err := LoadManifest(manifestPath)
3270 require.NoError(t, err)
3271
3272 scenario, ok := manifest.FindScenario("nms7918_ospwl01_arp")
3273 require.True(t, ok)
3274
3275 enableOSPF := false
3276 enableISIS := false
3277 require.False(t, scenario.Protocols.LLDP)
3278 require.False(t, scenario.Protocols.CDP)
3279 require.False(t, enableOSPF)
3280 require.False(t, scenario.Protocols.Bridge)
3281 require.False(t, enableISIS)
3282 require.True(t, scenario.Protocols.ARPND)
3283 require.Equal(t, ManifestProtocols{ARPND: true}, scenario.Protocols)
3284
3285 resolved, err := ResolveScenario(manifestPath, scenario)
3286 require.NoError(t, err)
3287 require.Len(t, resolved.Fixtures, 1)
3288 require.Equal(t, "ospwl01", resolved.Fixtures[0].DeviceID)
3289
3290 preCollectionBridgeLinks := 0
3291 preCollectionBridgeMacLinks := 0
3292 preCollectionARPNDEntries := 0
3293 require.Equal(t, 0, preCollectionBridgeLinks)
3294 require.Equal(t, 0, preCollectionBridgeMacLinks)
3295 require.Equal(t, 0, preCollectionARPNDEntries)
3296
3297 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableARP: true})
3298 require.NoError(t, errStep1)
3299 require.Len(t, step1.Devices, 1)
3300 require.Empty(t, step1.Adjacencies)
3301 require.Empty(t, step1.Attachments)
3302 require.Len(t, step1.Enrichments, 1)
3303 require.Equal(t, 1, step1.Stats["enrichments_arp_nd"])
3304
3305 enrichment := step1.Enrichments[0]
3306 require.Equal(t, "00:13:19:bd:b4:40", enrichment.MAC)
3307 require.Empty(t, step1.Attachments)
3308 require.Len(t, step1.Enrichments, 1)
3309 require.Len(t, enrichment.IPs, 1)
3310 require.Contains(t, enrichment.IPs[0].String(), "10.25.19.1")
3311 require.Equal(t, "arp", enrichment.Labels["sources"])
3312 require.Equal(t, "7", enrichment.Labels["if_indexes"])
3313 require.Equal(t, "bridge1", enrichment.Labels["if_names"])
3314 require.Equal(t, "3", enrichment.Labels["states"])
3315 require.Equal(t, "ipv4", enrichment.Labels["addr_types"])
3316 }
3317
3318 func TestBuildL2ResultFromWalks_ARP_NMS7918_OSPESS01(t *testing.T) {
3319 manifestPath := "../../../testdata/snmp/enlinkd/nms7918/manifest.yaml"
3320 manifest, err := LoadManifest(manifestPath)
3321 require.NoError(t, err)
3322
3323 scenario, ok := manifest.FindScenario("nms7918_ospess01_arp")
3324 require.True(t, ok)
3325
3326 enableOSPF := false
3327 enableISIS := false
3328 require.False(t, scenario.Protocols.LLDP)
3329 require.False(t, scenario.Protocols.CDP)
3330 require.False(t, enableOSPF)
3331 require.False(t, scenario.Protocols.Bridge)
3332 require.False(t, enableISIS)
3333 require.True(t, scenario.Protocols.ARPND)
3334 require.Equal(t, ManifestProtocols{ARPND: true}, scenario.Protocols)
3335
3336 resolved, err := ResolveScenario(manifestPath, scenario)
3337 require.NoError(t, err)
3338 require.Len(t, resolved.Fixtures, 1)
3339 require.Equal(t, "ospess01", resolved.Fixtures[0].DeviceID)
3340
3341 preCollectionBridgeLinks := 0
3342 preCollectionBridgeMacLinks := 0
3343 preCollectionARPNDEntries := 0
3344 require.Equal(t, 0, preCollectionBridgeLinks)
3345 require.Equal(t, 0, preCollectionBridgeMacLinks)
3346 require.Equal(t, 0, preCollectionARPNDEntries)
3347
3348 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableARP: true})
3349 require.NoError(t, errStep1)
3350 require.Len(t, step1.Devices, 1)
3351 require.Empty(t, step1.Adjacencies)
3352 require.Empty(t, step1.Attachments)
3353 require.Len(t, step1.Enrichments, 4)
3354 require.Equal(t, 4, step1.Stats["enrichments_arp_nd"])
3355 require.Equal(t, 5, countEnrichmentIPs(step1.Enrichments))
3356
3357 byMAC := enrichmentByMAC(step1.Enrichments)
3358 require.Len(t, byMAC, 4)
3359
3360 pe01 := mustEnrichmentByMAC(t, byMAC, "00:13:19:bd:b4:40")
3361 require.Equal(t, "mac:00:13:19:bd:b4:40", pe01.EndpointID)
3362 require.Equal(t, "00:13:19:bd:b4:40", pe01.MAC)
3363 require.Len(t, pe01.IPs, 2)
3364 require.True(t, enrichmentContainsIP(pe01, "10.25.19.1"))
3365 require.True(t, enrichmentContainsIP(pe01, "10.27.19.1"))
3366 require.Equal(t, "arp", pe01.Labels["sources"])
3367 require.Equal(t, "ipv4", pe01.Labels["addr_types"])
3368 require.Contains(t, pe01.Labels["if_indexes"], "10")
3369 require.Contains(t, pe01.Labels["if_indexes"], "11")
3370
3371 nonPEWithSingleIP := 0
3372 for mac, enrichment := range byMAC {
3373 if mac == pe01.MAC {
3374 continue
3375 }
3376 if len(enrichment.IPs) == 1 {
3377 nonPEWithSingleIP++
3378 }
3379 }
3380 require.Equal(t, 3, nonPEWithSingleIP)
3381 }
3382
3383 func TestBuildL2ResultFromWalks_ARP_NMS7918_PE01(t *testing.T) {
3384 manifestPath := "../../../testdata/snmp/enlinkd/nms7918/manifest.yaml"
3385 manifest, err := LoadManifest(manifestPath)
3386 require.NoError(t, err)
3387
3388 scenario, ok := manifest.FindScenario("nms7918_pe01_arp")
3389 require.True(t, ok)
3390
3391 enableOSPF := false
3392 enableISIS := false
3393 require.False(t, scenario.Protocols.LLDP)
3394 require.False(t, scenario.Protocols.CDP)
3395 require.False(t, enableOSPF)
3396 require.False(t, scenario.Protocols.Bridge)
3397 require.False(t, enableISIS)
3398 require.True(t, scenario.Protocols.ARPND)
3399 require.Equal(t, ManifestProtocols{ARPND: true}, scenario.Protocols)
3400
3401 resolved, err := ResolveScenario(manifestPath, scenario)
3402 require.NoError(t, err)
3403 require.Len(t, resolved.Fixtures, 1)
3404 require.Equal(t, "pe01", resolved.Fixtures[0].DeviceID)
3405
3406 preCollectionBridgeLinks := 0
3407 preCollectionBridgeMacLinks := 0
3408 preCollectionARPNDEntries := 0
3409 require.Equal(t, 0, preCollectionBridgeLinks)
3410 require.Equal(t, 0, preCollectionBridgeMacLinks)
3411 require.Equal(t, 0, preCollectionARPNDEntries)
3412
3413 step1, errStep1 := buildResultFromScenarioPrefix(resolved, 1, BuildOptions{EnableARP: true})
3414 require.NoError(t, errStep1)
3415 require.Len(t, step1.Devices, 1)
3416 require.Empty(t, step1.Adjacencies)
3417 require.Empty(t, step1.Attachments)
3418 require.Len(t, step1.Enrichments, 37)
3419 require.Equal(t, 37, step1.Stats["enrichments_arp_nd"])
3420 require.Equal(t, 113, countEnrichmentIPs(step1.Enrichments))
3421
3422 byMAC := enrichmentByMAC(step1.Enrichments)
3423 require.Len(t, byMAC, 37)
3424
3425 pe01 := mustEnrichmentByMAC(t, byMAC, "00:13:19:bd:b4:40")
3426 require.Equal(t, "mac:00:13:19:bd:b4:40", pe01.EndpointID)
3427 require.Len(t, pe01.IPs, 45)
3428 require.True(t, enrichmentContainsIP(pe01, "10.25.19.1"))
3429 require.True(t, enrichmentContainsIP(pe01, "10.27.19.1"))
3430 require.Equal(t, "arp", pe01.Labels["sources"])
3431 require.Equal(t, "ipv4", pe01.Labels["addr_types"])
3432 require.Equal(t, "pe01", pe01.Labels["device_ids"])
3433
3434 asw01 := mustEnrichmentByMAC(t, byMAC, "00:e0:b1:bd:2f:5c")
3435 require.Equal(t, "mac:00:e0:b1:bd:2f:5c", asw01.EndpointID)
3436 require.Len(t, asw01.IPs, 1)
3437 require.True(t, enrichmentContainsIP(asw01, "10.25.19.2"))
3438
3439 ospess01 := mustEnrichmentByMAC(t, byMAC, "00:17:63:01:0d:4f")
3440 require.Equal(t, "mac:00:17:63:01:0d:4f", ospess01.EndpointID)
3441 require.Len(t, ospess01.IPs, 5)
3442 require.True(t, enrichmentContainsIP(ospess01, "10.25.19.3"))
3443
3444 ospwl01 := mustEnrichmentByMAC(t, byMAC, "d4:ca:6d:ed:84:d6")
3445 require.Equal(t, "mac:d4:ca:6d:ed:84:d6", ospwl01.EndpointID)
3446 require.Len(t, ospwl01.IPs, 1)
3447 require.True(t, enrichmentContainsIP(ospwl01, "10.25.19.4"))
3448
3449 samasw01 := mustEnrichmentByMAC(t, byMAC, "00:12:cf:3f:4e:e0")
3450 require.Equal(t, "mac:00:12:cf:3f:4e:e0", samasw01.EndpointID)
3451 require.Len(t, samasw01.IPs, 2)
3452 require.True(t, enrichmentContainsIP(samasw01, "10.25.19.211"))
3453
3454 stcasw01 := mustEnrichmentByMAC(t, byMAC, "00:e0:b1:bd:26:52")
3455 require.Equal(t, "mac:00:e0:b1:bd:26:52", stcasw01.EndpointID)
3456 require.Len(t, stcasw01.IPs, 1)
3457 require.True(t, enrichmentContainsIP(stcasw01, "10.25.19.216"))
3458 }
3459
3460 func buildDot1qMacPortMap(ds WalkDataset) map[string]int {
3461 const dot1qPortPrefix = "1.3.6.1.2.1.17.7.1.2.2.1.2."
3462 const dot1qStatusPrefix = "1.3.6.1.2.1.17.7.1.2.2.1.3."
3463
3464 macPort := make(map[string]int)
3465 portByIndex := make(map[string]int)
3466 macByIndex := make(map[string]string)
3467
3468 for _, rec := range ds.Records {
3469 oid := normalizeOID(rec.OID)
3470 if !strings.HasPrefix(oid, dot1qPortPrefix) {
3471 continue
3472 }
3473
3474 index, mac, ok := dot1qIndexFromOID(oid, dot1qPortPrefix)
3475 if !ok {
3476 continue
3477 }
3478 port, err := strconv.Atoi(strings.TrimSpace(rec.Value))
3479 if err != nil {
3480 continue
3481 }
3482
3483 portByIndex[index] = port
3484 macByIndex[index] = mac
3485 macPort[mac] = port
3486 }
3487
3488 for _, rec := range ds.Records {
3489 oid := normalizeOID(rec.OID)
3490 if !strings.HasPrefix(oid, dot1qStatusPrefix) {
3491 continue
3492 }
3493
3494 index, mac, ok := dot1qIndexFromOID(oid, dot1qStatusPrefix)
3495 if !ok {
3496 continue
3497 }
3498 if _, exists := macPort[mac]; exists {
3499 continue
3500 }
3501
3502 if port, ok := portByIndex[index]; ok {
3503 macPort[mac] = port
3504 continue
3505 }
3506 if indexedMAC, ok := macByIndex[index]; ok {
3507 macPort[indexedMAC] = 0
3508 continue
3509 }
3510 macPort[mac] = 0
3511 }
3512
3513 return macPort
3514 }
3515
3516 func dot1qIndexFromOID(oid, prefix string) (key string, mac string, ok bool) {
3517 suffix := strings.TrimPrefix(oid, prefix)
3518 suffix = strings.TrimPrefix(suffix, ".")
3519 parts := strings.Split(suffix, ".")
3520 if len(parts) < 7 {
3521 return "", "", false
3522 }
3523
3524 macParts := parts[len(parts)-6:]
3525 octets := make([]byte, 0, 6)
3526 for _, part := range macParts {
3527 n, err := strconv.Atoi(strings.TrimSpace(part))
3528 if err != nil || n < 0 || n > 255 {
3529 return "", "", false
3530 }
3531 octets = append(octets, byte(n))
3532 }
3533
3534 macBuilder := strings.Builder{}
3535 for _, octet := range octets {
3536 _, _ = fmt.Fprintf(&macBuilder, "%02x", octet)
3537 }
3538
3539 return strings.Join(parts, "."), macBuilder.String(), true
3540 }
3541
3542 type timeTetraRemoteRow struct {
3543 IndexKey string
3544 LocalPortNum int
3545 IfIndex int
3546 LocalDestMACAddress int
3547 RemIndex int
3548 ChassisSubtype int
3549 ChassisID string
3550 PortSubtype int
3551 PortID string
3552 PortDescr string
3553 SysName string
3554 }
3555
3556 type timeTetraLocalPortRow struct {
3557 Key string
3558 PortSubtype int
3559 PortID string
3560 PortDescr string
3561 }
3562
3563 func collectTimeTetraRemoteRows(t *testing.T, ds WalkDataset) []timeTetraRemoteRow {
3564 t.Helper()
3565
3566 const prefix = "1.3.6.1.4.1.6527.3.1.2.59.4.1.1."
3567 chassisRows := ds.Prefix(prefix + "5.")
3568 rows := make([]timeTetraRemoteRow, 0, len(chassisRows))
3569
3570 for _, rec := range chassisRows {
3571 oid := normalizeOID(rec.OID)
3572 index := strings.TrimPrefix(oid, prefix+"5.")
3573 require.NotEmpty(t, index)
3574
3575 localPortNum, ifIndex, localDestMACAddress, remIndex := parseTimeTetraRemoteIndex(t, index)
3576 row := timeTetraRemoteRow{
3577 IndexKey: index,
3578 LocalPortNum: localPortNum,
3579 IfIndex: ifIndex,
3580 LocalDestMACAddress: localDestMACAddress,
3581 RemIndex: remIndex,
3582 ChassisSubtype: mustAtoi(t, mustLookupWalkValue(t, ds, prefix+"4."+index)),
3583 ChassisID: normalizeHexToken(rec.Value),
3584 PortSubtype: mustAtoi(t, mustLookupWalkValue(t, ds, prefix+"6."+index)),
3585 PortID: strings.TrimSpace(mustLookupWalkValue(t, ds, prefix+"7."+index)),
3586 PortDescr: strings.TrimSpace(mustLookupWalkValue(t, ds, prefix+"8."+index)),
3587 }
3588
3589 if rec9, ok := ds.Lookup(prefix + "9." + index); ok {
3590 row.SysName = strings.TrimSpace(rec9.Value)
3591 }
3592
3593 rows = append(rows, row)
3594 }
3595
3596 sort.Slice(rows, func(i, j int) bool {
3597 return rows[i].IndexKey < rows[j].IndexKey
3598 })
3599
3600 return rows
3601 }
3602
3603 func parseTimeTetraRemoteIndex(t *testing.T, index string) (localPortNum int, ifIndex int, localDestMACAddress int, remIndex int) {
3604 t.Helper()
3605
3606 parts := strings.Split(strings.TrimSpace(index), ".")
3607 require.Len(t, parts, 4)
3608
3609 localPortNum = mustAtoi(t, parts[0])
3610 ifIndex = mustAtoi(t, parts[1])
3611 localDestMACAddress = mustAtoi(t, parts[2])
3612 remIndex = mustAtoi(t, parts[3])
3613 return
3614 }
3615
3616 func timeTetraLocalPortRowsByIfIndex(t *testing.T, ds WalkDataset, ifIndex int) []timeTetraLocalPortRow {
3617 t.Helper()
3618
3619 index := strconv.Itoa(ifIndex)
3620 subtypePrefix := "1.3.6.1.4.1.6527.3.1.2.59.3.1.1.2." + index + "."
3621 oidPrefix := "1.3.6.1.4.1.6527.3.1.2.59.3.1.1.3." + index + "."
3622 descrPrefix := "1.3.6.1.4.1.6527.3.1.2.59.3.1.1.4." + index + "."
3623
3624 subtypeRows := ds.Prefix(subtypePrefix)
3625 rows := make([]timeTetraLocalPortRow, 0, len(subtypeRows))
3626 for _, rec := range subtypeRows {
3627 key := strings.TrimPrefix(normalizeOID(rec.OID), subtypePrefix)
3628 if strings.TrimSpace(key) == "" {
3629 continue
3630 }
3631
3632 portIDRec, ok := ds.Lookup(oidPrefix + key)
3633 if !ok {
3634 continue
3635 }
3636 portDescrRec, ok := ds.Lookup(descrPrefix + key)
3637 if !ok {
3638 continue
3639 }
3640
3641 rows = append(rows, timeTetraLocalPortRow{
3642 Key: key,
3643 PortSubtype: mustAtoi(t, rec.Value),
3644 PortID: strings.TrimSpace(portIDRec.Value),
3645 PortDescr: strings.TrimSpace(portDescrRec.Value),
3646 })
3647 }
3648
3649 sort.Slice(rows, func(i, j int) bool {
3650 return rows[i].Key < rows[j].Key
3651 })
3652
3653 return rows
3654 }
3655
3656 func buildResultFromScenarioPrefix(scenario ResolvedScenario, prefixLen int, opts BuildOptions) (l2topology.Result, error) {
3657 walks, err := loadScenarioWalkPrefix(scenario, prefixLen)
3658 if err != nil {
3659 return l2topology.Result{}, err
3660 }
3661
3662 return BuildL2ResultFromWalks(walks, opts)
3663 }
3664
3665 func loadScenarioWalkPrefix(scenario ResolvedScenario, prefixLen int) ([]FixtureWalk, error) {
3666 if prefixLen <= 0 || prefixLen > len(scenario.Fixtures) {
3667 return nil, fmt.Errorf("invalid fixture prefix len %d", prefixLen)
3668 }
3669
3670 walks := make([]FixtureWalk, 0, prefixLen)
3671 for i := 0; i < prefixLen; i++ {
3672 fixture := scenario.Fixtures[i]
3673 ds, err := LoadWalkFile(fixture.WalkFile)
3674 if err != nil {
3675 return nil, fmt.Errorf("load walk for fixture %q: %w", fixture.DeviceID, err)
3676 }
3677 walks = append(walks, FixtureWalk{
3678 DeviceID: fixture.DeviceID,
3679 Hostname: fixture.Hostname,
3680 Address: fixture.Address,
3681 Records: ds.Records,
3682 })
3683 }
3684
3685 return walks, nil
3686 }
3687
3688 func countAdjacenciesBySource(adjacencies []l2topology.Adjacency, protocol string) map[string]int {
3689 out := make(map[string]int)
3690 for _, adj := range adjacencies {
3691 if adj.Protocol != protocol {
3692 continue
3693 }
3694 out[adj.SourceID]++
3695 }
3696 return out
3697 }
3698
3699 func countFixturesWithLLDPLocalElements(fixtures []FixtureWalk) int {
3700 count := 0
3701 for _, fixture := range fixtures {
3702 hasLLDPIdentity := false
3703 for _, rec := range fixture.Records {
3704 switch normalizeOID(rec.OID) {
3705 case "1.0.8802.1.1.2.1.3.2.0", "1.0.8802.1.1.2.1.3.3.0":
3706 if strings.TrimSpace(rec.Value) != "" {
3707 hasLLDPIdentity = true
3708 }
3709 }
3710 if hasLLDPIdentity {
3711 break
3712 }
3713 }
3714 if hasLLDPIdentity {
3715 count++
3716 }
3717 }
3718 return count
3719 }
3720
3721 func goldenAdjacencyKeySet(adjacencies []GoldenAdjacency) map[string]struct{} {
3722 out := make(map[string]struct{}, len(adjacencies))
3723 for _, adj := range adjacencies {
3724 out[adj.Protocol+"|"+adj.SourceDevice+"|"+adj.SourcePort+"|"+adj.TargetDevice+"|"+adj.TargetPort] = struct{}{}
3725 }
3726 return out
3727 }
3728
3729 func mustLookupWalkValue(t *testing.T, ds WalkDataset, oid string) string {
3730 t.Helper()
3731 record, ok := ds.Lookup(oid)
3732 require.True(t, ok, "missing OID %s", oid)
3733 return strings.TrimSpace(record.Value)
3734 }
3735
3736 func mustAtoi(t *testing.T, v string) int {
3737 t.Helper()
3738 out, err := strconv.Atoi(strings.TrimSpace(v))
3739 require.NoError(t, err)
3740 return out
3741 }
3742
3743 func compactHexToken(v string) string {
3744 v = strings.TrimSpace(strings.ToLower(v))
3745 return strings.NewReplacer(":", "", "-", "", ".", "", " ", "").Replace(v)
3746 }
3747
3748 func countCDPCacheRows(ds WalkDataset) int {
3749 const prefix = "1.3.6.1.4.1.9.9.23.1.2.1.1.3."
3750
3751 count := 0
3752 for _, rec := range ds.Records {
3753 if strings.HasPrefix(normalizeOID(rec.OID), prefix) {
3754 count++
3755 }
3756 }
3757 return count
3758 }
3759
3760 func lldpLocPortTriplet(t *testing.T, ds WalkDataset, portNum string) []string {
3761 t.Helper()
3762 return []string{
3763 mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.7.1.2."+portNum),
3764 mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.7.1.3."+portNum),
3765 mustLookupWalkValue(t, ds, "1.0.8802.1.1.2.1.3.7.1.4."+portNum),
3766 }
3767 }
3768
3769 func collectLLDPRemoteRows(ds WalkDataset) map[string]map[int]string {
3770 const base = "1.0.8802.1.1.2.1.4.1.1."
3771 rows := make(map[string]map[int]string)
3772
3773 for _, rec := range ds.Records {
3774 oid := normalizeOID(rec.OID)
3775 if !strings.HasPrefix(oid, base) {
3776 continue
3777 }
3778
3779 suffix := strings.TrimPrefix(oid, base)
3780 suffix = strings.TrimPrefix(suffix, ".")
3781 parts := strings.Split(suffix, ".")
3782 if len(parts) < 4 {
3783 continue
3784 }
3785
3786 column, err := strconv.Atoi(strings.TrimSpace(parts[0]))
3787 if err != nil || column < 4 || column > 9 {
3788 continue
3789 }
3790
3791 localPort := strings.TrimSpace(parts[len(parts)-2])
3792 remIndex := strings.TrimSpace(parts[len(parts)-1])
3793 if localPort == "" || remIndex == "" {
3794 continue
3795 }
3796
3797 key := localPort + "|" + remIndex
3798 row, ok := rows[key]
3799 if !ok {
3800 row = make(map[int]string, 6)
3801 rows[key] = row
3802 }
3803 row[column] = strings.TrimSpace(rec.Value)
3804 }
3805
3806 return rows
3807 }
3808
3809 func adjacencyKeySet(adjacencies []l2topology.Adjacency) map[string]struct{} {
3810 out := make(map[string]struct{}, len(adjacencies))
3811 for _, adj := range adjacencies {
3812 key := fmt.Sprintf("%s|%s|%s|%s|%s", adj.Protocol, adj.SourceID, adj.SourcePort, adj.TargetID, adj.TargetPort)
3813 out[key] = struct{}{}
3814 }
3815 return out
3816 }
3817
3818 func countBidirectionalPairs(adjacencies []l2topology.Adjacency, protocol string) int {
3819 directed := make(map[string]struct{}, len(adjacencies))
3820 for _, adj := range adjacencies {
3821 if adj.Protocol != protocol {
3822 continue
3823 }
3824 directed[fmt.Sprintf("%s|%s|%s|%s|%s", adj.Protocol, adj.SourceID, adj.SourcePort, adj.TargetID, adj.TargetPort)] = struct{}{}
3825 }
3826
3827 counted := make(map[string]struct{}, len(directed))
3828 pairs := 0
3829 for _, adj := range adjacencies {
3830 if adj.Protocol != protocol {
3831 continue
3832 }
3833
3834 forward := fmt.Sprintf("%s|%s|%s|%s|%s", adj.Protocol, adj.SourceID, adj.SourcePort, adj.TargetID, adj.TargetPort)
3835 reverse := fmt.Sprintf("%s|%s|%s|%s|%s", adj.Protocol, adj.TargetID, adj.TargetPort, adj.SourceID, adj.SourcePort)
3836
3837 canonical := forward
3838 if reverse < canonical {
3839 canonical = reverse
3840 }
3841 if _, done := counted[canonical]; done {
3842 continue
3843 }
3844 if _, ok := directed[reverse]; !ok {
3845 continue
3846 }
3847
3848 counted[canonical] = struct{}{}
3849 pairs++
3850 }
3851
3852 return pairs
3853 }
3854
3855 func countMutualLocalAdjacencyEdges(adjacencies []l2topology.Adjacency, protocol string, localDevices map[string]struct{}) int {
3856 directedCounts := make(map[string]int)
3857 for _, adj := range adjacencies {
3858 if adj.Protocol != protocol || adj.SourceID == adj.TargetID {
3859 continue
3860 }
3861 if _, ok := localDevices[adj.SourceID]; !ok {
3862 continue
3863 }
3864 if _, ok := localDevices[adj.TargetID]; !ok {
3865 continue
3866 }
3867 directedCounts[adj.SourceID+"|"+adj.TargetID]++
3868 }
3869
3870 counted := make(map[string]struct{}, len(directedCounts))
3871 edges := 0
3872 for key, forwardCount := range directedCounts {
3873 parts := strings.SplitN(key, "|", 2)
3874 if len(parts) != 2 {
3875 continue
3876 }
3877
3878 left, right := parts[0], parts[1]
3879 canonical := left + "|" + right
3880 if right < left {
3881 canonical = right + "|" + left
3882 }
3883 if _, ok := counted[canonical]; ok {
3884 continue
3885 }
3886
3887 reverseCount := directedCounts[right+"|"+left]
3888 if forwardCount < reverseCount {
3889 edges += forwardCount
3890 } else {
3891 edges += reverseCount
3892 }
3893
3894 counted[canonical] = struct{}{}
3895 }
3896
3897 return edges
3898 }
3899
3900 func enrichmentByMAC(enrichments []l2topology.Enrichment) map[string]l2topology.Enrichment {
3901 byMAC := make(map[string]l2topology.Enrichment, len(enrichments))
3902 for _, enrichment := range enrichments {
3903 if enrichment.MAC == "" {
3904 continue
3905 }
3906 byMAC[enrichment.MAC] = enrichment
3907 }
3908 return byMAC
3909 }
3910
3911 func mustEnrichmentByMAC(t *testing.T, byMAC map[string]l2topology.Enrichment, mac string) l2topology.Enrichment {
3912 t.Helper()
3913 enrichment, ok := byMAC[mac]
3914 require.True(t, ok, "missing enrichment for mac=%s", mac)
3915 return enrichment
3916 }
3917
3918 func enrichmentContainsIP(enrichment l2topology.Enrichment, ip string) bool {
3919 for _, addr := range enrichment.IPs {
3920 if addr.String() == ip {
3921 return true
3922 }
3923 }
3924 return false
3925 }
3926
3927 func countEnrichmentIPs(enrichments []l2topology.Enrichment) int {
3928 total := 0
3929 for _, enrichment := range enrichments {
3930 total += len(enrichment.IPs)
3931 }
3932 return total
3933 }
3934
3935 func countUndirectedLocalDevicePairs(adjacencies []l2topology.Adjacency, protocol string, localDevices map[string]struct{}) int {
3936 pairs := make(map[string]struct{})
3937 for _, adj := range adjacencies {
3938 if adj.Protocol != protocol || adj.SourceID == adj.TargetID {
3939 continue
3940 }
3941 if _, ok := localDevices[adj.SourceID]; !ok {
3942 continue
3943 }
3944 if _, ok := localDevices[adj.TargetID]; !ok {
3945 continue
3946 }
3947 left, right := adj.SourceID, adj.TargetID
3948 if right < left {
3949 left, right = right, left
3950 }
3951 pairs[left+"|"+right] = struct{}{}
3952 }
3953 return len(pairs)
3954 }
3955
3956 func countLocalTopologyVertices(adjacencies []l2topology.Adjacency, protocol string, localDevices map[string]struct{}) int {
3957 vertices := make(map[string]struct{})
3958 for _, adj := range adjacencies {
3959 if adj.Protocol != protocol || adj.SourceID == adj.TargetID {
3960 continue
3961 }
3962 if _, ok := localDevices[adj.SourceID]; !ok {
3963 continue
3964 }
3965 if _, ok := localDevices[adj.TargetID]; !ok {
3966 continue
3967 }
3968 vertices[adj.SourceID] = struct{}{}
3969 vertices[adj.TargetID] = struct{}{}
3970 }
3971 return len(vertices)
3972 }