master
go 131 lines 4.18 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package l2topology
4
5 import (
6 "sort"
7 "strings"
8 )
9
10 func topologyNeighborCapabilitiesFromLabels(labels map[string]string) []string {
11 if len(labels) == 0 {
12 return nil
13 }
14 seen := make(map[string]struct{})
15 for _, key := range []string{"capabilities_enabled", "capabilities_supported", "capabilities"} {
16 for _, capability := range labelsCSVToSlice(labels, key) {
17 capability = strings.TrimSpace(capability)
18 if capability == "" {
19 continue
20 }
21 seen[capability] = struct{}{}
22 }
23 }
24 return sortedTopologySet(seen)
25 }
26
27 func buildTopologyPortNeighborStatus(protocol string, adj Adjacency, deviceByID map[string]Device) topologyPortNeighborStatus {
28 protocol = strings.ToLower(strings.TrimSpace(protocol))
29 targetID := strings.TrimSpace(adj.TargetID)
30
31 neighbor := topologyPortNeighborStatus{
32 Protocol: protocol,
33 RemoteDevice: targetID,
34 RemotePort: strings.TrimSpace(adj.TargetPort),
35 }
36 if targetID == "" {
37 return neighbor
38 }
39
40 remote, ok := deviceByID[targetID]
41 if !ok {
42 if protocol == "cdp" {
43 neighbor.RemoteIP = strings.TrimSpace(adj.Labels["remote_address_raw"])
44 }
45 return neighbor
46 }
47
48 if remoteName := strings.TrimSpace(remote.Hostname); remoteName != "" {
49 neighbor.RemoteDevice = remoteName
50 }
51 neighbor.RemoteIP = firstAddress(remote.Addresses)
52 neighbor.RemoteChassisID = strings.TrimSpace(remote.ChassisID)
53 neighbor.RemoteCapabilities = topologyNeighborCapabilitiesFromLabels(remote.Labels)
54 if neighbor.RemoteIP == "" && protocol == "cdp" {
55 neighbor.RemoteIP = strings.TrimSpace(adj.Labels["remote_address_raw"])
56 }
57 return neighbor
58 }
59
60 func topologyPortNeighborStatusKey(status topologyPortNeighborStatus) string {
61 protocol := strings.ToLower(strings.TrimSpace(status.Protocol))
62 remoteDevice := strings.ToLower(strings.TrimSpace(status.RemoteDevice))
63 remotePort := strings.ToLower(strings.TrimSpace(status.RemotePort))
64 remoteIP := strings.ToLower(strings.TrimSpace(status.RemoteIP))
65 remoteChassisID := normalizeMAC(status.RemoteChassisID)
66 if remoteChassisID == "" {
67 remoteChassisID = strings.ToLower(strings.TrimSpace(status.RemoteChassisID))
68 }
69
70 if protocol == "" && remoteDevice == "" && remotePort == "" && remoteIP == "" && remoteChassisID == "" {
71 return ""
72 }
73 return strings.Join([]string{
74 protocol,
75 remoteDevice,
76 remotePort,
77 remoteIP,
78 remoteChassisID,
79 }, keySep)
80 }
81
82 func sortedTopologyPortNeighbors(neighbors map[string]topologyPortNeighborStatus) []topologyPortNeighborStatus {
83 if len(neighbors) == 0 {
84 return nil
85 }
86 out := make([]topologyPortNeighborStatus, 0, len(neighbors))
87 for _, neighbor := range neighbors {
88 neighbor.Protocol = strings.ToLower(strings.TrimSpace(neighbor.Protocol))
89 neighbor.RemoteDevice = strings.TrimSpace(neighbor.RemoteDevice)
90 neighbor.RemotePort = strings.TrimSpace(neighbor.RemotePort)
91 neighbor.RemoteIP = strings.TrimSpace(neighbor.RemoteIP)
92 neighbor.RemoteChassisID = strings.TrimSpace(neighbor.RemoteChassisID)
93 neighbor.RemoteCapabilities = uniqueTopologyStrings(neighbor.RemoteCapabilities)
94 if topologyPortNeighborStatusKey(neighbor) == "" {
95 continue
96 }
97 out = append(out, neighbor)
98 }
99 sort.Slice(out, func(i, j int) bool {
100 left, right := out[i], out[j]
101 if left.Protocol != right.Protocol {
102 return left.Protocol < right.Protocol
103 }
104 if left.RemoteDevice != right.RemoteDevice {
105 return left.RemoteDevice < right.RemoteDevice
106 }
107 if left.RemotePort != right.RemotePort {
108 return left.RemotePort < right.RemotePort
109 }
110 if left.RemoteIP != right.RemoteIP {
111 return left.RemoteIP < right.RemoteIP
112 }
113 return left.RemoteChassisID < right.RemoteChassisID
114 })
115 if len(out) == 0 {
116 return nil
117 }
118 return out
119 }
120
121 func topologyPortNeighborStatusToAttributes(status topologyPortNeighborStatus) map[string]any {
122 attrs := map[string]any{
123 "protocol": strings.ToLower(strings.TrimSpace(status.Protocol)),
124 "remote_device": strings.TrimSpace(status.RemoteDevice),
125 "remote_port": strings.TrimSpace(status.RemotePort),
126 "remote_ip": strings.TrimSpace(status.RemoteIP),
127 "remote_chassis_id": strings.TrimSpace(status.RemoteChassisID),
128 "remote_capabilities": uniqueTopologyStrings(status.RemoteCapabilities),
129 }
130 return pruneTopologyAttributes(attrs)
131 }