| package org.onlab.onos.net.trivial.impl; |
| |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Maps; |
| import com.google.common.collect.Sets; |
| import org.onlab.graph.AdjacencyListsGraph; |
| import org.onlab.graph.DijkstraGraphSearch; |
| import org.onlab.graph.Graph; |
| import org.onlab.graph.GraphPathSearch; |
| import org.onlab.onos.net.ConnectPoint; |
| import org.onlab.onos.net.Device; |
| import org.onlab.onos.net.DeviceId; |
| import org.onlab.onos.net.Link; |
| import org.onlab.onos.net.topology.ClusterId; |
| import org.onlab.onos.net.topology.LinkWeight; |
| import org.onlab.onos.net.topology.TopoEdge; |
| import org.onlab.onos.net.topology.TopoVertex; |
| import org.onlab.onos.net.topology.TopologyCluster; |
| import org.onlab.onos.net.topology.TopologyDescription; |
| |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| |
| import static com.google.common.base.MoreObjects.toStringHelper; |
| import static org.onlab.graph.GraphPathSearch.Result; |
| import static org.onlab.onos.net.Link.Type.INDIRECT; |
| |
| /** |
| * Default implementation of an immutable topology data carrier. |
| */ |
| class DefaultTopologyDescription implements TopologyDescription { |
| |
| private static final GraphPathSearch<TopoVertex, TopoEdge> DIJKSTRA = |
| new DijkstraGraphSearch<>(); |
| |
| private final long nanos; |
| private final Map<DeviceId, TopoVertex> vertexesById = Maps.newHashMap(); |
| private final Graph<TopoVertex, TopoEdge> graph; |
| private final Map<DeviceId, Result<TopoVertex, TopoEdge>> results; |
| private final Map<ClusterId, TopologyCluster> clusters; |
| // private final Multimap<ClusterId, DeviceId> clusterDevices; |
| // private final Multimap<ClusterId, Link> clusterLinks; |
| // private final Map<DeviceId, TopologyCluster> deviceClusters; |
| |
| |
| DefaultTopologyDescription(long nanos, Iterable<Device> devices, Iterable<Link> links) { |
| this.nanos = nanos; |
| this.graph = buildGraph(devices, links); |
| this.results = computeDefaultPaths(); |
| this.clusters = computeClusters(); |
| // this.clusterDevices = clusterDevices; |
| // this.clusterLinks = clusterLinks; |
| // this.deviceClusters = deviceClusters; |
| } |
| |
| // Constructs the topology graph using the supplied devices and links. |
| private Graph<TopoVertex, TopoEdge> buildGraph(Iterable<Device> devices, |
| Iterable<Link> links) { |
| Graph<TopoVertex, TopoEdge> graph = |
| new AdjacencyListsGraph<>(buildVertexes(devices), |
| buildEdges(links)); |
| return graph; |
| } |
| |
| // Builds a set of topology vertexes from the specified list of devices |
| private Set<TopoVertex> buildVertexes(Iterable<Device> devices) { |
| Set<TopoVertex> vertexes = Sets.newHashSet(); |
| for (Device device : devices) { |
| TopoVertex vertex = new TVertex(device.id()); |
| vertexesById.put(vertex.deviceId(), vertex); |
| vertexes.add(vertex); |
| } |
| return vertexes; |
| } |
| |
| // Builds a set of topology vertexes from the specified list of links |
| private Set<TopoEdge> buildEdges(Iterable<Link> links) { |
| Set<TopoEdge> edges = Sets.newHashSet(); |
| for (Link link : links) { |
| edges.add(new TEdge(vertexOf(link.src()), vertexOf(link.dst()), link)); |
| } |
| return edges; |
| } |
| |
| // Computes the default shortest paths for all source/dest pairs using |
| // the multi-path Dijkstra and hop-count as path cost. |
| private Map<DeviceId, Result<TopoVertex, TopoEdge>> computeDefaultPaths() { |
| LinkWeight weight = new HopCountLinkWeight(graph.getVertexes().size()); |
| Map<DeviceId, Result<TopoVertex, TopoEdge>> results = Maps.newHashMap(); |
| |
| // Search graph paths for each source to all destinations. |
| for (TopoVertex src : vertexesById.values()) { |
| results.put(src.deviceId(), DIJKSTRA.search(graph, src, null, weight)); |
| } |
| return results; |
| } |
| |
| // Computes topology SCC clusters using Tarjan algorithm. |
| private Map<ClusterId, TopologyCluster> computeClusters() { |
| Map<ClusterId, TopologyCluster> clusters = Maps.newHashMap(); |
| return clusters; |
| } |
| |
| // Fetches a vertex corresponding to the given connection point device. |
| private TopoVertex vertexOf(ConnectPoint connectPoint) { |
| DeviceId id = connectPoint.deviceId(); |
| TopoVertex vertex = vertexesById.get(id); |
| if (vertex == null) { |
| // If vertex does not exist, create one and register it. |
| vertex = new TVertex(id); |
| vertexesById.put(id, vertex); |
| } |
| return vertex; |
| } |
| |
| @Override |
| public long timestamp() { |
| return nanos; |
| } |
| |
| @Override |
| public Graph<TopoVertex, TopoEdge> graph() { |
| return graph; |
| } |
| |
| @Override |
| public Result<TopoVertex, TopoEdge> pathResults(DeviceId srcDeviceId) { |
| return results.get(srcDeviceId); |
| } |
| |
| @Override |
| public Set<TopologyCluster> clusters() { |
| return ImmutableSet.copyOf(clusters.values()); |
| } |
| |
| @Override |
| public Set<DeviceId> clusterDevices(TopologyCluster cluster) { |
| return null; // clusterDevices.get(cluster.id()); |
| } |
| |
| @Override |
| public Set<Link> clusterLinks(TopologyCluster cluster) { |
| return null; // clusterLinks.get(cluster.id()); |
| } |
| |
| @Override |
| public TopologyCluster clusterFor(DeviceId deviceId) { |
| return null; // deviceClusters.get(deviceId); |
| } |
| |
| // Implementation of the topology vertex backed by a device id |
| private static class TVertex implements TopoVertex { |
| |
| private final DeviceId deviceId; |
| |
| public TVertex(DeviceId deviceId) { |
| this.deviceId = deviceId; |
| } |
| |
| @Override |
| public DeviceId deviceId() { |
| return deviceId; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(deviceId); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj instanceof TVertex) { |
| final TVertex other = (TVertex) obj; |
| return Objects.equals(this.deviceId, other.deviceId); |
| } |
| return false; |
| } |
| |
| @Override |
| public String toString() { |
| return deviceId.toString(); |
| } |
| } |
| |
| // Implementation of the topology edge backed by a link |
| private class TEdge implements TopoEdge { |
| private final Link link; |
| private final TopoVertex src; |
| private final TopoVertex dst; |
| |
| public TEdge(TopoVertex src, TopoVertex dst, Link link) { |
| this.src = src; |
| this.dst = dst; |
| this.link = link; |
| } |
| |
| @Override |
| public Link link() { |
| return link; |
| } |
| |
| @Override |
| public TopoVertex src() { |
| return src; |
| } |
| |
| @Override |
| public TopoVertex dst() { |
| return dst; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(link); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj instanceof TEdge) { |
| final TEdge other = (TEdge) obj; |
| return Objects.equals(this.link, other.link); |
| } |
| return false; |
| } |
| |
| @Override |
| public String toString() { |
| return toStringHelper(this).add("src", src).add("dst", dst).toString(); |
| } |
| } |
| |
| // Link weight for measuring link cost as hop count with indirect links |
| // being as expensive as traversing the entire graph to assume the worst. |
| private class HopCountLinkWeight implements LinkWeight { |
| private final int indirectLinkCost; |
| |
| public HopCountLinkWeight(int indirectLinkCost) { |
| this.indirectLinkCost = indirectLinkCost; |
| } |
| |
| @Override |
| public double weight(TopoEdge edge) { |
| // To force preference to use direct paths first, make indirect |
| // links as expensive as the linear vertex traversal. |
| return edge.link().type() == INDIRECT ? indirectLinkCost : 1; |
| } |
| } |
| |
| } |