master
go 230 lines 6.62 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package l2topology
4
5 import (
6 "testing"
7
8 "github.com/stretchr/testify/require"
9 )
10
11 func TestBuildBridgeDomainModel_MergesRootSetsLikeEnlinkdGetAllPersisted(t *testing.T) {
12 model := buildBridgeDomainModel(
13 []bridgeBridgeLinkRecord{
14 {
15 port: bridgePortRef{deviceID: "node-a", bridgePort: "10", ifIndex: 10},
16 designatedPort: bridgePortRef{deviceID: "node-b", bridgePort: "1", ifIndex: 1},
17 },
18 {
19 port: bridgePortRef{deviceID: "node-b", bridgePort: "2", ifIndex: 2},
20 designatedPort: bridgePortRef{deviceID: "node-c", bridgePort: "1", ifIndex: 1},
21 },
22 },
23 nil,
24 )
25
26 require.Len(t, model.domains, 1)
27 domain := model.domains[0]
28 require.Len(t, domain.bridges, 3)
29 require.True(t, domain.bridges["node-c"].root)
30 require.False(t, domain.bridges["node-a"].root)
31 require.False(t, domain.bridges["node-b"].root)
32 require.Len(t, domain.segments, 2)
33 }
34
35 func TestBuildBridgeDomainModel_AttachesMacsToMatchingBridgeSegments(t *testing.T) {
36 model := buildBridgeDomainModel(
37 []bridgeBridgeLinkRecord{
38 {
39 port: bridgePortRef{deviceID: "leaf", bridgePort: "2", ifIndex: 2, ifName: "Gi0/2"},
40 designatedPort: bridgePortRef{deviceID: "root", bridgePort: "1", ifIndex: 1, ifName: "Gi0/1"},
41 },
42 },
43 []bridgeMacLinkRecord{
44 {port: bridgePortRef{deviceID: "leaf", bridgePort: "2", ifIndex: 2, ifName: "Gi0/2"}, endpointID: "mac:00:11:22:33:44:55", method: "fdb"},
45 {port: bridgePortRef{deviceID: "leaf", bridgePort: "9", ifIndex: 9, ifName: "Gi0/9"}, endpointID: "mac:aa:bb:cc:dd:ee:ff", method: "fdb"},
46 },
47 )
48
49 require.Len(t, model.domains, 1)
50 domain := model.domains[0]
51 require.Len(t, domain.segments, 2)
52
53 var shared *bridgeDomainSegment
54 var standalone *bridgeDomainSegment
55 for _, segment := range domain.segments {
56 if segment == nil {
57 continue
58 }
59 if len(segment.ports) == 2 {
60 shared = segment
61 }
62 if len(segment.ports) == 1 {
63 standalone = segment
64 }
65 }
66
67 require.NotNil(t, shared)
68 require.Contains(t, shared.endpointIDs, "mac:00:11:22:33:44:55")
69 require.NotNil(t, standalone)
70 require.Contains(t, standalone.endpointIDs, "mac:aa:bb:cc:dd:ee:ff")
71 }
72
73 func TestMergeRootDomainSets_MergesNodeMemberIntoExistingDesignatedRoot(t *testing.T) {
74 rootToNodes := map[string]bridgeNodeSet{
75 "designated-root": {"leaf-a": {}},
76 "other-root": {"node-x": {}, "leaf-b": {}},
77 }
78
79 mergeRootDomainSets(rootToNodes, "designated-root", "node-x")
80
81 require.Len(t, rootToNodes, 1)
82 require.Contains(t, rootToNodes, "designated-root")
83 require.Equal(t, bridgeNodeSet{
84 "leaf-a": {},
85 "other-root": {},
86 "node-x": {},
87 "leaf-b": {},
88 }, rootToNodes["designated-root"])
89 }
90
91 func TestMergeRootDomainSets_MergesTwoNonRootMembersAcrossDomains(t *testing.T) {
92 rootToNodes := map[string]bridgeNodeSet{
93 "root-a": {"designated-member": {}, "leaf-a": {}},
94 "root-b": {"node-member": {}, "leaf-b": {}},
95 }
96
97 mergeRootDomainSets(rootToNodes, "designated-member", "node-member")
98
99 require.Len(t, rootToNodes, 1)
100 require.Contains(t, rootToNodes, "root-a")
101 require.Equal(t, bridgeNodeSet{
102 "designated-member": {},
103 "leaf-a": {},
104 "root-b": {},
105 "node-member": {},
106 "leaf-b": {},
107 }, rootToNodes["root-a"])
108 }
109
110 func TestCollectBridgeLinkRecords_DeduplicatesUndirectedAdjacencies(t *testing.T) {
111 records := collectBridgeLinkRecords([]Adjacency{
112 {
113 Protocol: "lldp",
114 SourceID: "a",
115 SourcePort: "Gi0/1",
116 TargetID: "b",
117 TargetPort: "Gi0/2",
118 },
119 {
120 Protocol: "lldp",
121 SourceID: "b",
122 SourcePort: "Gi0/2",
123 TargetID: "a",
124 TargetPort: "Gi0/1",
125 },
126 }, map[string]int{
127 deviceIfNameKey("a", "Gi0/1"): 1,
128 deviceIfNameKey("b", "Gi0/2"): 2,
129 }, topologyInferenceStrategyConfigFor(topologyInferenceStrategyFDBMinimumKnowledge))
130
131 require.Len(t, records, 1)
132 require.Equal(t, "a", records[0].designatedPort.deviceID)
133 require.Equal(t, "b", records[0].port.deviceID)
134 }
135
136 func TestCollectBridgeLinkRecords_SkipsAdjacencyWithoutRemotePort(t *testing.T) {
137 records := collectBridgeLinkRecords([]Adjacency{
138 {
139 Protocol: "lldp",
140 SourceID: "a",
141 SourcePort: "Gi0/1",
142 TargetID: "b",
143 TargetPort: "",
144 },
145 }, map[string]int{
146 deviceIfNameKey("a", "Gi0/1"): 1,
147 }, topologyInferenceStrategyConfigFor(topologyInferenceStrategyFDBMinimumKnowledge))
148
149 require.Empty(t, records)
150 }
151
152 func TestCollectBridgeLinkRecords_STPParentTreeUsesDesignatedTargetPort(t *testing.T) {
153 records := collectBridgeLinkRecords([]Adjacency{
154 {
155 Protocol: "stp",
156 SourceID: "child",
157 SourcePort: "Gi0/10",
158 TargetID: "root",
159 TargetPort: "Gi0/1",
160 },
161 }, map[string]int{
162 deviceIfNameKey("child", "Gi0/10"): 10,
163 deviceIfNameKey("root", "Gi0/1"): 1,
164 }, topologyInferenceStrategyConfigFor(topologyInferenceStrategySTPParentTree))
165
166 require.Len(t, records, 1)
167 require.Equal(t, "root", records[0].designatedPort.deviceID)
168 require.Equal(t, "child", records[0].port.deviceID)
169 require.Equal(t, "stp", records[0].method)
170 }
171
172 func TestCollectBridgeLinkRecords_CDPHybridSkipsLLDPAdjacencies(t *testing.T) {
173 records := collectBridgeLinkRecords([]Adjacency{
174 {
175 Protocol: "lldp",
176 SourceID: "a",
177 SourcePort: "Gi0/1",
178 TargetID: "b",
179 TargetPort: "Gi0/2",
180 },
181 {
182 Protocol: "cdp",
183 SourceID: "a",
184 SourcePort: "Gi0/3",
185 TargetID: "c",
186 TargetPort: "Gi0/4",
187 },
188 }, map[string]int{
189 deviceIfNameKey("a", "Gi0/1"): 1,
190 deviceIfNameKey("b", "Gi0/2"): 2,
191 deviceIfNameKey("a", "Gi0/3"): 3,
192 deviceIfNameKey("c", "Gi0/4"): 4,
193 }, topologyInferenceStrategyConfigFor(topologyInferenceStrategyCDPFDBHybrid))
194
195 require.Len(t, records, 1)
196 require.Equal(t, "cdp", records[0].method)
197 require.Equal(t, "a", records[0].designatedPort.deviceID)
198 require.Equal(t, "c", records[0].port.deviceID)
199 }
200
201 func TestInferFDBPairwiseBridgeLinks_ReciprocalUniquePortPerSide(t *testing.T) {
202 attachments := []Attachment{
203 {
204 DeviceID: "sw-a",
205 IfIndex: 1,
206 EndpointID: "mac:bb:bb:bb:bb:bb:bb",
207 Method: "fdb",
208 },
209 {
210 DeviceID: "sw-b",
211 IfIndex: 2,
212 EndpointID: "mac:aa:aa:aa:aa:aa:aa",
213 Method: "fdb",
214 },
215 }
216 ifaceByDeviceIndex := map[string]Interface{
217 deviceIfIndexKey("sw-a", 1): {DeviceID: "sw-a", IfIndex: 1, IfName: "Gi0/1"},
218 deviceIfIndexKey("sw-b", 2): {DeviceID: "sw-b", IfIndex: 2, IfName: "Gi0/2"},
219 }
220 reporterAliases := map[string][]string{
221 "sw-a": {"mac:aa:aa:aa:aa:aa:aa"},
222 "sw-b": {"mac:bb:bb:bb:bb:bb:bb"},
223 }
224
225 records := inferFDBPairwiseBridgeLinks(attachments, ifaceByDeviceIndex, reporterAliases)
226 require.Len(t, records, 1)
227 require.Equal(t, "fdb_pairwise", records[0].method)
228 require.Equal(t, "sw-a", records[0].designatedPort.deviceID)
229 require.Equal(t, "sw-b", records[0].port.deviceID)
230 }