blob: 8c15e45a5db118b8b0e9a3274c5436e908ae744a [file] [log] [blame]
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.store.topology.impl;
alshabib339a3d92014-09-26 17:54:32 -070017
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -080018import com.google.common.base.Supplier;
19import com.google.common.base.Suppliers;
alshabib339a3d92014-09-26 17:54:32 -070020import com.google.common.collect.ImmutableMap;
21import com.google.common.collect.ImmutableSet;
22import com.google.common.collect.ImmutableSetMultimap;
23import org.onlab.graph.DijkstraGraphSearch;
24import org.onlab.graph.GraphPathSearch;
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -080025import org.onlab.graph.GraphPathSearch.Result;
alshabib339a3d92014-09-26 17:54:32 -070026import org.onlab.graph.TarjanGraphSearch;
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -080027import org.onlab.graph.TarjanGraphSearch.SCCResult;
Brian O'Connorabafb502014-12-02 22:26:20 -080028import org.onosproject.net.AbstractModel;
29import org.onosproject.net.ConnectPoint;
30import org.onosproject.net.DefaultPath;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.Link;
33import org.onosproject.net.Path;
34import org.onosproject.net.provider.ProviderId;
35import org.onosproject.net.topology.ClusterId;
36import org.onosproject.net.topology.DefaultTopologyCluster;
37import org.onosproject.net.topology.DefaultTopologyVertex;
38import org.onosproject.net.topology.GraphDescription;
39import org.onosproject.net.topology.LinkWeight;
40import org.onosproject.net.topology.Topology;
41import org.onosproject.net.topology.TopologyCluster;
42import org.onosproject.net.topology.TopologyEdge;
43import org.onosproject.net.topology.TopologyGraph;
44import org.onosproject.net.topology.TopologyVertex;
alshabib339a3d92014-09-26 17:54:32 -070045
46import java.util.ArrayList;
47import java.util.List;
48import java.util.Map;
49import java.util.Set;
50
51import static com.google.common.base.MoreObjects.toStringHelper;
52import static com.google.common.collect.ImmutableSetMultimap.Builder;
Thomas Vachuskac31d9f12015-01-22 12:33:27 -080053import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
Brian O'Connorabafb502014-12-02 22:26:20 -080054import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
55import static org.onosproject.net.Link.State.ACTIVE;
56import static org.onosproject.net.Link.State.INACTIVE;
57import static org.onosproject.net.Link.Type.INDIRECT;
alshabib339a3d92014-09-26 17:54:32 -070058
Thomas Vachuskac31d9f12015-01-22 12:33:27 -080059// FIXME: Move to onos-core-common when ready
alshabib339a3d92014-09-26 17:54:32 -070060/**
61 * Default implementation of the topology descriptor. This carries the
62 * backing topology data.
63 */
64public class DefaultTopology extends AbstractModel implements Topology {
65
66 private static final DijkstraGraphSearch<TopologyVertex, TopologyEdge> DIJKSTRA =
67 new DijkstraGraphSearch<>();
68 private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN =
69 new TarjanGraphSearch<>();
70
alshabib339a3d92014-09-26 17:54:32 -070071 private final long time;
Thomas Vachuska6b7920d2014-11-25 19:48:39 -080072 private final long computeCost;
alshabib339a3d92014-09-26 17:54:32 -070073 private final TopologyGraph graph;
74
Thomas Vachuskac31d9f12015-01-22 12:33:27 -080075 private final LinkWeight weight;
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -080076 private final Supplier<SCCResult<TopologyVertex, TopologyEdge>> clusterResults;
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -080077 private final Supplier<ImmutableMap<ClusterId, TopologyCluster>> clusters;
78 private final Supplier<ImmutableSet<ConnectPoint>> infrastructurePoints;
79 private final Supplier<ImmutableSetMultimap<ClusterId, ConnectPoint>> broadcastSets;
alshabib339a3d92014-09-26 17:54:32 -070080
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -080081 private final Supplier<ClusterIndexes> clusterIndexes;
alshabib339a3d92014-09-26 17:54:32 -070082
83 /**
84 * Creates a topology descriptor attributed to the specified provider.
85 *
86 * @param providerId identity of the provider
87 * @param description data describing the new topology
88 */
89 DefaultTopology(ProviderId providerId, GraphDescription description) {
90 super(providerId);
91 this.time = description.timestamp();
92
93 // Build the graph
94 this.graph = new DefaultTopologyGraph(description.vertexes(),
95 description.edges());
96
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -080097 this.clusterResults = Suppliers.memoize(() -> searchForClusters());
98 this.clusters = Suppliers.memoize(() -> buildTopologyClusters());
alshabib339a3d92014-09-26 17:54:32 -070099
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800100 this.clusterIndexes = Suppliers.memoize(() -> buildIndexes());
101
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800102 this.weight = new HopCountLinkWeight(graph.getVertexes().size());
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800103 this.broadcastSets = Suppliers.memoize(() -> buildBroadcastSets());
104 this.infrastructurePoints = Suppliers.memoize(() -> findInfrastructurePoints());
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800105 this.computeCost = Math.max(0, System.nanoTime() - time);
alshabib339a3d92014-09-26 17:54:32 -0700106 }
107
108 @Override
109 public long time() {
110 return time;
111 }
112
113 @Override
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800114 public long computeCost() {
115 return computeCost;
116 }
117
118 @Override
alshabib339a3d92014-09-26 17:54:32 -0700119 public int clusterCount() {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800120 return clusters.get().size();
alshabib339a3d92014-09-26 17:54:32 -0700121 }
122
123 @Override
124 public int deviceCount() {
125 return graph.getVertexes().size();
126 }
127
128 @Override
129 public int linkCount() {
130 return graph.getEdges().size();
131 }
132
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800133 private ImmutableMap<DeviceId, TopologyCluster> clustersByDevice() {
134 return clusterIndexes.get().clustersByDevice;
135 }
136
137 private ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster() {
138 return clusterIndexes.get().devicesByCluster;
139 }
140
141 private ImmutableSetMultimap<TopologyCluster, Link> linksByCluster() {
142 return clusterIndexes.get().linksByCluster;
alshabib339a3d92014-09-26 17:54:32 -0700143 }
144
145 /**
146 * Returns the backing topology graph.
147 *
148 * @return topology graph
149 */
150 TopologyGraph getGraph() {
151 return graph;
152 }
153
154 /**
155 * Returns the set of topology clusters.
156 *
157 * @return set of clusters
158 */
159 Set<TopologyCluster> getClusters() {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800160 return ImmutableSet.copyOf(clusters.get().values());
alshabib339a3d92014-09-26 17:54:32 -0700161 }
162
163 /**
164 * Returns the specified topology cluster.
165 *
166 * @param clusterId cluster identifier
167 * @return topology cluster
168 */
169 TopologyCluster getCluster(ClusterId clusterId) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800170 return clusters.get().get(clusterId);
alshabib339a3d92014-09-26 17:54:32 -0700171 }
172
173 /**
174 * Returns the topology cluster that contains the given device.
175 *
176 * @param deviceId device identifier
177 * @return topology cluster
178 */
179 TopologyCluster getCluster(DeviceId deviceId) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800180 return clustersByDevice().get(deviceId);
alshabib339a3d92014-09-26 17:54:32 -0700181 }
182
183 /**
184 * Returns the set of cluster devices.
185 *
186 * @param cluster topology cluster
187 * @return cluster devices
188 */
189 Set<DeviceId> getClusterDevices(TopologyCluster cluster) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800190 return devicesByCluster().get(cluster);
alshabib339a3d92014-09-26 17:54:32 -0700191 }
192
193 /**
194 * Returns the set of cluster links.
195 *
196 * @param cluster topology cluster
197 * @return cluster links
198 */
199 Set<Link> getClusterLinks(TopologyCluster cluster) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800200 return linksByCluster().get(cluster);
alshabib339a3d92014-09-26 17:54:32 -0700201 }
202
203 /**
204 * Indicates whether the given point is an infrastructure link end-point.
205 *
206 * @param connectPoint connection point
207 * @return true if infrastructure
208 */
209 boolean isInfrastructure(ConnectPoint connectPoint) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800210 return infrastructurePoints.get().contains(connectPoint);
alshabib339a3d92014-09-26 17:54:32 -0700211 }
212
213 /**
214 * Indicates whether the given point is part of a broadcast set.
215 *
216 * @param connectPoint connection point
217 * @return true if in broadcast set
218 */
219 boolean isBroadcastPoint(ConnectPoint connectPoint) {
220 // Any non-infrastructure, i.e. edge points are assumed to be OK.
221 if (!isInfrastructure(connectPoint)) {
222 return true;
223 }
224
225 // Find the cluster to which the device belongs.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800226 TopologyCluster cluster = clustersByDevice().get(connectPoint.deviceId());
alshabib339a3d92014-09-26 17:54:32 -0700227 if (cluster == null) {
228 throw new IllegalArgumentException("No cluster found for device " + connectPoint.deviceId());
229 }
230
231 // If the broadcast set is null or empty, or if the point explicitly
232 // belongs to it, return true;
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800233 Set<ConnectPoint> points = broadcastSets.get().get(cluster.id());
alshabib339a3d92014-09-26 17:54:32 -0700234 return points == null || points.isEmpty() || points.contains(connectPoint);
235 }
236
237 /**
238 * Returns the size of the cluster broadcast set.
239 *
240 * @param clusterId cluster identifier
241 * @return size of the cluster broadcast set
242 */
243 int broadcastSetSize(ClusterId clusterId) {
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800244 return broadcastSets.get().get(clusterId).size();
alshabib339a3d92014-09-26 17:54:32 -0700245 }
246
247 /**
248 * Returns the set of pre-computed shortest paths between source and
249 * destination devices.
250 *
251 * @param src source device
252 * @param dst destination device
253 * @return set of shortest paths
254 */
255 Set<Path> getPaths(DeviceId src, DeviceId dst) {
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800256 return getPaths(src, dst, null);
alshabib339a3d92014-09-26 17:54:32 -0700257 }
258
259 /**
260 * Computes on-demand the set of shortest paths between source and
261 * destination devices.
262 *
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800263 * @param src source device
264 * @param dst destination device
265 * @param weight link weight function
alshabib339a3d92014-09-26 17:54:32 -0700266 * @return set of shortest paths
267 */
268 Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeight weight) {
Yuta HIGUCHI82e53262014-11-27 10:28:51 -0800269 final DefaultTopologyVertex srcV = new DefaultTopologyVertex(src);
270 final DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst);
271 Set<TopologyVertex> vertices = graph.getVertexes();
272 if (!vertices.contains(srcV) || !vertices.contains(dstV)) {
273 // src or dst not part of the current graph
274 return ImmutableSet.of();
275 }
276
alshabib339a3d92014-09-26 17:54:32 -0700277 GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800278 DIJKSTRA.search(graph, srcV, dstV, weight, ALL_PATHS);
alshabib339a3d92014-09-26 17:54:32 -0700279 ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
280 for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
281 builder.add(networkPath(path));
282 }
283 return builder.build();
284 }
285
286
alshabib339a3d92014-09-26 17:54:32 -0700287 // Converts graph path to a network path with the same cost.
288 private Path networkPath(org.onlab.graph.Path<TopologyVertex, TopologyEdge> path) {
289 List<Link> links = new ArrayList<>();
290 for (TopologyEdge edge : path.edges()) {
291 links.add(edge.link());
292 }
Thomas Vachuska6acd3bb2014-11-09 23:44:22 -0800293 return new DefaultPath(CORE_PROVIDER_ID, links, path.cost());
alshabib339a3d92014-09-26 17:54:32 -0700294 }
295
296
297 // Searches for SCC clusters in the network topology graph using Tarjan
298 // algorithm.
299 private SCCResult<TopologyVertex, TopologyEdge> searchForClusters() {
300 return TARJAN.search(graph, new NoIndirectLinksWeight());
301 }
302
303 // Builds the topology clusters and returns the id-cluster bindings.
304 private ImmutableMap<ClusterId, TopologyCluster> buildTopologyClusters() {
305 ImmutableMap.Builder<ClusterId, TopologyCluster> clusterBuilder = ImmutableMap.builder();
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800306 SCCResult<TopologyVertex, TopologyEdge> results = clusterResults.get();
alshabib339a3d92014-09-26 17:54:32 -0700307 // Extract both vertexes and edges from the results; the lists form
308 // pairs along the same index.
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800309 List<Set<TopologyVertex>> clusterVertexes = results.clusterVertexes();
310 List<Set<TopologyEdge>> clusterEdges = results.clusterEdges();
alshabib339a3d92014-09-26 17:54:32 -0700311
312 // Scan over the lists and create a cluster from the results.
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800313 for (int i = 0, n = results.clusterCount(); i < n; i++) {
alshabib339a3d92014-09-26 17:54:32 -0700314 Set<TopologyVertex> vertexSet = clusterVertexes.get(i);
315 Set<TopologyEdge> edgeSet = clusterEdges.get(i);
316
317 ClusterId cid = ClusterId.clusterId(i);
318 DefaultTopologyCluster cluster =
319 new DefaultTopologyCluster(cid, vertexSet.size(), edgeSet.size(),
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800320 findRoot(vertexSet));
alshabib339a3d92014-09-26 17:54:32 -0700321 clusterBuilder.put(cid, cluster);
322 }
323 return clusterBuilder.build();
324 }
325
326 // Finds the vertex whose device id is the lexicographical minimum in the
327 // specified set.
328 private TopologyVertex findRoot(Set<TopologyVertex> vertexSet) {
329 TopologyVertex minVertex = null;
330 for (TopologyVertex vertex : vertexSet) {
331 if (minVertex == null ||
332 minVertex.deviceId().toString()
333 .compareTo(minVertex.deviceId().toString()) < 0) {
334 minVertex = vertex;
335 }
336 }
337 return minVertex;
338 }
339
340 // Processes a map of broadcast sets for each cluster.
341 private ImmutableSetMultimap<ClusterId, ConnectPoint> buildBroadcastSets() {
342 Builder<ClusterId, ConnectPoint> builder = ImmutableSetMultimap.builder();
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800343 for (TopologyCluster cluster : clusters.get().values()) {
alshabib339a3d92014-09-26 17:54:32 -0700344 addClusterBroadcastSet(cluster, builder);
345 }
346 return builder.build();
347 }
348
349 // Finds all broadcast points for the cluster. These are those connection
350 // points which lie along the shortest paths between the cluster root and
351 // all other devices within the cluster.
352 private void addClusterBroadcastSet(TopologyCluster cluster,
353 Builder<ClusterId, ConnectPoint> builder) {
354 // Use the graph root search results to build the broadcast set.
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800355 Result<TopologyVertex, TopologyEdge> result =
356 DIJKSTRA.search(graph, cluster.root(), null, weight, 1);
alshabib339a3d92014-09-26 17:54:32 -0700357 for (Map.Entry<TopologyVertex, Set<TopologyEdge>> entry : result.parents().entrySet()) {
358 TopologyVertex vertex = entry.getKey();
359
360 // Ignore any parents that lead outside the cluster.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800361 if (clustersByDevice().get(vertex.deviceId()) != cluster) {
alshabib339a3d92014-09-26 17:54:32 -0700362 continue;
363 }
364
365 // Ignore any back-link sets that are empty.
366 Set<TopologyEdge> parents = entry.getValue();
367 if (parents.isEmpty()) {
368 continue;
369 }
370
371 // Use the first back-link source and destinations to add to the
372 // broadcast set.
373 Link link = parents.iterator().next().link();
374 builder.put(cluster.id(), link.src());
375 builder.put(cluster.id(), link.dst());
376 }
377 }
378
379 // Collects and returns an set of all infrastructure link end-points.
380 private ImmutableSet<ConnectPoint> findInfrastructurePoints() {
381 ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
382 for (TopologyEdge edge : graph.getEdges()) {
383 builder.add(edge.link().src());
384 builder.add(edge.link().dst());
385 }
386 return builder.build();
387 }
388
389 // Builds cluster-devices, cluster-links and device-cluster indexes.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800390 private ClusterIndexes buildIndexes() {
alshabib339a3d92014-09-26 17:54:32 -0700391 // Prepare the index builders
392 ImmutableMap.Builder<DeviceId, TopologyCluster> clusterBuilder = ImmutableMap.builder();
393 ImmutableSetMultimap.Builder<TopologyCluster, DeviceId> devicesBuilder = ImmutableSetMultimap.builder();
394 ImmutableSetMultimap.Builder<TopologyCluster, Link> linksBuilder = ImmutableSetMultimap.builder();
395
396 // Now scan through all the clusters
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800397 for (TopologyCluster cluster : clusters.get().values()) {
alshabib339a3d92014-09-26 17:54:32 -0700398 int i = cluster.id().index();
399
400 // Scan through all the cluster vertexes.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800401 for (TopologyVertex vertex : clusterResults.get().clusterVertexes().get(i)) {
alshabib339a3d92014-09-26 17:54:32 -0700402 devicesBuilder.put(cluster, vertex.deviceId());
403 clusterBuilder.put(vertex.deviceId(), cluster);
404 }
405
406 // Scan through all the cluster edges.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800407 for (TopologyEdge edge : clusterResults.get().clusterEdges().get(i)) {
alshabib339a3d92014-09-26 17:54:32 -0700408 linksBuilder.put(cluster, edge.link());
409 }
410 }
411
412 // Finalize all indexes.
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800413 return new ClusterIndexes(clusterBuilder.build(),
414 devicesBuilder.build(),
415 linksBuilder.build());
alshabib339a3d92014-09-26 17:54:32 -0700416 }
417
418 // Link weight for measuring link cost as hop count with indirect links
419 // being as expensive as traversing the entire graph to assume the worst.
420 private static class HopCountLinkWeight implements LinkWeight {
421 private final int indirectLinkCost;
422
423 HopCountLinkWeight(int indirectLinkCost) {
424 this.indirectLinkCost = indirectLinkCost;
425 }
426
427 @Override
428 public double weight(TopologyEdge edge) {
429 // To force preference to use direct paths first, make indirect
430 // links as expensive as the linear vertex traversal.
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800431 return edge.link().state() == ACTIVE ?
432 (edge.link().type() == INDIRECT ? indirectLinkCost : 1) : -1;
alshabib339a3d92014-09-26 17:54:32 -0700433 }
434 }
435
436 // Link weight for preventing traversal over indirect links.
437 private static class NoIndirectLinksWeight implements LinkWeight {
438 @Override
439 public double weight(TopologyEdge edge) {
Thomas Vachuska57126fe2014-11-11 17:13:24 -0800440 return edge.link().state() == INACTIVE || edge.link().type() == INDIRECT ? -1 : 1;
alshabib339a3d92014-09-26 17:54:32 -0700441 }
442 }
443
Yuta HIGUCHIb2e74a02014-11-30 13:54:38 -0800444 static final class ClusterIndexes {
445 final ImmutableMap<DeviceId, TopologyCluster> clustersByDevice;
446 final ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
447 final ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
448
449 public ClusterIndexes(ImmutableMap<DeviceId, TopologyCluster> clustersByDevice,
450 ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster,
451 ImmutableSetMultimap<TopologyCluster, Link> linksByCluster) {
452 this.clustersByDevice = clustersByDevice;
453 this.devicesByCluster = devicesByCluster;
454 this.linksByCluster = linksByCluster;
455 }
456 }
457
alshabib339a3d92014-09-26 17:54:32 -0700458 @Override
459 public String toString() {
460 return toStringHelper(this)
461 .add("time", time)
Thomas Vachuska6b7920d2014-11-25 19:48:39 -0800462 .add("computeCost", computeCost)
alshabib339a3d92014-09-26 17:54:32 -0700463 .add("clusters", clusterCount())
464 .add("devices", deviceCount())
465 .add("links", linkCount())
alshabib339a3d92014-09-26 17:54:32 -0700466 .toString();
467 }
468}