blob: 53bebb6f741ae0502a71f88e4405c9c968550fc0 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070016package org.onosproject.common;
alshabib339a3d92014-09-26 17:54:32 -070017
Thomas Vachuskab2c47a72015-08-05 14:22:54 -070018import com.google.common.base.Function;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070019import com.google.common.base.Supplier;
20import com.google.common.base.Suppliers;
21import com.google.common.collect.ImmutableMap;
22import com.google.common.collect.ImmutableSet;
23import com.google.common.collect.ImmutableSetMultimap;
24import com.google.common.collect.ImmutableSetMultimap.Builder;
Andrey Komarov2398d962016-09-26 15:11:23 +030025import org.onlab.graph.DefaultEdgeWeigher;
alshabib339a3d92014-09-26 17:54:32 -070026import org.onlab.graph.DijkstraGraphSearch;
Thomas Vachuska48e64e42015-09-22 15:32:55 -070027import org.onlab.graph.DisjointPathPair;
alshabib339a3d92014-09-26 17:54:32 -070028import org.onlab.graph.GraphPathSearch;
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -080029import org.onlab.graph.GraphPathSearch.Result;
Andrey Komarov2398d962016-09-26 15:11:23 +030030import org.onlab.graph.ScalarWeight;
Jonathan Hartd9df7bd2015-11-10 17:10:25 -080031import org.onlab.graph.SrlgGraphSearch;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -070032import org.onlab.graph.SuurballeGraphSearch;
alshabib339a3d92014-09-26 17:54:32 -070033import org.onlab.graph.TarjanGraphSearch;
Jonathan Hartd9df7bd2015-11-10 17:10:25 -080034import org.onlab.graph.TarjanGraphSearch.SccResult;
Andrey Komarov2398d962016-09-26 15:11:23 +030035import org.onlab.graph.Weight;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.net.AbstractModel;
37import org.onosproject.net.ConnectPoint;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -070038import org.onosproject.net.DefaultDisjointPath;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.net.DefaultPath;
40import org.onosproject.net.DeviceId;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -070041import org.onosproject.net.DisjointPath;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.net.Link;
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -070043import org.onosproject.net.Link.Type;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import org.onosproject.net.Path;
45import org.onosproject.net.provider.ProviderId;
46import org.onosproject.net.topology.ClusterId;
47import org.onosproject.net.topology.DefaultTopologyCluster;
48import org.onosproject.net.topology.DefaultTopologyVertex;
49import org.onosproject.net.topology.GraphDescription;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080050import org.onosproject.net.topology.HopCountLinkWeight;
Andrey Komarov2398d962016-09-26 15:11:23 +030051import org.onosproject.net.topology.LinkWeigher;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.topology.Topology;
53import org.onosproject.net.topology.TopologyCluster;
54import org.onosproject.net.topology.TopologyEdge;
55import org.onosproject.net.topology.TopologyGraph;
56import org.onosproject.net.topology.TopologyVertex;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080057import org.slf4j.Logger;
58import org.slf4j.LoggerFactory;
alshabib339a3d92014-09-26 17:54:32 -070059
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -070060import java.util.HashMap;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070061import java.util.List;
62import java.util.Map;
63import java.util.Set;
Thomas Vachuska48e64e42015-09-22 15:32:55 -070064import java.util.stream.Collectors;
alshabib339a3d92014-09-26 17:54:32 -070065
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070066import static com.google.common.base.MoreObjects.toStringHelper;
Thomas Vachuska320c58f2015-08-05 10:42:32 -070067import static com.google.common.base.Preconditions.checkArgument;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070068import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
Thomas Vachuska320c58f2015-08-05 10:42:32 -070069import static org.onlab.util.Tools.isNullOrEmpty;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070070import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070071import static org.onosproject.net.Link.State.INACTIVE;
72import static org.onosproject.net.Link.Type.INDIRECT;
Andrey Komarov2398d962016-09-26 15:11:23 +030073import static org.onosproject.net.topology.AdapterLinkWeigher.adapt;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070074
alshabib339a3d92014-09-26 17:54:32 -070075/**
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -050076 * Default implementation of the topology descriptor. This carries the backing
77 * topology data.
alshabib339a3d92014-09-26 17:54:32 -070078 */
79public class DefaultTopology extends AbstractModel implements Topology {
80
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080081 private static final Logger log = LoggerFactory.getLogger(DefaultTopology.class);
82
Andrey Komarov2398d962016-09-26 15:11:23 +030083 private static final DijkstraGraphSearch<TopologyVertex, TopologyEdge> DIJKSTRA =
84 new DijkstraGraphSearch<>();
85 private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN =
86 new TarjanGraphSearch<>();
87 private static final SuurballeGraphSearch<TopologyVertex, TopologyEdge> SUURBALLE =
88 new SuurballeGraphSearch<>();
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -070089
Andrey Komarov2398d962016-09-26 15:11:23 +030090 private static LinkWeigher defaultLinkWeigher = null;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080091 private static GraphPathSearch<TopologyVertex, TopologyEdge> defaultGraphPathSearch = null;
alshabib339a3d92014-09-26 17:54:32 -070092
alshabib339a3d92014-09-26 17:54:32 -070093 private final long time;
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -050094 private final long creationTime;
Thomas Vachuska6b7920d2014-11-25 19:48:39 -080095 private final long computeCost;
alshabib339a3d92014-09-26 17:54:32 -070096 private final TopologyGraph graph;
97
Andrey Komarov2398d962016-09-26 15:11:23 +030098 private final LinkWeigher hopCountWeigher;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080099
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800100 private final Supplier<SccResult<TopologyVertex, TopologyEdge>> clusterResults;
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800101 private final Supplier<ImmutableMap<ClusterId, TopologyCluster>> clusters;
102 private final Supplier<ImmutableSet<ConnectPoint>> infrastructurePoints;
103 private final Supplier<ImmutableSetMultimap<ClusterId, ConnectPoint>> broadcastSets;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700104 private final Function<ConnectPoint, Boolean> broadcastFunction;
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800105 private final Supplier<ClusterIndexes> clusterIndexes;
alshabib339a3d92014-09-26 17:54:32 -0700106
107 /**
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800108 * Sets the default link-weight to be used when computing paths. If null is
109 * specified, the builtin default link-weight measuring hop-counts will be
110 * used.
111 *
Andrey Komarov2398d962016-09-26 15:11:23 +0300112 * @param linkWeigher new default link-weight
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800113 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300114 public static void setDefaultLinkWeigher(LinkWeigher linkWeigher) {
115 log.info("Setting new default link-weight function to {}", linkWeigher);
116 defaultLinkWeigher = linkWeigher;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800117 }
118
119 /**
120 * Sets the default lpath search algorighm to be used when computing paths.
121 * If null is specified, the builtin default Dijkstra will be used.
122 *
123 * @param graphPathSearch new default algorithm
124 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300125 public static void setDefaultGraphPathSearch(
126 GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800127 log.info("Setting new default graph path algorithm to {}", graphPathSearch);
128 defaultGraphPathSearch = graphPathSearch;
129 }
130
131
132 /**
alshabib339a3d92014-09-26 17:54:32 -0700133 * Creates a topology descriptor attributed to the specified provider.
134 *
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700135 * @param providerId identity of the provider
136 * @param description data describing the new topology
137 * @param broadcastFunction broadcast point function
alshabib339a3d92014-09-26 17:54:32 -0700138 */
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700139 public DefaultTopology(ProviderId providerId, GraphDescription description,
140 Function<ConnectPoint, Boolean> broadcastFunction) {
alshabib339a3d92014-09-26 17:54:32 -0700141 super(providerId);
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700142 this.broadcastFunction = broadcastFunction;
alshabib339a3d92014-09-26 17:54:32 -0700143 this.time = description.timestamp();
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500144 this.creationTime = description.creationTime();
alshabib339a3d92014-09-26 17:54:32 -0700145
146 // Build the graph
147 this.graph = new DefaultTopologyGraph(description.vertexes(),
Andrey Komarov2398d962016-09-26 15:11:23 +0300148 description.edges());
alshabib339a3d92014-09-26 17:54:32 -0700149
Andrey Komarov2398d962016-09-26 15:11:23 +0300150 this.clusterResults = Suppliers.memoize(this::searchForClusters);
151 this.clusters = Suppliers.memoize(this::buildTopologyClusters);
alshabib339a3d92014-09-26 17:54:32 -0700152
Andrey Komarov2398d962016-09-26 15:11:23 +0300153 this.clusterIndexes = Suppliers.memoize(this::buildIndexes);
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800154
Andrey Komarov2398d962016-09-26 15:11:23 +0300155 this.hopCountWeigher = adapt(new HopCountLinkWeight(graph.getVertexes().size()));
156 this.broadcastSets = Suppliers.memoize(this::buildBroadcastSets);
157 this.infrastructurePoints = Suppliers.memoize(this::findInfrastructurePoints);
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800158 this.computeCost = Math.max(0, System.nanoTime() - time);
alshabib339a3d92014-09-26 17:54:32 -0700159 }
160
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700161 /**
162 * Creates a topology descriptor attributed to the specified provider.
163 *
164 * @param providerId identity of the provider
165 * @param description data describing the new topology
166 */
167 public DefaultTopology(ProviderId providerId, GraphDescription description) {
168 this(providerId, description, null);
169 }
170
alshabib339a3d92014-09-26 17:54:32 -0700171 @Override
172 public long time() {
173 return time;
174 }
175
176 @Override
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500177 public long creationTime() {
178 return creationTime;
179 }
180
181 @Override
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800182 public long computeCost() {
183 return computeCost;
184 }
185
186 @Override
alshabib339a3d92014-09-26 17:54:32 -0700187 public int clusterCount() {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800188 return clusters.get().size();
alshabib339a3d92014-09-26 17:54:32 -0700189 }
190
191 @Override
192 public int deviceCount() {
193 return graph.getVertexes().size();
194 }
195
196 @Override
197 public int linkCount() {
198 return graph.getEdges().size();
199 }
200
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800201 private ImmutableMap<DeviceId, TopologyCluster> clustersByDevice() {
202 return clusterIndexes.get().clustersByDevice;
203 }
204
205 private ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster() {
206 return clusterIndexes.get().devicesByCluster;
207 }
208
209 private ImmutableSetMultimap<TopologyCluster, Link> linksByCluster() {
210 return clusterIndexes.get().linksByCluster;
alshabib339a3d92014-09-26 17:54:32 -0700211 }
212
213 /**
214 * Returns the backing topology graph.
215 *
216 * @return topology graph
217 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700218 public TopologyGraph getGraph() {
alshabib339a3d92014-09-26 17:54:32 -0700219 return graph;
220 }
221
222 /**
223 * Returns the set of topology clusters.
224 *
225 * @return set of clusters
226 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700227 public Set<TopologyCluster> getClusters() {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800228 return ImmutableSet.copyOf(clusters.get().values());
alshabib339a3d92014-09-26 17:54:32 -0700229 }
230
231 /**
232 * Returns the specified topology cluster.
233 *
234 * @param clusterId cluster identifier
235 * @return topology cluster
236 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700237 public TopologyCluster getCluster(ClusterId clusterId) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800238 return clusters.get().get(clusterId);
alshabib339a3d92014-09-26 17:54:32 -0700239 }
240
241 /**
242 * Returns the topology cluster that contains the given device.
243 *
244 * @param deviceId device identifier
245 * @return topology cluster
246 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700247 public TopologyCluster getCluster(DeviceId deviceId) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800248 return clustersByDevice().get(deviceId);
alshabib339a3d92014-09-26 17:54:32 -0700249 }
250
251 /**
252 * Returns the set of cluster devices.
253 *
254 * @param cluster topology cluster
255 * @return cluster devices
256 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700257 public Set<DeviceId> getClusterDevices(TopologyCluster cluster) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800258 return devicesByCluster().get(cluster);
alshabib339a3d92014-09-26 17:54:32 -0700259 }
260
261 /**
262 * Returns the set of cluster links.
263 *
264 * @param cluster topology cluster
265 * @return cluster links
266 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700267 public Set<Link> getClusterLinks(TopologyCluster cluster) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800268 return linksByCluster().get(cluster);
alshabib339a3d92014-09-26 17:54:32 -0700269 }
270
271 /**
272 * Indicates whether the given point is an infrastructure link end-point.
273 *
274 * @param connectPoint connection point
275 * @return true if infrastructure
276 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700277 public boolean isInfrastructure(ConnectPoint connectPoint) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800278 return infrastructurePoints.get().contains(connectPoint);
alshabib339a3d92014-09-26 17:54:32 -0700279 }
280
281 /**
282 * Indicates whether the given point is part of a broadcast set.
283 *
284 * @param connectPoint connection point
285 * @return true if in broadcast set
286 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700287 public boolean isBroadcastPoint(ConnectPoint connectPoint) {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700288 if (broadcastFunction != null) {
289 return broadcastFunction.apply(connectPoint);
290 }
291
alshabib339a3d92014-09-26 17:54:32 -0700292 // Any non-infrastructure, i.e. edge points are assumed to be OK.
293 if (!isInfrastructure(connectPoint)) {
294 return true;
295 }
296
297 // Find the cluster to which the device belongs.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800298 TopologyCluster cluster = clustersByDevice().get(connectPoint.deviceId());
Andrey Komarov2398d962016-09-26 15:11:23 +0300299 checkArgument(cluster != null,
300 "No cluster found for device %s", connectPoint.deviceId());
alshabib339a3d92014-09-26 17:54:32 -0700301
302 // If the broadcast set is null or empty, or if the point explicitly
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700303 // belongs to it, return true.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800304 Set<ConnectPoint> points = broadcastSets.get().get(cluster.id());
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700305 return isNullOrEmpty(points) || points.contains(connectPoint);
alshabib339a3d92014-09-26 17:54:32 -0700306 }
307
308 /**
309 * Returns the size of the cluster broadcast set.
310 *
311 * @param clusterId cluster identifier
312 * @return size of the cluster broadcast set
313 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700314 public int broadcastSetSize(ClusterId clusterId) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800315 return broadcastSets.get().get(clusterId).size();
alshabib339a3d92014-09-26 17:54:32 -0700316 }
317
318 /**
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700319 * Returns the set of the cluster broadcast points.
320 *
321 * @param clusterId cluster identifier
322 * @return set of cluster broadcast points
323 */
324 public Set<ConnectPoint> broadcastPoints(ClusterId clusterId) {
325 return broadcastSets.get().get(clusterId);
326 }
327
328 /**
alshabib339a3d92014-09-26 17:54:32 -0700329 * Returns the set of pre-computed shortest paths between source and
330 * destination devices.
331 *
332 * @param src source device
333 * @param dst destination device
334 * @return set of shortest paths
335 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700336 public Set<Path> getPaths(DeviceId src, DeviceId dst) {
Aaron Kruglikovf6aed992016-11-08 15:16:03 -0800337 return getPaths(src, dst, linkWeight(), ALL_PATHS);
alshabib339a3d92014-09-26 17:54:32 -0700338 }
339
340 /**
341 * Computes on-demand the set of shortest paths between source and
342 * destination devices.
343 *
Andrey Komarov2398d962016-09-26 15:11:23 +0300344 * @param src source device
345 * @param dst destination device
346 * @param weigher link weight function
alshabib339a3d92014-09-26 17:54:32 -0700347 * @return set of shortest paths
348 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300349 public Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeigher weigher) {
350 return getPaths(src, dst, weigher, ALL_PATHS);
Aaron Kruglikovf6aed992016-11-08 15:16:03 -0800351 }
352
353 /**
354 * Computes on-demand the set of shortest paths between source and
355 * destination devices, the set of returned paths will be no more than,
356 * maxPaths in size. The first {@code maxPaths} paths will be returned
357 * maintaining any ordering guarantees provided by the underlying
358 * (default or if no default is specified {@link DijkstraGraphSearch})
359 * search. If returning all paths of a given length would exceed
360 * {@code maxPaths} a subset of paths of that length will be returned,
361 * which paths will be returned depends on the currently specified
362 * {@code GraphPathSearch}. See {@link #setDefaultGraphPathSearch}.
363 *
364 * @param src source device
365 * @param dst destination device
Andrey Komarov2398d962016-09-26 15:11:23 +0300366 * @param weigher link weight function
Aaron Kruglikovf6aed992016-11-08 15:16:03 -0800367 * @param maxPaths maximum number of paths
368 * @return set of shortest paths
369 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300370 public Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeigher weigher,
Aaron Kruglikovf6aed992016-11-08 15:16:03 -0800371 int maxPaths) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800372 DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
373 DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800374 Set<TopologyVertex> vertices = graph.getVertexes();
375 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
376 // src or dst not part of the current graph
377 return ImmutableSet.of();
378 }
379
alshabib339a3d92014-09-26 17:54:32 -0700380 GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
Andrey Komarov2398d962016-09-26 15:11:23 +0300381 graphPathSearch().search(graph, srcV, dstV, weigher, maxPaths);
alshabib339a3d92014-09-26 17:54:32 -0700382 ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
383 for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
384 builder.add(networkPath(path));
385 }
386 return builder.build();
387 }
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700388
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700389 /**
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700390 * /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300391 * Returns the set of pre-computed shortest disjoint path pairs between
392 * source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700393 *
394 * @param src source device
395 * @param dst destination device
396 * @return set of shortest disjoint path pairs
397 */
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700398 public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800399 return getDisjointPaths(src, dst, linkWeight());
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700400 }
401
402 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300403 * Computes on-demand the set of shortest disjoint path pairs between
404 * source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700405 *
Andrey Komarov2398d962016-09-26 15:11:23 +0300406 * @param src source device
407 * @param dst destination device
408 * @param weigher link weight function
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700409 * @return set of disjoint shortest path pairs
410 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300411 public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst,
412 LinkWeigher weigher) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800413 DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
414 DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700415 Set<TopologyVertex> vertices = graph.getVertexes();
416 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
417 // src or dst not part of the current graph
418 return ImmutableSet.of();
419 }
420
421 GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
Andrey Komarov2398d962016-09-26 15:11:23 +0300422 SUURBALLE.search(graph, srcV, dstV, weigher, ALL_PATHS);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700423 ImmutableSet.Builder<DisjointPath> builder = ImmutableSet.builder();
424 for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
helenyrwue3c43342016-08-11 10:33:12 -0700425 DisjointPath disjointPath =
Andrey Komarov2398d962016-09-26 15:11:23 +0300426 networkDisjointPath((DisjointPathPair<TopologyVertex, TopologyEdge>) path);
helenyrwue3c43342016-08-11 10:33:12 -0700427 if (disjointPath.backup() != null) {
428 builder.add(disjointPath);
429 }
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700430 }
431 return builder.build();
432 }
433
434 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300435 * Computes on-demand the set of shortest disjoint risk groups path pairs
436 * between source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700437 *
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700438 * @param src source device
439 * @param dst destination device
Andrey Komarov2398d962016-09-26 15:11:23 +0300440 * @param weigher edge weight object
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700441 * @param riskProfile map representing risk groups for each edge
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700442 * @return set of shortest disjoint paths
443 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300444 private Set<DisjointPath> disjointPaths(DeviceId src, DeviceId dst,
445 LinkWeigher weigher,
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800446 Map<TopologyEdge, Object> riskProfile) {
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700447 DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
448 DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
449
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700450 Set<TopologyVertex> vertices = graph.getVertexes();
451 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
452 // src or dst not part of the current graph
453 return ImmutableSet.of();
454 }
455
Andrey Komarov2398d962016-09-26 15:11:23 +0300456 SrlgGraphSearch<TopologyVertex, TopologyEdge> srlg =
457 new SrlgGraphSearch<>(riskProfile);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700458 GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
Andrey Komarov2398d962016-09-26 15:11:23 +0300459 srlg.search(graph, srcV, dstV, weigher, ALL_PATHS);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700460 ImmutableSet.Builder<DisjointPath> builder = ImmutableSet.builder();
461 for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
helenyrwue3c43342016-08-11 10:33:12 -0700462 DisjointPath disjointPath =
Andrey Komarov2398d962016-09-26 15:11:23 +0300463 networkDisjointPath((DisjointPathPair<TopologyVertex, TopologyEdge>) path);
helenyrwue3c43342016-08-11 10:33:12 -0700464 if (disjointPath.backup() != null) {
465 builder.add(disjointPath);
466 }
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700467 }
468 return builder.build();
469 }
470
471 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300472 * Computes on-demand the set of shortest disjoint risk groups path pairs
473 * between source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700474 *
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700475 * @param src source device
476 * @param dst destination device
Andrey Komarov2398d962016-09-26 15:11:23 +0300477 * @param weigher edge weight object
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700478 * @param riskProfile map representing risk groups for each link
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700479 * @return set of shortest disjoint paths
480 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300481 public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst,
482 LinkWeigher weigher,
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700483 Map<Link, Object> riskProfile) {
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700484 Map<TopologyEdge, Object> riskProfile2 = new HashMap<>();
485 for (Link l : riskProfile.keySet()) {
486 riskProfile2.put(new TopologyEdge() {
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700487 Link cur = l;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700488
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -0700489 @Override
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700490 public Link link() {
491 return cur;
492 }
493
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -0700494 @Override
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700495 public TopologyVertex src() {
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700496 return () -> src;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700497 }
498
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -0700499 @Override
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700500 public TopologyVertex dst() {
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700501 return () -> dst;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700502 }
503 }, riskProfile.get(l));
504 }
Andrey Komarov2398d962016-09-26 15:11:23 +0300505 return disjointPaths(src, dst, weigher, riskProfile2);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700506 }
507
508 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300509 * Computes on-demand the set of shortest disjoint risk groups path pairs
510 * between source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700511 *
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700512 * @param src source device
513 * @param dst destination device
514 * @param riskProfile map representing risk groups for each link
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700515 * @return set of shortest disjoint paths
516 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300517 public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst,
518 Map<Link, Object> riskProfile) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800519 return getDisjointPaths(src, dst, linkWeight(), riskProfile);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700520 }
alshabib339a3d92014-09-26 17:54:32 -0700521
alshabib339a3d92014-09-26 17:54:32 -0700522 // Converts graph path to a network path with the same cost.
523 private Path networkPath(org.onlab.graph.Path<TopologyVertex, TopologyEdge> path) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300524 List<Link> links = path.edges().stream().map(TopologyEdge::link)
525 .collect(Collectors.toList());
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -0800526 return new DefaultPath(CORE_PROVIDER_ID, links, path.cost());
alshabib339a3d92014-09-26 17:54:32 -0700527 }
528
Andrey Komarov2398d962016-09-26 15:11:23 +0300529 private DisjointPath networkDisjointPath(
530 DisjointPathPair<TopologyVertex, TopologyEdge> path) {
Yuta HIGUCHIc3d69f52016-08-08 11:52:52 -0700531 if (!path.hasBackup()) {
532 // There was no secondary path available.
533 return new DefaultDisjointPath(CORE_PROVIDER_ID,
Andrey Komarov2398d962016-09-26 15:11:23 +0300534 (DefaultPath) networkPath(path.primary()),
535 null);
Yuta HIGUCHIc3d69f52016-08-08 11:52:52 -0700536 }
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700537 return new DefaultDisjointPath(CORE_PROVIDER_ID,
Andrey Komarov2398d962016-09-26 15:11:23 +0300538 (DefaultPath) networkPath(path.primary()),
539 (DefaultPath) networkPath(path.secondary()));
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700540 }
541
alshabib339a3d92014-09-26 17:54:32 -0700542 // Searches for SCC clusters in the network topology graph using Tarjan
543 // algorithm.
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800544 private SccResult<TopologyVertex, TopologyEdge> searchForClusters() {
Andrey Komarov2398d962016-09-26 15:11:23 +0300545 return TARJAN.search(graph, new NoIndirectLinksWeigher());
alshabib339a3d92014-09-26 17:54:32 -0700546 }
547
548 // Builds the topology clusters and returns the id-cluster bindings.
549 private ImmutableMap<ClusterId, TopologyCluster> buildTopologyClusters() {
Andrey Komarov2398d962016-09-26 15:11:23 +0300550 ImmutableMap.Builder<ClusterId, TopologyCluster> clusterBuilder =
551 ImmutableMap.builder();
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800552 SccResult<TopologyVertex, TopologyEdge> results = clusterResults.get();
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700553
alshabib339a3d92014-09-26 17:54:32 -0700554 // Extract both vertexes and edges from the results; the lists form
555 // pairs along the same index.
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800556 List<Set<TopologyVertex>> clusterVertexes = results.clusterVertexes();
557 List<Set<TopologyEdge>> clusterEdges = results.clusterEdges();
alshabib339a3d92014-09-26 17:54:32 -0700558
559 // Scan over the lists and create a cluster from the results.
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800560 for (int i = 0, n = results.clusterCount(); i < n; i++) {
alshabib339a3d92014-09-26 17:54:32 -0700561 Set<TopologyVertex> vertexSet = clusterVertexes.get(i);
562 Set<TopologyEdge> edgeSet = clusterEdges.get(i);
563
564 ClusterId cid = ClusterId.clusterId(i);
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500565 DefaultTopologyCluster cluster = new DefaultTopologyCluster(cid,
Andrey Komarov2398d962016-09-26 15:11:23 +0300566 vertexSet.size(),
567 edgeSet.size(),
568 findRoot(vertexSet));
alshabib339a3d92014-09-26 17:54:32 -0700569 clusterBuilder.put(cid, cluster);
570 }
571 return clusterBuilder.build();
572 }
573
574 // Finds the vertex whose device id is the lexicographical minimum in the
575 // specified set.
576 private TopologyVertex findRoot(Set<TopologyVertex> vertexSet) {
577 TopologyVertex minVertex = null;
578 for (TopologyVertex vertex : vertexSet) {
Jon Halle7539632016-05-11 15:44:31 -0700579 if ((minVertex == null) || (vertex.deviceId()
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500580 .toString().compareTo(minVertex.deviceId().toString()) < 0)) {
alshabib339a3d92014-09-26 17:54:32 -0700581 minVertex = vertex;
582 }
583 }
584 return minVertex;
585 }
586
587 // Processes a map of broadcast sets for each cluster.
588 private ImmutableSetMultimap<ClusterId, ConnectPoint> buildBroadcastSets() {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800589 Builder<ClusterId, ConnectPoint> builder = ImmutableSetMultimap.builder();
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800590 for (TopologyCluster cluster : clusters.get().values()) {
alshabib339a3d92014-09-26 17:54:32 -0700591 addClusterBroadcastSet(cluster, builder);
592 }
593 return builder.build();
594 }
595
596 // Finds all broadcast points for the cluster. These are those connection
597 // points which lie along the shortest paths between the cluster root and
598 // all other devices within the cluster.
Andrey Komarov2398d962016-09-26 15:11:23 +0300599 private void addClusterBroadcastSet(TopologyCluster cluster,
600 Builder<ClusterId, ConnectPoint> builder) {
alshabib339a3d92014-09-26 17:54:32 -0700601 // Use the graph root search results to build the broadcast set.
Andrey Komarov2398d962016-09-26 15:11:23 +0300602 Result<TopologyVertex, TopologyEdge> result =
603 DIJKSTRA.search(graph, cluster.root(), null, hopCountWeigher, 1);
604 for (Map.Entry<TopologyVertex, Set<TopologyEdge>> entry :
605 result.parents().entrySet()) {
alshabib339a3d92014-09-26 17:54:32 -0700606 TopologyVertex vertex = entry.getKey();
607
608 // Ignore any parents that lead outside the cluster.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800609 if (clustersByDevice().get(vertex.deviceId()) != cluster) {
alshabib339a3d92014-09-26 17:54:32 -0700610 continue;
611 }
612
613 // Ignore any back-link sets that are empty.
614 Set<TopologyEdge> parents = entry.getValue();
615 if (parents.isEmpty()) {
616 continue;
617 }
618
619 // Use the first back-link source and destinations to add to the
620 // broadcast set.
621 Link link = parents.iterator().next().link();
622 builder.put(cluster.id(), link.src());
623 builder.put(cluster.id(), link.dst());
624 }
625 }
626
627 // Collects and returns an set of all infrastructure link end-points.
628 private ImmutableSet<ConnectPoint> findInfrastructurePoints() {
629 ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
630 for (TopologyEdge edge : graph.getEdges()) {
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -0700631 if (edge.link().type() == Type.EDGE) {
632 // exclude EDGE link from infrastructure link
633 // - Device <-> Host
634 // - Device <-> remote domain Device
635 continue;
636 }
alshabib339a3d92014-09-26 17:54:32 -0700637 builder.add(edge.link().src());
638 builder.add(edge.link().dst());
639 }
640 return builder.build();
641 }
642
643 // Builds cluster-devices, cluster-links and device-cluster indexes.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800644 private ClusterIndexes buildIndexes() {
alshabib339a3d92014-09-26 17:54:32 -0700645 // Prepare the index builders
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500646 ImmutableMap.Builder<DeviceId, TopologyCluster> clusterBuilder =
647 ImmutableMap.builder();
648 ImmutableSetMultimap.Builder<TopologyCluster, DeviceId> devicesBuilder =
649 ImmutableSetMultimap.builder();
650 ImmutableSetMultimap.Builder<TopologyCluster, Link> linksBuilder =
651 ImmutableSetMultimap.builder();
alshabib339a3d92014-09-26 17:54:32 -0700652
653 // Now scan through all the clusters
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800654 for (TopologyCluster cluster : clusters.get().values()) {
alshabib339a3d92014-09-26 17:54:32 -0700655 int i = cluster.id().index();
656
657 // Scan through all the cluster vertexes.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800658 for (TopologyVertex vertex : clusterResults.get().clusterVertexes().get(i)) {
alshabib339a3d92014-09-26 17:54:32 -0700659 devicesBuilder.put(cluster, vertex.deviceId());
660 clusterBuilder.put(vertex.deviceId(), cluster);
661 }
662
663 // Scan through all the cluster edges.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800664 for (TopologyEdge edge : clusterResults.get().clusterEdges().get(i)) {
alshabib339a3d92014-09-26 17:54:32 -0700665 linksBuilder.put(cluster, edge.link());
666 }
667 }
668
669 // Finalize all indexes.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800670 return new ClusterIndexes(clusterBuilder.build(),
Andrey Komarov2398d962016-09-26 15:11:23 +0300671 devicesBuilder.build(),
672 linksBuilder.build());
alshabib339a3d92014-09-26 17:54:32 -0700673 }
674
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800675 private GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch() {
676 return defaultGraphPathSearch != null ? defaultGraphPathSearch : DIJKSTRA;
677 }
alshabib339a3d92014-09-26 17:54:32 -0700678
Andrey Komarov2398d962016-09-26 15:11:23 +0300679 private LinkWeigher linkWeight() {
680 return defaultLinkWeigher != null ? defaultLinkWeigher : hopCountWeigher;
alshabib339a3d92014-09-26 17:54:32 -0700681 }
682
683 // Link weight for preventing traversal over indirect links.
Andrey Komarov2398d962016-09-26 15:11:23 +0300684 private static class NoIndirectLinksWeigher
685 extends DefaultEdgeWeigher<TopologyVertex, TopologyEdge>
686 implements LinkWeigher {
alshabib339a3d92014-09-26 17:54:32 -0700687 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +0300688 public Weight weight(TopologyEdge edge) {
689 return (edge.link().state() == INACTIVE) ||
690 (edge.link().type() == INDIRECT) ?
691 getNonViableWeight() : new ScalarWeight(HOP_WEIGHT_VALUE);
alshabib339a3d92014-09-26 17:54:32 -0700692 }
693 }
694
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800695 static final class ClusterIndexes {
696 final ImmutableMap<DeviceId, TopologyCluster> clustersByDevice;
697 final ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
698 final ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
699
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700700 public ClusterIndexes(ImmutableMap<DeviceId, TopologyCluster> clustersByDevice,
701 ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster,
702 ImmutableSetMultimap<TopologyCluster, Link> linksByCluster) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800703 this.clustersByDevice = clustersByDevice;
704 this.devicesByCluster = devicesByCluster;
705 this.linksByCluster = linksByCluster;
706 }
707 }
708
alshabib339a3d92014-09-26 17:54:32 -0700709 @Override
710 public String toString() {
711 return toStringHelper(this)
712 .add("time", time)
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500713 .add("creationTime", creationTime)
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800714 .add("computeCost", computeCost)
alshabib339a3d92014-09-26 17:54:32 -0700715 .add("clusters", clusterCount())
716 .add("devices", deviceCount())
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500717 .add("links", linkCount()).toString();
alshabib339a3d92014-09-26 17:54:32 -0700718 }
719}