| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | package l2topology |
| 4 | |
| 5 | import ( |
| 6 | "strings" |
| 7 | "time" |
| 8 | ) |
| 9 | |
| 10 | type l2BuildState struct { |
| 11 | devices map[string]Device |
| 12 | managedObservationByDeviceID map[string]bool |
| 13 | interfaces map[string]Interface |
| 14 | adjacencies map[string]Adjacency |
| 15 | attachments map[string]Attachment |
| 16 | enrichments map[string]*enrichmentAccumulator |
| 17 | ifNameByDeviceIfIndex map[string]string |
| 18 | |
| 19 | hostToID map[string]string |
| 20 | ipToID map[string]string |
| 21 | chassisToID map[string]string |
| 22 | macToID map[string]string |
| 23 | bridgeAddrToID map[string]string |
| 24 | |
| 25 | linksLLDP int |
| 26 | linksCDP int |
| 27 | linksSTP int |
| 28 | attachmentsFDB int |
| 29 | enrichmentsARPND int |
| 30 | |
| 31 | bridgeDomains map[string]struct{} |
| 32 | endpointIDs map[string]struct{} |
| 33 | } |
| 34 | |
| 35 | func newL2BuildState(observationCount int) *l2BuildState { |
| 36 | return &l2BuildState{ |
| 37 | devices: make(map[string]Device, observationCount), |
| 38 | managedObservationByDeviceID: make(map[string]bool, observationCount), |
| 39 | interfaces: make(map[string]Interface), |
| 40 | adjacencies: make(map[string]Adjacency), |
| 41 | attachments: make(map[string]Attachment), |
| 42 | enrichments: make(map[string]*enrichmentAccumulator), |
| 43 | ifNameByDeviceIfIndex: make(map[string]string), |
| 44 | hostToID: make(map[string]string, observationCount), |
| 45 | ipToID: make(map[string]string, observationCount), |
| 46 | chassisToID: make(map[string]string, observationCount), |
| 47 | macToID: make(map[string]string, observationCount), |
| 48 | bridgeAddrToID: make(map[string]string, observationCount), |
| 49 | bridgeDomains: make(map[string]struct{}), |
| 50 | endpointIDs: make(map[string]struct{}), |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | func (s *l2BuildState) refreshEndpointIndex() { |
| 55 | for endpointID := range s.endpointIDs { |
| 56 | delete(s.endpointIDs, endpointID) |
| 57 | } |
| 58 | for _, attachment := range s.attachments { |
| 59 | endpointID := strings.TrimSpace(attachment.EndpointID) |
| 60 | if endpointID == "" { |
| 61 | continue |
| 62 | } |
| 63 | s.endpointIDs[endpointID] = struct{}{} |
| 64 | } |
| 65 | for _, enrichment := range s.enrichments { |
| 66 | if enrichment == nil { |
| 67 | continue |
| 68 | } |
| 69 | endpointID := strings.TrimSpace(enrichment.EndpointID) |
| 70 | if endpointID == "" { |
| 71 | continue |
| 72 | } |
| 73 | s.endpointIDs[endpointID] = struct{}{} |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | func (s *l2BuildState) markManagedDevices() { |
| 78 | for id, dev := range s.devices { |
| 79 | if dev.Labels == nil { |
| 80 | dev.Labels = make(map[string]string) |
| 81 | } |
| 82 | if s.managedObservationByDeviceID[id] { |
| 83 | dev.Labels["inferred"] = "false" |
| 84 | } else { |
| 85 | dev.Labels["inferred"] = "true" |
| 86 | } |
| 87 | s.devices[id] = dev |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | func (s *l2BuildState) buildResult(identityAliasStats identityAliasReconcileStats, collectedAt time.Time) Result { |
| 92 | if !collectedAt.IsZero() { |
| 93 | collectedAt = collectedAt.UTC() |
| 94 | } |
| 95 | |
| 96 | stats := newL2ResultStats() |
| 97 | stats["devices_total"] = len(s.devices) |
| 98 | stats["links_total"] = len(s.adjacencies) |
| 99 | stats["links_lldp"] = s.linksLLDP |
| 100 | stats["links_cdp"] = s.linksCDP |
| 101 | stats["links_stp"] = s.linksSTP |
| 102 | stats["attachments_total"] = len(s.attachments) |
| 103 | stats["attachments_fdb"] = s.attachmentsFDB |
| 104 | stats["enrichments_total"] = len(s.enrichments) |
| 105 | stats["enrichments_arp_nd"] = s.enrichmentsARPND |
| 106 | stats["bridge_domains_total"] = len(s.bridgeDomains) |
| 107 | stats["endpoints_total"] = len(s.endpointIDs) |
| 108 | stats["identity_alias_endpoints_mapped"] = identityAliasStats.endpointsMapped |
| 109 | stats["identity_alias_endpoints_ambiguous_mac"] = identityAliasStats.endpointsAmbiguousMAC |
| 110 | stats["identity_alias_ips_merged"] = identityAliasStats.ipsMerged |
| 111 | stats["identity_alias_ips_conflict_skipped"] = identityAliasStats.ipsConflictSkipped |
| 112 | |
| 113 | return Result{ |
| 114 | CollectedAt: collectedAt, |
| 115 | Devices: sortedDevices(s.devices), |
| 116 | Interfaces: sortedInterfaces(s.interfaces), |
| 117 | Adjacencies: sortedAdjacencies(s.adjacencies), |
| 118 | Attachments: sortedAttachments(s.attachments), |
| 119 | Enrichments: sortedEnrichments(s.enrichments), |
| 120 | Stats: stats, |
| 121 | } |
| 122 | } |