blob: 72134977f143102da40d9e3a8015d664b1c3c8bd [file] [log] [blame]
tom8bf2e6b2014-09-10 20:53:54 -07001package org.onlab.onos.net.trivial.topology.impl;
tomdc361b62014-09-09 20:36:52 -07002
tome52ce702014-09-11 00:12:54 -07003import com.google.common.collect.ImmutableMap;
4import com.google.common.collect.ImmutableSet;
5import com.google.common.collect.ImmutableSetMultimap;
tom97937552014-09-11 10:48:42 -07006import org.onlab.graph.DijkstraGraphSearch;
tome52ce702014-09-11 00:12:54 -07007import org.onlab.graph.GraphPathSearch;
tom97937552014-09-11 10:48:42 -07008import org.onlab.graph.TarjanGraphSearch;
tomdc361b62014-09-09 20:36:52 -07009import org.onlab.onos.net.AbstractModel;
tome52ce702014-09-11 00:12:54 -070010import org.onlab.onos.net.ConnectPoint;
tom97937552014-09-11 10:48:42 -070011import org.onlab.onos.net.DefaultPath;
tome52ce702014-09-11 00:12:54 -070012import org.onlab.onos.net.DeviceId;
13import org.onlab.onos.net.Link;
14import org.onlab.onos.net.Path;
tomdc361b62014-09-09 20:36:52 -070015import org.onlab.onos.net.provider.ProviderId;
tom97937552014-09-11 10:48:42 -070016import org.onlab.onos.net.topology.ClusterId;
17import org.onlab.onos.net.topology.DefaultTopologyCluster;
18import org.onlab.onos.net.topology.GraphDescription;
19import org.onlab.onos.net.topology.LinkWeight;
tomdc361b62014-09-09 20:36:52 -070020import org.onlab.onos.net.topology.Topology;
tome52ce702014-09-11 00:12:54 -070021import org.onlab.onos.net.topology.TopologyCluster;
tom97937552014-09-11 10:48:42 -070022import org.onlab.onos.net.topology.TopologyEdge;
23import org.onlab.onos.net.topology.TopologyGraph;
24import org.onlab.onos.net.topology.TopologyVertex;
tome52ce702014-09-11 00:12:54 -070025
tom97937552014-09-11 10:48:42 -070026import java.util.ArrayList;
27import java.util.List;
28import java.util.Map;
tome52ce702014-09-11 00:12:54 -070029import java.util.Set;
tomdc361b62014-09-09 20:36:52 -070030
tom97937552014-09-11 10:48:42 -070031import static com.google.common.base.MoreObjects.toStringHelper;
32import static com.google.common.collect.ImmutableSetMultimap.Builder;
33import static org.onlab.graph.GraphPathSearch.Result;
34import static org.onlab.graph.TarjanGraphSearch.SCCResult;
35import static org.onlab.onos.net.Link.Type.INDIRECT;
36
tomdc361b62014-09-09 20:36:52 -070037/**
38 * Default implementation of the topology descriptor. This carries the
39 * backing topology data.
40 */
41public class DefaultTopology extends AbstractModel implements Topology {
42
tom97937552014-09-11 10:48:42 -070043 private static final DijkstraGraphSearch<TopologyVertex, TopologyEdge> DIJKSTRA =
44 new DijkstraGraphSearch<>();
45 private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN =
46 new TarjanGraphSearch<>();
47
48 private static final ProviderId PID = new ProviderId("org.onlab.onos.net");
49
tomdc361b62014-09-09 20:36:52 -070050 private final long time;
tom97937552014-09-11 10:48:42 -070051 private final TopologyGraph graph;
tomdc361b62014-09-09 20:36:52 -070052
tom97937552014-09-11 10:48:42 -070053 private final SCCResult<TopologyVertex, TopologyEdge> clusterResults;
54 private final ImmutableMap<DeviceId, Result<TopologyVertex, TopologyEdge>> results;
55 private final ImmutableSetMultimap<PathKey, Path> paths;
tome52ce702014-09-11 00:12:54 -070056
tom97937552014-09-11 10:48:42 -070057 private final ImmutableMap<ClusterId, TopologyCluster> clusters;
58 private final ImmutableSet<ConnectPoint> infrastructurePoints;
59 private final ImmutableSetMultimap<ClusterId, ConnectPoint> broadcastSets;
60
61 private ImmutableMap<DeviceId, TopologyCluster> clustersByDevice;
62 private ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
63 private ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
64
tome52ce702014-09-11 00:12:54 -070065
tomdc361b62014-09-09 20:36:52 -070066 /**
67 * Creates a topology descriptor attributed to the specified provider.
68 *
tome52ce702014-09-11 00:12:54 -070069 * @param providerId identity of the provider
70 * @param description data describing the new topology
tomdc361b62014-09-09 20:36:52 -070071 */
tom97937552014-09-11 10:48:42 -070072 DefaultTopology(ProviderId providerId, GraphDescription description) {
tomdc361b62014-09-09 20:36:52 -070073 super(providerId);
tome52ce702014-09-11 00:12:54 -070074 this.time = description.timestamp();
tome52ce702014-09-11 00:12:54 -070075
tom97937552014-09-11 10:48:42 -070076 // Build the graph
77 this.graph = new DefaultTopologyGraph(description.vertexes(),
78 description.edges());
79
80 this.results = searchForShortestPaths();
81 this.paths = buildPaths();
82
83 this.clusterResults = searchForClusters();
84 this.clusters = buildTopologyClusters();
85
86 buildIndexes();
87
88 this.broadcastSets = buildBroadcastSets();
89 this.infrastructurePoints = findInfrastructurePoints();
tomdc361b62014-09-09 20:36:52 -070090 }
91
92 @Override
93 public long time() {
94 return time;
95 }
96
97 @Override
98 public int clusterCount() {
tome52ce702014-09-11 00:12:54 -070099 return clusters.size();
tomdc361b62014-09-09 20:36:52 -0700100 }
101
102 @Override
103 public int deviceCount() {
tome52ce702014-09-11 00:12:54 -0700104 return graph.getVertexes().size();
tomdc361b62014-09-09 20:36:52 -0700105 }
106
107 @Override
108 public int linkCount() {
tome52ce702014-09-11 00:12:54 -0700109 return graph.getEdges().size();
tomdc361b62014-09-09 20:36:52 -0700110 }
111
112 @Override
113 public int pathCount() {
tom97937552014-09-11 10:48:42 -0700114 return paths.size();
tomdc361b62014-09-09 20:36:52 -0700115 }
116
tom97937552014-09-11 10:48:42 -0700117 /**
118 * Returns the backing topology graph.
119 *
120 * @return topology graph
121 */
122 TopologyGraph getGraph() {
tome52ce702014-09-11 00:12:54 -0700123 return graph;
124 }
125
tom97937552014-09-11 10:48:42 -0700126 /**
127 * Returns the set of topology clusters.
128 *
129 * @return set of clusters
130 */
131 Set<TopologyCluster> getClusters() {
132 return ImmutableSet.copyOf(clusters.values());
133 }
134
135 /**
tom13cb4852014-09-11 12:44:17 -0700136 * Returns the specified topology cluster.
137 *
138 * @param clusterId cluster identifier
139 * @return topology cluster
140 */
141 TopologyCluster getCluster(ClusterId clusterId) {
142 return clusters.get(clusterId);
143 }
144
tomdc95b8a2014-09-17 08:07:26 -0700145 /**
146 * Returns the topology cluster that contains the given device.
147 *
148 * @param deviceId device identifier
149 * @return topology cluster
150 */
151 TopologyCluster getCluster(DeviceId deviceId) {
152 return clustersByDevice.get(deviceId);
153 }
tom13cb4852014-09-11 12:44:17 -0700154
155 /**
tom97937552014-09-11 10:48:42 -0700156 * Returns the set of cluster devices.
157 *
158 * @param cluster topology cluster
159 * @return cluster devices
160 */
161 Set<DeviceId> getClusterDevices(TopologyCluster cluster) {
162 return devicesByCluster.get(cluster);
163 }
164
165 /**
166 * Returns the set of cluster links.
167 *
168 * @param cluster topology cluster
169 * @return cluster links
170 */
171 Set<Link> getClusterLinks(TopologyCluster cluster) {
172 return linksByCluster.get(cluster);
173 }
174
175 /**
176 * Indicates whether the given point is an infrastructure link end-point.
177 *
178 * @param connectPoint connection point
179 * @return true if infrastructure
180 */
tome52ce702014-09-11 00:12:54 -0700181 boolean isInfrastructure(ConnectPoint connectPoint) {
tom97937552014-09-11 10:48:42 -0700182 return infrastructurePoints.contains(connectPoint);
tome52ce702014-09-11 00:12:54 -0700183 }
184
tom97937552014-09-11 10:48:42 -0700185 /**
tomdc95b8a2014-09-17 08:07:26 -0700186 * Indicates whether the given point is part of a broadcast set.
tom97937552014-09-11 10:48:42 -0700187 *
188 * @param connectPoint connection point
tomdc95b8a2014-09-17 08:07:26 -0700189 * @return true if in broadcast set
tom97937552014-09-11 10:48:42 -0700190 */
tomdc95b8a2014-09-17 08:07:26 -0700191 boolean isBroadcastPoint(ConnectPoint connectPoint) {
192 // Any non-infrastructure, i.e. edge points are assumed to be OK.
tom97937552014-09-11 10:48:42 -0700193 if (!isInfrastructure(connectPoint)) {
194 return true;
195 }
196
197 // Find the cluster to which the device belongs.
198 TopologyCluster cluster = clustersByDevice.get(connectPoint.deviceId());
199 if (cluster == null) {
200 throw new IllegalArgumentException("No cluster found for device " + connectPoint.deviceId());
201 }
202
tomdc95b8a2014-09-17 08:07:26 -0700203 // If the broadcast set is null or empty, or if the point explicitly
204 // belongs to it, return true;
tom97937552014-09-11 10:48:42 -0700205 Set<ConnectPoint> points = broadcastSets.get(cluster.id());
206 return points == null || points.isEmpty() || points.contains(connectPoint);
tome52ce702014-09-11 00:12:54 -0700207 }
208
tom97937552014-09-11 10:48:42 -0700209 /**
tomdc95b8a2014-09-17 08:07:26 -0700210 * Returns the size of the cluster broadcast set.
211 *
212 * @param clusterId cluster identifier
213 * @return size of the cluster broadcast set
214 */
215 int broadcastSetSize(ClusterId clusterId) {
216 return broadcastSets.get(clusterId).size();
217 }
218
219 /**
tom97937552014-09-11 10:48:42 -0700220 * Returns the set of pre-computed shortest paths between source and
221 * destination devices.
222 *
223 * @param src source device
224 * @param dst destination device
225 * @return set of shortest paths
226 */
tome52ce702014-09-11 00:12:54 -0700227 Set<Path> getPaths(DeviceId src, DeviceId dst) {
tom97937552014-09-11 10:48:42 -0700228 return paths.get(new PathKey(src, dst));
229 }
230
231 /**
232 * Computes on-demand the set of shortest paths between source and
233 * destination devices.
234 *
235 * @param src source device
236 * @param dst destination device
237 * @return set of shortest paths
238 */
239 Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeight weight) {
240 GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
241 DIJKSTRA.search(graph, new DefaultTopologyVertex(src),
242 new DefaultTopologyVertex(dst), weight);
243 ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
244 for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
245 builder.add(networkPath(path));
246 }
247 return builder.build();
248 }
249
250
251 // Searches the graph for all shortest paths and returns the search results.
252 private ImmutableMap<DeviceId, Result<TopologyVertex, TopologyEdge>> searchForShortestPaths() {
253 ImmutableMap.Builder<DeviceId, Result<TopologyVertex, TopologyEdge>> builder = ImmutableMap.builder();
254
255 // Search graph paths for each source to all destinations.
256 LinkWeight weight = new HopCountLinkWeight(graph.getVertexes().size());
257 for (TopologyVertex src : graph.getVertexes()) {
258 builder.put(src.deviceId(), DIJKSTRA.search(graph, src, null, weight));
259 }
260 return builder.build();
261 }
262
263 // Builds network paths from the graph path search results
264 private ImmutableSetMultimap<PathKey, Path> buildPaths() {
265 Builder<PathKey, Path> builder = ImmutableSetMultimap.builder();
266 for (DeviceId deviceId : results.keySet()) {
267 Result<TopologyVertex, TopologyEdge> result = results.get(deviceId);
268 for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
269 builder.put(new PathKey(path.src().deviceId(), path.dst().deviceId()),
270 networkPath(path));
271 }
272 }
273 return builder.build();
274 }
275
276 // Converts graph path to a network path with the same cost.
277 private Path networkPath(org.onlab.graph.Path<TopologyVertex, TopologyEdge> path) {
278 List<Link> links = new ArrayList<>();
279 for (TopologyEdge edge : path.edges()) {
280 links.add(edge.link());
281 }
282 return new DefaultPath(PID, links, path.cost());
283 }
284
285
286 // Searches for SCC clusters in the network topology graph using Tarjan
287 // algorithm.
288 private SCCResult<TopologyVertex, TopologyEdge> searchForClusters() {
289 return TARJAN.search(graph, new NoIndirectLinksWeight());
290 }
291
292 // Builds the topology clusters and returns the id-cluster bindings.
293 private ImmutableMap<ClusterId, TopologyCluster> buildTopologyClusters() {
294 ImmutableMap.Builder<ClusterId, TopologyCluster> clusterBuilder = ImmutableMap.builder();
295 SCCResult<TopologyVertex, TopologyEdge> result =
296 TARJAN.search(graph, new NoIndirectLinksWeight());
297
298 // Extract both vertexes and edges from the results; the lists form
299 // pairs along the same index.
300 List<Set<TopologyVertex>> clusterVertexes = result.clusterVertexes();
301 List<Set<TopologyEdge>> clusterEdges = result.clusterEdges();
302
303 // Scan over the lists and create a cluster from the results.
304 for (int i = 0, n = result.clusterCount(); i < n; i++) {
305 Set<TopologyVertex> vertexSet = clusterVertexes.get(i);
306 Set<TopologyEdge> edgeSet = clusterEdges.get(i);
307
308 ClusterId cid = ClusterId.clusterId(i);
309 DefaultTopologyCluster cluster =
310 new DefaultTopologyCluster(cid, vertexSet.size(), edgeSet.size(),
311 findRoot(vertexSet).deviceId());
312 clusterBuilder.put(cid, cluster);
313 }
314 return clusterBuilder.build();
315 }
316
317 // Finds the vertex whose device id is the lexicographical minimum in the
318 // specified set.
319 private TopologyVertex findRoot(Set<TopologyVertex> vertexSet) {
320 TopologyVertex minVertex = null;
321 for (TopologyVertex vertex : vertexSet) {
322 if (minVertex == null ||
323 minVertex.deviceId().toString()
324 .compareTo(minVertex.deviceId().toString()) < 0) {
325 minVertex = vertex;
326 }
327 }
328 return minVertex;
329 }
330
331 // Processes a map of broadcast sets for each cluster.
332 private ImmutableSetMultimap<ClusterId, ConnectPoint> buildBroadcastSets() {
333 Builder<ClusterId, ConnectPoint> builder = ImmutableSetMultimap.builder();
334 for (TopologyCluster cluster : clusters.values()) {
335 addClusterBroadcastSet(cluster, builder);
336 }
337 return builder.build();
338 }
339
340 // Finds all broadcast points for the cluster. These are those connection
341 // points which lie along the shortest paths between the cluster root and
342 // all other devices within the cluster.
343 private void addClusterBroadcastSet(TopologyCluster cluster,
344 Builder<ClusterId, ConnectPoint> builder) {
345 // Use the graph root search results to build the broadcast set.
346 Result<TopologyVertex, TopologyEdge> result = results.get(cluster.root());
347 for (Map.Entry<TopologyVertex, Set<TopologyEdge>> entry : result.parents().entrySet()) {
348 TopologyVertex vertex = entry.getKey();
349
350 // Ignore any parents that lead outside the cluster.
351 if (clustersByDevice.get(vertex.deviceId()) != cluster) {
352 continue;
353 }
354
355 // Ignore any back-link sets that are empty.
356 Set<TopologyEdge> parents = entry.getValue();
357 if (parents.isEmpty()) {
358 continue;
359 }
360
361 // Use the first back-link source and destinations to add to the
362 // broadcast set.
363 Link link = parents.iterator().next().link();
364 builder.put(cluster.id(), link.src());
365 builder.put(cluster.id(), link.dst());
366 }
367 }
368
369 // Collects and returns an set of all infrastructure link end-points.
370 private ImmutableSet<ConnectPoint> findInfrastructurePoints() {
371 ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
372 for (TopologyEdge edge : graph.getEdges()) {
373 builder.add(edge.link().src());
374 builder.add(edge.link().dst());
375 }
376 return builder.build();
377 }
378
379 // Builds cluster-devices, cluster-links and device-cluster indexes.
380 private void buildIndexes() {
381 // Prepare the index builders
382 ImmutableMap.Builder<DeviceId, TopologyCluster> clusterBuilder = ImmutableMap.builder();
383 ImmutableSetMultimap.Builder<TopologyCluster, DeviceId> devicesBuilder = ImmutableSetMultimap.builder();
384 ImmutableSetMultimap.Builder<TopologyCluster, Link> linksBuilder = ImmutableSetMultimap.builder();
385
386 // Now scan through all the clusters
387 for (TopologyCluster cluster : clusters.values()) {
388 int i = cluster.id().index();
389
390 // Scan through all the cluster vertexes.
391 for (TopologyVertex vertex : clusterResults.clusterVertexes().get(i)) {
392 devicesBuilder.put(cluster, vertex.deviceId());
393 clusterBuilder.put(vertex.deviceId(), cluster);
394 }
395
396 // Scan through all the cluster edges.
397 for (TopologyEdge edge : clusterResults.clusterEdges().get(i)) {
398 linksBuilder.put(cluster, edge.link());
399 }
400 }
401
402 // Finalize all indexes.
403 clustersByDevice = clusterBuilder.build();
404 devicesByCluster = devicesBuilder.build();
405 linksByCluster = linksBuilder.build();
406 }
407
408 // Link weight for measuring link cost as hop count with indirect links
409 // being as expensive as traversing the entire graph to assume the worst.
410 private static class HopCountLinkWeight implements LinkWeight {
411 private final int indirectLinkCost;
412
413 HopCountLinkWeight(int indirectLinkCost) {
414 this.indirectLinkCost = indirectLinkCost;
415 }
416
417 @Override
418 public double weight(TopologyEdge edge) {
419 // To force preference to use direct paths first, make indirect
420 // links as expensive as the linear vertex traversal.
421 return edge.link().type() == INDIRECT ? indirectLinkCost : 1;
422 }
423 }
424
425 // Link weight for preventing traversal over indirect links.
426 private static class NoIndirectLinksWeight implements LinkWeight {
427 @Override
428 public double weight(TopologyEdge edge) {
429 return edge.link().type() == INDIRECT ? -1 : 1;
430 }
431 }
432
433 @Override
434 public String toString() {
435 return toStringHelper(this)
436 .add("time", time)
437 .add("clusters", clusterCount())
438 .add("devices", deviceCount())
439 .add("links", linkCount())
440 .add("pathCount", pathCount())
441 .toString();
tome52ce702014-09-11 00:12:54 -0700442 }
tomdc361b62014-09-09 20:36:52 -0700443}