blob: ecbb573d40e4a1ef09b6c5a08b6046def2d5e295 [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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;
Yuta HIGUCHIac8b2292017-03-30 19:21:57 -070030import org.onlab.graph.KShortestPathsSearch;
31import org.onlab.graph.LazyKShortestPathsSearch;
Andrey Komarov2398d962016-09-26 15:11:23 +030032import org.onlab.graph.ScalarWeight;
Jonathan Hartd9df7bd2015-11-10 17:10:25 -080033import org.onlab.graph.SrlgGraphSearch;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -070034import org.onlab.graph.SuurballeGraphSearch;
alshabib339a3d92014-09-26 17:54:32 -070035import org.onlab.graph.TarjanGraphSearch;
Jonathan Hartd9df7bd2015-11-10 17:10:25 -080036import org.onlab.graph.TarjanGraphSearch.SccResult;
Andrey Komarov2398d962016-09-26 15:11:23 +030037import org.onlab.graph.Weight;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.net.AbstractModel;
39import org.onosproject.net.ConnectPoint;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -070040import org.onosproject.net.DefaultDisjointPath;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.net.DefaultPath;
42import org.onosproject.net.DeviceId;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -070043import org.onosproject.net.DisjointPath;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import org.onosproject.net.Link;
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -070045import org.onosproject.net.Link.Type;
Brian O'Connorabafb502014-12-02 22:26:20 -080046import org.onosproject.net.Path;
47import org.onosproject.net.provider.ProviderId;
48import org.onosproject.net.topology.ClusterId;
49import org.onosproject.net.topology.DefaultTopologyCluster;
50import org.onosproject.net.topology.DefaultTopologyVertex;
51import org.onosproject.net.topology.GraphDescription;
Ray Milkey7483e1b2018-02-07 15:43:01 -080052import org.onosproject.net.topology.HopCountLinkWeigher;
Andrey Komarov2398d962016-09-26 15:11:23 +030053import org.onosproject.net.topology.LinkWeigher;
Brian O'Connorabafb502014-12-02 22:26:20 -080054import org.onosproject.net.topology.Topology;
55import org.onosproject.net.topology.TopologyCluster;
56import org.onosproject.net.topology.TopologyEdge;
57import org.onosproject.net.topology.TopologyGraph;
58import org.onosproject.net.topology.TopologyVertex;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080059import org.slf4j.Logger;
60import org.slf4j.LoggerFactory;
alshabib339a3d92014-09-26 17:54:32 -070061
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -070062import java.util.HashMap;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070063import java.util.List;
64import java.util.Map;
65import java.util.Set;
Thomas Vachuska48e64e42015-09-22 15:32:55 -070066import java.util.stream.Collectors;
Yuta HIGUCHIac8b2292017-03-30 19:21:57 -070067import java.util.stream.Stream;
alshabib339a3d92014-09-26 17:54:32 -070068
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070069import static com.google.common.base.MoreObjects.toStringHelper;
Thomas Vachuska320c58f2015-08-05 10:42:32 -070070import static com.google.common.base.Preconditions.checkArgument;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070071import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
Thomas Vachuska320c58f2015-08-05 10:42:32 -070072import static org.onlab.util.Tools.isNullOrEmpty;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070073import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070074import static org.onosproject.net.Link.State.INACTIVE;
75import static org.onosproject.net.Link.Type.INDIRECT;
76
alshabib339a3d92014-09-26 17:54:32 -070077/**
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -050078 * Default implementation of the topology descriptor. This carries the backing
79 * topology data.
alshabib339a3d92014-09-26 17:54:32 -070080 */
81public class DefaultTopology extends AbstractModel implements Topology {
82
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080083 private static final Logger log = LoggerFactory.getLogger(DefaultTopology.class);
84
Andrey Komarov2398d962016-09-26 15:11:23 +030085 private static final DijkstraGraphSearch<TopologyVertex, TopologyEdge> DIJKSTRA =
86 new DijkstraGraphSearch<>();
87 private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN =
88 new TarjanGraphSearch<>();
89 private static final SuurballeGraphSearch<TopologyVertex, TopologyEdge> SUURBALLE =
90 new SuurballeGraphSearch<>();
Yuta HIGUCHIac8b2292017-03-30 19:21:57 -070091 private static final KShortestPathsSearch<TopologyVertex, TopologyEdge> KSHORTEST =
92 new KShortestPathsSearch<>();
93 private static final LazyKShortestPathsSearch<TopologyVertex, TopologyEdge> LAZY_KSHORTEST =
94 new LazyKShortestPathsSearch<>();
95
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -070096
Andrey Komarov2398d962016-09-26 15:11:23 +030097 private static LinkWeigher defaultLinkWeigher = null;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080098 private static GraphPathSearch<TopologyVertex, TopologyEdge> defaultGraphPathSearch = null;
alshabib339a3d92014-09-26 17:54:32 -070099
alshabib339a3d92014-09-26 17:54:32 -0700100 private final long time;
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500101 private final long creationTime;
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800102 private final long computeCost;
alshabib339a3d92014-09-26 17:54:32 -0700103 private final TopologyGraph graph;
104
Andrey Komarov2398d962016-09-26 15:11:23 +0300105 private final LinkWeigher hopCountWeigher;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800106
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800107 private final Supplier<SccResult<TopologyVertex, TopologyEdge>> clusterResults;
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800108 private final Supplier<ImmutableMap<ClusterId, TopologyCluster>> clusters;
109 private final Supplier<ImmutableSet<ConnectPoint>> infrastructurePoints;
110 private final Supplier<ImmutableSetMultimap<ClusterId, ConnectPoint>> broadcastSets;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700111 private final Function<ConnectPoint, Boolean> broadcastFunction;
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800112 private final Supplier<ClusterIndexes> clusterIndexes;
alshabib339a3d92014-09-26 17:54:32 -0700113
114 /**
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800115 * Sets the default link-weight to be used when computing paths. If null is
116 * specified, the builtin default link-weight measuring hop-counts will be
117 * used.
118 *
Andrey Komarov2398d962016-09-26 15:11:23 +0300119 * @param linkWeigher new default link-weight
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800120 */
Ray Milkey06297ed2018-01-22 17:13:41 -0800121 public static synchronized void setDefaultLinkWeigher(LinkWeigher linkWeigher) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300122 log.info("Setting new default link-weight function to {}", linkWeigher);
123 defaultLinkWeigher = linkWeigher;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800124 }
125
126 /**
127 * Sets the default lpath search algorighm to be used when computing paths.
128 * If null is specified, the builtin default Dijkstra will be used.
129 *
130 * @param graphPathSearch new default algorithm
131 */
Ray Milkey06297ed2018-01-22 17:13:41 -0800132 public static synchronized void setDefaultGraphPathSearch(
Andrey Komarov2398d962016-09-26 15:11:23 +0300133 GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800134 log.info("Setting new default graph path algorithm to {}", graphPathSearch);
135 defaultGraphPathSearch = graphPathSearch;
136 }
137
138
139 /**
alshabib339a3d92014-09-26 17:54:32 -0700140 * Creates a topology descriptor attributed to the specified provider.
141 *
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700142 * @param providerId identity of the provider
143 * @param description data describing the new topology
144 * @param broadcastFunction broadcast point function
alshabib339a3d92014-09-26 17:54:32 -0700145 */
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700146 public DefaultTopology(ProviderId providerId, GraphDescription description,
147 Function<ConnectPoint, Boolean> broadcastFunction) {
alshabib339a3d92014-09-26 17:54:32 -0700148 super(providerId);
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700149 this.broadcastFunction = broadcastFunction;
alshabib339a3d92014-09-26 17:54:32 -0700150 this.time = description.timestamp();
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500151 this.creationTime = description.creationTime();
alshabib339a3d92014-09-26 17:54:32 -0700152
153 // Build the graph
154 this.graph = new DefaultTopologyGraph(description.vertexes(),
Andrey Komarov2398d962016-09-26 15:11:23 +0300155 description.edges());
alshabib339a3d92014-09-26 17:54:32 -0700156
Andrey Komarov2398d962016-09-26 15:11:23 +0300157 this.clusterResults = Suppliers.memoize(this::searchForClusters);
158 this.clusters = Suppliers.memoize(this::buildTopologyClusters);
alshabib339a3d92014-09-26 17:54:32 -0700159
Andrey Komarov2398d962016-09-26 15:11:23 +0300160 this.clusterIndexes = Suppliers.memoize(this::buildIndexes);
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800161
Ray Milkey7483e1b2018-02-07 15:43:01 -0800162 this.hopCountWeigher = new HopCountLinkWeigher(graph.getVertexes().size());
Andrey Komarov2398d962016-09-26 15:11:23 +0300163 this.broadcastSets = Suppliers.memoize(this::buildBroadcastSets);
164 this.infrastructurePoints = Suppliers.memoize(this::findInfrastructurePoints);
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800165 this.computeCost = Math.max(0, System.nanoTime() - time);
alshabib339a3d92014-09-26 17:54:32 -0700166 }
167
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700168 /**
169 * Creates a topology descriptor attributed to the specified provider.
170 *
171 * @param providerId identity of the provider
172 * @param description data describing the new topology
173 */
174 public DefaultTopology(ProviderId providerId, GraphDescription description) {
175 this(providerId, description, null);
176 }
177
alshabib339a3d92014-09-26 17:54:32 -0700178 @Override
179 public long time() {
180 return time;
181 }
182
183 @Override
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500184 public long creationTime() {
185 return creationTime;
186 }
187
188 @Override
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800189 public long computeCost() {
190 return computeCost;
191 }
192
193 @Override
alshabib339a3d92014-09-26 17:54:32 -0700194 public int clusterCount() {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800195 return clusters.get().size();
alshabib339a3d92014-09-26 17:54:32 -0700196 }
197
198 @Override
199 public int deviceCount() {
200 return graph.getVertexes().size();
201 }
202
203 @Override
204 public int linkCount() {
205 return graph.getEdges().size();
206 }
207
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800208 private ImmutableMap<DeviceId, TopologyCluster> clustersByDevice() {
209 return clusterIndexes.get().clustersByDevice;
210 }
211
212 private ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster() {
213 return clusterIndexes.get().devicesByCluster;
214 }
215
216 private ImmutableSetMultimap<TopologyCluster, Link> linksByCluster() {
217 return clusterIndexes.get().linksByCluster;
alshabib339a3d92014-09-26 17:54:32 -0700218 }
219
220 /**
221 * Returns the backing topology graph.
222 *
223 * @return topology graph
224 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700225 public TopologyGraph getGraph() {
alshabib339a3d92014-09-26 17:54:32 -0700226 return graph;
227 }
228
229 /**
230 * Returns the set of topology clusters.
231 *
232 * @return set of clusters
233 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700234 public Set<TopologyCluster> getClusters() {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800235 return ImmutableSet.copyOf(clusters.get().values());
alshabib339a3d92014-09-26 17:54:32 -0700236 }
237
238 /**
239 * Returns the specified topology cluster.
240 *
241 * @param clusterId cluster identifier
242 * @return topology cluster
243 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700244 public TopologyCluster getCluster(ClusterId clusterId) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800245 return clusters.get().get(clusterId);
alshabib339a3d92014-09-26 17:54:32 -0700246 }
247
248 /**
249 * Returns the topology cluster that contains the given device.
250 *
251 * @param deviceId device identifier
252 * @return topology cluster
253 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700254 public TopologyCluster getCluster(DeviceId deviceId) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800255 return clustersByDevice().get(deviceId);
alshabib339a3d92014-09-26 17:54:32 -0700256 }
257
258 /**
259 * Returns the set of cluster devices.
260 *
261 * @param cluster topology cluster
262 * @return cluster devices
263 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700264 public Set<DeviceId> getClusterDevices(TopologyCluster cluster) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800265 return devicesByCluster().get(cluster);
alshabib339a3d92014-09-26 17:54:32 -0700266 }
267
268 /**
269 * Returns the set of cluster links.
270 *
271 * @param cluster topology cluster
272 * @return cluster links
273 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700274 public Set<Link> getClusterLinks(TopologyCluster cluster) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800275 return linksByCluster().get(cluster);
alshabib339a3d92014-09-26 17:54:32 -0700276 }
277
278 /**
279 * Indicates whether the given point is an infrastructure link end-point.
280 *
281 * @param connectPoint connection point
282 * @return true if infrastructure
283 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700284 public boolean isInfrastructure(ConnectPoint connectPoint) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800285 return infrastructurePoints.get().contains(connectPoint);
alshabib339a3d92014-09-26 17:54:32 -0700286 }
287
288 /**
289 * Indicates whether the given point is part of a broadcast set.
290 *
291 * @param connectPoint connection point
292 * @return true if in broadcast set
293 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700294 public boolean isBroadcastPoint(ConnectPoint connectPoint) {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700295 if (broadcastFunction != null) {
296 return broadcastFunction.apply(connectPoint);
297 }
298
alshabib339a3d92014-09-26 17:54:32 -0700299 // Any non-infrastructure, i.e. edge points are assumed to be OK.
300 if (!isInfrastructure(connectPoint)) {
301 return true;
302 }
303
304 // Find the cluster to which the device belongs.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800305 TopologyCluster cluster = clustersByDevice().get(connectPoint.deviceId());
Andrey Komarov2398d962016-09-26 15:11:23 +0300306 checkArgument(cluster != null,
307 "No cluster found for device %s", connectPoint.deviceId());
alshabib339a3d92014-09-26 17:54:32 -0700308
309 // If the broadcast set is null or empty, or if the point explicitly
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700310 // belongs to it, return true.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800311 Set<ConnectPoint> points = broadcastSets.get().get(cluster.id());
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700312 return isNullOrEmpty(points) || points.contains(connectPoint);
alshabib339a3d92014-09-26 17:54:32 -0700313 }
314
315 /**
316 * Returns the size of the cluster broadcast set.
317 *
318 * @param clusterId cluster identifier
319 * @return size of the cluster broadcast set
320 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700321 public int broadcastSetSize(ClusterId clusterId) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800322 return broadcastSets.get().get(clusterId).size();
alshabib339a3d92014-09-26 17:54:32 -0700323 }
324
325 /**
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700326 * Returns the set of the cluster broadcast points.
327 *
328 * @param clusterId cluster identifier
329 * @return set of cluster broadcast points
330 */
331 public Set<ConnectPoint> broadcastPoints(ClusterId clusterId) {
332 return broadcastSets.get().get(clusterId);
333 }
334
335 /**
alshabib339a3d92014-09-26 17:54:32 -0700336 * Returns the set of pre-computed shortest paths between source and
337 * destination devices.
338 *
339 * @param src source device
340 * @param dst destination device
341 * @return set of shortest paths
342 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700343 public Set<Path> getPaths(DeviceId src, DeviceId dst) {
Aaron Kruglikovf6aed992016-11-08 15:16:03 -0800344 return getPaths(src, dst, linkWeight(), ALL_PATHS);
alshabib339a3d92014-09-26 17:54:32 -0700345 }
346
347 /**
348 * Computes on-demand the set of shortest paths between source and
349 * destination devices.
350 *
Andrey Komarov2398d962016-09-26 15:11:23 +0300351 * @param src source device
352 * @param dst destination device
353 * @param weigher link weight function
alshabib339a3d92014-09-26 17:54:32 -0700354 * @return set of shortest paths
355 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300356 public Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeigher weigher) {
357 return getPaths(src, dst, weigher, ALL_PATHS);
Aaron Kruglikovf6aed992016-11-08 15:16:03 -0800358 }
359
360 /**
361 * Computes on-demand the set of shortest paths between source and
362 * destination devices, the set of returned paths will be no more than,
363 * maxPaths in size. The first {@code maxPaths} paths will be returned
364 * maintaining any ordering guarantees provided by the underlying
365 * (default or if no default is specified {@link DijkstraGraphSearch})
366 * search. If returning all paths of a given length would exceed
367 * {@code maxPaths} a subset of paths of that length will be returned,
368 * which paths will be returned depends on the currently specified
369 * {@code GraphPathSearch}. See {@link #setDefaultGraphPathSearch}.
370 *
371 * @param src source device
372 * @param dst destination device
Andrey Komarov2398d962016-09-26 15:11:23 +0300373 * @param weigher link weight function
Aaron Kruglikovf6aed992016-11-08 15:16:03 -0800374 * @param maxPaths maximum number of paths
375 * @return set of shortest paths
376 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300377 public Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeigher weigher,
Aaron Kruglikovf6aed992016-11-08 15:16:03 -0800378 int maxPaths) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800379 DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
380 DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800381 Set<TopologyVertex> vertices = graph.getVertexes();
382 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
383 // src or dst not part of the current graph
384 return ImmutableSet.of();
385 }
386
alshabib339a3d92014-09-26 17:54:32 -0700387 GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
Andrey Komarov2398d962016-09-26 15:11:23 +0300388 graphPathSearch().search(graph, srcV, dstV, weigher, maxPaths);
alshabib339a3d92014-09-26 17:54:32 -0700389 ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
390 for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
391 builder.add(networkPath(path));
392 }
393 return builder.build();
394 }
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700395
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700396 /**
Yuta HIGUCHIac8b2292017-03-30 19:21:57 -0700397 * Computes on-demand the k-shortest paths between source and
398 * destination devices.
399 *
400 * @param src source device
401 * @param dst destination device
402 * @param maxPaths maximum number of paths (k)
403 * @return set of k-shortest paths
404 */
405 public Set<Path> getKShortestPaths(DeviceId src, DeviceId dst,
406 int maxPaths) {
407
408 return getKShortestPaths(src, dst, linkWeight(), maxPaths);
409 }
410
411 /**
412 * Computes on-demand the k-shortest paths between source and
413 * destination devices.
414 *
415 * The first {@code maxPaths} paths will be returned
416 * in ascending order according to the provided {@code weigher}
417 *
418 * @param src source device
419 * @param dst destination device
420 * @param weigher link weight function
421 * @param maxPaths maximum number of paths (k)
422 * @return set of k-shortest paths
423 */
424 public Set<Path> getKShortestPaths(DeviceId src, DeviceId dst,
425 LinkWeigher weigher,
426 int maxPaths) {
427 DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
428 DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
429 Set<TopologyVertex> vertices = graph.getVertexes();
430 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
431 // src or dst not part of the current graph
432 return ImmutableSet.of();
433 }
434
435 return KSHORTEST.search(graph, srcV, dstV, weigher, maxPaths)
436 .paths().stream()
437 .map(this::networkPath)
Yuta HIGUCHI498fa1d2017-05-17 16:08:40 -0700438 .collect(ImmutableSet.toImmutableSet());
Yuta HIGUCHIac8b2292017-03-30 19:21:57 -0700439 }
440
441 /**
442 * Lazily computes on-demand the k-shortest paths between source and
443 * destination devices.
444 *
445 *
446 * @param src source device
447 * @param dst destination device
448 * @return stream of k-shortest paths
449 */
450 public Stream<Path> getKShortestPaths(DeviceId src, DeviceId dst) {
451 return getKShortestPaths(src, dst, linkWeight());
452 }
453
454 /**
455 * Lazily computes on-demand the k-shortest paths between source and
456 * destination devices.
457 *
458 *
459 * @param src source device
460 * @param dst destination device
461 * @param weigher link weight function
462 * @return stream of k-shortest paths
463 */
464 public Stream<Path> getKShortestPaths(DeviceId src, DeviceId dst,
465 LinkWeigher weigher) {
466 DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
467 DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
468 Set<TopologyVertex> vertices = graph.getVertexes();
469 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
470 // src or dst not part of the current graph
471 return Stream.empty();
472 }
473
474 return LAZY_KSHORTEST.lazyPathSearch(graph, srcV, dstV, weigher)
475 .map(this::networkPath);
476 }
477
478 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300479 * Returns the set of pre-computed shortest disjoint path pairs between
480 * source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700481 *
482 * @param src source device
483 * @param dst destination device
484 * @return set of shortest disjoint path pairs
485 */
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700486 public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800487 return getDisjointPaths(src, dst, linkWeight());
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700488 }
489
490 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300491 * Computes on-demand the set of shortest disjoint path pairs between
492 * source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700493 *
Andrey Komarov2398d962016-09-26 15:11:23 +0300494 * @param src source device
495 * @param dst destination device
496 * @param weigher link weight function
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700497 * @return set of disjoint shortest path pairs
498 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300499 public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst,
500 LinkWeigher weigher) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800501 DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
502 DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700503 Set<TopologyVertex> vertices = graph.getVertexes();
504 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
505 // src or dst not part of the current graph
506 return ImmutableSet.of();
507 }
508
509 GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
Andrey Komarov2398d962016-09-26 15:11:23 +0300510 SUURBALLE.search(graph, srcV, dstV, weigher, ALL_PATHS);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700511 ImmutableSet.Builder<DisjointPath> builder = ImmutableSet.builder();
512 for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
helenyrwue3c43342016-08-11 10:33:12 -0700513 DisjointPath disjointPath =
Andrey Komarov2398d962016-09-26 15:11:23 +0300514 networkDisjointPath((DisjointPathPair<TopologyVertex, TopologyEdge>) path);
helenyrwue3c43342016-08-11 10:33:12 -0700515 if (disjointPath.backup() != null) {
516 builder.add(disjointPath);
517 }
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700518 }
519 return builder.build();
520 }
521
522 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300523 * Computes on-demand the set of shortest disjoint risk groups path pairs
524 * between source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700525 *
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700526 * @param src source device
527 * @param dst destination device
Andrey Komarov2398d962016-09-26 15:11:23 +0300528 * @param weigher edge weight object
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700529 * @param riskProfile map representing risk groups for each edge
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700530 * @return set of shortest disjoint paths
531 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300532 private Set<DisjointPath> disjointPaths(DeviceId src, DeviceId dst,
533 LinkWeigher weigher,
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800534 Map<TopologyEdge, Object> riskProfile) {
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700535 DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
536 DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
537
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700538 Set<TopologyVertex> vertices = graph.getVertexes();
539 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
540 // src or dst not part of the current graph
541 return ImmutableSet.of();
542 }
543
Andrey Komarov2398d962016-09-26 15:11:23 +0300544 SrlgGraphSearch<TopologyVertex, TopologyEdge> srlg =
545 new SrlgGraphSearch<>(riskProfile);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700546 GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
Andrey Komarov2398d962016-09-26 15:11:23 +0300547 srlg.search(graph, srcV, dstV, weigher, ALL_PATHS);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700548 ImmutableSet.Builder<DisjointPath> builder = ImmutableSet.builder();
549 for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
helenyrwue3c43342016-08-11 10:33:12 -0700550 DisjointPath disjointPath =
Andrey Komarov2398d962016-09-26 15:11:23 +0300551 networkDisjointPath((DisjointPathPair<TopologyVertex, TopologyEdge>) path);
helenyrwue3c43342016-08-11 10:33:12 -0700552 if (disjointPath.backup() != null) {
553 builder.add(disjointPath);
554 }
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700555 }
556 return builder.build();
557 }
558
559 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300560 * Computes on-demand the set of shortest disjoint risk groups path pairs
561 * between source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700562 *
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700563 * @param src source device
564 * @param dst destination device
Andrey Komarov2398d962016-09-26 15:11:23 +0300565 * @param weigher edge weight object
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700566 * @param riskProfile map representing risk groups for each link
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700567 * @return set of shortest disjoint paths
568 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300569 public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst,
570 LinkWeigher weigher,
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700571 Map<Link, Object> riskProfile) {
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700572 Map<TopologyEdge, Object> riskProfile2 = new HashMap<>();
573 for (Link l : riskProfile.keySet()) {
574 riskProfile2.put(new TopologyEdge() {
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700575 Link cur = l;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700576
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -0700577 @Override
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700578 public Link link() {
579 return cur;
580 }
581
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -0700582 @Override
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700583 public TopologyVertex src() {
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700584 return () -> src;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700585 }
586
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -0700587 @Override
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700588 public TopologyVertex dst() {
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700589 return () -> dst;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700590 }
591 }, riskProfile.get(l));
592 }
Andrey Komarov2398d962016-09-26 15:11:23 +0300593 return disjointPaths(src, dst, weigher, riskProfile2);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700594 }
595
596 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300597 * Computes on-demand the set of shortest disjoint risk groups path pairs
598 * between source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700599 *
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700600 * @param src source device
601 * @param dst destination device
602 * @param riskProfile map representing risk groups for each link
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700603 * @return set of shortest disjoint paths
604 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300605 public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst,
606 Map<Link, Object> riskProfile) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800607 return getDisjointPaths(src, dst, linkWeight(), riskProfile);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700608 }
alshabib339a3d92014-09-26 17:54:32 -0700609
alshabib339a3d92014-09-26 17:54:32 -0700610 // Converts graph path to a network path with the same cost.
611 private Path networkPath(org.onlab.graph.Path<TopologyVertex, TopologyEdge> path) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300612 List<Link> links = path.edges().stream().map(TopologyEdge::link)
613 .collect(Collectors.toList());
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -0800614 return new DefaultPath(CORE_PROVIDER_ID, links, path.cost());
alshabib339a3d92014-09-26 17:54:32 -0700615 }
616
Andrey Komarov2398d962016-09-26 15:11:23 +0300617 private DisjointPath networkDisjointPath(
618 DisjointPathPair<TopologyVertex, TopologyEdge> path) {
Yuta HIGUCHIc3d69f52016-08-08 11:52:52 -0700619 if (!path.hasBackup()) {
620 // There was no secondary path available.
621 return new DefaultDisjointPath(CORE_PROVIDER_ID,
Andrey Komarov2398d962016-09-26 15:11:23 +0300622 (DefaultPath) networkPath(path.primary()),
623 null);
Yuta HIGUCHIc3d69f52016-08-08 11:52:52 -0700624 }
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700625 return new DefaultDisjointPath(CORE_PROVIDER_ID,
Andrey Komarov2398d962016-09-26 15:11:23 +0300626 (DefaultPath) networkPath(path.primary()),
627 (DefaultPath) networkPath(path.secondary()));
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700628 }
629
alshabib339a3d92014-09-26 17:54:32 -0700630 // Searches for SCC clusters in the network topology graph using Tarjan
631 // algorithm.
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800632 private SccResult<TopologyVertex, TopologyEdge> searchForClusters() {
Andrey Komarov2398d962016-09-26 15:11:23 +0300633 return TARJAN.search(graph, new NoIndirectLinksWeigher());
alshabib339a3d92014-09-26 17:54:32 -0700634 }
635
636 // Builds the topology clusters and returns the id-cluster bindings.
637 private ImmutableMap<ClusterId, TopologyCluster> buildTopologyClusters() {
Andrey Komarov2398d962016-09-26 15:11:23 +0300638 ImmutableMap.Builder<ClusterId, TopologyCluster> clusterBuilder =
639 ImmutableMap.builder();
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800640 SccResult<TopologyVertex, TopologyEdge> results = clusterResults.get();
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700641
alshabib339a3d92014-09-26 17:54:32 -0700642 // Extract both vertexes and edges from the results; the lists form
643 // pairs along the same index.
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800644 List<Set<TopologyVertex>> clusterVertexes = results.clusterVertexes();
645 List<Set<TopologyEdge>> clusterEdges = results.clusterEdges();
alshabib339a3d92014-09-26 17:54:32 -0700646
647 // Scan over the lists and create a cluster from the results.
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800648 for (int i = 0, n = results.clusterCount(); i < n; i++) {
alshabib339a3d92014-09-26 17:54:32 -0700649 Set<TopologyVertex> vertexSet = clusterVertexes.get(i);
650 Set<TopologyEdge> edgeSet = clusterEdges.get(i);
651
652 ClusterId cid = ClusterId.clusterId(i);
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500653 DefaultTopologyCluster cluster = new DefaultTopologyCluster(cid,
Andrey Komarov2398d962016-09-26 15:11:23 +0300654 vertexSet.size(),
655 edgeSet.size(),
656 findRoot(vertexSet));
alshabib339a3d92014-09-26 17:54:32 -0700657 clusterBuilder.put(cid, cluster);
658 }
659 return clusterBuilder.build();
660 }
661
662 // Finds the vertex whose device id is the lexicographical minimum in the
663 // specified set.
664 private TopologyVertex findRoot(Set<TopologyVertex> vertexSet) {
665 TopologyVertex minVertex = null;
666 for (TopologyVertex vertex : vertexSet) {
Jon Halle7539632016-05-11 15:44:31 -0700667 if ((minVertex == null) || (vertex.deviceId()
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500668 .toString().compareTo(minVertex.deviceId().toString()) < 0)) {
alshabib339a3d92014-09-26 17:54:32 -0700669 minVertex = vertex;
670 }
671 }
672 return minVertex;
673 }
674
675 // Processes a map of broadcast sets for each cluster.
676 private ImmutableSetMultimap<ClusterId, ConnectPoint> buildBroadcastSets() {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800677 Builder<ClusterId, ConnectPoint> builder = ImmutableSetMultimap.builder();
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800678 for (TopologyCluster cluster : clusters.get().values()) {
alshabib339a3d92014-09-26 17:54:32 -0700679 addClusterBroadcastSet(cluster, builder);
680 }
681 return builder.build();
682 }
683
684 // Finds all broadcast points for the cluster. These are those connection
685 // points which lie along the shortest paths between the cluster root and
686 // all other devices within the cluster.
Andrey Komarov2398d962016-09-26 15:11:23 +0300687 private void addClusterBroadcastSet(TopologyCluster cluster,
688 Builder<ClusterId, ConnectPoint> builder) {
alshabib339a3d92014-09-26 17:54:32 -0700689 // Use the graph root search results to build the broadcast set.
Andrey Komarov2398d962016-09-26 15:11:23 +0300690 Result<TopologyVertex, TopologyEdge> result =
691 DIJKSTRA.search(graph, cluster.root(), null, hopCountWeigher, 1);
692 for (Map.Entry<TopologyVertex, Set<TopologyEdge>> entry :
693 result.parents().entrySet()) {
alshabib339a3d92014-09-26 17:54:32 -0700694 TopologyVertex vertex = entry.getKey();
695
696 // Ignore any parents that lead outside the cluster.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800697 if (clustersByDevice().get(vertex.deviceId()) != cluster) {
alshabib339a3d92014-09-26 17:54:32 -0700698 continue;
699 }
700
701 // Ignore any back-link sets that are empty.
702 Set<TopologyEdge> parents = entry.getValue();
703 if (parents.isEmpty()) {
704 continue;
705 }
706
707 // Use the first back-link source and destinations to add to the
708 // broadcast set.
709 Link link = parents.iterator().next().link();
710 builder.put(cluster.id(), link.src());
711 builder.put(cluster.id(), link.dst());
712 }
713 }
714
715 // Collects and returns an set of all infrastructure link end-points.
716 private ImmutableSet<ConnectPoint> findInfrastructurePoints() {
717 ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
718 for (TopologyEdge edge : graph.getEdges()) {
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -0700719 if (edge.link().type() == Type.EDGE) {
720 // exclude EDGE link from infrastructure link
721 // - Device <-> Host
722 // - Device <-> remote domain Device
723 continue;
724 }
alshabib339a3d92014-09-26 17:54:32 -0700725 builder.add(edge.link().src());
726 builder.add(edge.link().dst());
727 }
728 return builder.build();
729 }
730
731 // Builds cluster-devices, cluster-links and device-cluster indexes.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800732 private ClusterIndexes buildIndexes() {
alshabib339a3d92014-09-26 17:54:32 -0700733 // Prepare the index builders
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500734 ImmutableMap.Builder<DeviceId, TopologyCluster> clusterBuilder =
735 ImmutableMap.builder();
736 ImmutableSetMultimap.Builder<TopologyCluster, DeviceId> devicesBuilder =
737 ImmutableSetMultimap.builder();
738 ImmutableSetMultimap.Builder<TopologyCluster, Link> linksBuilder =
739 ImmutableSetMultimap.builder();
alshabib339a3d92014-09-26 17:54:32 -0700740
741 // Now scan through all the clusters
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800742 for (TopologyCluster cluster : clusters.get().values()) {
alshabib339a3d92014-09-26 17:54:32 -0700743 int i = cluster.id().index();
744
745 // Scan through all the cluster vertexes.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800746 for (TopologyVertex vertex : clusterResults.get().clusterVertexes().get(i)) {
alshabib339a3d92014-09-26 17:54:32 -0700747 devicesBuilder.put(cluster, vertex.deviceId());
748 clusterBuilder.put(vertex.deviceId(), cluster);
749 }
750
751 // Scan through all the cluster edges.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800752 for (TopologyEdge edge : clusterResults.get().clusterEdges().get(i)) {
alshabib339a3d92014-09-26 17:54:32 -0700753 linksBuilder.put(cluster, edge.link());
754 }
755 }
756
757 // Finalize all indexes.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800758 return new ClusterIndexes(clusterBuilder.build(),
Andrey Komarov2398d962016-09-26 15:11:23 +0300759 devicesBuilder.build(),
760 linksBuilder.build());
alshabib339a3d92014-09-26 17:54:32 -0700761 }
762
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800763 private GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch() {
764 return defaultGraphPathSearch != null ? defaultGraphPathSearch : DIJKSTRA;
765 }
alshabib339a3d92014-09-26 17:54:32 -0700766
Andrey Komarov2398d962016-09-26 15:11:23 +0300767 private LinkWeigher linkWeight() {
768 return defaultLinkWeigher != null ? defaultLinkWeigher : hopCountWeigher;
alshabib339a3d92014-09-26 17:54:32 -0700769 }
770
771 // Link weight for preventing traversal over indirect links.
Andrey Komarov2398d962016-09-26 15:11:23 +0300772 private static class NoIndirectLinksWeigher
773 extends DefaultEdgeWeigher<TopologyVertex, TopologyEdge>
774 implements LinkWeigher {
alshabib339a3d92014-09-26 17:54:32 -0700775 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +0300776 public Weight weight(TopologyEdge edge) {
777 return (edge.link().state() == INACTIVE) ||
778 (edge.link().type() == INDIRECT) ?
779 getNonViableWeight() : new ScalarWeight(HOP_WEIGHT_VALUE);
alshabib339a3d92014-09-26 17:54:32 -0700780 }
781 }
782
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800783 static final class ClusterIndexes {
784 final ImmutableMap<DeviceId, TopologyCluster> clustersByDevice;
785 final ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
786 final ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
787
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700788 public ClusterIndexes(ImmutableMap<DeviceId, TopologyCluster> clustersByDevice,
789 ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster,
790 ImmutableSetMultimap<TopologyCluster, Link> linksByCluster) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800791 this.clustersByDevice = clustersByDevice;
792 this.devicesByCluster = devicesByCluster;
793 this.linksByCluster = linksByCluster;
794 }
795 }
796
alshabib339a3d92014-09-26 17:54:32 -0700797 @Override
798 public String toString() {
799 return toStringHelper(this)
800 .add("time", time)
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500801 .add("creationTime", creationTime)
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800802 .add("computeCost", computeCost)
alshabib339a3d92014-09-26 17:54:32 -0700803 .add("clusters", clusterCount())
804 .add("devices", deviceCount())
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500805 .add("links", linkCount()).toString();
alshabib339a3d92014-09-26 17:54:32 -0700806 }
807}