blob: 81822c10bcd03b7de6ffeaccbfc23a03364e3d34 [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;
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;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080052import org.onosproject.net.topology.HopCountLinkWeight;
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;
Andrey Komarov2398d962016-09-26 15:11:23 +030076import static org.onosproject.net.topology.AdapterLinkWeigher.adapt;
Thomas Vachuska930a8ee2015-08-04 18:49:36 -070077
alshabib339a3d92014-09-26 17:54:32 -070078/**
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -050079 * Default implementation of the topology descriptor. This carries the backing
80 * topology data.
alshabib339a3d92014-09-26 17:54:32 -070081 */
82public class DefaultTopology extends AbstractModel implements Topology {
83
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080084 private static final Logger log = LoggerFactory.getLogger(DefaultTopology.class);
85
Andrey Komarov2398d962016-09-26 15:11:23 +030086 private static final DijkstraGraphSearch<TopologyVertex, TopologyEdge> DIJKSTRA =
87 new DijkstraGraphSearch<>();
88 private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN =
89 new TarjanGraphSearch<>();
90 private static final SuurballeGraphSearch<TopologyVertex, TopologyEdge> SUURBALLE =
91 new SuurballeGraphSearch<>();
Yuta HIGUCHIac8b2292017-03-30 19:21:57 -070092 private static final KShortestPathsSearch<TopologyVertex, TopologyEdge> KSHORTEST =
93 new KShortestPathsSearch<>();
94 private static final LazyKShortestPathsSearch<TopologyVertex, TopologyEdge> LAZY_KSHORTEST =
95 new LazyKShortestPathsSearch<>();
96
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -070097
Andrey Komarov2398d962016-09-26 15:11:23 +030098 private static LinkWeigher defaultLinkWeigher = null;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -080099 private static GraphPathSearch<TopologyVertex, TopologyEdge> defaultGraphPathSearch = null;
alshabib339a3d92014-09-26 17:54:32 -0700100
alshabib339a3d92014-09-26 17:54:32 -0700101 private final long time;
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500102 private final long creationTime;
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800103 private final long computeCost;
alshabib339a3d92014-09-26 17:54:32 -0700104 private final TopologyGraph graph;
105
Andrey Komarov2398d962016-09-26 15:11:23 +0300106 private final LinkWeigher hopCountWeigher;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800107
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800108 private final Supplier<SccResult<TopologyVertex, TopologyEdge>> clusterResults;
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800109 private final Supplier<ImmutableMap<ClusterId, TopologyCluster>> clusters;
110 private final Supplier<ImmutableSet<ConnectPoint>> infrastructurePoints;
111 private final Supplier<ImmutableSetMultimap<ClusterId, ConnectPoint>> broadcastSets;
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700112 private final Function<ConnectPoint, Boolean> broadcastFunction;
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800113 private final Supplier<ClusterIndexes> clusterIndexes;
alshabib339a3d92014-09-26 17:54:32 -0700114
115 /**
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800116 * Sets the default link-weight to be used when computing paths. If null is
117 * specified, the builtin default link-weight measuring hop-counts will be
118 * used.
119 *
Andrey Komarov2398d962016-09-26 15:11:23 +0300120 * @param linkWeigher new default link-weight
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800121 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300122 public static void setDefaultLinkWeigher(LinkWeigher linkWeigher) {
123 log.info("Setting new default link-weight function to {}", linkWeigher);
124 defaultLinkWeigher = linkWeigher;
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800125 }
126
127 /**
128 * Sets the default lpath search algorighm to be used when computing paths.
129 * If null is specified, the builtin default Dijkstra will be used.
130 *
131 * @param graphPathSearch new default algorithm
132 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300133 public static void setDefaultGraphPathSearch(
134 GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800135 log.info("Setting new default graph path algorithm to {}", graphPathSearch);
136 defaultGraphPathSearch = graphPathSearch;
137 }
138
139
140 /**
alshabib339a3d92014-09-26 17:54:32 -0700141 * Creates a topology descriptor attributed to the specified provider.
142 *
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700143 * @param providerId identity of the provider
144 * @param description data describing the new topology
145 * @param broadcastFunction broadcast point function
alshabib339a3d92014-09-26 17:54:32 -0700146 */
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700147 public DefaultTopology(ProviderId providerId, GraphDescription description,
148 Function<ConnectPoint, Boolean> broadcastFunction) {
alshabib339a3d92014-09-26 17:54:32 -0700149 super(providerId);
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700150 this.broadcastFunction = broadcastFunction;
alshabib339a3d92014-09-26 17:54:32 -0700151 this.time = description.timestamp();
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500152 this.creationTime = description.creationTime();
alshabib339a3d92014-09-26 17:54:32 -0700153
154 // Build the graph
155 this.graph = new DefaultTopologyGraph(description.vertexes(),
Andrey Komarov2398d962016-09-26 15:11:23 +0300156 description.edges());
alshabib339a3d92014-09-26 17:54:32 -0700157
Andrey Komarov2398d962016-09-26 15:11:23 +0300158 this.clusterResults = Suppliers.memoize(this::searchForClusters);
159 this.clusters = Suppliers.memoize(this::buildTopologyClusters);
alshabib339a3d92014-09-26 17:54:32 -0700160
Andrey Komarov2398d962016-09-26 15:11:23 +0300161 this.clusterIndexes = Suppliers.memoize(this::buildIndexes);
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800162
Andrey Komarov2398d962016-09-26 15:11:23 +0300163 this.hopCountWeigher = adapt(new HopCountLinkWeight(graph.getVertexes().size()));
164 this.broadcastSets = Suppliers.memoize(this::buildBroadcastSets);
165 this.infrastructurePoints = Suppliers.memoize(this::findInfrastructurePoints);
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800166 this.computeCost = Math.max(0, System.nanoTime() - time);
alshabib339a3d92014-09-26 17:54:32 -0700167 }
168
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700169 /**
170 * Creates a topology descriptor attributed to the specified provider.
171 *
172 * @param providerId identity of the provider
173 * @param description data describing the new topology
174 */
175 public DefaultTopology(ProviderId providerId, GraphDescription description) {
176 this(providerId, description, null);
177 }
178
alshabib339a3d92014-09-26 17:54:32 -0700179 @Override
180 public long time() {
181 return time;
182 }
183
184 @Override
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500185 public long creationTime() {
186 return creationTime;
187 }
188
189 @Override
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800190 public long computeCost() {
191 return computeCost;
192 }
193
194 @Override
alshabib339a3d92014-09-26 17:54:32 -0700195 public int clusterCount() {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800196 return clusters.get().size();
alshabib339a3d92014-09-26 17:54:32 -0700197 }
198
199 @Override
200 public int deviceCount() {
201 return graph.getVertexes().size();
202 }
203
204 @Override
205 public int linkCount() {
206 return graph.getEdges().size();
207 }
208
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800209 private ImmutableMap<DeviceId, TopologyCluster> clustersByDevice() {
210 return clusterIndexes.get().clustersByDevice;
211 }
212
213 private ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster() {
214 return clusterIndexes.get().devicesByCluster;
215 }
216
217 private ImmutableSetMultimap<TopologyCluster, Link> linksByCluster() {
218 return clusterIndexes.get().linksByCluster;
alshabib339a3d92014-09-26 17:54:32 -0700219 }
220
221 /**
222 * Returns the backing topology graph.
223 *
224 * @return topology graph
225 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700226 public TopologyGraph getGraph() {
alshabib339a3d92014-09-26 17:54:32 -0700227 return graph;
228 }
229
230 /**
231 * Returns the set of topology clusters.
232 *
233 * @return set of clusters
234 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700235 public Set<TopologyCluster> getClusters() {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800236 return ImmutableSet.copyOf(clusters.get().values());
alshabib339a3d92014-09-26 17:54:32 -0700237 }
238
239 /**
240 * Returns the specified topology cluster.
241 *
242 * @param clusterId cluster identifier
243 * @return topology cluster
244 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700245 public TopologyCluster getCluster(ClusterId clusterId) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800246 return clusters.get().get(clusterId);
alshabib339a3d92014-09-26 17:54:32 -0700247 }
248
249 /**
250 * Returns the topology cluster that contains the given device.
251 *
252 * @param deviceId device identifier
253 * @return topology cluster
254 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700255 public TopologyCluster getCluster(DeviceId deviceId) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800256 return clustersByDevice().get(deviceId);
alshabib339a3d92014-09-26 17:54:32 -0700257 }
258
259 /**
260 * Returns the set of cluster devices.
261 *
262 * @param cluster topology cluster
263 * @return cluster devices
264 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700265 public Set<DeviceId> getClusterDevices(TopologyCluster cluster) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800266 return devicesByCluster().get(cluster);
alshabib339a3d92014-09-26 17:54:32 -0700267 }
268
269 /**
270 * Returns the set of cluster links.
271 *
272 * @param cluster topology cluster
273 * @return cluster links
274 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700275 public Set<Link> getClusterLinks(TopologyCluster cluster) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800276 return linksByCluster().get(cluster);
alshabib339a3d92014-09-26 17:54:32 -0700277 }
278
279 /**
280 * Indicates whether the given point is an infrastructure link end-point.
281 *
282 * @param connectPoint connection point
283 * @return true if infrastructure
284 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700285 public boolean isInfrastructure(ConnectPoint connectPoint) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800286 return infrastructurePoints.get().contains(connectPoint);
alshabib339a3d92014-09-26 17:54:32 -0700287 }
288
289 /**
290 * Indicates whether the given point is part of a broadcast set.
291 *
292 * @param connectPoint connection point
293 * @return true if in broadcast set
294 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700295 public boolean isBroadcastPoint(ConnectPoint connectPoint) {
Thomas Vachuskab2c47a72015-08-05 14:22:54 -0700296 if (broadcastFunction != null) {
297 return broadcastFunction.apply(connectPoint);
298 }
299
alshabib339a3d92014-09-26 17:54:32 -0700300 // Any non-infrastructure, i.e. edge points are assumed to be OK.
301 if (!isInfrastructure(connectPoint)) {
302 return true;
303 }
304
305 // Find the cluster to which the device belongs.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800306 TopologyCluster cluster = clustersByDevice().get(connectPoint.deviceId());
Andrey Komarov2398d962016-09-26 15:11:23 +0300307 checkArgument(cluster != null,
308 "No cluster found for device %s", connectPoint.deviceId());
alshabib339a3d92014-09-26 17:54:32 -0700309
310 // If the broadcast set is null or empty, or if the point explicitly
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700311 // belongs to it, return true.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800312 Set<ConnectPoint> points = broadcastSets.get().get(cluster.id());
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700313 return isNullOrEmpty(points) || points.contains(connectPoint);
alshabib339a3d92014-09-26 17:54:32 -0700314 }
315
316 /**
317 * Returns the size of the cluster broadcast set.
318 *
319 * @param clusterId cluster identifier
320 * @return size of the cluster broadcast set
321 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700322 public int broadcastSetSize(ClusterId clusterId) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800323 return broadcastSets.get().get(clusterId).size();
alshabib339a3d92014-09-26 17:54:32 -0700324 }
325
326 /**
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700327 * Returns the set of the cluster broadcast points.
328 *
329 * @param clusterId cluster identifier
330 * @return set of cluster broadcast points
331 */
332 public Set<ConnectPoint> broadcastPoints(ClusterId clusterId) {
333 return broadcastSets.get().get(clusterId);
334 }
335
336 /**
alshabib339a3d92014-09-26 17:54:32 -0700337 * Returns the set of pre-computed shortest paths between source and
338 * destination devices.
339 *
340 * @param src source device
341 * @param dst destination device
342 * @return set of shortest paths
343 */
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700344 public Set<Path> getPaths(DeviceId src, DeviceId dst) {
Aaron Kruglikovf6aed992016-11-08 15:16:03 -0800345 return getPaths(src, dst, linkWeight(), ALL_PATHS);
alshabib339a3d92014-09-26 17:54:32 -0700346 }
347
348 /**
349 * Computes on-demand the set of shortest paths between source and
350 * destination devices.
351 *
Andrey Komarov2398d962016-09-26 15:11:23 +0300352 * @param src source device
353 * @param dst destination device
354 * @param weigher link weight function
alshabib339a3d92014-09-26 17:54:32 -0700355 * @return set of shortest paths
356 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300357 public Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeigher weigher) {
358 return getPaths(src, dst, weigher, ALL_PATHS);
Aaron Kruglikovf6aed992016-11-08 15:16:03 -0800359 }
360
361 /**
362 * Computes on-demand the set of shortest paths between source and
363 * destination devices, the set of returned paths will be no more than,
364 * maxPaths in size. The first {@code maxPaths} paths will be returned
365 * maintaining any ordering guarantees provided by the underlying
366 * (default or if no default is specified {@link DijkstraGraphSearch})
367 * search. If returning all paths of a given length would exceed
368 * {@code maxPaths} a subset of paths of that length will be returned,
369 * which paths will be returned depends on the currently specified
370 * {@code GraphPathSearch}. See {@link #setDefaultGraphPathSearch}.
371 *
372 * @param src source device
373 * @param dst destination device
Andrey Komarov2398d962016-09-26 15:11:23 +0300374 * @param weigher link weight function
Aaron Kruglikovf6aed992016-11-08 15:16:03 -0800375 * @param maxPaths maximum number of paths
376 * @return set of shortest paths
377 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300378 public Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeigher weigher,
Aaron Kruglikovf6aed992016-11-08 15:16:03 -0800379 int maxPaths) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800380 DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
381 DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800382 Set<TopologyVertex> vertices = graph.getVertexes();
383 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
384 // src or dst not part of the current graph
385 return ImmutableSet.of();
386 }
387
alshabib339a3d92014-09-26 17:54:32 -0700388 GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
Andrey Komarov2398d962016-09-26 15:11:23 +0300389 graphPathSearch().search(graph, srcV, dstV, weigher, maxPaths);
alshabib339a3d92014-09-26 17:54:32 -0700390 ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
391 for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
392 builder.add(networkPath(path));
393 }
394 return builder.build();
395 }
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700396
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700397 /**
Yuta HIGUCHIac8b2292017-03-30 19:21:57 -0700398 * Computes on-demand the k-shortest paths between source and
399 * destination devices.
400 *
401 * @param src source device
402 * @param dst destination device
403 * @param maxPaths maximum number of paths (k)
404 * @return set of k-shortest paths
405 */
406 public Set<Path> getKShortestPaths(DeviceId src, DeviceId dst,
407 int maxPaths) {
408
409 return getKShortestPaths(src, dst, linkWeight(), maxPaths);
410 }
411
412 /**
413 * Computes on-demand the k-shortest paths between source and
414 * destination devices.
415 *
416 * The first {@code maxPaths} paths will be returned
417 * in ascending order according to the provided {@code weigher}
418 *
419 * @param src source device
420 * @param dst destination device
421 * @param weigher link weight function
422 * @param maxPaths maximum number of paths (k)
423 * @return set of k-shortest paths
424 */
425 public Set<Path> getKShortestPaths(DeviceId src, DeviceId dst,
426 LinkWeigher weigher,
427 int maxPaths) {
428 DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
429 DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
430 Set<TopologyVertex> vertices = graph.getVertexes();
431 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
432 // src or dst not part of the current graph
433 return ImmutableSet.of();
434 }
435
436 return KSHORTEST.search(graph, srcV, dstV, weigher, maxPaths)
437 .paths().stream()
438 .map(this::networkPath)
Yuta HIGUCHI498fa1d2017-05-17 16:08:40 -0700439 .collect(ImmutableSet.toImmutableSet());
Yuta HIGUCHIac8b2292017-03-30 19:21:57 -0700440 }
441
442 /**
443 * Lazily computes on-demand the k-shortest paths between source and
444 * destination devices.
445 *
446 *
447 * @param src source device
448 * @param dst destination device
449 * @return stream of k-shortest paths
450 */
451 public Stream<Path> getKShortestPaths(DeviceId src, DeviceId dst) {
452 return getKShortestPaths(src, dst, linkWeight());
453 }
454
455 /**
456 * Lazily computes on-demand the k-shortest paths between source and
457 * destination devices.
458 *
459 *
460 * @param src source device
461 * @param dst destination device
462 * @param weigher link weight function
463 * @return stream of k-shortest paths
464 */
465 public Stream<Path> getKShortestPaths(DeviceId src, DeviceId dst,
466 LinkWeigher weigher) {
467 DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
468 DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
469 Set<TopologyVertex> vertices = graph.getVertexes();
470 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
471 // src or dst not part of the current graph
472 return Stream.empty();
473 }
474
475 return LAZY_KSHORTEST.lazyPathSearch(graph, srcV, dstV, weigher)
476 .map(this::networkPath);
477 }
478
479 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300480 * Returns the set of pre-computed shortest disjoint path pairs between
481 * source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700482 *
483 * @param src source device
484 * @param dst destination device
485 * @return set of shortest disjoint path pairs
486 */
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700487 public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800488 return getDisjointPaths(src, dst, linkWeight());
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700489 }
490
491 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300492 * Computes on-demand the set of shortest disjoint path pairs between
493 * source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700494 *
Andrey Komarov2398d962016-09-26 15:11:23 +0300495 * @param src source device
496 * @param dst destination device
497 * @param weigher link weight function
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700498 * @return set of disjoint shortest path pairs
499 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300500 public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst,
501 LinkWeigher weigher) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800502 DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
503 DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700504 Set<TopologyVertex> vertices = graph.getVertexes();
505 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
506 // src or dst not part of the current graph
507 return ImmutableSet.of();
508 }
509
510 GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
Andrey Komarov2398d962016-09-26 15:11:23 +0300511 SUURBALLE.search(graph, srcV, dstV, weigher, ALL_PATHS);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700512 ImmutableSet.Builder<DisjointPath> builder = ImmutableSet.builder();
513 for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
helenyrwue3c43342016-08-11 10:33:12 -0700514 DisjointPath disjointPath =
Andrey Komarov2398d962016-09-26 15:11:23 +0300515 networkDisjointPath((DisjointPathPair<TopologyVertex, TopologyEdge>) path);
helenyrwue3c43342016-08-11 10:33:12 -0700516 if (disjointPath.backup() != null) {
517 builder.add(disjointPath);
518 }
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700519 }
520 return builder.build();
521 }
522
523 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300524 * Computes on-demand the set of shortest disjoint risk groups path pairs
525 * between source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700526 *
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700527 * @param src source device
528 * @param dst destination device
Andrey Komarov2398d962016-09-26 15:11:23 +0300529 * @param weigher edge weight object
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700530 * @param riskProfile map representing risk groups for each edge
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700531 * @return set of shortest disjoint paths
532 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300533 private Set<DisjointPath> disjointPaths(DeviceId src, DeviceId dst,
534 LinkWeigher weigher,
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800535 Map<TopologyEdge, Object> riskProfile) {
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700536 DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
537 DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
538
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700539 Set<TopologyVertex> vertices = graph.getVertexes();
540 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
541 // src or dst not part of the current graph
542 return ImmutableSet.of();
543 }
544
Andrey Komarov2398d962016-09-26 15:11:23 +0300545 SrlgGraphSearch<TopologyVertex, TopologyEdge> srlg =
546 new SrlgGraphSearch<>(riskProfile);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700547 GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
Andrey Komarov2398d962016-09-26 15:11:23 +0300548 srlg.search(graph, srcV, dstV, weigher, ALL_PATHS);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700549 ImmutableSet.Builder<DisjointPath> builder = ImmutableSet.builder();
550 for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
helenyrwue3c43342016-08-11 10:33:12 -0700551 DisjointPath disjointPath =
Andrey Komarov2398d962016-09-26 15:11:23 +0300552 networkDisjointPath((DisjointPathPair<TopologyVertex, TopologyEdge>) path);
helenyrwue3c43342016-08-11 10:33:12 -0700553 if (disjointPath.backup() != null) {
554 builder.add(disjointPath);
555 }
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700556 }
557 return builder.build();
558 }
559
560 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300561 * Computes on-demand the set of shortest disjoint risk groups path pairs
562 * between source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700563 *
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700564 * @param src source device
565 * @param dst destination device
Andrey Komarov2398d962016-09-26 15:11:23 +0300566 * @param weigher edge weight object
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700567 * @param riskProfile map representing risk groups for each link
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700568 * @return set of shortest disjoint paths
569 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300570 public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst,
571 LinkWeigher weigher,
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700572 Map<Link, Object> riskProfile) {
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700573 Map<TopologyEdge, Object> riskProfile2 = new HashMap<>();
574 for (Link l : riskProfile.keySet()) {
575 riskProfile2.put(new TopologyEdge() {
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700576 Link cur = l;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700577
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -0700578 @Override
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700579 public Link link() {
580 return cur;
581 }
582
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -0700583 @Override
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700584 public TopologyVertex src() {
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700585 return () -> src;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700586 }
587
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -0700588 @Override
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700589 public TopologyVertex dst() {
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700590 return () -> dst;
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700591 }
592 }, riskProfile.get(l));
593 }
Andrey Komarov2398d962016-09-26 15:11:23 +0300594 return disjointPaths(src, dst, weigher, riskProfile2);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700595 }
596
597 /**
Andrey Komarov2398d962016-09-26 15:11:23 +0300598 * Computes on-demand the set of shortest disjoint risk groups path pairs
599 * between source and destination devices.
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700600 *
Nikhil Cheerla2ec191f2015-07-09 12:34:54 -0700601 * @param src source device
602 * @param dst destination device
603 * @param riskProfile map representing risk groups for each link
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700604 * @return set of shortest disjoint paths
605 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300606 public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst,
607 Map<Link, Object> riskProfile) {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800608 return getDisjointPaths(src, dst, linkWeight(), riskProfile);
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700609 }
alshabib339a3d92014-09-26 17:54:32 -0700610
alshabib339a3d92014-09-26 17:54:32 -0700611 // Converts graph path to a network path with the same cost.
612 private Path networkPath(org.onlab.graph.Path<TopologyVertex, TopologyEdge> path) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300613 List<Link> links = path.edges().stream().map(TopologyEdge::link)
614 .collect(Collectors.toList());
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -0800615 return new DefaultPath(CORE_PROVIDER_ID, links, path.cost());
alshabib339a3d92014-09-26 17:54:32 -0700616 }
617
Andrey Komarov2398d962016-09-26 15:11:23 +0300618 private DisjointPath networkDisjointPath(
619 DisjointPathPair<TopologyVertex, TopologyEdge> path) {
Yuta HIGUCHIc3d69f52016-08-08 11:52:52 -0700620 if (!path.hasBackup()) {
621 // There was no secondary path available.
622 return new DefaultDisjointPath(CORE_PROVIDER_ID,
Andrey Komarov2398d962016-09-26 15:11:23 +0300623 (DefaultPath) networkPath(path.primary()),
624 null);
Yuta HIGUCHIc3d69f52016-08-08 11:52:52 -0700625 }
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700626 return new DefaultDisjointPath(CORE_PROVIDER_ID,
Andrey Komarov2398d962016-09-26 15:11:23 +0300627 (DefaultPath) networkPath(path.primary()),
628 (DefaultPath) networkPath(path.secondary()));
Nikhil Cheerla1cf0f9a2015-07-09 12:26:48 -0700629 }
630
alshabib339a3d92014-09-26 17:54:32 -0700631 // Searches for SCC clusters in the network topology graph using Tarjan
632 // algorithm.
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800633 private SccResult<TopologyVertex, TopologyEdge> searchForClusters() {
Andrey Komarov2398d962016-09-26 15:11:23 +0300634 return TARJAN.search(graph, new NoIndirectLinksWeigher());
alshabib339a3d92014-09-26 17:54:32 -0700635 }
636
637 // Builds the topology clusters and returns the id-cluster bindings.
638 private ImmutableMap<ClusterId, TopologyCluster> buildTopologyClusters() {
Andrey Komarov2398d962016-09-26 15:11:23 +0300639 ImmutableMap.Builder<ClusterId, TopologyCluster> clusterBuilder =
640 ImmutableMap.builder();
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800641 SccResult<TopologyVertex, TopologyEdge> results = clusterResults.get();
Thomas Vachuska48e64e42015-09-22 15:32:55 -0700642
alshabib339a3d92014-09-26 17:54:32 -0700643 // Extract both vertexes and edges from the results; the lists form
644 // pairs along the same index.
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800645 List<Set<TopologyVertex>> clusterVertexes = results.clusterVertexes();
646 List<Set<TopologyEdge>> clusterEdges = results.clusterEdges();
alshabib339a3d92014-09-26 17:54:32 -0700647
648 // Scan over the lists and create a cluster from the results.
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800649 for (int i = 0, n = results.clusterCount(); i < n; i++) {
alshabib339a3d92014-09-26 17:54:32 -0700650 Set<TopologyVertex> vertexSet = clusterVertexes.get(i);
651 Set<TopologyEdge> edgeSet = clusterEdges.get(i);
652
653 ClusterId cid = ClusterId.clusterId(i);
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500654 DefaultTopologyCluster cluster = new DefaultTopologyCluster(cid,
Andrey Komarov2398d962016-09-26 15:11:23 +0300655 vertexSet.size(),
656 edgeSet.size(),
657 findRoot(vertexSet));
alshabib339a3d92014-09-26 17:54:32 -0700658 clusterBuilder.put(cid, cluster);
659 }
660 return clusterBuilder.build();
661 }
662
663 // Finds the vertex whose device id is the lexicographical minimum in the
664 // specified set.
665 private TopologyVertex findRoot(Set<TopologyVertex> vertexSet) {
666 TopologyVertex minVertex = null;
667 for (TopologyVertex vertex : vertexSet) {
Jon Halle7539632016-05-11 15:44:31 -0700668 if ((minVertex == null) || (vertex.deviceId()
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500669 .toString().compareTo(minVertex.deviceId().toString()) < 0)) {
alshabib339a3d92014-09-26 17:54:32 -0700670 minVertex = vertex;
671 }
672 }
673 return minVertex;
674 }
675
676 // Processes a map of broadcast sets for each cluster.
677 private ImmutableSetMultimap<ClusterId, ConnectPoint> buildBroadcastSets() {
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800678 Builder<ClusterId, ConnectPoint> builder = ImmutableSetMultimap.builder();
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800679 for (TopologyCluster cluster : clusters.get().values()) {
alshabib339a3d92014-09-26 17:54:32 -0700680 addClusterBroadcastSet(cluster, builder);
681 }
682 return builder.build();
683 }
684
685 // Finds all broadcast points for the cluster. These are those connection
686 // points which lie along the shortest paths between the cluster root and
687 // all other devices within the cluster.
Andrey Komarov2398d962016-09-26 15:11:23 +0300688 private void addClusterBroadcastSet(TopologyCluster cluster,
689 Builder<ClusterId, ConnectPoint> builder) {
alshabib339a3d92014-09-26 17:54:32 -0700690 // Use the graph root search results to build the broadcast set.
Andrey Komarov2398d962016-09-26 15:11:23 +0300691 Result<TopologyVertex, TopologyEdge> result =
692 DIJKSTRA.search(graph, cluster.root(), null, hopCountWeigher, 1);
693 for (Map.Entry<TopologyVertex, Set<TopologyEdge>> entry :
694 result.parents().entrySet()) {
alshabib339a3d92014-09-26 17:54:32 -0700695 TopologyVertex vertex = entry.getKey();
696
697 // Ignore any parents that lead outside the cluster.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800698 if (clustersByDevice().get(vertex.deviceId()) != cluster) {
alshabib339a3d92014-09-26 17:54:32 -0700699 continue;
700 }
701
702 // Ignore any back-link sets that are empty.
703 Set<TopologyEdge> parents = entry.getValue();
704 if (parents.isEmpty()) {
705 continue;
706 }
707
708 // Use the first back-link source and destinations to add to the
709 // broadcast set.
710 Link link = parents.iterator().next().link();
711 builder.put(cluster.id(), link.src());
712 builder.put(cluster.id(), link.dst());
713 }
714 }
715
716 // Collects and returns an set of all infrastructure link end-points.
717 private ImmutableSet<ConnectPoint> findInfrastructurePoints() {
718 ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
719 for (TopologyEdge edge : graph.getEdges()) {
Yuta HIGUCHIb440ef42016-07-15 09:09:09 -0700720 if (edge.link().type() == Type.EDGE) {
721 // exclude EDGE link from infrastructure link
722 // - Device <-> Host
723 // - Device <-> remote domain Device
724 continue;
725 }
alshabib339a3d92014-09-26 17:54:32 -0700726 builder.add(edge.link().src());
727 builder.add(edge.link().dst());
728 }
729 return builder.build();
730 }
731
732 // Builds cluster-devices, cluster-links and device-cluster indexes.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800733 private ClusterIndexes buildIndexes() {
alshabib339a3d92014-09-26 17:54:32 -0700734 // Prepare the index builders
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500735 ImmutableMap.Builder<DeviceId, TopologyCluster> clusterBuilder =
736 ImmutableMap.builder();
737 ImmutableSetMultimap.Builder<TopologyCluster, DeviceId> devicesBuilder =
738 ImmutableSetMultimap.builder();
739 ImmutableSetMultimap.Builder<TopologyCluster, Link> linksBuilder =
740 ImmutableSetMultimap.builder();
alshabib339a3d92014-09-26 17:54:32 -0700741
742 // Now scan through all the clusters
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800743 for (TopologyCluster cluster : clusters.get().values()) {
alshabib339a3d92014-09-26 17:54:32 -0700744 int i = cluster.id().index();
745
746 // Scan through all the cluster vertexes.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800747 for (TopologyVertex vertex : clusterResults.get().clusterVertexes().get(i)) {
alshabib339a3d92014-09-26 17:54:32 -0700748 devicesBuilder.put(cluster, vertex.deviceId());
749 clusterBuilder.put(vertex.deviceId(), cluster);
750 }
751
752 // Scan through all the cluster edges.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800753 for (TopologyEdge edge : clusterResults.get().clusterEdges().get(i)) {
alshabib339a3d92014-09-26 17:54:32 -0700754 linksBuilder.put(cluster, edge.link());
755 }
756 }
757
758 // Finalize all indexes.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800759 return new ClusterIndexes(clusterBuilder.build(),
Andrey Komarov2398d962016-09-26 15:11:23 +0300760 devicesBuilder.build(),
761 linksBuilder.build());
alshabib339a3d92014-09-26 17:54:32 -0700762 }
763
Thomas Vachuska41fe1ec2015-12-03 23:17:02 -0800764 private GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch() {
765 return defaultGraphPathSearch != null ? defaultGraphPathSearch : DIJKSTRA;
766 }
alshabib339a3d92014-09-26 17:54:32 -0700767
Andrey Komarov2398d962016-09-26 15:11:23 +0300768 private LinkWeigher linkWeight() {
769 return defaultLinkWeigher != null ? defaultLinkWeigher : hopCountWeigher;
alshabib339a3d92014-09-26 17:54:32 -0700770 }
771
772 // Link weight for preventing traversal over indirect links.
Andrey Komarov2398d962016-09-26 15:11:23 +0300773 private static class NoIndirectLinksWeigher
774 extends DefaultEdgeWeigher<TopologyVertex, TopologyEdge>
775 implements LinkWeigher {
alshabib339a3d92014-09-26 17:54:32 -0700776 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +0300777 public Weight weight(TopologyEdge edge) {
778 return (edge.link().state() == INACTIVE) ||
779 (edge.link().type() == INDIRECT) ?
780 getNonViableWeight() : new ScalarWeight(HOP_WEIGHT_VALUE);
alshabib339a3d92014-09-26 17:54:32 -0700781 }
782 }
783
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800784 static final class ClusterIndexes {
785 final ImmutableMap<DeviceId, TopologyCluster> clustersByDevice;
786 final ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
787 final ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
788
Thomas Vachuska930a8ee2015-08-04 18:49:36 -0700789 public ClusterIndexes(ImmutableMap<DeviceId, TopologyCluster> clustersByDevice,
790 ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster,
791 ImmutableSetMultimap<TopologyCluster, Link> linksByCluster) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800792 this.clustersByDevice = clustersByDevice;
793 this.devicesByCluster = devicesByCluster;
794 this.linksByCluster = linksByCluster;
795 }
796 }
797
alshabib339a3d92014-09-26 17:54:32 -0700798 @Override
799 public String toString() {
800 return toStringHelper(this)
801 .add("time", time)
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500802 .add("creationTime", creationTime)
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800803 .add("computeCost", computeCost)
alshabib339a3d92014-09-26 17:54:32 -0700804 .add("clusters", clusterCount())
805 .add("devices", deviceCount())
Abhishek Dwaraki1e5873e2015-03-08 00:01:17 -0500806 .add("links", linkCount()).toString();
alshabib339a3d92014-09-26 17:54:32 -0700807 }
808}