Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
Showing
23 changed files
with
1260 additions
and
103 deletions
1 | package org.onlab.onos.event; | 1 | package org.onlab.onos.event; |
2 | 2 | ||
3 | +import static com.google.common.base.MoreObjects.toStringHelper; | ||
4 | + | ||
3 | /** | 5 | /** |
4 | * Base event implementation. | 6 | * Base event implementation. |
5 | */ | 7 | */ |
... | @@ -48,4 +50,10 @@ public class AbstractEvent<T extends Enum, S extends Object> implements Event<T, | ... | @@ -48,4 +50,10 @@ public class AbstractEvent<T extends Enum, S extends Object> implements Event<T, |
48 | return subject; | 50 | return subject; |
49 | } | 51 | } |
50 | 52 | ||
53 | + @Override | ||
54 | + public String toString() { | ||
55 | + return toStringHelper(this).add("time", time).add("type", type()) | ||
56 | + .add("subject", subject()).toString(); | ||
57 | + } | ||
58 | + | ||
51 | } | 59 | } | ... | ... |
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 | +} |
... | @@ -20,6 +20,13 @@ public interface TopologyService { | ... | @@ -20,6 +20,13 @@ public interface TopologyService { |
20 | Topology currentTopology(); | 20 | Topology currentTopology(); |
21 | 21 | ||
22 | /** | 22 | /** |
23 | + * Indicates whether the specified topology is the latest or not. | ||
24 | + * @param topology topology descriptor | ||
25 | + * @return true if the topology is the most recent; false otherwise | ||
26 | + */ | ||
27 | + boolean isLatest(Topology topology); | ||
28 | + | ||
29 | + /** | ||
23 | * Returns the set of clusters in the specified topology. | 30 | * Returns the set of clusters in the specified topology. |
24 | * | 31 | * |
25 | * @param topology topology descriptor | 32 | * @param topology topology descriptor | ... | ... |
1 | +package org.onlab.onos.net.trivial.impl; | ||
2 | + | ||
3 | +import org.onlab.onos.net.AbstractModel; | ||
4 | +import org.onlab.onos.net.provider.ProviderId; | ||
5 | +import org.onlab.onos.net.topology.Topology; | ||
6 | + | ||
7 | +/** | ||
8 | + * Default implementation of the topology descriptor. This carries the | ||
9 | + * backing topology data. | ||
10 | + */ | ||
11 | +public class DefaultTopology extends AbstractModel implements Topology { | ||
12 | + | ||
13 | + private final long time; | ||
14 | + private final int clusterCount; | ||
15 | + private final int deviceCount; | ||
16 | + private final int linkCount; | ||
17 | + private final int pathCount; | ||
18 | + | ||
19 | + /** | ||
20 | + * Creates a topology descriptor attributed to the specified provider. | ||
21 | + * | ||
22 | + * @param providerId identity of the provider | ||
23 | + * @param time creation time in system nanos | ||
24 | + * @param clusterCount number of clusters | ||
25 | + * @param deviceCount number of devices | ||
26 | + * @param linkCount number of links | ||
27 | + * @param pathCount number of pre-computed paths | ||
28 | + */ | ||
29 | + DefaultTopology(ProviderId providerId, long time, int clusterCount, | ||
30 | + int deviceCount, int linkCount, int pathCount) { | ||
31 | + super(providerId); | ||
32 | + this.time = time; | ||
33 | + this.clusterCount = clusterCount; | ||
34 | + this.deviceCount = deviceCount; | ||
35 | + this.linkCount = linkCount; | ||
36 | + this.pathCount = pathCount; | ||
37 | + } | ||
38 | + | ||
39 | + @Override | ||
40 | + public long time() { | ||
41 | + return time; | ||
42 | + } | ||
43 | + | ||
44 | + @Override | ||
45 | + public int clusterCount() { | ||
46 | + return clusterCount; | ||
47 | + } | ||
48 | + | ||
49 | + @Override | ||
50 | + public int deviceCount() { | ||
51 | + return deviceCount; | ||
52 | + } | ||
53 | + | ||
54 | + @Override | ||
55 | + public int linkCount() { | ||
56 | + return linkCount; | ||
57 | + } | ||
58 | + | ||
59 | + @Override | ||
60 | + public int pathCount() { | ||
61 | + return pathCount; | ||
62 | + } | ||
63 | + | ||
64 | +} |
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 | } | ... | ... |
... | @@ -150,7 +150,8 @@ public class SimpleDeviceManager | ... | @@ -150,7 +150,8 @@ public class SimpleDeviceManager |
150 | } | 150 | } |
151 | 151 | ||
152 | // Personalized device provider service issued to the supplied provider. | 152 | // Personalized device provider service issued to the supplied provider. |
153 | - private class InternalDeviceProviderService extends AbstractProviderService<DeviceProvider> | 153 | + private class InternalDeviceProviderService |
154 | + extends AbstractProviderService<DeviceProvider> | ||
154 | implements DeviceProviderService { | 155 | implements DeviceProviderService { |
155 | 156 | ||
156 | InternalDeviceProviderService(DeviceProvider provider) { | 157 | InternalDeviceProviderService(DeviceProvider provider) { | ... | ... |
... | @@ -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 | ... | ... |
... | @@ -158,7 +158,7 @@ public class SimpleHostManager | ... | @@ -158,7 +158,7 @@ public class SimpleHostManager |
158 | 158 | ||
159 | // Posts the specified event to the local event dispatcher. | 159 | // Posts the specified event to the local event dispatcher. |
160 | private void post(HostEvent event) { | 160 | private void post(HostEvent event) { |
161 | - if (event != null && eventDispatcher != null) { | 161 | + if (event != null) { |
162 | eventDispatcher.post(event); | 162 | eventDispatcher.post(event); |
163 | } | 163 | } |
164 | } | 164 | } | ... | ... |
... | @@ -36,8 +36,8 @@ import com.google.common.collect.Sets; | ... | @@ -36,8 +36,8 @@ import com.google.common.collect.Sets; |
36 | @Component(immediate = true) | 36 | @Component(immediate = true) |
37 | @Service | 37 | @Service |
38 | public class SimpleLinkManager | 38 | public class SimpleLinkManager |
39 | -extends AbstractProviderRegistry<LinkProvider, LinkProviderService> | 39 | + extends AbstractProviderRegistry<LinkProvider, LinkProviderService> |
40 | -implements LinkService, LinkAdminService, LinkProviderRegistry { | 40 | + implements LinkService, LinkAdminService, LinkProviderRegistry { |
41 | 41 | ||
42 | private static final String DEVICE_ID_NULL = "Device ID cannot be null"; | 42 | private static final String DEVICE_ID_NULL = "Device ID cannot be null"; |
43 | private static final String LINK_DESC_NULL = "Link description cannot be null"; | 43 | private static final String LINK_DESC_NULL = "Link description cannot be null"; |
... | @@ -46,7 +46,7 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { | ... | @@ -46,7 +46,7 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { |
46 | private final Logger log = getLogger(getClass()); | 46 | private final Logger log = getLogger(getClass()); |
47 | 47 | ||
48 | private final AbstractListenerRegistry<LinkEvent, LinkListener> | 48 | private final AbstractListenerRegistry<LinkEvent, LinkListener> |
49 | - listenerRegistry = new AbstractListenerRegistry<>(); | 49 | + listenerRegistry = new AbstractListenerRegistry<>(); |
50 | 50 | ||
51 | private final SimpleLinkStore store = new SimpleLinkStore(); | 51 | private final SimpleLinkStore store = new SimpleLinkStore(); |
52 | 52 | ||
... | @@ -79,7 +79,7 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { | ... | @@ -79,7 +79,7 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { |
79 | public Set<Link> getDeviceLinks(DeviceId deviceId) { | 79 | public Set<Link> getDeviceLinks(DeviceId deviceId) { |
80 | checkNotNull(deviceId, DEVICE_ID_NULL); | 80 | checkNotNull(deviceId, DEVICE_ID_NULL); |
81 | return Sets.union(store.getDeviceEgressLinks(deviceId), | 81 | return Sets.union(store.getDeviceEgressLinks(deviceId), |
82 | - store.getDeviceIngressLinks(deviceId)); | 82 | + store.getDeviceIngressLinks(deviceId)); |
83 | } | 83 | } |
84 | 84 | ||
85 | @Override | 85 | @Override |
... | @@ -98,7 +98,7 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { | ... | @@ -98,7 +98,7 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { |
98 | public Set<Link> getLinks(ConnectPoint connectPoint) { | 98 | public Set<Link> getLinks(ConnectPoint connectPoint) { |
99 | checkNotNull(connectPoint, CONNECT_POINT_NULL); | 99 | checkNotNull(connectPoint, CONNECT_POINT_NULL); |
100 | return Sets.union(store.getEgressLinks(connectPoint), | 100 | return Sets.union(store.getEgressLinks(connectPoint), |
101 | - store.getIngressLinks(connectPoint)); | 101 | + store.getIngressLinks(connectPoint)); |
102 | } | 102 | } |
103 | 103 | ||
104 | @Override | 104 | @Override |
... | @@ -146,8 +146,9 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { | ... | @@ -146,8 +146,9 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { |
146 | } | 146 | } |
147 | 147 | ||
148 | // Personalized link provider service issued to the supplied provider. | 148 | // Personalized link provider service issued to the supplied provider. |
149 | - private class InternalLinkProviderService extends AbstractProviderService<LinkProvider> | 149 | + private class InternalLinkProviderService |
150 | - implements LinkProviderService { | 150 | + extends AbstractProviderService<LinkProvider> |
151 | + implements LinkProviderService { | ||
151 | 152 | ||
152 | InternalLinkProviderService(LinkProvider provider) { | 153 | InternalLinkProviderService(LinkProvider provider) { |
153 | super(provider); | 154 | super(provider); |
... | @@ -157,27 +158,31 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { | ... | @@ -157,27 +158,31 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { |
157 | public void linkDetected(LinkDescription linkDescription) { | 158 | public void linkDetected(LinkDescription linkDescription) { |
158 | checkNotNull(linkDescription, LINK_DESC_NULL); | 159 | checkNotNull(linkDescription, LINK_DESC_NULL); |
159 | checkValidity(); | 160 | checkValidity(); |
160 | - log.debug("Link {} detected", linkDescription); | ||
161 | LinkEvent event = store.createOrUpdateLink(provider().id(), | 161 | LinkEvent event = store.createOrUpdateLink(provider().id(), |
162 | - linkDescription); | 162 | + linkDescription); |
163 | - post(event); | 163 | + if (event != null) { |
164 | + log.debug("Link {} detected", linkDescription); | ||
165 | + post(event); | ||
166 | + } | ||
164 | } | 167 | } |
165 | 168 | ||
166 | @Override | 169 | @Override |
167 | public void linkVanished(LinkDescription linkDescription) { | 170 | public void linkVanished(LinkDescription linkDescription) { |
168 | checkNotNull(linkDescription, LINK_DESC_NULL); | 171 | checkNotNull(linkDescription, LINK_DESC_NULL); |
169 | checkValidity(); | 172 | checkValidity(); |
170 | - log.info("Link {} vanished", linkDescription); | ||
171 | LinkEvent event = store.removeLink(linkDescription.src(), | 173 | LinkEvent event = store.removeLink(linkDescription.src(), |
172 | - linkDescription.dst()); | 174 | + linkDescription.dst()); |
173 | - post(event); | 175 | + if (event != null) { |
176 | + log.info("Link {} vanished", linkDescription); | ||
177 | + post(event); | ||
178 | + } | ||
174 | } | 179 | } |
175 | 180 | ||
176 | @Override | 181 | @Override |
177 | public void linksVanished(ConnectPoint connectPoint) { | 182 | public void linksVanished(ConnectPoint connectPoint) { |
178 | checkNotNull(connectPoint, "Connect point cannot be null"); | 183 | checkNotNull(connectPoint, "Connect point cannot be null"); |
179 | checkValidity(); | 184 | checkValidity(); |
180 | - log.info("Link for connection point {} vanished", connectPoint); | 185 | + log.info("Links for connection point {} vanished", connectPoint); |
181 | removeLinks(getLinks(connectPoint)); | 186 | removeLinks(getLinks(connectPoint)); |
182 | } | 187 | } |
183 | 188 | ||
... | @@ -185,7 +190,7 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { | ... | @@ -185,7 +190,7 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { |
185 | public void linksVanished(DeviceId deviceId) { | 190 | public void linksVanished(DeviceId deviceId) { |
186 | checkNotNull(deviceId, DEVICE_ID_NULL); | 191 | checkNotNull(deviceId, DEVICE_ID_NULL); |
187 | checkValidity(); | 192 | checkValidity(); |
188 | - log.info("Link for device {} vanished", deviceId); | 193 | + log.info("Links for device {} vanished", deviceId); |
189 | removeLinks(getDeviceLinks(deviceId)); | 194 | removeLinks(getDeviceLinks(deviceId)); |
190 | } | 195 | } |
191 | } | 196 | } |
... | @@ -200,7 +205,7 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { | ... | @@ -200,7 +205,7 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { |
200 | 205 | ||
201 | // Posts the specified event to the local event dispatcher. | 206 | // Posts the specified event to the local event dispatcher. |
202 | private void post(LinkEvent event) { | 207 | private void post(LinkEvent event) { |
203 | - if (event != null && eventDispatcher != null) { | 208 | + if (event != null) { |
204 | eventDispatcher.post(event); | 209 | eventDispatcher.post(event); |
205 | } | 210 | } |
206 | } | 211 | } | ... | ... |
... | @@ -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 { | ... | ... |
... | @@ -72,25 +72,26 @@ public class SimpleTopologyManager | ... | @@ -72,25 +72,26 @@ public class SimpleTopologyManager |
72 | } | 72 | } |
73 | 73 | ||
74 | @Override | 74 | @Override |
75 | - protected TopologyProviderService createProviderService(TopologyProvider provider) { | 75 | + public Topology currentTopology() { |
76 | - return new InternalTopologyProviderService(provider); | 76 | + return store.currentTopology(); |
77 | } | 77 | } |
78 | 78 | ||
79 | @Override | 79 | @Override |
80 | - public Topology currentTopology() { | 80 | + public boolean isLatest(Topology topology) { |
81 | - return null; | 81 | + checkNotNull(topology, TOPOLOGY_NULL); |
82 | + return store.isLatest(topology); | ||
82 | } | 83 | } |
83 | 84 | ||
84 | @Override | 85 | @Override |
85 | public Set<TopologyCluster> getClusters(Topology topology) { | 86 | public Set<TopologyCluster> getClusters(Topology topology) { |
86 | checkNotNull(topology, TOPOLOGY_NULL); | 87 | checkNotNull(topology, TOPOLOGY_NULL); |
87 | - return null; | 88 | + return store.getClusters(topology); |
88 | } | 89 | } |
89 | 90 | ||
90 | @Override | 91 | @Override |
91 | public Graph<TopoVertex, TopoEdge> getGraph(Topology topology) { | 92 | public Graph<TopoVertex, TopoEdge> getGraph(Topology topology) { |
92 | checkNotNull(topology, TOPOLOGY_NULL); | 93 | checkNotNull(topology, TOPOLOGY_NULL); |
93 | - return null; | 94 | + return store.getGraph(topology); |
94 | } | 95 | } |
95 | 96 | ||
96 | @Override | 97 | @Override |
... | @@ -98,7 +99,7 @@ public class SimpleTopologyManager | ... | @@ -98,7 +99,7 @@ public class SimpleTopologyManager |
98 | checkNotNull(topology, TOPOLOGY_NULL); | 99 | checkNotNull(topology, TOPOLOGY_NULL); |
99 | checkNotNull(src, DEVICE_ID_NULL); | 100 | checkNotNull(src, DEVICE_ID_NULL); |
100 | checkNotNull(dst, DEVICE_ID_NULL); | 101 | checkNotNull(dst, DEVICE_ID_NULL); |
101 | - return null; | 102 | + return store.getPaths(topology, src, dst); |
102 | } | 103 | } |
103 | 104 | ||
104 | @Override | 105 | @Override |
... | @@ -107,21 +108,21 @@ public class SimpleTopologyManager | ... | @@ -107,21 +108,21 @@ public class SimpleTopologyManager |
107 | checkNotNull(src, DEVICE_ID_NULL); | 108 | checkNotNull(src, DEVICE_ID_NULL); |
108 | checkNotNull(dst, DEVICE_ID_NULL); | 109 | checkNotNull(dst, DEVICE_ID_NULL); |
109 | checkNotNull(weight, "Link weight cannot be null"); | 110 | checkNotNull(weight, "Link weight cannot be null"); |
110 | - return null; | 111 | + return store.getPaths(topology, src, dst, weight); |
111 | } | 112 | } |
112 | 113 | ||
113 | @Override | 114 | @Override |
114 | public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) { | 115 | public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) { |
115 | checkNotNull(topology, TOPOLOGY_NULL); | 116 | checkNotNull(topology, TOPOLOGY_NULL); |
116 | checkNotNull(connectPoint, CONNECTION_POINT_NULL); | 117 | checkNotNull(connectPoint, CONNECTION_POINT_NULL); |
117 | - return false; | 118 | + return store.isInfrastructure(topology, connectPoint); |
118 | } | 119 | } |
119 | 120 | ||
120 | @Override | 121 | @Override |
121 | public boolean isInBroadcastTree(Topology topology, ConnectPoint connectPoint) { | 122 | public boolean isInBroadcastTree(Topology topology, ConnectPoint connectPoint) { |
122 | checkNotNull(topology, TOPOLOGY_NULL); | 123 | checkNotNull(topology, TOPOLOGY_NULL); |
123 | checkNotNull(connectPoint, CONNECTION_POINT_NULL); | 124 | checkNotNull(connectPoint, CONNECTION_POINT_NULL); |
124 | - return false; | 125 | + return store.isInBroadcastTree(topology, connectPoint); |
125 | } | 126 | } |
126 | 127 | ||
127 | @Override | 128 | @Override |
... | @@ -135,6 +136,11 @@ public class SimpleTopologyManager | ... | @@ -135,6 +136,11 @@ public class SimpleTopologyManager |
135 | } | 136 | } |
136 | 137 | ||
137 | // Personalized host provider service issued to the supplied provider. | 138 | // Personalized host provider service issued to the supplied provider. |
139 | + @Override | ||
140 | + protected TopologyProviderService createProviderService(TopologyProvider provider) { | ||
141 | + return new InternalTopologyProviderService(provider); | ||
142 | + } | ||
143 | + | ||
138 | private class InternalTopologyProviderService | 144 | private class InternalTopologyProviderService |
139 | extends AbstractProviderService<TopologyProvider> | 145 | extends AbstractProviderService<TopologyProvider> |
140 | implements TopologyProviderService { | 146 | implements TopologyProviderService { |
... | @@ -147,8 +153,15 @@ public class SimpleTopologyManager | ... | @@ -147,8 +153,15 @@ public class SimpleTopologyManager |
147 | public void topologyChanged(TopologyDescription topoDescription, | 153 | public void topologyChanged(TopologyDescription topoDescription, |
148 | List<Event> reasons) { | 154 | List<Event> reasons) { |
149 | checkNotNull(topoDescription, "Topology description cannot be null"); | 155 | checkNotNull(topoDescription, "Topology description cannot be null"); |
150 | - log.info("Topology changed due to: {}", | 156 | + |
157 | + log.info("Topology changed due to: {}", // to be removed soon | ||
151 | reasons == null ? "initial compute" : reasons); | 158 | reasons == null ? "initial compute" : reasons); |
159 | + TopologyEvent event = store.updateTopology(topoDescription, reasons); | ||
160 | + if (event != null) { | ||
161 | + log.info("Topology changed due to: {}", | ||
162 | + reasons == null ? "initial compute" : reasons); | ||
163 | + eventDispatcher.post(event); | ||
164 | + } | ||
152 | } | 165 | } |
153 | } | 166 | } |
154 | 167 | ... | ... |
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 | +} |
1 | package org.onlab.onos.net.trivial.impl; | 1 | package org.onlab.onos.net.trivial.impl; |
2 | 2 | ||
3 | +import org.onlab.graph.Graph; | ||
4 | +import org.onlab.onos.event.Event; | ||
5 | +import org.onlab.onos.net.ConnectPoint; | ||
6 | +import org.onlab.onos.net.DeviceId; | ||
7 | +import org.onlab.onos.net.Path; | ||
8 | +import org.onlab.onos.net.topology.LinkWeight; | ||
9 | +import org.onlab.onos.net.topology.TopoEdge; | ||
10 | +import org.onlab.onos.net.topology.TopoVertex; | ||
11 | +import org.onlab.onos.net.topology.Topology; | ||
12 | +import org.onlab.onos.net.topology.TopologyCluster; | ||
13 | +import org.onlab.onos.net.topology.TopologyDescription; | ||
14 | +import org.onlab.onos.net.topology.TopologyEvent; | ||
15 | + | ||
16 | +import java.util.List; | ||
17 | +import java.util.Set; | ||
18 | + | ||
3 | /** | 19 | /** |
4 | * Manages inventory of topology snapshots using trivial in-memory | 20 | * Manages inventory of topology snapshots using trivial in-memory |
5 | - * implementation. | 21 | + * structures implementation. |
6 | */ | 22 | */ |
7 | -public class SimpleTopologyStore { | 23 | +class SimpleTopologyStore { |
24 | + | ||
25 | + private volatile DefaultTopology current; | ||
26 | + | ||
27 | + /** | ||
28 | + * Returns the current topology snapshot. | ||
29 | + * | ||
30 | + * @return current topology descriptor | ||
31 | + */ | ||
32 | + Topology currentTopology() { | ||
33 | + return current; | ||
34 | + } | ||
35 | + | ||
36 | + /** | ||
37 | + * Indicates whether the topology is the latest. | ||
38 | + * | ||
39 | + * @param topology topology descriptor | ||
40 | + * @return true if topology is the most recent one | ||
41 | + */ | ||
42 | + boolean isLatest(Topology topology) { | ||
43 | + // Topology is current only if it is the same as our current topology | ||
44 | + return topology == current; | ||
45 | + } | ||
46 | + | ||
47 | + /** | ||
48 | + * Returns the set of topology SCC clusters. | ||
49 | + * | ||
50 | + * @param topology topology descriptor | ||
51 | + * @return set of clusters | ||
52 | + */ | ||
53 | + Set<TopologyCluster> getClusters(Topology topology) { | ||
54 | + return null; | ||
55 | + } | ||
56 | + | ||
57 | + /** | ||
58 | + * Returns the immutable graph view of the current topology. | ||
59 | + * | ||
60 | + * @param topology topology descriptor | ||
61 | + * @return graph view | ||
62 | + */ | ||
63 | + Graph<TopoVertex, TopoEdge> getGraph(Topology topology) { | ||
64 | + return null; | ||
65 | + } | ||
66 | + | ||
67 | + /** | ||
68 | + * Returns the set of pre-computed shortest paths between src and dest. | ||
69 | + * | ||
70 | + * @param topology topology descriptor | ||
71 | + * @param src source device | ||
72 | + * @param dst destination device | ||
73 | + * @return set of shortest paths | ||
74 | + */ | ||
75 | + Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) { | ||
76 | + return null; | ||
77 | + } | ||
78 | + | ||
79 | + /** | ||
80 | + * Computes and returns the set of shortest paths between src and dest. | ||
81 | + * | ||
82 | + * @param topology topology descriptor | ||
83 | + * @param src source device | ||
84 | + * @param dst destination device | ||
85 | + * @param weight link weight function | ||
86 | + * @return set of shortest paths | ||
87 | + */ | ||
88 | + Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst, | ||
89 | + LinkWeight weight) { | ||
90 | + return null; | ||
91 | + } | ||
92 | + | ||
93 | + /** | ||
94 | + * Indicates whether the given connect point is part of the network fabric. | ||
95 | + * | ||
96 | + * @param topology topology descriptor | ||
97 | + * @param connectPoint connection point | ||
98 | + * @return true if infrastructure; false otherwise | ||
99 | + */ | ||
100 | + boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) { | ||
101 | + return false; | ||
102 | + } | ||
103 | + | ||
104 | + /** | ||
105 | + * Indicates whether the given connect point is part of the broadcast tree. | ||
106 | + * | ||
107 | + * @param topology topology descriptor | ||
108 | + * @param connectPoint connection point | ||
109 | + * @return true if in broadcast tree; false otherwise | ||
110 | + */ | ||
111 | + boolean isInBroadcastTree(Topology topology, ConnectPoint connectPoint) { | ||
112 | + return false; | ||
113 | + } | ||
114 | + | ||
115 | + /** | ||
116 | + * Generates a new topology snapshot from the specified description. | ||
117 | + * | ||
118 | + * @param topoDescription topology description | ||
119 | + * @param reasons list of events that triggered the update | ||
120 | + * @return topology update event or null if the description is old | ||
121 | + */ | ||
122 | + TopologyEvent updateTopology(TopologyDescription topoDescription, | ||
123 | + List<Event> reasons) { | ||
124 | + return null; | ||
125 | + } | ||
126 | + | ||
8 | } | 127 | } | ... | ... |
1 | +package org.onlab.onos.event.impl; | ||
2 | + | ||
3 | +import org.junit.After; | ||
4 | +import org.junit.Before; | ||
5 | +import org.junit.Test; | ||
6 | +import org.onlab.onos.event.AbstractEvent; | ||
7 | +import org.onlab.onos.event.EventSink; | ||
8 | + | ||
9 | +import java.util.ArrayList; | ||
10 | +import java.util.List; | ||
11 | +import java.util.concurrent.CountDownLatch; | ||
12 | +import java.util.concurrent.TimeUnit; | ||
13 | + | ||
14 | +import static org.junit.Assert.assertEquals; | ||
15 | + | ||
16 | +/** | ||
17 | + * Test of the even dispatcher mechanism. | ||
18 | + */ | ||
19 | +public class SimpleEventDispatcherTest { | ||
20 | + | ||
21 | + private final SimpleEventDispatcher dispatcher = new SimpleEventDispatcher(); | ||
22 | + private final PrickleSink prickleSink = new PrickleSink(); | ||
23 | + private final GooSink gooSink = new GooSink(); | ||
24 | + | ||
25 | + @Before | ||
26 | + public void setUp() { | ||
27 | + dispatcher.activate(); | ||
28 | + dispatcher.addSink(Prickle.class, prickleSink); | ||
29 | + dispatcher.addSink(Goo.class, gooSink); | ||
30 | + } | ||
31 | + | ||
32 | + @After | ||
33 | + public void tearDown() { | ||
34 | + dispatcher.removeSink(Goo.class); | ||
35 | + dispatcher.removeSink(Prickle.class); | ||
36 | + dispatcher.deactivate(); | ||
37 | + } | ||
38 | + | ||
39 | + @Test | ||
40 | + public void post() throws Exception { | ||
41 | + prickleSink.latch = new CountDownLatch(1); | ||
42 | + dispatcher.post(new Prickle("yo")); | ||
43 | + prickleSink.latch.await(100, TimeUnit.MILLISECONDS); | ||
44 | + validate(prickleSink, "yo"); | ||
45 | + validate(gooSink); | ||
46 | + } | ||
47 | + | ||
48 | + @Test | ||
49 | + public void postEventWithBadSink() throws Exception { | ||
50 | + gooSink.latch = new CountDownLatch(1); | ||
51 | + dispatcher.post(new Goo("boom")); | ||
52 | + gooSink.latch.await(100, TimeUnit.MILLISECONDS); | ||
53 | + validate(gooSink, "boom"); | ||
54 | + validate(prickleSink); | ||
55 | + } | ||
56 | + | ||
57 | + @Test | ||
58 | + public void postEventWithNoSink() throws Exception { | ||
59 | + dispatcher.post(new Thing("boom")); | ||
60 | + validate(gooSink); | ||
61 | + validate(prickleSink); | ||
62 | + } | ||
63 | + | ||
64 | + private void validate(Sink sink, String... strings) { | ||
65 | + int i = 0; | ||
66 | + assertEquals("incorrect event count", strings.length, sink.subjects.size()); | ||
67 | + for (String string : strings) { | ||
68 | + assertEquals("incorrect event", string, sink.subjects.get(i++)); | ||
69 | + } | ||
70 | + } | ||
71 | + | ||
72 | + private enum Type { FOO }; | ||
73 | + | ||
74 | + private static class Thing extends AbstractEvent<Type, String> { | ||
75 | + protected Thing(String subject) { | ||
76 | + super(Type.FOO, subject); | ||
77 | + } | ||
78 | + } | ||
79 | + | ||
80 | + private static class Prickle extends Thing { | ||
81 | + protected Prickle(String subject) { | ||
82 | + super(subject); | ||
83 | + } | ||
84 | + } | ||
85 | + | ||
86 | + private static class Goo extends Thing { | ||
87 | + protected Goo(String subject) { | ||
88 | + super(subject); | ||
89 | + } | ||
90 | + } | ||
91 | + | ||
92 | + private static class Sink { | ||
93 | + final List<String> subjects = new ArrayList<>(); | ||
94 | + CountDownLatch latch; | ||
95 | + | ||
96 | + protected void process(String subject) { | ||
97 | + subjects.add(subject); | ||
98 | + latch.countDown(); | ||
99 | + } | ||
100 | + } | ||
101 | + | ||
102 | + private static class PrickleSink extends Sink implements EventSink<Prickle> { | ||
103 | + @Override | ||
104 | + public void process(Prickle event) { | ||
105 | + process(event.subject()); | ||
106 | + } | ||
107 | + } | ||
108 | + | ||
109 | + private static class GooSink extends Sink implements EventSink<Goo> { | ||
110 | + @Override | ||
111 | + public void process(Goo event) { | ||
112 | + process(event.subject()); | ||
113 | + throw new IllegalStateException("BOOM!"); | ||
114 | + } | ||
115 | + } | ||
116 | + | ||
117 | +} |
... | @@ -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; | ... | ... |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import java.util.ArrayList; | ||
4 | +import java.util.Collections; | ||
5 | +import java.util.HashMap; | ||
6 | +import java.util.HashSet; | ||
7 | +import java.util.List; | ||
8 | +import java.util.Map; | ||
9 | +import java.util.Set; | ||
10 | + | ||
11 | +/** | ||
12 | + * Tarjan algorithm for searching a graph and producing results describing | ||
13 | + * the graph SCC (strongly-connected components). | ||
14 | + */ | ||
15 | +public class TarjanGraphSearch<V extends Vertex, E extends Edge<V>> | ||
16 | + implements GraphSearch<V, E> { | ||
17 | + | ||
18 | + /** | ||
19 | + * {@inheritDoc} | ||
20 | + * <p/> | ||
21 | + * This implementation produces results augmented with information on | ||
22 | + * SCCs within the graph. | ||
23 | + * <p/> | ||
24 | + * To prevent traversal of an edge, the {@link EdgeWeight#weight} should | ||
25 | + * return a negative value as an edge weight. | ||
26 | + */ | ||
27 | + @Override | ||
28 | + public SCCResult<V, E> search(Graph<V, E> graph, EdgeWeight<V, E> weight) { | ||
29 | + SCCResult<V, E> result = new SCCResult<>(graph); | ||
30 | + for (V vertex : graph.getVertexes()) { | ||
31 | + VertexData data = result.data(vertex); | ||
32 | + if (data == null) { | ||
33 | + connect(graph, vertex, weight, result); | ||
34 | + } | ||
35 | + } | ||
36 | + return result.build(); | ||
37 | + } | ||
38 | + | ||
39 | + /** | ||
40 | + * Scans the specified graph, using recursion, and produces SCC results. | ||
41 | + * | ||
42 | + * @param graph graph to search | ||
43 | + * @param vertex current vertex to scan and connect | ||
44 | + * @param weight optional edge weight | ||
45 | + * @param result graph search result | ||
46 | + * @return augmentation vertexData for the current vertex | ||
47 | + */ | ||
48 | + private VertexData<V> connect(Graph<V, E> graph, V vertex, | ||
49 | + EdgeWeight<V, E> weight, | ||
50 | + SCCResult<V, E> result) { | ||
51 | + VertexData<V> data = result.addData(vertex); | ||
52 | + | ||
53 | + // Scan through all egress edges of the current vertex. | ||
54 | + for (E edge : graph.getEdgesFrom(vertex)) { | ||
55 | + V nextVertex = edge.dst(); | ||
56 | + | ||
57 | + // If edge weight is negative, skip it. | ||
58 | + if (weight != null && weight.weight(edge) < 0) { | ||
59 | + continue; | ||
60 | + } | ||
61 | + | ||
62 | + // Attempt to get the augmentation vertexData for the next vertex. | ||
63 | + VertexData<V> nextData = result.data(nextVertex); | ||
64 | + if (nextData == null) { | ||
65 | + // Next vertex has not been visited yet, so do this now. | ||
66 | + nextData = connect(graph, nextVertex, weight, result); | ||
67 | + data.lowLink = Math.min(data.lowLink, nextData.lowLink); | ||
68 | + | ||
69 | + } else if (result.visited(nextData)) { | ||
70 | + // Next vertex has been visited, which means it is in the | ||
71 | + // same cluster as the current vertex. | ||
72 | + data.lowLink = Math.min(data.lowLink, nextData.index); | ||
73 | + } | ||
74 | + } | ||
75 | + | ||
76 | + if (data.lowLink == data.index) { | ||
77 | + result.addCluster(data); | ||
78 | + } | ||
79 | + return data; | ||
80 | + } | ||
81 | + | ||
82 | + /** | ||
83 | + * Graph search result augmented with SCC vertexData. | ||
84 | + */ | ||
85 | + public static final class SCCResult<V extends Vertex, E extends Edge<V>> | ||
86 | + implements Result { | ||
87 | + | ||
88 | + private final Graph<V, E> graph; | ||
89 | + private List<Set<V>> clusterVertexes = new ArrayList<>(); | ||
90 | + private List<Set<E>> clusterEdges = new ArrayList<>(); | ||
91 | + | ||
92 | + private int index = 0; | ||
93 | + private final Map<V, VertexData<V>> vertexData = new HashMap<>(); | ||
94 | + private final List<VertexData<V>> visited = new ArrayList<>(); | ||
95 | + | ||
96 | + private SCCResult(Graph<V, E> graph) { | ||
97 | + this.graph = graph; | ||
98 | + } | ||
99 | + | ||
100 | + /** | ||
101 | + * Returns the number of SCC clusters in the graph. | ||
102 | + * | ||
103 | + * @return number of clusters | ||
104 | + */ | ||
105 | + public int clusterCount() { | ||
106 | + return clusterEdges.size(); | ||
107 | + } | ||
108 | + | ||
109 | + /** | ||
110 | + * Returns the list of strongly connected vertex clusters. | ||
111 | + * | ||
112 | + * @return list of strongly connected vertex sets | ||
113 | + */ | ||
114 | + public List<Set<V>> clusterVertexes() { | ||
115 | + return clusterVertexes; | ||
116 | + } | ||
117 | + | ||
118 | + /** | ||
119 | + * Returns the list of edges linking strongly connected vertex clusters. | ||
120 | + * | ||
121 | + * @return list of strongly connected edge sets | ||
122 | + */ | ||
123 | + public List<Set<E>> clusterEdges() { | ||
124 | + return clusterEdges; | ||
125 | + } | ||
126 | + | ||
127 | + // Gets the augmentation vertexData for the specified vertex | ||
128 | + private VertexData<V> data(V vertex) { | ||
129 | + return vertexData.get(vertex); | ||
130 | + } | ||
131 | + | ||
132 | + // Adds augmentation vertexData for the specified vertex | ||
133 | + private VertexData<V> addData(V vertex) { | ||
134 | + VertexData<V> d = new VertexData<>(vertex, index); | ||
135 | + vertexData.put(vertex, d); | ||
136 | + visited.add(0, d); | ||
137 | + index++; | ||
138 | + return d; | ||
139 | + } | ||
140 | + | ||
141 | + // Indicates whether the given vertex has been visited | ||
142 | + private boolean visited(VertexData data) { | ||
143 | + return visited.contains(data); | ||
144 | + } | ||
145 | + | ||
146 | + // Adds a new cluster for the specified vertex | ||
147 | + private void addCluster(VertexData data) { | ||
148 | + Set<V> vertexes = findClusterVertices(data); | ||
149 | + clusterVertexes.add(vertexes); | ||
150 | + clusterEdges.add(findClusterEdges(vertexes)); | ||
151 | + } | ||
152 | + | ||
153 | + private Set<V> findClusterVertices(VertexData data) { | ||
154 | + VertexData<V> nextVertexData; | ||
155 | + Set<V> vertexes = new HashSet<>(); | ||
156 | + do { | ||
157 | + nextVertexData = visited.remove(0); | ||
158 | + vertexes.add(nextVertexData.vertex); | ||
159 | + } while (data != nextVertexData); | ||
160 | + return Collections.unmodifiableSet(vertexes); | ||
161 | + } | ||
162 | + | ||
163 | + private Set<E> findClusterEdges(Set<V> vertexes) { | ||
164 | + Set<E> edges = new HashSet<>(); | ||
165 | + for (V vertex : vertexes) { | ||
166 | + for (E edge : graph.getEdgesFrom(vertex)) { | ||
167 | + if (vertexes.contains((edge.dst()))) { | ||
168 | + edges.add(edge); | ||
169 | + } | ||
170 | + } | ||
171 | + } | ||
172 | + return Collections.unmodifiableSet(edges); | ||
173 | + } | ||
174 | + | ||
175 | + public SCCResult<V, E> build() { | ||
176 | + clusterVertexes = Collections.unmodifiableList(clusterVertexes); | ||
177 | + clusterEdges = Collections.unmodifiableList(clusterEdges); | ||
178 | + return this; | ||
179 | + } | ||
180 | + } | ||
181 | + | ||
182 | + // Augments the vertex to assist in determining SCC clusters. | ||
183 | + private static final class VertexData<V extends Vertex> { | ||
184 | + final V vertex; | ||
185 | + int index; | ||
186 | + int lowLink; | ||
187 | + | ||
188 | + private VertexData(V vertex, int index) { | ||
189 | + this.vertex = vertex; | ||
190 | + this.index = index; | ||
191 | + this.lowLink = index; | ||
192 | + } | ||
193 | + } | ||
194 | + | ||
195 | +} |
... | @@ -31,17 +31,17 @@ public class BellmanFordGraphSearchTest extends BreadthFirstSearchTest { | ... | @@ -31,17 +31,17 @@ public class BellmanFordGraphSearchTest extends BreadthFirstSearchTest { |
31 | 31 | ||
32 | @Test | 32 | @Test |
33 | public void searchGraphWithNegativeCycles() { | 33 | public void searchGraphWithNegativeCycles() { |
34 | - Set<TestVertex> vertexes = new HashSet<>(vertices()); | 34 | + Set<TestVertex> vertexes = new HashSet<>(vertexes()); |
35 | vertexes.add(Z); | 35 | vertexes.add(Z); |
36 | 36 | ||
37 | Set<TestEdge> edges = new HashSet<>(edges()); | 37 | Set<TestEdge> edges = new HashSet<>(edges()); |
38 | edges.add(new TestEdge(G, Z, 1.0)); | 38 | edges.add(new TestEdge(G, Z, 1.0)); |
39 | edges.add(new TestEdge(Z, G, -2.0)); | 39 | edges.add(new TestEdge(Z, G, -2.0)); |
40 | 40 | ||
41 | - g = new AdjacencyListsGraph<>(vertexes, edges); | 41 | + graph = new AdjacencyListsGraph<>(vertexes, edges); |
42 | 42 | ||
43 | GraphPathSearch<TestVertex, TestEdge> search = graphSearch(); | 43 | GraphPathSearch<TestVertex, TestEdge> search = graphSearch(); |
44 | - Set<Path<TestVertex, TestEdge>> paths = search.search(g, A, H, weight).paths(); | 44 | + Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, H, weight).paths(); |
45 | assertEquals("incorrect paths count", 1, paths.size()); | 45 | assertEquals("incorrect paths count", 1, paths.size()); |
46 | 46 | ||
47 | Path p = paths.iterator().next(); | 47 | Path p = paths.iterator().next(); |
... | @@ -50,10 +50,10 @@ public class BellmanFordGraphSearchTest extends BreadthFirstSearchTest { | ... | @@ -50,10 +50,10 @@ public class BellmanFordGraphSearchTest extends BreadthFirstSearchTest { |
50 | assertEquals("incorrect path length", 5, p.edges().size()); | 50 | assertEquals("incorrect path length", 5, p.edges().size()); |
51 | assertEquals("incorrect path cost", 5.0, p.cost(), 0.1); | 51 | assertEquals("incorrect path cost", 5.0, p.cost(), 0.1); |
52 | 52 | ||
53 | - paths = search.search(g, A, G, weight).paths(); | 53 | + paths = search.search(graph, A, G, weight).paths(); |
54 | assertEquals("incorrect paths count", 0, paths.size()); | 54 | assertEquals("incorrect paths count", 0, paths.size()); |
55 | 55 | ||
56 | - paths = search.search(g, A, null, weight).paths(); | 56 | + paths = search.search(graph, A, null, weight).paths(); |
57 | printPaths(paths); | 57 | printPaths(paths); |
58 | assertEquals("incorrect paths count", 6, paths.size()); | 58 | assertEquals("incorrect paths count", 6, paths.size()); |
59 | } | 59 | } | ... | ... |
... | @@ -29,10 +29,10 @@ public class BreadthFirstSearchTest extends AbstractGraphPathSearchTest { | ... | @@ -29,10 +29,10 @@ public class BreadthFirstSearchTest extends AbstractGraphPathSearchTest { |
29 | 29 | ||
30 | // Executes the default test | 30 | // Executes the default test |
31 | protected void executeDefaultTest(int pathCount, int pathLength, double pathCost) { | 31 | protected void executeDefaultTest(int pathCount, int pathLength, double pathCost) { |
32 | - g = new AdjacencyListsGraph<>(vertices(), edges()); | 32 | + graph = new AdjacencyListsGraph<>(vertexes(), edges()); |
33 | 33 | ||
34 | GraphPathSearch<TestVertex, TestEdge> search = graphSearch(); | 34 | GraphPathSearch<TestVertex, TestEdge> search = graphSearch(); |
35 | - Set<Path<TestVertex, TestEdge>> paths = search.search(g, A, H, weight).paths(); | 35 | + Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, H, weight).paths(); |
36 | assertEquals("incorrect paths count", 1, paths.size()); | 36 | assertEquals("incorrect paths count", 1, paths.size()); |
37 | 37 | ||
38 | Path p = paths.iterator().next(); | 38 | Path p = paths.iterator().next(); |
... | @@ -41,7 +41,7 @@ public class BreadthFirstSearchTest extends AbstractGraphPathSearchTest { | ... | @@ -41,7 +41,7 @@ public class BreadthFirstSearchTest extends AbstractGraphPathSearchTest { |
41 | assertEquals("incorrect path length", pathLength, p.edges().size()); | 41 | assertEquals("incorrect path length", pathLength, p.edges().size()); |
42 | assertEquals("incorrect path cost", pathCost, p.cost(), 0.1); | 42 | assertEquals("incorrect path cost", pathCost, p.cost(), 0.1); |
43 | 43 | ||
44 | - paths = search.search(g, A, null, weight).paths(); | 44 | + paths = search.search(graph, A, null, weight).paths(); |
45 | printPaths(paths); | 45 | printPaths(paths); |
46 | assertEquals("incorrect paths count", pathCount, paths.size()); | 46 | assertEquals("incorrect paths count", pathCount, paths.size()); |
47 | } | 47 | } | ... | ... |
... | @@ -33,11 +33,11 @@ public class DepthFirstSearchTest extends AbstractGraphPathSearchTest { | ... | @@ -33,11 +33,11 @@ public class DepthFirstSearchTest extends AbstractGraphPathSearchTest { |
33 | 33 | ||
34 | protected void executeDefaultTest(int minLength, int maxLength, | 34 | protected void executeDefaultTest(int minLength, int maxLength, |
35 | double minCost, double maxCost) { | 35 | double minCost, double maxCost) { |
36 | - g = new AdjacencyListsGraph<>(vertices(), edges()); | 36 | + graph = new AdjacencyListsGraph<>(vertexes(), edges()); |
37 | DepthFirstSearch<TestVertex, TestEdge> search = graphSearch(); | 37 | DepthFirstSearch<TestVertex, TestEdge> search = graphSearch(); |
38 | 38 | ||
39 | DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result = | 39 | DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result = |
40 | - search.search(g, A, H, weight); | 40 | + search.search(graph, A, H, weight); |
41 | Set<Path<TestVertex, TestEdge>> paths = result.paths(); | 41 | Set<Path<TestVertex, TestEdge>> paths = result.paths(); |
42 | assertEquals("incorrect path count", 1, paths.size()); | 42 | assertEquals("incorrect path count", 1, paths.size()); |
43 | 43 | ||
... | @@ -57,12 +57,12 @@ public class DepthFirstSearchTest extends AbstractGraphPathSearchTest { | ... | @@ -57,12 +57,12 @@ public class DepthFirstSearchTest extends AbstractGraphPathSearchTest { |
57 | } | 57 | } |
58 | 58 | ||
59 | public void executeBroadSearch() { | 59 | public void executeBroadSearch() { |
60 | - g = new AdjacencyListsGraph<>(vertices(), edges()); | 60 | + graph = new AdjacencyListsGraph<>(vertexes(), edges()); |
61 | DepthFirstSearch<TestVertex, TestEdge> search = graphSearch(); | 61 | DepthFirstSearch<TestVertex, TestEdge> search = graphSearch(); |
62 | 62 | ||
63 | // Perform narrow path search to a specific destination. | 63 | // Perform narrow path search to a specific destination. |
64 | DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result = | 64 | DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result = |
65 | - search.search(g, A, null, weight); | 65 | + search.search(graph, A, null, weight); |
66 | assertEquals("incorrect paths count", 7, result.paths().size()); | 66 | assertEquals("incorrect paths count", 7, result.paths().size()); |
67 | 67 | ||
68 | int[] types = new int[]{0, 0, 0, 0}; | 68 | int[] types = new int[]{0, 0, 0, 0}; | ... | ... |
... | @@ -32,22 +32,22 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest { | ... | @@ -32,22 +32,22 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest { |
32 | 32 | ||
33 | @Test | 33 | @Test |
34 | public void noPath() { | 34 | public void noPath() { |
35 | - g = new AdjacencyListsGraph<>(of(A, B, C, D), | 35 | + graph = new AdjacencyListsGraph<>(of(A, B, C, D), |
36 | - of(new TestEdge(A, B, 1), | 36 | + of(new TestEdge(A, B, 1), |
37 | - new TestEdge(B, A, 1), | 37 | + new TestEdge(B, A, 1), |
38 | - new TestEdge(C, D, 1), | 38 | + new TestEdge(C, D, 1), |
39 | - new TestEdge(D, C, 1))); | 39 | + new TestEdge(D, C, 1))); |
40 | GraphPathSearch<TestVertex, TestEdge> gs = graphSearch(); | 40 | GraphPathSearch<TestVertex, TestEdge> gs = graphSearch(); |
41 | - Set<Path<TestVertex, TestEdge>> paths = gs.search(g, A, B, weight).paths(); | 41 | + Set<Path<TestVertex, TestEdge>> paths = gs.search(graph, A, B, weight).paths(); |
42 | printPaths(paths); | 42 | printPaths(paths); |
43 | assertEquals("incorrect paths count", 1, paths.size()); | 43 | assertEquals("incorrect paths count", 1, paths.size()); |
44 | assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1); | 44 | assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1); |
45 | 45 | ||
46 | - paths = gs.search(g, A, D, weight).paths(); | 46 | + paths = gs.search(graph, A, D, weight).paths(); |
47 | printPaths(paths); | 47 | printPaths(paths); |
48 | assertEquals("incorrect paths count", 0, paths.size()); | 48 | assertEquals("incorrect paths count", 0, paths.size()); |
49 | 49 | ||
50 | - paths = gs.search(g, A, null, weight).paths(); | 50 | + paths = gs.search(graph, A, null, weight).paths(); |
51 | printPaths(paths); | 51 | printPaths(paths); |
52 | assertEquals("incorrect paths count", 1, paths.size()); | 52 | assertEquals("incorrect paths count", 1, paths.size()); |
53 | assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1); | 53 | assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1); |
... | @@ -55,40 +55,40 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest { | ... | @@ -55,40 +55,40 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest { |
55 | 55 | ||
56 | @Test | 56 | @Test |
57 | public void simpleMultiplePath() { | 57 | public void simpleMultiplePath() { |
58 | - g = new AdjacencyListsGraph<>(of(A, B, C, D), | 58 | + graph = new AdjacencyListsGraph<>(of(A, B, C, D), |
59 | - of(new TestEdge(A, B, 1), | 59 | + of(new TestEdge(A, B, 1), |
60 | - new TestEdge(A, C, 1), | 60 | + new TestEdge(A, C, 1), |
61 | - new TestEdge(B, D, 1), | 61 | + new TestEdge(B, D, 1), |
62 | - new TestEdge(C, D, 1))); | 62 | + new TestEdge(C, D, 1))); |
63 | - executeSearch(graphSearch(), g, A, D, weight, 2, 2.0); | 63 | + executeSearch(graphSearch(), graph, A, D, weight, 2, 2.0); |
64 | } | 64 | } |
65 | 65 | ||
66 | @Test | 66 | @Test |
67 | public void denseMultiplePath() { | 67 | public void denseMultiplePath() { |
68 | - g = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G), | 68 | + graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G), |
69 | - of(new TestEdge(A, B, 1), | 69 | + of(new TestEdge(A, B, 1), |
70 | - new TestEdge(A, C, 1), | 70 | + new TestEdge(A, C, 1), |
71 | - new TestEdge(B, D, 1), | 71 | + new TestEdge(B, D, 1), |
72 | - new TestEdge(C, D, 1), | 72 | + new TestEdge(C, D, 1), |
73 | - new TestEdge(D, E, 1), | 73 | + new TestEdge(D, E, 1), |
74 | - new TestEdge(D, F, 1), | 74 | + new TestEdge(D, F, 1), |
75 | - new TestEdge(E, G, 1), | 75 | + new TestEdge(E, G, 1), |
76 | - new TestEdge(F, G, 1), | 76 | + new TestEdge(F, G, 1), |
77 | - new TestEdge(A, G, 4))); | 77 | + new TestEdge(A, G, 4))); |
78 | - executeSearch(graphSearch(), g, A, G, weight, 5, 4.0); | 78 | + executeSearch(graphSearch(), graph, A, G, weight, 5, 4.0); |
79 | } | 79 | } |
80 | 80 | ||
81 | @Test | 81 | @Test |
82 | public void dualEdgeMultiplePath() { | 82 | public void dualEdgeMultiplePath() { |
83 | - g = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G, H), | 83 | + graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G, H), |
84 | - of(new TestEdge(A, B, 1), new TestEdge(A, C, 3), | 84 | + of(new TestEdge(A, B, 1), new TestEdge(A, C, 3), |
85 | - new TestEdge(B, D, 2), new TestEdge(B, C, 1), | 85 | + new TestEdge(B, D, 2), new TestEdge(B, C, 1), |
86 | - new TestEdge(B, E, 4), new TestEdge(C, E, 1), | 86 | + new TestEdge(B, E, 4), new TestEdge(C, E, 1), |
87 | - new TestEdge(D, H, 5), new TestEdge(D, E, 1), | 87 | + new TestEdge(D, H, 5), new TestEdge(D, E, 1), |
88 | - new TestEdge(E, F, 1), new TestEdge(F, D, 1), | 88 | + new TestEdge(E, F, 1), new TestEdge(F, D, 1), |
89 | - new TestEdge(F, G, 1), new TestEdge(F, H, 1), | 89 | + new TestEdge(F, G, 1), new TestEdge(F, H, 1), |
90 | - new TestEdge(A, E, 3), new TestEdge(B, D, 1))); | 90 | + new TestEdge(A, E, 3), new TestEdge(B, D, 1))); |
91 | - executeSearch(graphSearch(), g, A, E, weight, 3, 3.0); | 91 | + executeSearch(graphSearch(), graph, A, E, weight, 3, 3.0); |
92 | } | 92 | } |
93 | 93 | ||
94 | } | 94 | } | ... | ... |
... | @@ -19,7 +19,7 @@ public class GraphTest { | ... | @@ -19,7 +19,7 @@ public class GraphTest { |
19 | static final TestVertex H = new TestVertex("H"); | 19 | static final TestVertex H = new TestVertex("H"); |
20 | static final TestVertex Z = new TestVertex("Z"); | 20 | static final TestVertex Z = new TestVertex("Z"); |
21 | 21 | ||
22 | - protected Graph<TestVertex, TestEdge> g; | 22 | + protected Graph<TestVertex, TestEdge> graph; |
23 | 23 | ||
24 | protected EdgeWeight<TestVertex, TestEdge> weight = | 24 | protected EdgeWeight<TestVertex, TestEdge> weight = |
25 | new EdgeWeight<TestVertex, TestEdge>() { | 25 | new EdgeWeight<TestVertex, TestEdge>() { |
... | @@ -35,7 +35,7 @@ public class GraphTest { | ... | @@ -35,7 +35,7 @@ public class GraphTest { |
35 | } | 35 | } |
36 | } | 36 | } |
37 | 37 | ||
38 | - protected Set<TestVertex> vertices() { | 38 | + protected Set<TestVertex> vertexes() { |
39 | return of(A, B, C, D, E, F, G, H); | 39 | return of(A, B, C, D, E, F, G, H); |
40 | } | 40 | } |
41 | 41 | ... | ... |
1 | +package org.onlab.graph; | ||
2 | + | ||
3 | +import org.junit.Test; | ||
4 | + | ||
5 | +import static com.google.common.collect.ImmutableSet.of; | ||
6 | +import static org.junit.Assert.assertEquals; | ||
7 | +import static org.onlab.graph.TarjanGraphSearch.SCCResult; | ||
8 | + | ||
9 | +/** | ||
10 | + * Tarjan graph search tests. | ||
11 | + */ | ||
12 | +public class TarjanGraphSearchTest extends GraphTest { | ||
13 | + | ||
14 | + private void validate(SCCResult<TestVertex, TestEdge> result, int cc) { | ||
15 | + System.out.println("Cluster count: " + result.clusterVertexes().size()); | ||
16 | + System.out.println("Clusters: " + result.clusterVertexes()); | ||
17 | + assertEquals("incorrect cluster count", cc, result.clusterCount()); | ||
18 | + } | ||
19 | + | ||
20 | + private void validate(SCCResult<TestVertex, TestEdge> result, | ||
21 | + int i, int vc, int ec) { | ||
22 | + assertEquals("incorrect cluster count", vc, result.clusterVertexes().get(i).size()); | ||
23 | + assertEquals("incorrect edge count", ec, result.clusterEdges().get(i).size()); | ||
24 | + } | ||
25 | + | ||
26 | + @Test | ||
27 | + public void basic() { | ||
28 | + graph = new AdjacencyListsGraph<>(vertexes(), edges()); | ||
29 | + TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>(); | ||
30 | + SCCResult<TestVertex, TestEdge> result = gs.search(graph, null); | ||
31 | + validate(result, 6); | ||
32 | + } | ||
33 | + | ||
34 | + @Test | ||
35 | + public void singleCluster() { | ||
36 | + graph = new AdjacencyListsGraph<>(vertexes(), | ||
37 | + of(new TestEdge(A, B, 1), | ||
38 | + new TestEdge(B, C, 1), | ||
39 | + new TestEdge(C, D, 1), | ||
40 | + new TestEdge(D, E, 1), | ||
41 | + new TestEdge(E, F, 1), | ||
42 | + new TestEdge(F, G, 1), | ||
43 | + new TestEdge(G, H, 1), | ||
44 | + new TestEdge(H, A, 1))); | ||
45 | + | ||
46 | + TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>(); | ||
47 | + SCCResult<TestVertex, TestEdge> result = gs.search(graph, null); | ||
48 | + validate(result, 1); | ||
49 | + validate(result, 0, 8, 8); | ||
50 | + } | ||
51 | + | ||
52 | + @Test | ||
53 | + public void twoUnconnectedCluster() { | ||
54 | + graph = new AdjacencyListsGraph<>(vertexes(), | ||
55 | + of(new TestEdge(A, B, 1), | ||
56 | + new TestEdge(B, C, 1), | ||
57 | + new TestEdge(C, D, 1), | ||
58 | + new TestEdge(D, A, 1), | ||
59 | + new TestEdge(E, F, 1), | ||
60 | + new TestEdge(F, G, 1), | ||
61 | + new TestEdge(G, H, 1), | ||
62 | + new TestEdge(H, E, 1))); | ||
63 | + TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>(); | ||
64 | + SCCResult<TestVertex, TestEdge> result = gs.search(graph, null); | ||
65 | + validate(result, 2); | ||
66 | + validate(result, 0, 4, 4); | ||
67 | + validate(result, 1, 4, 4); | ||
68 | + } | ||
69 | + | ||
70 | + @Test | ||
71 | + public void twoWeaklyConnectedClusters() { | ||
72 | + graph = new AdjacencyListsGraph<>(vertexes(), | ||
73 | + of(new TestEdge(A, B, 1), | ||
74 | + new TestEdge(B, C, 1), | ||
75 | + new TestEdge(C, D, 1), | ||
76 | + new TestEdge(D, A, 1), | ||
77 | + new TestEdge(E, F, 1), | ||
78 | + new TestEdge(F, G, 1), | ||
79 | + new TestEdge(G, H, 1), | ||
80 | + new TestEdge(H, E, 1), | ||
81 | + new TestEdge(B, E, 1))); | ||
82 | + TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>(); | ||
83 | + SCCResult<TestVertex, TestEdge> result = gs.search(graph, null); | ||
84 | + validate(result, 2); | ||
85 | + validate(result, 0, 4, 4); | ||
86 | + validate(result, 1, 4, 4); | ||
87 | + } | ||
88 | + | ||
89 | + @Test | ||
90 | + public void twoClustersConnectedWithIgnoredEdges() { | ||
91 | + graph = new AdjacencyListsGraph<>(vertexes(), | ||
92 | + of(new TestEdge(A, B, 1), | ||
93 | + new TestEdge(B, C, 1), | ||
94 | + new TestEdge(C, D, 1), | ||
95 | + new TestEdge(D, A, 1), | ||
96 | + new TestEdge(E, F, 1), | ||
97 | + new TestEdge(F, G, 1), | ||
98 | + new TestEdge(G, H, 1), | ||
99 | + new TestEdge(H, E, 1), | ||
100 | + new TestEdge(B, E, -1), | ||
101 | + new TestEdge(E, B, -1))); | ||
102 | + | ||
103 | + TarjanGraphSearch<TestVertex, TestEdge> gs = new TarjanGraphSearch<>(); | ||
104 | + SCCResult<TestVertex, TestEdge> result = gs.search(graph, weight); | ||
105 | + validate(result, 2); | ||
106 | + validate(result, 0, 4, 4); | ||
107 | + validate(result, 1, 4, 4); | ||
108 | + } | ||
109 | + | ||
110 | +} |
-
Please register or login to post a comment