alshabib

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Showing 23 changed files with 1227 additions and 70 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;
79 } 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 + }
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";
...@@ -146,7 +146,8 @@ implements LinkService, LinkAdminService, LinkProviderRegistry { ...@@ -146,7 +146,8 @@ 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 + extends AbstractProviderService<LinkProvider>
150 implements LinkProviderService { 151 implements LinkProviderService {
151 152
152 InternalLinkProviderService(LinkProvider provider) { 153 InternalLinkProviderService(LinkProvider 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 + if (event != null) {
164 + log.debug("Link {} detected", linkDescription);
163 post(event); 165 post(event);
164 } 166 }
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());
175 + if (event != null) {
176 + log.info("Link {} vanished", linkDescription);
173 post(event); 177 post(event);
174 } 178 }
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");
156 +
157 + log.info("Topology changed due to: {}", // to be removed soon
158 + reasons == null ? "initial compute" : reasons);
159 + TopologyEvent event = store.updateTopology(topoDescription, reasons);
160 + if (event != null) {
150 log.info("Topology changed due to: {}", 161 log.info("Topology changed due to: {}",
151 reasons == null ? "initial compute" : reasons); 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.
22 + */
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
6 */ 62 */
7 -public class SimpleTopologyStore { 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,17 +55,17 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest { ...@@ -55,17 +55,17 @@ 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),
...@@ -75,12 +75,12 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest { ...@@ -75,12 +75,12 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest {
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),
...@@ -88,7 +88,7 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest { ...@@ -88,7 +88,7 @@ public class DijkstraGraphSearchTest extends BreadthFirstSearchTest {
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 +}