Showing
9 changed files
with
551 additions
and
27 deletions
1 | +package org.onlab.onos.event; | ||
2 | + | ||
3 | +import com.google.common.collect.Lists; | ||
4 | + | ||
5 | +import java.util.List; | ||
6 | +import java.util.Timer; | ||
7 | +import java.util.TimerTask; | ||
8 | + | ||
9 | +import static com.google.common.base.Preconditions.checkArgument; | ||
10 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
11 | + | ||
12 | +/** | ||
13 | + * Base implementation of an event accumulator. It allows triggering based on | ||
14 | + * event inter-arrival time threshold, maximum batch life threshold and maximum | ||
15 | + * batch size. | ||
16 | + */ | ||
17 | +public abstract class AbstractEventAccumulator implements EventAccumulator { | ||
18 | + | ||
19 | + private final Timer timer; | ||
20 | + private final int maxEvents; | ||
21 | + private final int maxBatchMillis; | ||
22 | + private final int maxIdleMillis; | ||
23 | + | ||
24 | + private TimerTask idleTask = new ProcessorTask(); | ||
25 | + private TimerTask maxTask = new ProcessorTask(); | ||
26 | + | ||
27 | + private List<Event> events = Lists.newArrayList(); | ||
28 | + | ||
29 | + /** | ||
30 | + * Creates an event accumulator capable of triggering on the specified | ||
31 | + * thresholds. | ||
32 | + * | ||
33 | + * @param timer timer to use for scheduling check-points | ||
34 | + * @param maxEvents maximum number of events to accumulate before | ||
35 | + * processing is triggered | ||
36 | + * @param maxBatchMillis maximum number of millis allowed since the first | ||
37 | + * event before processing is triggered | ||
38 | + * @param maxIdleMillis maximum number millis between events before | ||
39 | + * processing is triggered | ||
40 | + */ | ||
41 | + protected AbstractEventAccumulator(Timer timer, int maxEvents, | ||
42 | + int maxBatchMillis, int maxIdleMillis) { | ||
43 | + this.timer = checkNotNull(timer, "Timer cannot be null"); | ||
44 | + | ||
45 | + checkArgument(maxEvents > 1, "Maximum number of events must be > 1"); | ||
46 | + checkArgument(maxBatchMillis > 0, "Maximum millis must be positive"); | ||
47 | + checkArgument(maxIdleMillis > 0, "Maximum idle millis must be positive"); | ||
48 | + | ||
49 | + this.maxEvents = maxEvents; | ||
50 | + this.maxBatchMillis = maxBatchMillis; | ||
51 | + this.maxIdleMillis = maxIdleMillis; | ||
52 | + } | ||
53 | + | ||
54 | + @Override | ||
55 | + public void add(Event event) { | ||
56 | + idleTask = cancelIfActive(idleTask); | ||
57 | + events.add(event); | ||
58 | + | ||
59 | + // Did we hit the max event threshold? | ||
60 | + if (events.size() == maxEvents) { | ||
61 | + maxTask = cancelIfActive(maxTask); | ||
62 | + schedule(1); | ||
63 | + } else { | ||
64 | + // Otherwise, schedule idle task and if this is a first event | ||
65 | + // also schedule the max batch age task. | ||
66 | + idleTask = schedule(maxIdleMillis); | ||
67 | + if (events.size() == 1) { | ||
68 | + maxTask = schedule(maxBatchMillis); | ||
69 | + } | ||
70 | + } | ||
71 | + } | ||
72 | + | ||
73 | + // Schedules a new processor task given number of millis in the future. | ||
74 | + private TimerTask schedule(int millis) { | ||
75 | + TimerTask task = new ProcessorTask(); | ||
76 | + timer.schedule(task, millis); | ||
77 | + return task; | ||
78 | + } | ||
79 | + | ||
80 | + // Cancels the specified task if it is active. | ||
81 | + private TimerTask cancelIfActive(TimerTask task) { | ||
82 | + if (task != null) { | ||
83 | + task.cancel(); | ||
84 | + } | ||
85 | + return task; | ||
86 | + } | ||
87 | + | ||
88 | + // Task for triggering processing of accumulated events | ||
89 | + private class ProcessorTask extends TimerTask { | ||
90 | + @Override | ||
91 | + public void run() { | ||
92 | + idleTask = cancelIfActive(idleTask); | ||
93 | + maxTask = cancelIfActive(maxTask); | ||
94 | + processEvents(finalizeCurrentBatch()); | ||
95 | + } | ||
96 | + } | ||
97 | + | ||
98 | + // Demotes and returns the current batch of events and promotes a new one. | ||
99 | + private synchronized List<Event> finalizeCurrentBatch() { | ||
100 | + List<Event> toBeProcessed = events; | ||
101 | + events = Lists.newArrayList(); | ||
102 | + return toBeProcessed; | ||
103 | + } | ||
104 | + | ||
105 | + /** | ||
106 | + * Returns the backing timer. | ||
107 | + * | ||
108 | + * @return backing timer | ||
109 | + */ | ||
110 | + public Timer timer() { | ||
111 | + return timer; | ||
112 | + } | ||
113 | + | ||
114 | + /** | ||
115 | + * Returns the maximum number of events allowed to accumulate before | ||
116 | + * processing is triggered. | ||
117 | + * | ||
118 | + * @return max number of events | ||
119 | + */ | ||
120 | + public int maxEvents() { | ||
121 | + return maxEvents; | ||
122 | + } | ||
123 | + | ||
124 | + /** | ||
125 | + * Returns the maximum number of millis allowed to expire since the first | ||
126 | + * event before processing is triggered. | ||
127 | + * | ||
128 | + * @return max number of millis a batch is allowed to last | ||
129 | + */ | ||
130 | + public int maxBatchMillis() { | ||
131 | + return maxBatchMillis; | ||
132 | + } | ||
133 | + | ||
134 | + /** | ||
135 | + * Returns the maximum number of millis allowed to expire since the last | ||
136 | + * event arrival before processing is triggered. | ||
137 | + * | ||
138 | + * @return max number of millis since the last event | ||
139 | + */ | ||
140 | + public int maxIdleMillis() { | ||
141 | + return maxIdleMillis; | ||
142 | + } | ||
143 | +} |
1 | +package org.onlab.onos.event; | ||
2 | + | ||
3 | +import java.util.List; | ||
4 | + | ||
5 | +/** | ||
6 | + * Abstraction of an accumulator capable of collecting events and at some | ||
7 | + * point in time triggers processing of all previously accumulated events. | ||
8 | + */ | ||
9 | +public interface EventAccumulator { | ||
10 | + | ||
11 | + /** | ||
12 | + * Adds an event to the current batch. This operation may, or may not | ||
13 | + * trigger processing of the current batch of events. | ||
14 | + * | ||
15 | + * @param event event to be added to the current batch | ||
16 | + */ | ||
17 | + void add(Event event); | ||
18 | + | ||
19 | + /** | ||
20 | + * Processes the specified list of accumulated events. | ||
21 | + * | ||
22 | + * @param events list of accumulated events | ||
23 | + */ | ||
24 | + void processEvents(List<Event> events); | ||
25 | + | ||
26 | +} |
1 | package org.onlab.onos.net.trivial.impl; | 1 | package org.onlab.onos.net.trivial.impl; |
2 | 2 | ||
3 | import com.google.common.collect.ImmutableSet; | 3 | import com.google.common.collect.ImmutableSet; |
4 | -import com.google.common.collect.Multimap; | 4 | +import com.google.common.collect.Maps; |
5 | +import com.google.common.collect.Sets; | ||
6 | +import org.onlab.graph.AdjacencyListsGraph; | ||
7 | +import org.onlab.graph.DijkstraGraphSearch; | ||
5 | import org.onlab.graph.Graph; | 8 | import org.onlab.graph.Graph; |
6 | import org.onlab.graph.GraphPathSearch; | 9 | import org.onlab.graph.GraphPathSearch; |
10 | +import org.onlab.onos.net.ConnectPoint; | ||
11 | +import org.onlab.onos.net.Device; | ||
7 | import org.onlab.onos.net.DeviceId; | 12 | import org.onlab.onos.net.DeviceId; |
8 | import org.onlab.onos.net.Link; | 13 | import org.onlab.onos.net.Link; |
9 | import org.onlab.onos.net.topology.ClusterId; | 14 | import org.onlab.onos.net.topology.ClusterId; |
15 | +import org.onlab.onos.net.topology.LinkWeight; | ||
10 | import org.onlab.onos.net.topology.TopoEdge; | 16 | import org.onlab.onos.net.topology.TopoEdge; |
11 | import org.onlab.onos.net.topology.TopoVertex; | 17 | import org.onlab.onos.net.topology.TopoVertex; |
12 | import org.onlab.onos.net.topology.TopologyCluster; | 18 | import org.onlab.onos.net.topology.TopologyCluster; |
13 | import org.onlab.onos.net.topology.TopologyDescription; | 19 | import org.onlab.onos.net.topology.TopologyDescription; |
14 | 20 | ||
15 | import java.util.Map; | 21 | import java.util.Map; |
22 | +import java.util.Objects; | ||
16 | import java.util.Set; | 23 | import java.util.Set; |
17 | 24 | ||
25 | +import static com.google.common.base.MoreObjects.toStringHelper; | ||
26 | +import static org.onlab.graph.GraphPathSearch.Result; | ||
27 | +import static org.onlab.onos.net.Link.Type.INDIRECT; | ||
28 | + | ||
18 | /** | 29 | /** |
19 | * Default implementation of an immutable topology data carrier. | 30 | * Default implementation of an immutable topology data carrier. |
20 | */ | 31 | */ |
21 | -public class DefaultTopologyDescription implements TopologyDescription { | 32 | +class DefaultTopologyDescription implements TopologyDescription { |
33 | + | ||
34 | + private static final GraphPathSearch<TopoVertex, TopoEdge> DIJKSTRA = | ||
35 | + new DijkstraGraphSearch<>(); | ||
22 | 36 | ||
23 | private final long nanos; | 37 | private final long nanos; |
38 | + private final Map<DeviceId, TopoVertex> vertexesById = Maps.newHashMap(); | ||
24 | private final Graph<TopoVertex, TopoEdge> graph; | 39 | private final Graph<TopoVertex, TopoEdge> graph; |
25 | - private final Map<DeviceId, GraphPathSearch.Result<TopoVertex, TopoEdge>> results; | 40 | + private final Map<DeviceId, Result<TopoVertex, TopoEdge>> results; |
26 | private final Map<ClusterId, TopologyCluster> clusters; | 41 | private final Map<ClusterId, TopologyCluster> clusters; |
27 | - private final Multimap<ClusterId, DeviceId> clusterDevices; | 42 | +// private final Multimap<ClusterId, DeviceId> clusterDevices; |
28 | - private final Multimap<ClusterId, Link> clusterLinks; | 43 | +// private final Multimap<ClusterId, Link> clusterLinks; |
29 | - private final Map<DeviceId, TopologyCluster> deviceClusters; | 44 | +// private final Map<DeviceId, TopologyCluster> deviceClusters; |
30 | - | 45 | + |
31 | - public DefaultTopologyDescription(long nanos, Graph<TopoVertex, TopoEdge> graph, | 46 | + |
32 | - Map<DeviceId, GraphPathSearch.Result<TopoVertex, TopoEdge>> results, | 47 | + DefaultTopologyDescription(long nanos, Iterable<Device> devices, Iterable<Link> links) { |
33 | - Map<ClusterId, TopologyCluster> clusters, | ||
34 | - Multimap<ClusterId, DeviceId> clusterDevices, | ||
35 | - Multimap<ClusterId, Link> clusterLinks, | ||
36 | - Map<DeviceId, TopologyCluster> deviceClusters) { | ||
37 | this.nanos = nanos; | 48 | this.nanos = nanos; |
38 | - this.graph = graph; | 49 | + this.graph = buildGraph(devices, links); |
39 | - this.results = results; | 50 | + this.results = computeDefaultPaths(); |
40 | - this.clusters = clusters; | 51 | + this.clusters = computeClusters(); |
41 | - this.clusterDevices = clusterDevices; | 52 | +// this.clusterDevices = clusterDevices; |
42 | - this.clusterLinks = clusterLinks; | 53 | +// this.clusterLinks = clusterLinks; |
43 | - this.deviceClusters = deviceClusters; | 54 | +// this.deviceClusters = deviceClusters; |
55 | + } | ||
56 | + | ||
57 | + // Constructs the topology graph using the supplied devices and links. | ||
58 | + private Graph<TopoVertex, TopoEdge> buildGraph(Iterable<Device> devices, | ||
59 | + Iterable<Link> links) { | ||
60 | + Graph<TopoVertex, TopoEdge> graph = | ||
61 | + new AdjacencyListsGraph<>(buildVertexes(devices), | ||
62 | + buildEdges(links)); | ||
63 | + return graph; | ||
64 | + } | ||
65 | + | ||
66 | + // Builds a set of topology vertexes from the specified list of devices | ||
67 | + private Set<TopoVertex> buildVertexes(Iterable<Device> devices) { | ||
68 | + Set<TopoVertex> vertexes = Sets.newHashSet(); | ||
69 | + for (Device device : devices) { | ||
70 | + TopoVertex vertex = new TVertex(device.id()); | ||
71 | + vertexesById.put(vertex.deviceId(), vertex); | ||
72 | + vertexes.add(vertex); | ||
73 | + } | ||
74 | + return vertexes; | ||
75 | + } | ||
76 | + | ||
77 | + // Builds a set of topology vertexes from the specified list of links | ||
78 | + private Set<TopoEdge> buildEdges(Iterable<Link> links) { | ||
79 | + Set<TopoEdge> edges = Sets.newHashSet(); | ||
80 | + for (Link link : links) { | ||
81 | + edges.add(new TEdge(vertexOf(link.src()), vertexOf(link.dst()), link)); | ||
82 | + } | ||
83 | + return edges; | ||
84 | + } | ||
85 | + | ||
86 | + // Computes the default shortest paths for all source/dest pairs using | ||
87 | + // the multi-path Dijkstra and hop-count as path cost. | ||
88 | + private Map<DeviceId, Result<TopoVertex, TopoEdge>> computeDefaultPaths() { | ||
89 | + LinkWeight weight = new HopCountLinkWeight(graph.getVertexes().size()); | ||
90 | + Map<DeviceId, Result<TopoVertex, TopoEdge>> results = Maps.newHashMap(); | ||
91 | + | ||
92 | + // Search graph paths for each source to all destinations. | ||
93 | + for (TopoVertex src : vertexesById.values()) { | ||
94 | + results.put(src.deviceId(), DIJKSTRA.search(graph, src, null, weight)); | ||
95 | + } | ||
96 | + return results; | ||
97 | + } | ||
98 | + | ||
99 | + // Computes topology SCC clusters using Tarjan algorithm. | ||
100 | + private Map<ClusterId, TopologyCluster> computeClusters() { | ||
101 | + Map<ClusterId, TopologyCluster> clusters = Maps.newHashMap(); | ||
102 | + return clusters; | ||
103 | + } | ||
104 | + | ||
105 | + // Fetches a vertex corresponding to the given connection point device. | ||
106 | + private TopoVertex vertexOf(ConnectPoint connectPoint) { | ||
107 | + DeviceId id = connectPoint.deviceId(); | ||
108 | + TopoVertex vertex = vertexesById.get(id); | ||
109 | + if (vertex == null) { | ||
110 | + // If vertex does not exist, create one and register it. | ||
111 | + vertex = new TVertex(id); | ||
112 | + vertexesById.put(id, vertex); | ||
113 | + } | ||
114 | + return vertex; | ||
44 | } | 115 | } |
45 | 116 | ||
46 | @Override | 117 | @Override |
... | @@ -54,7 +125,7 @@ public class DefaultTopologyDescription implements TopologyDescription { | ... | @@ -54,7 +125,7 @@ public class DefaultTopologyDescription implements TopologyDescription { |
54 | } | 125 | } |
55 | 126 | ||
56 | @Override | 127 | @Override |
57 | - public GraphPathSearch.Result<TopoVertex, TopoEdge> pathResults(DeviceId srcDeviceId) { | 128 | + public Result<TopoVertex, TopoEdge> pathResults(DeviceId srcDeviceId) { |
58 | return results.get(srcDeviceId); | 129 | return results.get(srcDeviceId); |
59 | } | 130 | } |
60 | 131 | ||
... | @@ -75,6 +146,105 @@ public class DefaultTopologyDescription implements TopologyDescription { | ... | @@ -75,6 +146,105 @@ public class DefaultTopologyDescription implements TopologyDescription { |
75 | 146 | ||
76 | @Override | 147 | @Override |
77 | public TopologyCluster clusterFor(DeviceId deviceId) { | 148 | public TopologyCluster clusterFor(DeviceId deviceId) { |
78 | - return deviceClusters.get(deviceId); | 149 | + return null; // deviceClusters.get(deviceId); |
150 | + } | ||
151 | + | ||
152 | + // Implementation of the topology vertex backed by a device id | ||
153 | + private static class TVertex implements TopoVertex { | ||
154 | + | ||
155 | + private final DeviceId deviceId; | ||
156 | + | ||
157 | + public TVertex(DeviceId deviceId) { | ||
158 | + this.deviceId = deviceId; | ||
159 | + } | ||
160 | + | ||
161 | + @Override | ||
162 | + public DeviceId deviceId() { | ||
163 | + return deviceId; | ||
164 | + } | ||
165 | + | ||
166 | + @Override | ||
167 | + public int hashCode() { | ||
168 | + return Objects.hash(deviceId); | ||
169 | + } | ||
170 | + | ||
171 | + @Override | ||
172 | + public boolean equals(Object obj) { | ||
173 | + if (obj instanceof TVertex) { | ||
174 | + final TVertex other = (TVertex) obj; | ||
175 | + return Objects.equals(this.deviceId, other.deviceId); | ||
176 | + } | ||
177 | + return false; | ||
178 | + } | ||
179 | + | ||
180 | + @Override | ||
181 | + public String toString() { | ||
182 | + return deviceId.toString(); | ||
183 | + } | ||
184 | + } | ||
185 | + | ||
186 | + // Implementation of the topology edge backed by a link | ||
187 | + private class TEdge implements TopoEdge { | ||
188 | + private final Link link; | ||
189 | + private final TopoVertex src; | ||
190 | + private final TopoVertex dst; | ||
191 | + | ||
192 | + public TEdge(TopoVertex src, TopoVertex dst, Link link) { | ||
193 | + this.src = src; | ||
194 | + this.dst = dst; | ||
195 | + this.link = link; | ||
196 | + } | ||
197 | + | ||
198 | + @Override | ||
199 | + public Link link() { | ||
200 | + return link; | ||
201 | + } | ||
202 | + | ||
203 | + @Override | ||
204 | + public TopoVertex src() { | ||
205 | + return src; | ||
206 | + } | ||
207 | + | ||
208 | + @Override | ||
209 | + public TopoVertex dst() { | ||
210 | + return dst; | ||
211 | + } | ||
212 | + | ||
213 | + @Override | ||
214 | + public int hashCode() { | ||
215 | + return Objects.hash(link); | ||
216 | + } | ||
217 | + | ||
218 | + @Override | ||
219 | + public boolean equals(Object obj) { | ||
220 | + if (obj instanceof TEdge) { | ||
221 | + final TEdge other = (TEdge) obj; | ||
222 | + return Objects.equals(this.link, other.link); | ||
223 | + } | ||
224 | + return false; | ||
225 | + } | ||
226 | + | ||
227 | + @Override | ||
228 | + public String toString() { | ||
229 | + return toStringHelper(this).add("src", src).add("dst", dst).toString(); | ||
230 | + } | ||
79 | } | 231 | } |
232 | + | ||
233 | + // Link weight for measuring link cost as hop count with indirect links | ||
234 | + // being as expensive as traversing the entire graph to assume the worst. | ||
235 | + private class HopCountLinkWeight implements LinkWeight { | ||
236 | + private final int indirectLinkCost; | ||
237 | + | ||
238 | + public HopCountLinkWeight(int indirectLinkCost) { | ||
239 | + this.indirectLinkCost = indirectLinkCost; | ||
240 | + } | ||
241 | + | ||
242 | + @Override | ||
243 | + public double weight(TopoEdge edge) { | ||
244 | + // To force preference to use direct paths first, make indirect | ||
245 | + // links as expensive as the linear vertex traversal. | ||
246 | + return edge.link().type() == INDIRECT ? indirectLinkCost : 1; | ||
247 | + } | ||
248 | + } | ||
249 | + | ||
80 | } | 250 | } | ... | ... |
... | @@ -28,7 +28,8 @@ import static com.google.common.base.Preconditions.checkArgument; | ... | @@ -28,7 +28,8 @@ import static com.google.common.base.Preconditions.checkArgument; |
28 | import static org.onlab.onos.net.device.DeviceEvent.Type.*; | 28 | import static org.onlab.onos.net.device.DeviceEvent.Type.*; |
29 | 29 | ||
30 | /** | 30 | /** |
31 | - | 31 | + * Manages inventory of infrastructure DEVICES using trivial in-memory |
32 | + * structures implementation. | ||
32 | */ | 33 | */ |
33 | class SimpleDeviceStore { | 34 | class SimpleDeviceStore { |
34 | 35 | ... | ... |
... | @@ -25,7 +25,7 @@ import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED; | ... | @@ -25,7 +25,7 @@ import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED; |
25 | import static org.onlab.onos.net.link.LinkEvent.Type.LINK_UPDATED; | 25 | import static org.onlab.onos.net.link.LinkEvent.Type.LINK_UPDATED; |
26 | 26 | ||
27 | /** | 27 | /** |
28 | - * Manages inventory of infrastructure links using trivial in-memory link | 28 | + * Manages inventory of infrastructure links using trivial in-memory structures |
29 | * implementation. | 29 | * implementation. |
30 | */ | 30 | */ |
31 | class SimpleLinkStore { | 31 | class SimpleLinkStore { | ... | ... |
... | @@ -153,6 +153,9 @@ public class SimpleTopologyManager | ... | @@ -153,6 +153,9 @@ public class SimpleTopologyManager |
153 | public void topologyChanged(TopologyDescription topoDescription, | 153 | public void topologyChanged(TopologyDescription topoDescription, |
154 | List<Event> reasons) { | 154 | List<Event> reasons) { |
155 | checkNotNull(topoDescription, "Topology description cannot be null"); | 155 | checkNotNull(topoDescription, "Topology description cannot be null"); |
156 | + | ||
157 | + log.info("Topology changed due to: {}", // to be removed soon | ||
158 | + reasons == null ? "initial compute" : reasons); | ||
156 | TopologyEvent event = store.updateTopology(topoDescription, reasons); | 159 | TopologyEvent event = store.updateTopology(topoDescription, reasons); |
157 | if (event != null) { | 160 | if (event != null) { |
158 | log.info("Topology changed due to: {}", | 161 | log.info("Topology changed due to: {}", | ... | ... |
1 | +package org.onlab.onos.net.trivial.impl; | ||
2 | + | ||
3 | +import org.apache.felix.scr.annotations.Activate; | ||
4 | +import org.apache.felix.scr.annotations.Component; | ||
5 | +import org.apache.felix.scr.annotations.Deactivate; | ||
6 | +import org.apache.felix.scr.annotations.Reference; | ||
7 | +import org.apache.felix.scr.annotations.ReferenceCardinality; | ||
8 | +import org.onlab.onos.event.AbstractEventAccumulator; | ||
9 | +import org.onlab.onos.event.Event; | ||
10 | +import org.onlab.onos.event.EventAccumulator; | ||
11 | +import org.onlab.onos.net.device.DeviceEvent; | ||
12 | +import org.onlab.onos.net.device.DeviceListener; | ||
13 | +import org.onlab.onos.net.device.DeviceService; | ||
14 | +import org.onlab.onos.net.link.LinkEvent; | ||
15 | +import org.onlab.onos.net.link.LinkListener; | ||
16 | +import org.onlab.onos.net.link.LinkService; | ||
17 | +import org.onlab.onos.net.provider.AbstractProvider; | ||
18 | +import org.onlab.onos.net.provider.ProviderId; | ||
19 | +import org.onlab.onos.net.topology.TopologyDescription; | ||
20 | +import org.onlab.onos.net.topology.TopologyProvider; | ||
21 | +import org.onlab.onos.net.topology.TopologyProviderRegistry; | ||
22 | +import org.onlab.onos.net.topology.TopologyProviderService; | ||
23 | +import org.slf4j.Logger; | ||
24 | + | ||
25 | +import java.util.List; | ||
26 | +import java.util.Timer; | ||
27 | +import java.util.concurrent.ExecutorService; | ||
28 | + | ||
29 | +import static java.util.concurrent.Executors.newFixedThreadPool; | ||
30 | +import static org.onlab.onos.net.device.DeviceEvent.Type.*; | ||
31 | +import static org.onlab.util.Tools.namedThreads; | ||
32 | +import static org.slf4j.LoggerFactory.getLogger; | ||
33 | + | ||
34 | +/** | ||
35 | + * Simple implementation of a network topology provider/computor. | ||
36 | + */ | ||
37 | +@Component(immediate = true) | ||
38 | +public class SimpleTopologyProvider extends AbstractProvider | ||
39 | + implements TopologyProvider { | ||
40 | + | ||
41 | + // TODO: make these configurable | ||
42 | + private static final int MAX_EVENTS = 100; | ||
43 | + private static final int MAX_IDLE_MS = 50; | ||
44 | + private static final int MAX_BATCH_MS = 200; | ||
45 | + private static final int MAX_THREADS = 8; | ||
46 | + | ||
47 | + // FIXME: Replace with a system-wide timer instance | ||
48 | + private static final Timer TIMER = new Timer(); | ||
49 | + | ||
50 | + private final Logger log = getLogger(getClass()); | ||
51 | + | ||
52 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
53 | + protected TopologyProviderRegistry providerRegistry; | ||
54 | + | ||
55 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
56 | + protected DeviceService deviceService; | ||
57 | + | ||
58 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
59 | + protected LinkService linkService; | ||
60 | + | ||
61 | + private volatile boolean isStarted = false; | ||
62 | + | ||
63 | + private TopologyProviderService providerService; | ||
64 | + private DeviceListener deviceListener = new InnerDeviceListener(); | ||
65 | + private LinkListener linkListener = new InnerLinkListener(); | ||
66 | + | ||
67 | + private EventAccumulator accumulator; | ||
68 | + private ExecutorService executor; | ||
69 | + | ||
70 | + /** | ||
71 | + * Creates a provider with the supplier identifier. | ||
72 | + */ | ||
73 | + public SimpleTopologyProvider() { | ||
74 | + super(new ProviderId("org.onlab.onos.provider.topology")); | ||
75 | + } | ||
76 | + | ||
77 | + @Activate | ||
78 | + public synchronized void activate() { | ||
79 | + executor = newFixedThreadPool(MAX_THREADS, namedThreads("topo-compute-%d")); | ||
80 | + accumulator = new TopologyChangeAccumulator(); | ||
81 | + | ||
82 | + providerService = providerRegistry.register(this); | ||
83 | + deviceService.addListener(deviceListener); | ||
84 | + linkService.addListener(linkListener); | ||
85 | + | ||
86 | + isStarted = true; | ||
87 | + triggerTopologyBuild(null); | ||
88 | + log.info("Started"); | ||
89 | + } | ||
90 | + | ||
91 | + @Deactivate | ||
92 | + public synchronized void deactivate() { | ||
93 | + deviceService.removeListener(deviceListener); | ||
94 | + linkService.removeListener(linkListener); | ||
95 | + providerRegistry.unregister(this); | ||
96 | + providerService = null; | ||
97 | + | ||
98 | + executor.shutdownNow(); | ||
99 | + executor = null; | ||
100 | + | ||
101 | + isStarted = false; | ||
102 | + log.info("Stopped"); | ||
103 | + } | ||
104 | + | ||
105 | + /** | ||
106 | + * Triggers assembly of topology data citing the specified events as the | ||
107 | + * reason. | ||
108 | + * | ||
109 | + * @param reasons events which triggered the topology change | ||
110 | + */ | ||
111 | + private void triggerTopologyBuild(List<Event> reasons) { | ||
112 | + executor.execute(new TopologyBuilderTask(reasons)); | ||
113 | + } | ||
114 | + | ||
115 | + // Builds the topology using the latest device and link information | ||
116 | + // and citing the specified events as reasons for the change. | ||
117 | + private void buildTopology(List<Event> reasons) { | ||
118 | + log.info("YO! Computing topology"); | ||
119 | + if (isStarted) { | ||
120 | + TopologyDescription desc = | ||
121 | + new DefaultTopologyDescription(System.nanoTime(), | ||
122 | + deviceService.getDevices(), | ||
123 | + linkService.getLinks()); | ||
124 | + providerService.topologyChanged(desc, reasons); | ||
125 | + } | ||
126 | + } | ||
127 | + | ||
128 | + // Callback for device events | ||
129 | + private class InnerDeviceListener implements DeviceListener { | ||
130 | + @Override | ||
131 | + public void event(DeviceEvent event) { | ||
132 | + DeviceEvent.Type type = event.type(); | ||
133 | + if (type == DEVICE_ADDED || type == DEVICE_REMOVED || | ||
134 | + type == DEVICE_AVAILABILITY_CHANGED) { | ||
135 | + accumulator.add(event); | ||
136 | + } | ||
137 | + } | ||
138 | + } | ||
139 | + | ||
140 | + // Callback for link events | ||
141 | + private class InnerLinkListener implements LinkListener { | ||
142 | + @Override | ||
143 | + public void event(LinkEvent event) { | ||
144 | + accumulator.add(event); | ||
145 | + } | ||
146 | + } | ||
147 | + | ||
148 | + // Event accumulator for paced triggering of topology assembly. | ||
149 | + private class TopologyChangeAccumulator | ||
150 | + extends AbstractEventAccumulator implements EventAccumulator { | ||
151 | + | ||
152 | + TopologyChangeAccumulator() { | ||
153 | + super(TIMER, MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS); | ||
154 | + } | ||
155 | + | ||
156 | + @Override | ||
157 | + public void processEvents(List<Event> events) { | ||
158 | + triggerTopologyBuild(events); | ||
159 | + } | ||
160 | + | ||
161 | + } | ||
162 | + | ||
163 | + // Task for building topology data in a separate thread. | ||
164 | + private class TopologyBuilderTask implements Runnable { | ||
165 | + private final List<Event> reasons; | ||
166 | + | ||
167 | + public TopologyBuilderTask(List<Event> reasons) { | ||
168 | + this.reasons = reasons; | ||
169 | + } | ||
170 | + | ||
171 | + @Override | ||
172 | + public void run() { | ||
173 | + buildTopology(reasons); | ||
174 | + } | ||
175 | + } | ||
176 | + | ||
177 | +} |
... | @@ -18,9 +18,9 @@ import java.util.Set; | ... | @@ -18,9 +18,9 @@ import java.util.Set; |
18 | 18 | ||
19 | /** | 19 | /** |
20 | * Manages inventory of topology snapshots using trivial in-memory | 20 | * Manages inventory of topology snapshots using trivial in-memory |
21 | - * implementation. | 21 | + * structures implementation. |
22 | */ | 22 | */ |
23 | -public class SimpleTopologyStore { | 23 | +class SimpleTopologyStore { |
24 | 24 | ||
25 | private volatile DefaultTopology current; | 25 | private volatile DefaultTopology current; |
26 | 26 | ||
... | @@ -35,10 +35,12 @@ public class SimpleTopologyStore { | ... | @@ -35,10 +35,12 @@ public class SimpleTopologyStore { |
35 | 35 | ||
36 | /** | 36 | /** |
37 | * Indicates whether the topology is the latest. | 37 | * Indicates whether the topology is the latest. |
38 | + * | ||
38 | * @param topology topology descriptor | 39 | * @param topology topology descriptor |
39 | * @return true if topology is the most recent one | 40 | * @return true if topology is the most recent one |
40 | */ | 41 | */ |
41 | boolean isLatest(Topology topology) { | 42 | boolean isLatest(Topology topology) { |
43 | + // Topology is current only if it is the same as our current topology | ||
42 | return topology == current; | 44 | return topology == current; |
43 | } | 45 | } |
44 | 46 | ||
... | @@ -117,7 +119,8 @@ public class SimpleTopologyStore { | ... | @@ -117,7 +119,8 @@ public class SimpleTopologyStore { |
117 | * @param reasons list of events that triggered the update | 119 | * @param reasons list of events that triggered the update |
118 | * @return topology update event or null if the description is old | 120 | * @return topology update event or null if the description is old |
119 | */ | 121 | */ |
120 | - TopologyEvent updateTopology(TopologyDescription topoDescription, List<Event> reasons) { | 122 | + TopologyEvent updateTopology(TopologyDescription topoDescription, |
123 | + List<Event> reasons) { | ||
121 | return null; | 124 | return null; |
122 | } | 125 | } |
123 | 126 | ... | ... |
... | @@ -15,7 +15,8 @@ import static com.google.common.base.Preconditions.checkNotNull; | ... | @@ -15,7 +15,8 @@ import static com.google.common.base.Preconditions.checkNotNull; |
15 | * @param <V> vertex type | 15 | * @param <V> vertex type |
16 | * @param <E> edge type | 16 | * @param <E> edge type |
17 | */ | 17 | */ |
18 | -public class AdjacencyListsGraph<V extends Vertex, E extends Edge<V>> implements Graph<V, E> { | 18 | +public class AdjacencyListsGraph<V extends Vertex, E extends Edge<V>> |
19 | + implements Graph<V, E> { | ||
19 | 20 | ||
20 | private final Set<V> vertexes; | 21 | private final Set<V> vertexes; |
21 | private final Set<E> edges; | 22 | private final Set<E> edges; | ... | ... |
-
Please register or login to post a comment