master
go 189 lines 5.45 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package l2topology
4
5 import (
6 "strconv"
7 "strings"
8 )
9
10 func (b *deviceInterfaceSummaryBuilder) collectInterfaces() {
11 for _, iface := range b.interfaces {
12 deviceID := strings.TrimSpace(iface.DeviceID)
13 if deviceID == "" || iface.IfIndex <= 0 {
14 continue
15 }
16 col := b.collectors[deviceID]
17 if col == nil {
18 col = &deviceInterfaceCollector{
19 ifIndexes: make(map[string]struct{}),
20 ifNames: make(map[string]struct{}),
21 ifTypes: make(map[string]int),
22 adminCounts: make(map[string]int),
23 operCounts: make(map[string]int),
24 portEvidence: make(map[int]*topologyDevicePortEvidence),
25 }
26 b.collectors[deviceID] = col
27 }
28
29 ifIndex := strconv.Itoa(iface.IfIndex)
30 col.ifIndexes[ifIndex] = struct{}{}
31 if ifName := strings.TrimSpace(iface.IfName); ifName != "" {
32 col.ifNames[ifName] = struct{}{}
33 }
34
35 admin := strings.TrimSpace(iface.Labels["admin_status"])
36 oper := strings.TrimSpace(iface.Labels["oper_status"])
37 ifType := strings.TrimSpace(iface.Labels["if_type"])
38 ifAlias := strings.TrimSpace(iface.Labels["if_alias"])
39 ifDescr := strings.TrimSpace(iface.IfDescr)
40 if ifDescr == "" {
41 ifDescr = strings.TrimSpace(iface.IfName)
42 }
43 speedBps := parseTopologyLabelInt64(iface.Labels["speed_bps"])
44 lastChange := parseTopologyLabelInt64(iface.Labels["last_change"])
45 duplex := normalizeTopologyDuplex(iface.Labels["duplex"])
46 mac := normalizeMAC(iface.MAC)
47 if mac == "" {
48 mac = normalizeMAC(iface.Labels["mac"])
49 }
50 if ifType != "" {
51 col.ifTypes[ifType]++
52 }
53 if admin != "" {
54 col.adminCounts[admin]++
55 }
56 if oper != "" {
57 col.operCounts[oper]++
58 }
59
60 col.portStatuses = append(col.portStatuses, topologyDevicePortStatus{
61 IfIndex: iface.IfIndex,
62 IfName: strings.TrimSpace(iface.IfName),
63 IfDescr: ifDescr,
64 IfAlias: ifAlias,
65 MAC: mac,
66 SpeedBps: speedBps,
67 LastChange: lastChange,
68 Duplex: duplex,
69 InterfaceType: ifType,
70 AdminStatus: admin,
71 OperStatus: oper,
72 LinkMode: "unknown",
73 ModeConfidence: "low",
74 TopologyRole: "unknown",
75 RoleConfidence: "low",
76 })
77
78 if isTopologyLAGInterfaceType(ifType) {
79 evidence := ensureTopologyPortEvidence(col.portEvidence, iface.IfIndex)
80 evidence.isLAG = true
81 }
82 }
83 }
84
85 func (b *deviceInterfaceSummaryBuilder) collectFDBAttachments() {
86 for _, attachment := range b.attachments {
87 deviceID := strings.TrimSpace(attachment.DeviceID)
88 if deviceID == "" || attachment.IfIndex <= 0 {
89 continue
90 }
91 col := b.collectors[deviceID]
92 if col == nil {
93 continue
94 }
95 if !strings.EqualFold(strings.TrimSpace(attachment.Method), "fdb") {
96 continue
97 }
98 fdbStatus := strings.ToLower(strings.TrimSpace(attachment.Labels["fdb_status"]))
99 if fdbStatus == "ignored" {
100 continue
101 }
102
103 evidence := ensureTopologyPortEvidence(col.portEvidence, attachment.IfIndex)
104 evidence.hasFDB = true
105 endpointID := normalizeFDBEndpointID(attachment.EndpointID)
106 if endpointID == "" {
107 endpointID = strings.TrimSpace(attachment.EndpointID)
108 }
109 if endpointID != "" {
110 evidence.fdbEndpointIDs[endpointID] = struct{}{}
111 if aliasOwners, ok := b.managedAliasOwners[endpointID]; ok {
112 for aliasOwnerID := range aliasOwners {
113 if !strings.EqualFold(strings.TrimSpace(aliasOwnerID), deviceID) {
114 evidence.hasFDBManagedAlias = true
115 break
116 }
117 }
118 }
119 }
120 vlanID := normalizeTopologyVLANID(firstNonEmpty(attachment.Labels["vlan_id"], attachment.Labels["vlan"]))
121 if vlanID != "" {
122 evidence.vlanIDs[vlanID] = struct{}{}
123 if vlanName := strings.TrimSpace(attachment.Labels["vlan_name"]); vlanName != "" {
124 if _, exists := evidence.vlanNames[vlanID]; !exists {
125 evidence.vlanNames[vlanID] = vlanName
126 }
127 }
128 }
129 }
130 }
131
132 func (b *deviceInterfaceSummaryBuilder) collectAdjacencyEvidence() {
133 for _, adj := range b.adjacencies {
134 deviceID := strings.TrimSpace(adj.SourceID)
135 if deviceID == "" {
136 continue
137 }
138 col := b.collectors[deviceID]
139 if col == nil {
140 continue
141 }
142
143 ifIndex := resolveAdjacencySourceIfIndex(adj, b.ifIndexByDeviceName)
144 if ifIndex <= 0 {
145 continue
146 }
147 evidence := ensureTopologyPortEvidence(col.portEvidence, ifIndex)
148 protocol := strings.ToLower(strings.TrimSpace(adj.Protocol))
149 switch protocol {
150 case "stp":
151 evidence.hasSTP = true
152 vlanID := normalizeTopologyVLANID(firstNonEmpty(adj.Labels["vlan_id"], adj.Labels["vlan"]))
153 if vlanID != "" {
154 evidence.vlanIDs[vlanID] = struct{}{}
155 if vlanName := strings.TrimSpace(adj.Labels["vlan_name"]); vlanName != "" {
156 if _, exists := evidence.vlanNames[vlanID]; !exists {
157 evidence.vlanNames[vlanID] = vlanName
158 }
159 }
160 }
161 if state := normalizeTopologySTPState(adj.Labels["stp_state"]); state != "" {
162 evidence.stpStates[state] = struct{}{}
163 }
164 case "lldp", "cdp":
165 evidence.hasPeer = true
166 neighbor := buildTopologyPortNeighborStatus(protocol, adj, b.deviceByID)
167 if key := topologyPortNeighborStatusKey(neighbor); key != "" {
168 evidence.neighbors[key] = neighbor
169 }
170 }
171 }
172 }
173
174 func (b *deviceInterfaceSummaryBuilder) collectBridgeLinkEvidence() {
175 for _, link := range b.bridgeLinks {
176 for _, port := range []bridgePortRef{link.designatedPort, link.port} {
177 deviceID := strings.TrimSpace(port.deviceID)
178 if deviceID == "" || port.ifIndex <= 0 {
179 continue
180 }
181 col := b.collectors[deviceID]
182 if col == nil {
183 continue
184 }
185 evidence := ensureTopologyPortEvidence(col.portEvidence, port.ifIndex)
186 evidence.hasBridgeLink = true
187 }
188 }
189 }