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 | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 4 | import com.google.common.collect.ImmutableSetMultimap; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 5 | import com.google.common.collect.Maps; |
| 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.List; |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 25 | import java.util.Map; |
| 26 | import java.util.Set; |
| 27 | |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 28 | import static com.google.common.collect.ImmutableSetMultimap.Builder; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 29 | import static org.onlab.graph.GraphPathSearch.Result; |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame] | 30 | import static org.onlab.graph.TarjanGraphSearch.SCCResult; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 31 | import static org.onlab.onos.net.Link.Type.INDIRECT; |
| 32 | |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 33 | /** |
| 34 | * Default implementation of an immutable topology data carrier. |
| 35 | */ |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 36 | class DefaultTopologyDescription implements TopologyDescription { |
| 37 | |
| 38 | private static final GraphPathSearch<TopoVertex, TopoEdge> DIJKSTRA = |
| 39 | new DijkstraGraphSearch<>(); |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame] | 40 | private static final TarjanGraphSearch<TopoVertex, TopoEdge> TARJAN = |
| 41 | new TarjanGraphSearch<>(); |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 42 | |
| 43 | private final long nanos; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 44 | private final Map<DeviceId, TopoVertex> vertexesById = Maps.newHashMap(); |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 45 | private final Graph<TopoVertex, TopoEdge> graph; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 46 | private final Map<DeviceId, Result<TopoVertex, TopoEdge>> results; |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 47 | private final Map<ClusterId, TopologyCluster> clusters; |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 48 | |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame] | 49 | // Secondary look-up indexes |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 50 | private ImmutableSetMultimap<ClusterId, DeviceId> devicesByCluster; |
| 51 | private ImmutableSetMultimap<ClusterId, Link> linksByCluster; |
| 52 | private Map<DeviceId, TopologyCluster> clustersByDevice = Maps.newHashMap(); |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 53 | |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame] | 54 | /** |
| 55 | * Creates a topology description to carry topology vitals to the core. |
| 56 | * |
| 57 | * @param nanos time in nanos of when the topology description was created |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 58 | * @param devices collection of infrastructure devices |
| 59 | * @param links collection of infrastructure links |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame] | 60 | */ |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 61 | DefaultTopologyDescription(long nanos, Iterable<Device> devices, Iterable<Link> links) { |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 62 | this.nanos = nanos; |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 63 | this.graph = buildGraph(devices, links); |
| 64 | this.results = computeDefaultPaths(); |
| 65 | this.clusters = computeClusters(); |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 66 | } |
| 67 | |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 68 | @Override |
| 69 | public long timestamp() { |
| 70 | return nanos; |
| 71 | } |
| 72 | |
| 73 | @Override |
| 74 | public Graph<TopoVertex, TopoEdge> graph() { |
| 75 | return graph; |
| 76 | } |
| 77 | |
| 78 | @Override |
| 79 | public Result<TopoVertex, TopoEdge> pathResults(DeviceId srcDeviceId) { |
| 80 | return results.get(srcDeviceId); |
| 81 | } |
| 82 | |
| 83 | @Override |
| 84 | public Set<TopologyCluster> clusters() { |
| 85 | return ImmutableSet.copyOf(clusters.values()); |
| 86 | } |
| 87 | |
| 88 | @Override |
| 89 | public Set<DeviceId> clusterDevices(TopologyCluster cluster) { |
| 90 | return devicesByCluster.get(cluster.id()); |
| 91 | } |
| 92 | |
| 93 | @Override |
| 94 | public Set<Link> clusterLinks(TopologyCluster cluster) { |
| 95 | return linksByCluster.get(cluster.id()); |
| 96 | } |
| 97 | |
| 98 | @Override |
| 99 | public TopologyCluster clusterFor(DeviceId deviceId) { |
| 100 | return clustersByDevice.get(deviceId); |
| 101 | } |
| 102 | |
| 103 | |
| 104 | // Link weight for measuring link cost as hop count with indirect links |
| 105 | // being as expensive as traversing the entire graph to assume the worst. |
| 106 | private static class HopCountLinkWeight implements LinkWeight { |
| 107 | private final int indirectLinkCost; |
| 108 | |
| 109 | HopCountLinkWeight(int indirectLinkCost) { |
| 110 | this.indirectLinkCost = indirectLinkCost; |
| 111 | } |
| 112 | |
| 113 | @Override |
| 114 | public double weight(TopoEdge edge) { |
| 115 | // To force preference to use direct paths first, make indirect |
| 116 | // links as expensive as the linear vertex traversal. |
| 117 | return edge.link().type() == INDIRECT ? indirectLinkCost : 1; |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | // Link weight for preventing traversal over indirect links. |
| 122 | private static class NoIndirectLinksWeight implements LinkWeight { |
| 123 | @Override |
| 124 | public double weight(TopoEdge edge) { |
| 125 | return edge.link().type() == INDIRECT ? -1 : 1; |
| 126 | } |
| 127 | } |
| 128 | |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 129 | // Constructs the topology graph using the supplied devices and links. |
| 130 | private Graph<TopoVertex, TopoEdge> buildGraph(Iterable<Device> devices, |
| 131 | Iterable<Link> links) { |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 132 | return new AdjacencyListsGraph<>(buildVertexes(devices), |
| 133 | buildEdges(links)); |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | // Builds a set of topology vertexes from the specified list of devices |
| 137 | private Set<TopoVertex> buildVertexes(Iterable<Device> devices) { |
| 138 | Set<TopoVertex> vertexes = Sets.newHashSet(); |
| 139 | for (Device device : devices) { |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 140 | TopoVertex vertex = new DefaultTopoVertex(device.id()); |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 141 | vertexes.add(vertex); |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 142 | vertexesById.put(vertex.deviceId(), vertex); |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 143 | } |
| 144 | return vertexes; |
| 145 | } |
| 146 | |
| 147 | // Builds a set of topology vertexes from the specified list of links |
| 148 | private Set<TopoEdge> buildEdges(Iterable<Link> links) { |
| 149 | Set<TopoEdge> edges = Sets.newHashSet(); |
| 150 | for (Link link : links) { |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 151 | edges.add(new DefaultTopoEdge(vertexOf(link.src()), |
| 152 | vertexOf(link.dst()), link)); |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 153 | } |
| 154 | return edges; |
| 155 | } |
| 156 | |
| 157 | // Computes the default shortest paths for all source/dest pairs using |
| 158 | // the multi-path Dijkstra and hop-count as path cost. |
| 159 | private Map<DeviceId, Result<TopoVertex, TopoEdge>> computeDefaultPaths() { |
| 160 | LinkWeight weight = new HopCountLinkWeight(graph.getVertexes().size()); |
| 161 | Map<DeviceId, Result<TopoVertex, TopoEdge>> results = Maps.newHashMap(); |
| 162 | |
| 163 | // Search graph paths for each source to all destinations. |
| 164 | for (TopoVertex src : vertexesById.values()) { |
| 165 | results.put(src.deviceId(), DIJKSTRA.search(graph, src, null, weight)); |
| 166 | } |
| 167 | return results; |
| 168 | } |
| 169 | |
| 170 | // Computes topology SCC clusters using Tarjan algorithm. |
| 171 | private Map<ClusterId, TopologyCluster> computeClusters() { |
| 172 | Map<ClusterId, TopologyCluster> clusters = Maps.newHashMap(); |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame] | 173 | SCCResult<TopoVertex, TopoEdge> result = TARJAN.search(graph, new NoIndirectLinksWeight()); |
| 174 | |
| 175 | // Extract both vertexes and edges from the results; the lists form |
| 176 | // pairs along the same index. |
| 177 | List<Set<TopoVertex>> clusterVertexes = result.clusterVertexes(); |
| 178 | List<Set<TopoEdge>> clusterEdges = result.clusterEdges(); |
| 179 | |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 180 | Builder<ClusterId, DeviceId> devicesBuilder = ImmutableSetMultimap.builder(); |
| 181 | Builder<ClusterId, Link> linksBuilder = ImmutableSetMultimap.builder(); |
| 182 | |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame] | 183 | // Scan over the lists and create a cluster from the results. |
| 184 | for (int i = 0, n = result.clusterCount(); i < n; i++) { |
| 185 | Set<TopoVertex> vertexSet = clusterVertexes.get(i); |
| 186 | Set<TopoEdge> edgeSet = clusterEdges.get(i); |
| 187 | |
| 188 | DefaultTopologyCluster cluster = |
| 189 | new DefaultTopologyCluster(ClusterId.clusterId(i), |
| 190 | vertexSet.size(), edgeSet.size(), |
| 191 | findRoot(vertexSet).deviceId()); |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 192 | findClusterDevices(vertexSet, cluster, devicesBuilder); |
| 193 | findClusterLinks(edgeSet, cluster, linksBuilder); |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame] | 194 | } |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 195 | return clusters; |
| 196 | } |
| 197 | |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 198 | // Scans through the set of cluster vertexes and puts their devices in a |
| 199 | // multi-map associated with the cluster. It also binds the devices to |
| 200 | // the cluster. |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame] | 201 | private void findClusterDevices(Set<TopoVertex> vertexSet, |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 202 | DefaultTopologyCluster cluster, |
| 203 | Builder<ClusterId, DeviceId> builder) { |
| 204 | for (TopoVertex vertex : vertexSet) { |
| 205 | DeviceId deviceId = vertex.deviceId(); |
| 206 | builder.put(cluster.id(), deviceId); |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame] | 207 | clustersByDevice.put(deviceId, cluster); |
| 208 | } |
| 209 | } |
| 210 | |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 211 | // Scans through the set of cluster edges and puts their links in a |
| 212 | // multi-map associated with the cluster. |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame] | 213 | private void findClusterLinks(Set<TopoEdge> edgeSet, |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 214 | DefaultTopologyCluster cluster, |
| 215 | Builder<ClusterId, Link> builder) { |
| 216 | for (TopoEdge edge : edgeSet) { |
| 217 | builder.put(cluster.id(), edge.link()); |
tom | 8bf2e6b | 2014-09-10 20:53:54 -0700 | [diff] [blame] | 218 | } |
| 219 | } |
| 220 | |
| 221 | // Finds the vertex whose device id is the lexicographical minimum in the |
| 222 | // specified set. |
| 223 | private TopoVertex findRoot(Set<TopoVertex> vertexSet) { |
| 224 | TopoVertex minVertex = null; |
| 225 | for (TopoVertex vertex : vertexSet) { |
| 226 | if (minVertex == null || |
| 227 | minVertex.deviceId().toString() |
| 228 | .compareTo(minVertex.deviceId().toString()) < 0) { |
| 229 | minVertex = vertex; |
| 230 | } |
| 231 | } |
| 232 | return minVertex; |
| 233 | } |
| 234 | |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 235 | // Fetches a vertex corresponding to the given connection point device. |
| 236 | private TopoVertex vertexOf(ConnectPoint connectPoint) { |
| 237 | DeviceId id = connectPoint.deviceId(); |
| 238 | TopoVertex vertex = vertexesById.get(id); |
| 239 | if (vertex == null) { |
| 240 | // If vertex does not exist, create one and register it. |
tom | 2d33141 | 2014-09-10 21:31:20 -0700 | [diff] [blame^] | 241 | vertex = new DefaultTopoVertex(id); |
tom | cbff939 | 2014-09-10 00:45:23 -0700 | [diff] [blame] | 242 | vertexesById.put(id, vertex); |
| 243 | } |
| 244 | return vertex; |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 245 | } |
| 246 | |
tom | cfde062 | 2014-09-09 11:02:42 -0700 | [diff] [blame] | 247 | } |