master
go 140 lines 2.99 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package l2topology
4
5 import "sync"
6
7 // TopologyBuilderFunc builds the current topology snapshot.
8 type TopologyBuilderFunc func() (NetworkRouterTopology, error)
9
10 // TopologyUpdater ports non-routing runtime semantics from Enlinkd TopologyUpdater.
11 type TopologyUpdater struct {
12 mu sync.RWMutex
13
14 topology NetworkRouterTopology
15 builder TopologyBuilderFunc
16 parseFn func() bool
17 refreshFn func()
18 hasRun bool
19 forceRun bool
20 runFailed bool
21 lastFailed error
22 }
23
24 // NewTopologyUpdater builds an updater. parseFn and refreshFn are optional.
25 func NewTopologyUpdater(builder TopologyBuilderFunc, parseFn func() bool, refreshFn func()) *TopologyUpdater {
26 if builder == nil {
27 return &TopologyUpdater{}
28 }
29 return &TopologyUpdater{
30 builder: builder,
31 parseFn: parseFn,
32 refreshFn: refreshFn,
33 topology: NetworkRouterTopology{},
34 }
35 }
36
37 // RunSchedulable executes one discovery cycle.
38 func (u *TopologyUpdater) RunSchedulable() {
39 if u == nil || u.builder == nil {
40 return
41 }
42
43 u.mu.Lock()
44 hasRun := u.hasRun
45 forceRun := u.forceRun
46 if forceRun {
47 u.forceRun = false
48 }
49 u.mu.Unlock()
50
51 if !hasRun {
52 topology, err := u.builder()
53 u.mu.Lock()
54 defer u.mu.Unlock()
55 if err != nil {
56 u.runFailed = true
57 u.lastFailed = err
58 return
59 }
60 u.topology = CloneNetworkRouterTopology(topology)
61 u.hasRun = true
62 u.runFailed = false
63 u.lastFailed = nil
64 return
65 }
66
67 parseUpdates := false
68 if u.parseFn != nil {
69 parseUpdates = u.parseFn()
70 }
71 if !parseUpdates && !forceRun {
72 return
73 }
74 if u.refreshFn != nil {
75 u.refreshFn()
76 }
77
78 topology, err := u.builder()
79 u.mu.Lock()
80 defer u.mu.Unlock()
81 if err != nil {
82 u.runFailed = true
83 u.lastFailed = err
84 return
85 }
86 u.topology = CloneNetworkRouterTopology(topology)
87 u.runFailed = false
88 u.lastFailed = nil
89 }
90
91 // GetTopology returns a non-blocking clone of the current topology.
92 func (u *TopologyUpdater) GetTopology() NetworkRouterTopology {
93 if u == nil {
94 return NetworkRouterTopology{}
95 }
96 u.mu.RLock()
97 topology := CloneNetworkRouterTopology(u.topology)
98 u.mu.RUnlock()
99 return topology
100 }
101
102 // ForceRun requests recomputation on the next RunSchedulable call.
103 func (u *TopologyUpdater) ForceRun() {
104 if u == nil {
105 return
106 }
107 u.mu.Lock()
108 u.forceRun = true
109 u.mu.Unlock()
110 }
111
112 // HasRun reports if the first successful run completed.
113 func (u *TopologyUpdater) HasRun() bool {
114 if u == nil {
115 return false
116 }
117 u.mu.RLock()
118 defer u.mu.RUnlock()
119 return u.hasRun
120 }
121
122 // LastError returns the last builder error, if any.
123 func (u *TopologyUpdater) LastError() error {
124 if u == nil {
125 return nil
126 }
127 u.mu.RLock()
128 defer u.mu.RUnlock()
129 return u.lastFailed
130 }
131
132 // CloneNetworkRouterTopology deep-copies topology structures.
133 func CloneNetworkRouterTopology(topology NetworkRouterTopology) NetworkRouterTopology {
134 out := NetworkRouterTopology{
135 Vertices: append([]NetworkRouterVertex(nil), topology.Vertices...),
136 Edges: append([]NetworkRouterEdge(nil), topology.Edges...),
137 DefaultVertex: topology.DefaultVertex,
138 }
139 return out
140 }