tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame^] | 1 | package org.onlab.onos.net.trivial.topology.provider.impl; |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 2 | |
| 3 | import com.google.common.collect.ImmutableSet; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 4 | import com.google.common.collect.Maps; |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame^] | 5 | import com.google.common.collect.Multimap; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 6 | import com.google.common.collect.Sets; |
| 7 | import org.onlab.graph.AdjacencyListsGraph; |
| 8 | import org.onlab.graph.DijkstraGraphSearch; |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 9 | import org.onlab.graph.Graph; |
| 10 | import org.onlab.graph.GraphPathSearch; |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame^] | 11 | import org.onlab.graph.TarjanGraphSearch; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 12 | import org.onlab.onos.net.ConnectPoint; |
| 13 | import org.onlab.onos.net.Device; |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 14 | import org.onlab.onos.net.DeviceId; |
| 15 | import org.onlab.onos.net.Link; |
| 16 | import org.onlab.onos.net.topology.ClusterId; |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame^] | 17 | import org.onlab.onos.net.topology.DefaultTopologyCluster; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 18 | import org.onlab.onos.net.topology.LinkWeight; |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 19 | import org.onlab.onos.net.topology.TopoEdge; |
| 20 | import org.onlab.onos.net.topology.TopoVertex; |
| 21 | import org.onlab.onos.net.topology.TopologyCluster; |
| 22 | import org.onlab.onos.net.topology.TopologyDescription; |
| 23 | |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame^] | 24 | import java.util.HashSet; |
| 25 | import java.util.List; |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 26 | import java.util.Map; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 27 | import java.util.Objects; |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 28 | import java.util.Set; |
| 29 | |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 30 | import static com.google.common.base.MoreObjects.toStringHelper; |
| 31 | import static org.onlab.graph.GraphPathSearch.Result; |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame^] | 32 | import static org.onlab.graph.TarjanGraphSearch.SCCResult; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 33 | import static org.onlab.onos.net.Link.Type.INDIRECT; |
| 34 | |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 35 | /** |
| 36 | * Default implementation of an immutable topology data carrier. |
| 37 | */ |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 38 | class DefaultTopologyDescription implements TopologyDescription { |
| 39 | |
| 40 | private static final GraphPathSearch<TopoVertex, TopoEdge> DIJKSTRA = |
| 41 | new DijkstraGraphSearch<>(); |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame^] | 42 | private static final TarjanGraphSearch<TopoVertex, TopoEdge> TARJAN = |
| 43 | new TarjanGraphSearch<>(); |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 44 | |
| 45 | private final long nanos; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 46 | private final Map<DeviceId, TopoVertex> vertexesById = Maps.newHashMap(); |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 47 | private final Graph<TopoVertex, TopoEdge> graph; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 48 | private final Map<DeviceId, Result<TopoVertex, TopoEdge>> results; |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 49 | private final Map<ClusterId, TopologyCluster> clusters; |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 50 | |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame^] | 51 | // Secondary look-up indexes |
| 52 | private Multimap<ClusterId, DeviceId> devicesByCluster; |
| 53 | private Multimap<ClusterId, Link> linksByCluster; |
| 54 | private Map<DeviceId, TopologyCluster> clustersByDevice; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 55 | |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame^] | 56 | /** |
| 57 | * Creates a topology description to carry topology vitals to the core. |
| 58 | * |
| 59 | * @param nanos time in nanos of when the topology description was created |
| 60 | * @param devices collection of devices |
| 61 | * @param links |
| 62 | */ |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 63 | DefaultTopologyDescription(long nanos, Iterable<Device> devices, Iterable<Link> links) { |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 64 | this.nanos = nanos; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 65 | this.graph = buildGraph(devices, links); |
| 66 | this.results = computeDefaultPaths(); |
| 67 | this.clusters = computeClusters(); |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 68 | } |
| 69 | |
| 70 | // Constructs the topology graph using the supplied devices and links. |
| 71 | private Graph<TopoVertex, TopoEdge> buildGraph(Iterable<Device> devices, |
| 72 | Iterable<Link> links) { |
| 73 | Graph<TopoVertex, TopoEdge> graph = |
| 74 | new AdjacencyListsGraph<>(buildVertexes(devices), |
| 75 | buildEdges(links)); |
| 76 | return graph; |
| 77 | } |
| 78 | |
| 79 | // Builds a set of topology vertexes from the specified list of devices |
| 80 | private Set<TopoVertex> buildVertexes(Iterable<Device> devices) { |
| 81 | Set<TopoVertex> vertexes = Sets.newHashSet(); |
| 82 | for (Device device : devices) { |
| 83 | TopoVertex vertex = new TVertex(device.id()); |
| 84 | vertexesById.put(vertex.deviceId(), vertex); |
| 85 | vertexes.add(vertex); |
| 86 | } |
| 87 | return vertexes; |
| 88 | } |
| 89 | |
| 90 | // Builds a set of topology vertexes from the specified list of links |
| 91 | private Set<TopoEdge> buildEdges(Iterable<Link> links) { |
| 92 | Set<TopoEdge> edges = Sets.newHashSet(); |
| 93 | for (Link link : links) { |
| 94 | edges.add(new TEdge(vertexOf(link.src()), vertexOf(link.dst()), link)); |
| 95 | } |
| 96 | return edges; |
| 97 | } |
| 98 | |
| 99 | // Computes the default shortest paths for all source/dest pairs using |
| 100 | // the multi-path Dijkstra and hop-count as path cost. |
| 101 | private Map<DeviceId, Result<TopoVertex, TopoEdge>> computeDefaultPaths() { |
| 102 | LinkWeight weight = new HopCountLinkWeight(graph.getVertexes().size()); |
| 103 | Map<DeviceId, Result<TopoVertex, TopoEdge>> results = Maps.newHashMap(); |
| 104 | |
| 105 | // Search graph paths for each source to all destinations. |
| 106 | for (TopoVertex src : vertexesById.values()) { |
| 107 | results.put(src.deviceId(), DIJKSTRA.search(graph, src, null, weight)); |
| 108 | } |
| 109 | return results; |
| 110 | } |
| 111 | |
| 112 | // Computes topology SCC clusters using Tarjan algorithm. |
| 113 | private Map<ClusterId, TopologyCluster> computeClusters() { |
| 114 | Map<ClusterId, TopologyCluster> clusters = Maps.newHashMap(); |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame^] | 115 | SCCResult<TopoVertex, TopoEdge> result = TARJAN.search(graph, new NoIndirectLinksWeight()); |
| 116 | |
| 117 | // Extract both vertexes and edges from the results; the lists form |
| 118 | // pairs along the same index. |
| 119 | List<Set<TopoVertex>> clusterVertexes = result.clusterVertexes(); |
| 120 | List<Set<TopoEdge>> clusterEdges = result.clusterEdges(); |
| 121 | |
| 122 | // Scan over the lists and create a cluster from the results. |
| 123 | for (int i = 0, n = result.clusterCount(); i < n; i++) { |
| 124 | Set<TopoVertex> vertexSet = clusterVertexes.get(i); |
| 125 | Set<TopoEdge> edgeSet = clusterEdges.get(i); |
| 126 | |
| 127 | DefaultTopologyCluster cluster = |
| 128 | new DefaultTopologyCluster(ClusterId.clusterId(i), |
| 129 | vertexSet.size(), edgeSet.size(), |
| 130 | findRoot(vertexSet).deviceId()); |
| 131 | |
| 132 | findClusterDevices(vertexSet, cluster); |
| 133 | findClusterLinks(edgeSet, cluster); |
| 134 | } |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 135 | return clusters; |
| 136 | } |
| 137 | |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame^] | 138 | // Scan through the set of cluster vertices and convert it to a set of |
| 139 | // device ids; register the cluster by device id as well. |
| 140 | private void findClusterDevices(Set<TopoVertex> vertexSet, |
| 141 | DefaultTopologyCluster cluster) { |
| 142 | Set<DeviceId> ids = new HashSet<>(vertexSet.size()); |
| 143 | for (TopoVertex v : vertexSet) { |
| 144 | DeviceId deviceId = v.deviceId(); |
| 145 | devicesByCluster.put(cluster.id(), deviceId); |
| 146 | clustersByDevice.put(deviceId, cluster); |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | private void findClusterLinks(Set<TopoEdge> edgeSet, |
| 151 | DefaultTopologyCluster cluster) { |
| 152 | for (TopoEdge e : edgeSet) { |
| 153 | linksByCluster.put(cluster.id(), e.link()); |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | // Finds the vertex whose device id is the lexicographical minimum in the |
| 158 | // specified set. |
| 159 | private TopoVertex findRoot(Set<TopoVertex> vertexSet) { |
| 160 | TopoVertex minVertex = null; |
| 161 | for (TopoVertex vertex : vertexSet) { |
| 162 | if (minVertex == null || |
| 163 | minVertex.deviceId().toString() |
| 164 | .compareTo(minVertex.deviceId().toString()) < 0) { |
| 165 | minVertex = vertex; |
| 166 | } |
| 167 | } |
| 168 | return minVertex; |
| 169 | } |
| 170 | |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 171 | // Fetches a vertex corresponding to the given connection point device. |
| 172 | private TopoVertex vertexOf(ConnectPoint connectPoint) { |
| 173 | DeviceId id = connectPoint.deviceId(); |
| 174 | TopoVertex vertex = vertexesById.get(id); |
| 175 | if (vertex == null) { |
| 176 | // If vertex does not exist, create one and register it. |
| 177 | vertex = new TVertex(id); |
| 178 | vertexesById.put(id, vertex); |
| 179 | } |
| 180 | return vertex; |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 181 | } |
| 182 | |
| 183 | @Override |
| 184 | public long timestamp() { |
| 185 | return nanos; |
| 186 | } |
| 187 | |
| 188 | @Override |
| 189 | public Graph<TopoVertex, TopoEdge> graph() { |
| 190 | return graph; |
| 191 | } |
| 192 | |
| 193 | @Override |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 194 | public Result<TopoVertex, TopoEdge> pathResults(DeviceId srcDeviceId) { |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 195 | return results.get(srcDeviceId); |
| 196 | } |
| 197 | |
| 198 | @Override |
| 199 | public Set<TopologyCluster> clusters() { |
| 200 | return ImmutableSet.copyOf(clusters.values()); |
| 201 | } |
| 202 | |
| 203 | @Override |
| 204 | public Set<DeviceId> clusterDevices(TopologyCluster cluster) { |
| 205 | return null; // clusterDevices.get(cluster.id()); |
| 206 | } |
| 207 | |
| 208 | @Override |
| 209 | public Set<Link> clusterLinks(TopologyCluster cluster) { |
| 210 | return null; // clusterLinks.get(cluster.id()); |
| 211 | } |
| 212 | |
| 213 | @Override |
| 214 | public TopologyCluster clusterFor(DeviceId deviceId) { |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 215 | return null; // deviceClusters.get(deviceId); |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 216 | } |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 217 | |
| 218 | // Implementation of the topology vertex backed by a device id |
| 219 | private static class TVertex implements TopoVertex { |
| 220 | |
| 221 | private final DeviceId deviceId; |
| 222 | |
| 223 | public TVertex(DeviceId deviceId) { |
| 224 | this.deviceId = deviceId; |
| 225 | } |
| 226 | |
| 227 | @Override |
| 228 | public DeviceId deviceId() { |
| 229 | return deviceId; |
| 230 | } |
| 231 | |
| 232 | @Override |
| 233 | public int hashCode() { |
| 234 | return Objects.hash(deviceId); |
| 235 | } |
| 236 | |
| 237 | @Override |
| 238 | public boolean equals(Object obj) { |
| 239 | if (obj instanceof TVertex) { |
| 240 | final TVertex other = (TVertex) obj; |
| 241 | return Objects.equals(this.deviceId, other.deviceId); |
| 242 | } |
| 243 | return false; |
| 244 | } |
| 245 | |
| 246 | @Override |
| 247 | public String toString() { |
| 248 | return deviceId.toString(); |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | // Implementation of the topology edge backed by a link |
| 253 | private class TEdge implements TopoEdge { |
| 254 | private final Link link; |
| 255 | private final TopoVertex src; |
| 256 | private final TopoVertex dst; |
| 257 | |
| 258 | public TEdge(TopoVertex src, TopoVertex dst, Link link) { |
| 259 | this.src = src; |
| 260 | this.dst = dst; |
| 261 | this.link = link; |
| 262 | } |
| 263 | |
| 264 | @Override |
| 265 | public Link link() { |
| 266 | return link; |
| 267 | } |
| 268 | |
| 269 | @Override |
| 270 | public TopoVertex src() { |
| 271 | return src; |
| 272 | } |
| 273 | |
| 274 | @Override |
| 275 | public TopoVertex dst() { |
| 276 | return dst; |
| 277 | } |
| 278 | |
| 279 | @Override |
| 280 | public int hashCode() { |
| 281 | return Objects.hash(link); |
| 282 | } |
| 283 | |
| 284 | @Override |
| 285 | public boolean equals(Object obj) { |
| 286 | if (obj instanceof TEdge) { |
| 287 | final TEdge other = (TEdge) obj; |
| 288 | return Objects.equals(this.link, other.link); |
| 289 | } |
| 290 | return false; |
| 291 | } |
| 292 | |
| 293 | @Override |
| 294 | public String toString() { |
| 295 | return toStringHelper(this).add("src", src).add("dst", dst).toString(); |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | // Link weight for measuring link cost as hop count with indirect links |
| 300 | // being as expensive as traversing the entire graph to assume the worst. |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame^] | 301 | private static class HopCountLinkWeight implements LinkWeight { |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 302 | private final int indirectLinkCost; |
| 303 | |
| 304 | public HopCountLinkWeight(int indirectLinkCost) { |
| 305 | this.indirectLinkCost = indirectLinkCost; |
| 306 | } |
| 307 | |
| 308 | @Override |
| 309 | public double weight(TopoEdge edge) { |
| 310 | // To force preference to use direct paths first, make indirect |
| 311 | // links as expensive as the linear vertex traversal. |
| 312 | return edge.link().type() == INDIRECT ? indirectLinkCost : 1; |
| 313 | } |
| 314 | } |
| 315 | |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame^] | 316 | // Link weight for preventing traversal over indirect links. |
| 317 | private static class NoIndirectLinksWeight implements LinkWeight { |
| 318 | @Override |
| 319 | public double weight(TopoEdge edge) { |
| 320 | return edge.link().type() == INDIRECT ? -1 : 1; |
| 321 | } |
| 322 | } |
| 323 | |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 324 | } |