blob: 87d2c470774d93f15958fb546a5a37ca119097b7 [file] [log] [blame]
tom8bf2e6b2014-09-10 20:53:54 -07001package org.onlab.onos.net.trivial.topology.provider.impl;
tomcfde0622014-09-09 11:02:42 -07002
3import com.google.common.collect.ImmutableSet;
tomcbff9392014-09-10 00:45:23 -07004import com.google.common.collect.Maps;
tom8bf2e6b2014-09-10 20:53:54 -07005import com.google.common.collect.Multimap;
tomcbff9392014-09-10 00:45:23 -07006import com.google.common.collect.Sets;
7import org.onlab.graph.AdjacencyListsGraph;
8import org.onlab.graph.DijkstraGraphSearch;
tomcfde0622014-09-09 11:02:42 -07009import org.onlab.graph.Graph;
10import org.onlab.graph.GraphPathSearch;
tom8bf2e6b2014-09-10 20:53:54 -070011import org.onlab.graph.TarjanGraphSearch;
tomcbff9392014-09-10 00:45:23 -070012import org.onlab.onos.net.ConnectPoint;
13import org.onlab.onos.net.Device;
tomcfde0622014-09-09 11:02:42 -070014import org.onlab.onos.net.DeviceId;
15import org.onlab.onos.net.Link;
16import org.onlab.onos.net.topology.ClusterId;
tom8bf2e6b2014-09-10 20:53:54 -070017import org.onlab.onos.net.topology.DefaultTopologyCluster;
tomcbff9392014-09-10 00:45:23 -070018import org.onlab.onos.net.topology.LinkWeight;
tomcfde0622014-09-09 11:02:42 -070019import org.onlab.onos.net.topology.TopoEdge;
20import org.onlab.onos.net.topology.TopoVertex;
21import org.onlab.onos.net.topology.TopologyCluster;
22import org.onlab.onos.net.topology.TopologyDescription;
23
tom8bf2e6b2014-09-10 20:53:54 -070024import java.util.HashSet;
25import java.util.List;
tomcfde0622014-09-09 11:02:42 -070026import java.util.Map;
tomcbff9392014-09-10 00:45:23 -070027import java.util.Objects;
tomcfde0622014-09-09 11:02:42 -070028import java.util.Set;
29
tomcbff9392014-09-10 00:45:23 -070030import static com.google.common.base.MoreObjects.toStringHelper;
31import static org.onlab.graph.GraphPathSearch.Result;
tom8bf2e6b2014-09-10 20:53:54 -070032import static org.onlab.graph.TarjanGraphSearch.SCCResult;
tomcbff9392014-09-10 00:45:23 -070033import static org.onlab.onos.net.Link.Type.INDIRECT;
34
tomcfde0622014-09-09 11:02:42 -070035/**
36 * Default implementation of an immutable topology data carrier.
37 */
tomcbff9392014-09-10 00:45:23 -070038class DefaultTopologyDescription implements TopologyDescription {
39
40 private static final GraphPathSearch<TopoVertex, TopoEdge> DIJKSTRA =
41 new DijkstraGraphSearch<>();
tom8bf2e6b2014-09-10 20:53:54 -070042 private static final TarjanGraphSearch<TopoVertex, TopoEdge> TARJAN =
43 new TarjanGraphSearch<>();
tomcfde0622014-09-09 11:02:42 -070044
45 private final long nanos;
tomcbff9392014-09-10 00:45:23 -070046 private final Map<DeviceId, TopoVertex> vertexesById = Maps.newHashMap();
tomcfde0622014-09-09 11:02:42 -070047 private final Graph<TopoVertex, TopoEdge> graph;
tomcbff9392014-09-10 00:45:23 -070048 private final Map<DeviceId, Result<TopoVertex, TopoEdge>> results;
tomcfde0622014-09-09 11:02:42 -070049 private final Map<ClusterId, TopologyCluster> clusters;
tomcfde0622014-09-09 11:02:42 -070050
tom8bf2e6b2014-09-10 20:53:54 -070051 // Secondary look-up indexes
52 private Multimap<ClusterId, DeviceId> devicesByCluster;
53 private Multimap<ClusterId, Link> linksByCluster;
54 private Map<DeviceId, TopologyCluster> clustersByDevice;
tomcbff9392014-09-10 00:45:23 -070055
tom8bf2e6b2014-09-10 20:53:54 -070056 /**
57 * Creates a topology description to carry topology vitals to the core.
58 *
59 * @param nanos time in nanos of when the topology description was created
60 * @param devices collection of devices
61 * @param links
62 */
tomcbff9392014-09-10 00:45:23 -070063 DefaultTopologyDescription(long nanos, Iterable<Device> devices, Iterable<Link> links) {
tomcfde0622014-09-09 11:02:42 -070064 this.nanos = nanos;
tomcbff9392014-09-10 00:45:23 -070065 this.graph = buildGraph(devices, links);
66 this.results = computeDefaultPaths();
67 this.clusters = computeClusters();
tomcbff9392014-09-10 00:45:23 -070068 }
69
70 // Constructs the topology graph using the supplied devices and links.
71 private Graph<TopoVertex, TopoEdge> buildGraph(Iterable<Device> devices,
72 Iterable<Link> links) {
73 Graph<TopoVertex, TopoEdge> graph =
74 new AdjacencyListsGraph<>(buildVertexes(devices),
75 buildEdges(links));
76 return graph;
77 }
78
79 // Builds a set of topology vertexes from the specified list of devices
80 private Set<TopoVertex> buildVertexes(Iterable<Device> devices) {
81 Set<TopoVertex> vertexes = Sets.newHashSet();
82 for (Device device : devices) {
83 TopoVertex vertex = new TVertex(device.id());
84 vertexesById.put(vertex.deviceId(), vertex);
85 vertexes.add(vertex);
86 }
87 return vertexes;
88 }
89
90 // Builds a set of topology vertexes from the specified list of links
91 private Set<TopoEdge> buildEdges(Iterable<Link> links) {
92 Set<TopoEdge> edges = Sets.newHashSet();
93 for (Link link : links) {
94 edges.add(new TEdge(vertexOf(link.src()), vertexOf(link.dst()), link));
95 }
96 return edges;
97 }
98
99 // Computes the default shortest paths for all source/dest pairs using
100 // the multi-path Dijkstra and hop-count as path cost.
101 private Map<DeviceId, Result<TopoVertex, TopoEdge>> computeDefaultPaths() {
102 LinkWeight weight = new HopCountLinkWeight(graph.getVertexes().size());
103 Map<DeviceId, Result<TopoVertex, TopoEdge>> results = Maps.newHashMap();
104
105 // Search graph paths for each source to all destinations.
106 for (TopoVertex src : vertexesById.values()) {
107 results.put(src.deviceId(), DIJKSTRA.search(graph, src, null, weight));
108 }
109 return results;
110 }
111
112 // Computes topology SCC clusters using Tarjan algorithm.
113 private Map<ClusterId, TopologyCluster> computeClusters() {
114 Map<ClusterId, TopologyCluster> clusters = Maps.newHashMap();
tom8bf2e6b2014-09-10 20:53:54 -0700115 SCCResult<TopoVertex, TopoEdge> result = TARJAN.search(graph, new NoIndirectLinksWeight());
116
117 // Extract both vertexes and edges from the results; the lists form
118 // pairs along the same index.
119 List<Set<TopoVertex>> clusterVertexes = result.clusterVertexes();
120 List<Set<TopoEdge>> clusterEdges = result.clusterEdges();
121
122 // Scan over the lists and create a cluster from the results.
123 for (int i = 0, n = result.clusterCount(); i < n; i++) {
124 Set<TopoVertex> vertexSet = clusterVertexes.get(i);
125 Set<TopoEdge> edgeSet = clusterEdges.get(i);
126
127 DefaultTopologyCluster cluster =
128 new DefaultTopologyCluster(ClusterId.clusterId(i),
129 vertexSet.size(), edgeSet.size(),
130 findRoot(vertexSet).deviceId());
131
132 findClusterDevices(vertexSet, cluster);
133 findClusterLinks(edgeSet, cluster);
134 }
tomcbff9392014-09-10 00:45:23 -0700135 return clusters;
136 }
137
tom8bf2e6b2014-09-10 20:53:54 -0700138 // Scan through the set of cluster vertices and convert it to a set of
139 // device ids; register the cluster by device id as well.
140 private void findClusterDevices(Set<TopoVertex> vertexSet,
141 DefaultTopologyCluster cluster) {
142 Set<DeviceId> ids = new HashSet<>(vertexSet.size());
143 for (TopoVertex v : vertexSet) {
144 DeviceId deviceId = v.deviceId();
145 devicesByCluster.put(cluster.id(), deviceId);
146 clustersByDevice.put(deviceId, cluster);
147 }
148 }
149
150 private void findClusterLinks(Set<TopoEdge> edgeSet,
151 DefaultTopologyCluster cluster) {
152 for (TopoEdge e : edgeSet) {
153 linksByCluster.put(cluster.id(), e.link());
154 }
155 }
156
157 // Finds the vertex whose device id is the lexicographical minimum in the
158 // specified set.
159 private TopoVertex findRoot(Set<TopoVertex> vertexSet) {
160 TopoVertex minVertex = null;
161 for (TopoVertex vertex : vertexSet) {
162 if (minVertex == null ||
163 minVertex.deviceId().toString()
164 .compareTo(minVertex.deviceId().toString()) < 0) {
165 minVertex = vertex;
166 }
167 }
168 return minVertex;
169 }
170
tomcbff9392014-09-10 00:45:23 -0700171 // Fetches a vertex corresponding to the given connection point device.
172 private TopoVertex vertexOf(ConnectPoint connectPoint) {
173 DeviceId id = connectPoint.deviceId();
174 TopoVertex vertex = vertexesById.get(id);
175 if (vertex == null) {
176 // If vertex does not exist, create one and register it.
177 vertex = new TVertex(id);
178 vertexesById.put(id, vertex);
179 }
180 return vertex;
tomcfde0622014-09-09 11:02:42 -0700181 }
182
183 @Override
184 public long timestamp() {
185 return nanos;
186 }
187
188 @Override
189 public Graph<TopoVertex, TopoEdge> graph() {
190 return graph;
191 }
192
193 @Override
tomcbff9392014-09-10 00:45:23 -0700194 public Result<TopoVertex, TopoEdge> pathResults(DeviceId srcDeviceId) {
tomcfde0622014-09-09 11:02:42 -0700195 return results.get(srcDeviceId);
196 }
197
198 @Override
199 public Set<TopologyCluster> clusters() {
200 return ImmutableSet.copyOf(clusters.values());
201 }
202
203 @Override
204 public Set<DeviceId> clusterDevices(TopologyCluster cluster) {
205 return null; // clusterDevices.get(cluster.id());
206 }
207
208 @Override
209 public Set<Link> clusterLinks(TopologyCluster cluster) {
210 return null; // clusterLinks.get(cluster.id());
211 }
212
213 @Override
214 public TopologyCluster clusterFor(DeviceId deviceId) {
tomcbff9392014-09-10 00:45:23 -0700215 return null; // deviceClusters.get(deviceId);
tomcfde0622014-09-09 11:02:42 -0700216 }
tomcbff9392014-09-10 00:45:23 -0700217
218 // Implementation of the topology vertex backed by a device id
219 private static class TVertex implements TopoVertex {
220
221 private final DeviceId deviceId;
222
223 public TVertex(DeviceId deviceId) {
224 this.deviceId = deviceId;
225 }
226
227 @Override
228 public DeviceId deviceId() {
229 return deviceId;
230 }
231
232 @Override
233 public int hashCode() {
234 return Objects.hash(deviceId);
235 }
236
237 @Override
238 public boolean equals(Object obj) {
239 if (obj instanceof TVertex) {
240 final TVertex other = (TVertex) obj;
241 return Objects.equals(this.deviceId, other.deviceId);
242 }
243 return false;
244 }
245
246 @Override
247 public String toString() {
248 return deviceId.toString();
249 }
250 }
251
252 // Implementation of the topology edge backed by a link
253 private class TEdge implements TopoEdge {
254 private final Link link;
255 private final TopoVertex src;
256 private final TopoVertex dst;
257
258 public TEdge(TopoVertex src, TopoVertex dst, Link link) {
259 this.src = src;
260 this.dst = dst;
261 this.link = link;
262 }
263
264 @Override
265 public Link link() {
266 return link;
267 }
268
269 @Override
270 public TopoVertex src() {
271 return src;
272 }
273
274 @Override
275 public TopoVertex dst() {
276 return dst;
277 }
278
279 @Override
280 public int hashCode() {
281 return Objects.hash(link);
282 }
283
284 @Override
285 public boolean equals(Object obj) {
286 if (obj instanceof TEdge) {
287 final TEdge other = (TEdge) obj;
288 return Objects.equals(this.link, other.link);
289 }
290 return false;
291 }
292
293 @Override
294 public String toString() {
295 return toStringHelper(this).add("src", src).add("dst", dst).toString();
296 }
297 }
298
299 // Link weight for measuring link cost as hop count with indirect links
300 // being as expensive as traversing the entire graph to assume the worst.
tom8bf2e6b2014-09-10 20:53:54 -0700301 private static class HopCountLinkWeight implements LinkWeight {
tomcbff9392014-09-10 00:45:23 -0700302 private final int indirectLinkCost;
303
304 public HopCountLinkWeight(int indirectLinkCost) {
305 this.indirectLinkCost = indirectLinkCost;
306 }
307
308 @Override
309 public double weight(TopoEdge edge) {
310 // To force preference to use direct paths first, make indirect
311 // links as expensive as the linear vertex traversal.
312 return edge.link().type() == INDIRECT ? indirectLinkCost : 1;
313 }
314 }
315
tom8bf2e6b2014-09-10 20:53:54 -0700316 // Link weight for preventing traversal over indirect links.
317 private static class NoIndirectLinksWeight implements LinkWeight {
318 @Override
319 public double weight(TopoEdge edge) {
320 return edge.link().type() == INDIRECT ? -1 : 1;
321 }
322 }
323
tomcfde0622014-09-09 11:02:42 -0700324}