Ayaka Koshibe

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Showing 40 changed files with 1116 additions and 475 deletions
package org.onlab.onos.cli;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.GreetService;
/**
* Simple command example to demonstrate use of Karaf shell extensions; shows
* use of an optional parameter as well.
*/
@Command(scope = "onos", name = "greet", description = "Issues a greeting")
public class GreetCommand extends AbstractShellCommand {
@Argument(index = 0, name = "name", description = "Name to greet",
required = false, multiValued = false)
String name = "dude";
@Override
protected Object doExecute() throws Exception {
print(getService(GreetService.class).yo(name));
return null;
}
}
package org.onlab.onos.cli.net;
import com.google.common.collect.Lists;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.topology.TopologyCluster;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import static org.onlab.onos.net.topology.ClusterId.clusterId;
/**
* Lists devices of the specified topology cluster in the current topology.
*/
@Command(scope = "onos", name = "cluster-devices",
description = "Lists devices of the specified topology cluster in the current topology")
public class ClusterDevicesCommand extends ClustersListCommand {
@Argument(index = 0, name = "id", description = "Cluster ID",
required = false, multiValued = false)
String id = null;
protected static final Comparator<DeviceId> ID_COMPARATOR = new Comparator<DeviceId>() {
@Override
public int compare(DeviceId id1, DeviceId id2) {
return id1.uri().toString().compareTo(id2.uri().toString());
}
};
@Override
protected Object doExecute() throws Exception {
int cid = Integer.parseInt(id);
init();
TopologyCluster cluster = service.getCluster(topology, clusterId(cid));
List<DeviceId> ids = Lists.newArrayList(service.getClusterDevices(topology, cluster));
Collections.sort(ids, ID_COMPARATOR);
for (DeviceId deviceId : ids) {
print("%s", deviceId);
}
return null;
}
}
package org.onlab.onos.cli;
package org.onlab.onos.cli.net;
import org.apache.karaf.shell.console.Completer;
import org.apache.karaf.shell.console.completer.StringsCompleter;
import org.onlab.onos.GreetService;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.net.topology.Topology;
import org.onlab.onos.net.topology.TopologyCluster;
import org.onlab.onos.net.topology.TopologyService;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
/**
* Simple example of a command-line parameter completer.
* For a more open-ended sets a more efficient implementation would be required.
* Cluster ID completer.
*/
public class NameCompleter implements Completer {
public class ClusterIdCompleter implements Completer {
@Override
public int complete(String buffer, int cursor, List<String> candidates) {
// Delegate string completer
StringsCompleter delegate = new StringsCompleter();
// Fetch our service and feed it's offerings to the string completer
GreetService greetService = AbstractShellCommand.get(GreetService.class);
Iterator<String> it = greetService.names().iterator();
TopologyService service = AbstractShellCommand.get(TopologyService.class);
Topology topology = service.currentTopology();
SortedSet<String> strings = delegate.getStrings();
while (it.hasNext()) {
strings.add(it.next());
for (TopologyCluster cluster : service.getClusters(topology)) {
strings.add(Integer.toString(cluster.id().index()));
}
// Now let the completer do the work for figuring out what to offer.
......
package org.onlab.onos.cli.net;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.topology.TopologyCluster;
import static org.onlab.onos.cli.net.LinksListCommand.linkString;
import static org.onlab.onos.net.topology.ClusterId.clusterId;
/**
* Lists links of the specified topology cluster in the current topology.
*/
@Command(scope = "onos", name = "cluster-links",
description = "Lists links of the specified topology cluster in the current topology")
public class ClusterLinksCommand extends ClustersListCommand {
@Argument(index = 0, name = "id", description = "Cluster ID",
required = false, multiValued = false)
String id = null;
@Override
protected Object doExecute() throws Exception {
int cid = Integer.parseInt(id);
init();
TopologyCluster cluster = service.getCluster(topology, clusterId(cid));
for (Link link : service.getClusterLinks(topology, cluster)) {
print(linkString(link));
}
return null;
}
}
package org.onlab.onos.cli.net;
import com.google.common.collect.Lists;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.net.topology.TopologyCluster;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Lists all clusters in the current topology.
*/
@Command(scope = "onos", name = "clusters",
description = "Lists all clusters in the current topology")
public class ClustersListCommand extends TopologyCommand {
private static final String FMT =
"id=%s, devices=%d, links=%d";
protected static final Comparator<TopologyCluster> ID_COMPARATOR =
new Comparator<TopologyCluster>() {
@Override
public int compare(TopologyCluster c1, TopologyCluster c2) {
return c1.id().index() - c2.id().index();
}
};
@Override
protected Object doExecute() throws Exception {
init();
List<TopologyCluster> clusters = Lists.newArrayList(service.getClusters(topology));
Collections.sort(clusters, ID_COMPARATOR);
for (TopologyCluster cluster : clusters) {
print(FMT, cluster.id(), cluster.deviceCount(), cluster.linkCount());
}
return null;
}
}
......@@ -27,9 +27,20 @@ public class LinksListCommand extends AbstractShellCommand {
Iterable<Link> links = uri != null ?
service.getDeviceLinks(deviceId(uri)) : service.getLinks();
for (Link link : links) {
print(FMT, link.src().deviceId(), link.src().port(),
link.dst().deviceId(), link.dst().port(), link.type());
print(linkString(link));
}
return null;
}
/**
* Returns a formated string representing the gien link.
*
* @param link infrastructure link
* @return formated link string
*/
public static String linkString(Link link) {
return String.format(FMT, link.src().deviceId(), link.src().port(),
link.dst().deviceId(), link.dst().port(), link.type());
}
}
......
package org.onlab.onos.cli.net;
import org.apache.karaf.shell.commands.Command;
import org.onlab.onos.cli.AbstractShellCommand;
import org.onlab.onos.net.topology.Topology;
import org.onlab.onos.net.topology.TopologyService;
/**
* Lists summary of the current topology.
*/
@Command(scope = "onos", name = "topology",
description = "Lists summary of the current topology")
public class TopologyCommand extends AbstractShellCommand {
private static final String FMT =
"time=%s, devices=%d, links=%d, clusters=%d, paths=%d";
protected TopologyService service;
protected Topology topology;
/**
* Initializes the context for all cluster commands.
*/
protected void init() {
service = getService(TopologyService.class);
topology = service.currentTopology();
}
@Override
protected Object doExecute() throws Exception {
init();
print(FMT, topology.time(), topology.deviceCount(), topology.linkCount(),
topology.clusterCount(), topology.pathCount());
return null;
}
}
......@@ -32,16 +32,27 @@
</command>
<command>
<action class="org.onlab.onos.cli.GreetCommand"/>
<action class="org.onlab.onos.cli.net.TopologyCommand"/>
</command>
<command>
<action class="org.onlab.onos.cli.net.ClustersListCommand"/>
</command>
<command>
<action class="org.onlab.onos.cli.net.ClusterDevicesCommand"/>
<completers>
<ref component-id="nameCompleter"/>
<ref component-id="clusterIdCompleter"/>
</completers>
</command>
<command>
<action class="org.onlab.onos.cli.net.ClusterLinksCommand"/>
<completers>
<ref component-id="clusterIdCompleter"/>
</completers>
</command>
</command-bundle>
<bean id="deviceIdCompleter" class="org.onlab.onos.cli.net.DeviceIdCompleter"/>
<bean id="clusterIdCompleter" class="org.onlab.onos.cli.net.ClusterIdCompleter"/>
<bean id="roleCompleter" class="org.onlab.onos.cli.net.RoleCompleter"/>
<bean id="nameCompleter" class="org.onlab.onos.cli.NameCompleter"/>
</blueprint>
......
......@@ -16,7 +16,7 @@ public class DefaultLink extends AbstractModel implements Link {
private final Type type;
/**
* Creates a link description using the supplied information.
* Creates an infrastructure link using the supplied information.
*
* @param providerId provider identity
* @param src link source
......
package org.onlab.onos.net;
import com.google.common.collect.ImmutableList;
import org.onlab.onos.net.provider.ProviderId;
import java.util.List;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Default implementation of a network path.
*/
public class DefaultPath extends DefaultLink implements Path {
private final List<Link> links;
private final double cost;
/**
* Creates a path from the specified source and destination using the
* supplied list of links.
*
* @param providerId provider identity
* @param links contiguous links that comprise the path
* @param cost unit-less path cost
*/
public DefaultPath(ProviderId providerId, List<Link> links, double cost) {
super(providerId, source(links), destination(links), Type.INDIRECT);
this.links = ImmutableList.copyOf(links);
this.cost = cost;
}
@Override
public List<Link> links() {
return links;
}
@Override
public double cost() {
return cost;
}
// Returns the source of the first link.
private static ConnectPoint source(List<Link> links) {
checkNotNull(links, "List of path links cannot be null");
checkArgument(!links.isEmpty(), "List of path links cannot be empty");
return links.get(0).src();
}
// Returns the destination of the last link.
private static ConnectPoint destination(List<Link> links) {
checkNotNull(links, "List of path links cannot be null");
checkArgument(!links.isEmpty(), "List of path links cannot be empty");
return links.get(links.size() - 1).dst();
}
@Override
public int hashCode() {
return 31 * super.hashCode() + Objects.hash(links);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DefaultPath) {
final DefaultPath other = (DefaultPath) obj;
return Objects.equals(this.links, other.links);
}
return false;
}
}
......@@ -17,4 +17,11 @@ public interface Path extends Link {
*/
List<Link> links();
/**
* Returns the path cost as a unit-less value.
*
* @return unit-less path cost
*/
double cost();
}
......
......@@ -27,6 +27,15 @@ public final class ClusterId {
return new ClusterId(id);
}
/**
* Returns the backing integer index.
*
* @return backing integer index
*/
public int index() {
return id;
}
@Override
public int hashCode() {
return Objects.hash(id);
......
package org.onlab.onos.net.topology;
import com.google.common.collect.ImmutableSet;
import org.onlab.onos.net.Description;
/**
* Describes attribute(s) of a network graph.
*/
public interface GraphDescription extends Description {
/**
* Returns the creation timestamp of the graph description. This is
* expressed in system nanos to allow proper sequencing.
*
* @return graph description creation timestamp
*/
long timestamp();
/**
* Returns the set of topology graph vertexes.
*
* @return set of graph vertexes
*/
ImmutableSet<TopologyVertex> vertexes();
/**
* Returns the set of topology graph edges.
*
* @return set of graph edges
*/
ImmutableSet<TopologyEdge> edges();
}
......@@ -6,5 +6,5 @@ import org.onlab.graph.EdgeWeight;
* Entity capable of determining cost or weight of a specified topology
* graph edge.
*/
public interface LinkWeight extends EdgeWeight<TopoVertex, TopoEdge> {
public interface LinkWeight extends EdgeWeight<TopologyVertex, TopologyEdge> {
}
......
package org.onlab.onos.net.topology;
import org.onlab.graph.Graph;
import org.onlab.graph.GraphPathSearch;
import org.onlab.onos.net.Description;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
import java.util.Set;
/**
* Describes attribute(s) of a network topology.
*/
public interface TopologyDescription extends Description {
/**
* Returns the creation timestamp of the topology description. This is
* expressed in system nanos to allow proper sequencing.
*
* @return topology description creation timestamp
*/
long timestamp();
/**
* Returns the topology graph.
*
* @return network graph
*/
Graph<TopoVertex, TopoEdge> graph();
/**
* Returns the results of the path search through the network graph. This
* is assumed to contain results of seach fro the given device to all
* other devices.
*
* @param srcDeviceId source device identifier
* @return path search result for the given source node
*/
GraphPathSearch.Result pathResults(DeviceId srcDeviceId);
/**
* Returns the set of topology SCC clusters.
*
* @return set of SCC clusters
*/
Set<TopologyCluster> clusters();
/**
* Returns the set of devices contained by the specified topology cluster.
*
* @return set of devices that belong to the specified cluster
*/
Set<DeviceId> clusterDevices(TopologyCluster cluster);
/**
* Returns the set of infrastructure links contained by the specified cluster.
*
* @return set of links that form the given cluster
*/
Set<Link> clusterLinks(TopologyCluster cluster);
/**
* Returns the topology SCC cluster which contains the given device.
*
* @param deviceId device identifier
* @return topology cluster that contains the specified device
*/
TopologyCluster clusterFor(DeviceId deviceId);
}
......@@ -6,7 +6,7 @@ import org.onlab.onos.net.Link;
/**
* Represents an edge in the topology graph.
*/
public interface TopoEdge extends Edge<TopoVertex> {
public interface TopologyEdge extends Edge<TopologyVertex> {
/**
* Returns the associated infrastructure link.
......
package org.onlab.onos.net.topology;
import org.onlab.graph.Graph;
/**
* Represents an immutable topology graph.
*/
public interface TopologyGraph extends Graph<TopologyVertex, TopologyEdge> {
}
......@@ -10,16 +10,13 @@ import java.util.List;
*/
public interface TopologyProviderService extends ProviderService<TopologyProvider> {
// What can be conveyed in a topology that isn't by individual
// providers?
/**
* Signals the core that some aspect of the topology has changed.
*
* @param topoDescription information about topology
* @param graphDescription information about the network graph
* @param reasons events that triggered topology change
*/
void topologyChanged(TopologyDescription topoDescription,
void topologyChanged(GraphDescription graphDescription,
List<Event> reasons);
}
......
package org.onlab.onos.net.topology;
import org.onlab.graph.Graph;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
import java.util.Set;
......@@ -21,12 +21,21 @@ public interface TopologyService {
/**
* Indicates whether the specified topology is the latest or not.
*
* @param topology topology descriptor
* @return true if the topology is the most recent; false otherwise
*/
boolean isLatest(Topology topology);
/**
* Returns the graph view of the specified topology.
*
* @param topology topology descriptor
* @return topology graph view
*/
TopologyGraph getGraph(Topology topology);
/**
* Returns the set of clusters in the specified topology.
*
* @param topology topology descriptor
......@@ -35,12 +44,31 @@ public interface TopologyService {
Set<TopologyCluster> getClusters(Topology topology);
/**
* Returns the graph view of the specified topology.
* Returns the cluster with the specified ID.
*
* @param topology topology descriptor
* @return topology graph view
* @param clusterId cluster identifier
* @return topology cluster
*/
TopologyCluster getCluster(Topology topology, ClusterId clusterId);
/**
* Returns the set of devices that belong to the specified cluster.
*
* @param topology topology descriptor
* @param cluster topology cluster
* @return set of cluster devices
*/
Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster);
/**
* Returns the set of links that form the specified cluster.
*
* @param topology topology descriptor
* @param cluster topology cluster
* @return set of cluster links
*/
Graph<TopoVertex, TopoEdge> getGraph(Topology topology);
Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster);
/**
* Returns the set of all shortest paths, precomputed in terms of hop-count,
......
......@@ -6,7 +6,7 @@ import org.onlab.onos.net.DeviceId;
/**
* Represents a vertex in the topology graph.
*/
public interface TopoVertex extends Vertex {
public interface TopologyVertex extends Vertex {
/**
* Returns the associated infrastructure device identification.
......
package org.onlab.onos.net.trivial.impl;
package org.onlab.onos.net.trivial.host.impl;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
......
package org.onlab.onos.net.trivial.impl;
package org.onlab.onos.net.trivial.host.impl;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_UPDATED;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultHost;
......@@ -15,17 +26,6 @@ import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_REMOVED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_ADDED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_UPDATED;
import static org.onlab.onos.net.host.HostEvent.Type.HOST_MOVED;
/**
* Manages inventory of end-station hosts using trivial in-memory
* implementation.
......@@ -36,6 +36,7 @@ public class SimpleHostStore {
// hosts sorted based on their location
private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
/**
* Creates a new host or updates the existing one based on the specified
* description.
......
package org.onlab.onos.net.trivial.topology.impl;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
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.GraphDescription;
import org.onlab.onos.net.topology.TopologyEdge;
import org.onlab.onos.net.topology.TopologyVertex;
import java.util.Map;
/**
* Default implementation of an immutable topology graph data carrier.
*/
class DefaultGraphDescription implements GraphDescription {
private final long nanos;
private final ImmutableSet<TopologyVertex> vertexes;
private final ImmutableSet<TopologyEdge> edges;
private final Map<DeviceId, TopologyVertex> vertexesById = Maps.newHashMap();
/**
* Creates a minimal topology graph description to allow core to construct
* and process the topology graph.
*
* @param nanos time in nanos of when the topology description was created
* @param devices collection of infrastructure devices
* @param links collection of infrastructure links
*/
DefaultGraphDescription(long nanos, Iterable<Device> devices, Iterable<Link> links) {
this.nanos = nanos;
this.vertexes = buildVertexes(devices);
this.edges = buildEdges(links);
vertexesById.clear();
}
@Override
public long timestamp() {
return nanos;
}
@Override
public ImmutableSet<TopologyVertex> vertexes() {
return vertexes;
}
@Override
public ImmutableSet<TopologyEdge> edges() {
return edges;
}
// Builds a set of topology vertexes from the specified list of devices
private ImmutableSet<TopologyVertex> buildVertexes(Iterable<Device> devices) {
ImmutableSet.Builder<TopologyVertex> vertexes = ImmutableSet.builder();
for (Device device : devices) {
TopologyVertex vertex = new DefaultTopologyVertex(device.id());
vertexes.add(vertex);
vertexesById.put(vertex.deviceId(), vertex);
}
return vertexes.build();
}
// Builds a set of topology vertexes from the specified list of links
private ImmutableSet<TopologyEdge> buildEdges(Iterable<Link> links) {
ImmutableSet.Builder<TopologyEdge> edges = ImmutableSet.builder();
for (Link link : links) {
edges.add(new DefaultTopologyEdge(vertexOf(link.src()),
vertexOf(link.dst()), link));
}
return edges.build();
}
// Fetches a vertex corresponding to the given connection point device.
private TopologyVertex vertexOf(ConnectPoint connectPoint) {
DeviceId id = connectPoint.deviceId();
TopologyVertex vertex = vertexesById.get(id);
if (vertex == null) {
// If vertex does not exist, create one and register it.
vertex = new DefaultTopologyVertex(id);
vertexesById.put(id, vertex);
}
return vertex;
}
}
package org.onlab.onos.net.trivial.topology.impl;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import org.onlab.graph.DijkstraGraphSearch;
import org.onlab.graph.GraphPathSearch;
import org.onlab.graph.TarjanGraphSearch;
import org.onlab.onos.net.AbstractModel;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultPath;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.net.topology.ClusterId;
import org.onlab.onos.net.topology.DefaultTopologyCluster;
import org.onlab.onos.net.topology.GraphDescription;
import org.onlab.onos.net.topology.LinkWeight;
import org.onlab.onos.net.topology.Topology;
import org.onlab.onos.net.topology.TopologyCluster;
import org.onlab.onos.net.topology.TopologyEdge;
import org.onlab.onos.net.topology.TopologyGraph;
import org.onlab.onos.net.topology.TopologyVertex;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.collect.ImmutableSetMultimap.Builder;
import static org.onlab.graph.GraphPathSearch.Result;
import static org.onlab.graph.TarjanGraphSearch.SCCResult;
import static org.onlab.onos.net.Link.Type.INDIRECT;
/**
* Default implementation of the topology descriptor. This carries the
......@@ -10,30 +40,53 @@ import org.onlab.onos.net.topology.Topology;
*/
public class DefaultTopology extends AbstractModel implements Topology {
private static final DijkstraGraphSearch<TopologyVertex, TopologyEdge> DIJKSTRA =
new DijkstraGraphSearch<>();
private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN =
new TarjanGraphSearch<>();
private static final ProviderId PID = new ProviderId("org.onlab.onos.net");
private final long time;
private final int clusterCount;
private final int deviceCount;
private final int linkCount;
private final int pathCount;
private final TopologyGraph graph;
private final SCCResult<TopologyVertex, TopologyEdge> clusterResults;
private final ImmutableMap<DeviceId, Result<TopologyVertex, TopologyEdge>> results;
private final ImmutableSetMultimap<PathKey, Path> paths;
private final ImmutableMap<ClusterId, TopologyCluster> clusters;
private final ImmutableSet<ConnectPoint> infrastructurePoints;
private final ImmutableSetMultimap<ClusterId, ConnectPoint> broadcastSets;
private ImmutableMap<DeviceId, TopologyCluster> clustersByDevice;
private ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
private ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
/**
* Creates a topology descriptor attributed to the specified provider.
*
* @param providerId identity of the provider
* @param time creation time in system nanos
* @param clusterCount number of clusters
* @param deviceCount number of devices
* @param linkCount number of links
* @param pathCount number of pre-computed paths
* @param description data describing the new topology
*/
DefaultTopology(ProviderId providerId, long time, int clusterCount,
int deviceCount, int linkCount, int pathCount) {
DefaultTopology(ProviderId providerId, GraphDescription description) {
super(providerId);
this.time = time;
this.clusterCount = clusterCount;
this.deviceCount = deviceCount;
this.linkCount = linkCount;
this.pathCount = pathCount;
this.time = description.timestamp();
// Build the graph
this.graph = new DefaultTopologyGraph(description.vertexes(),
description.edges());
this.results = searchForShortestPaths();
this.paths = buildPaths();
this.clusterResults = searchForClusters();
this.clusters = buildTopologyClusters();
buildIndexes();
this.broadcastSets = buildBroadcastSets();
this.infrastructurePoints = findInfrastructurePoints();
}
@Override
......@@ -43,22 +96,329 @@ public class DefaultTopology extends AbstractModel implements Topology {
@Override
public int clusterCount() {
return clusterCount;
return clusters.size();
}
@Override
public int deviceCount() {
return deviceCount;
return graph.getVertexes().size();
}
@Override
public int linkCount() {
return linkCount;
return graph.getEdges().size();
}
@Override
public int pathCount() {
return pathCount;
return paths.size();
}
/**
* Returns the backing topology graph.
*
* @return topology graph
*/
TopologyGraph getGraph() {
return graph;
}
/**
* Returns the set of topology clusters.
*
* @return set of clusters
*/
Set<TopologyCluster> getClusters() {
return ImmutableSet.copyOf(clusters.values());
}
/**
* Returns the specified topology cluster.
*
* @param clusterId cluster identifier
* @return topology cluster
*/
TopologyCluster getCluster(ClusterId clusterId) {
return clusters.get(clusterId);
}
/**
* Returns the set of cluster devices.
*
* @param cluster topology cluster
* @return cluster devices
*/
Set<DeviceId> getClusterDevices(TopologyCluster cluster) {
return devicesByCluster.get(cluster);
}
/**
* Returns the set of cluster links.
*
* @param cluster topology cluster
* @return cluster links
*/
Set<Link> getClusterLinks(TopologyCluster cluster) {
return linksByCluster.get(cluster);
}
/**
* Indicates whether the given point is an infrastructure link end-point.
*
* @param connectPoint connection point
* @return true if infrastructure
*/
boolean isInfrastructure(ConnectPoint connectPoint) {
return infrastructurePoints.contains(connectPoint);
}
/**
* Indicates whether the given point is part of a broadcast tree.
*
* @param connectPoint connection point
* @return true if in broadcast tree
*/
boolean isInBroadcastTree(ConnectPoint connectPoint) {
// Any non-infrastructure, i.e. edge points are assumed to be OK
if (!isInfrastructure(connectPoint)) {
return true;
}
// Find the cluster to which the device belongs.
TopologyCluster cluster = clustersByDevice.get(connectPoint.deviceId());
if (cluster == null) {
throw new IllegalArgumentException("No cluster found for device " + connectPoint.deviceId());
}
// If the broadcast tree is null or empty, or if the point explicitly
// belongs to the broadcast tree points, return true;
Set<ConnectPoint> points = broadcastSets.get(cluster.id());
return points == null || points.isEmpty() || points.contains(connectPoint);
}
/**
* Returns the set of pre-computed shortest paths between source and
* destination devices.
*
* @param src source device
* @param dst destination device
* @return set of shortest paths
*/
Set<Path> getPaths(DeviceId src, DeviceId dst) {
return paths.get(new PathKey(src, dst));
}
/**
* Computes on-demand the set of shortest paths between source and
* destination devices.
*
* @param src source device
* @param dst destination device
* @return set of shortest paths
*/
Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeight weight) {
GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
DIJKSTRA.search(graph, new DefaultTopologyVertex(src),
new DefaultTopologyVertex(dst), weight);
ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
builder.add(networkPath(path));
}
return builder.build();
}
// Searches the graph for all shortest paths and returns the search results.
private ImmutableMap<DeviceId, Result<TopologyVertex, TopologyEdge>> searchForShortestPaths() {
ImmutableMap.Builder<DeviceId, Result<TopologyVertex, TopologyEdge>> builder = ImmutableMap.builder();
// Search graph paths for each source to all destinations.
LinkWeight weight = new HopCountLinkWeight(graph.getVertexes().size());
for (TopologyVertex src : graph.getVertexes()) {
builder.put(src.deviceId(), DIJKSTRA.search(graph, src, null, weight));
}
return builder.build();
}
// Builds network paths from the graph path search results
private ImmutableSetMultimap<PathKey, Path> buildPaths() {
Builder<PathKey, Path> builder = ImmutableSetMultimap.builder();
for (DeviceId deviceId : results.keySet()) {
Result<TopologyVertex, TopologyEdge> result = results.get(deviceId);
for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
builder.put(new PathKey(path.src().deviceId(), path.dst().deviceId()),
networkPath(path));
}
}
return builder.build();
}
// Converts graph path to a network path with the same cost.
private Path networkPath(org.onlab.graph.Path<TopologyVertex, TopologyEdge> path) {
List<Link> links = new ArrayList<>();
for (TopologyEdge edge : path.edges()) {
links.add(edge.link());
}
return new DefaultPath(PID, links, path.cost());
}
// Searches for SCC clusters in the network topology graph using Tarjan
// algorithm.
private SCCResult<TopologyVertex, TopologyEdge> searchForClusters() {
return TARJAN.search(graph, new NoIndirectLinksWeight());
}
// Builds the topology clusters and returns the id-cluster bindings.
private ImmutableMap<ClusterId, TopologyCluster> buildTopologyClusters() {
ImmutableMap.Builder<ClusterId, TopologyCluster> clusterBuilder = ImmutableMap.builder();
SCCResult<TopologyVertex, TopologyEdge> result =
TARJAN.search(graph, new NoIndirectLinksWeight());
// Extract both vertexes and edges from the results; the lists form
// pairs along the same index.
List<Set<TopologyVertex>> clusterVertexes = result.clusterVertexes();
List<Set<TopologyEdge>> clusterEdges = result.clusterEdges();
// Scan over the lists and create a cluster from the results.
for (int i = 0, n = result.clusterCount(); i < n; i++) {
Set<TopologyVertex> vertexSet = clusterVertexes.get(i);
Set<TopologyEdge> edgeSet = clusterEdges.get(i);
ClusterId cid = ClusterId.clusterId(i);
DefaultTopologyCluster cluster =
new DefaultTopologyCluster(cid, vertexSet.size(), edgeSet.size(),
findRoot(vertexSet).deviceId());
clusterBuilder.put(cid, cluster);
}
return clusterBuilder.build();
}
// Finds the vertex whose device id is the lexicographical minimum in the
// specified set.
private TopologyVertex findRoot(Set<TopologyVertex> vertexSet) {
TopologyVertex minVertex = null;
for (TopologyVertex vertex : vertexSet) {
if (minVertex == null ||
minVertex.deviceId().toString()
.compareTo(minVertex.deviceId().toString()) < 0) {
minVertex = vertex;
}
}
return minVertex;
}
// Processes a map of broadcast sets for each cluster.
private ImmutableSetMultimap<ClusterId, ConnectPoint> buildBroadcastSets() {
Builder<ClusterId, ConnectPoint> builder = ImmutableSetMultimap.builder();
for (TopologyCluster cluster : clusters.values()) {
addClusterBroadcastSet(cluster, builder);
}
return builder.build();
}
// Finds all broadcast points for the cluster. These are those connection
// points which lie along the shortest paths between the cluster root and
// all other devices within the cluster.
private void addClusterBroadcastSet(TopologyCluster cluster,
Builder<ClusterId, ConnectPoint> builder) {
// Use the graph root search results to build the broadcast set.
Result<TopologyVertex, TopologyEdge> result = results.get(cluster.root());
for (Map.Entry<TopologyVertex, Set<TopologyEdge>> entry : result.parents().entrySet()) {
TopologyVertex vertex = entry.getKey();
// Ignore any parents that lead outside the cluster.
if (clustersByDevice.get(vertex.deviceId()) != cluster) {
continue;
}
// Ignore any back-link sets that are empty.
Set<TopologyEdge> parents = entry.getValue();
if (parents.isEmpty()) {
continue;
}
// Use the first back-link source and destinations to add to the
// broadcast set.
Link link = parents.iterator().next().link();
builder.put(cluster.id(), link.src());
builder.put(cluster.id(), link.dst());
}
}
// Collects and returns an set of all infrastructure link end-points.
private ImmutableSet<ConnectPoint> findInfrastructurePoints() {
ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
for (TopologyEdge edge : graph.getEdges()) {
builder.add(edge.link().src());
builder.add(edge.link().dst());
}
return builder.build();
}
// Builds cluster-devices, cluster-links and device-cluster indexes.
private void buildIndexes() {
// Prepare the index builders
ImmutableMap.Builder<DeviceId, TopologyCluster> clusterBuilder = ImmutableMap.builder();
ImmutableSetMultimap.Builder<TopologyCluster, DeviceId> devicesBuilder = ImmutableSetMultimap.builder();
ImmutableSetMultimap.Builder<TopologyCluster, Link> linksBuilder = ImmutableSetMultimap.builder();
// Now scan through all the clusters
for (TopologyCluster cluster : clusters.values()) {
int i = cluster.id().index();
// Scan through all the cluster vertexes.
for (TopologyVertex vertex : clusterResults.clusterVertexes().get(i)) {
devicesBuilder.put(cluster, vertex.deviceId());
clusterBuilder.put(vertex.deviceId(), cluster);
}
// Scan through all the cluster edges.
for (TopologyEdge edge : clusterResults.clusterEdges().get(i)) {
linksBuilder.put(cluster, edge.link());
}
}
// Finalize all indexes.
clustersByDevice = clusterBuilder.build();
devicesByCluster = devicesBuilder.build();
linksByCluster = linksBuilder.build();
}
// 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 static class HopCountLinkWeight implements LinkWeight {
private final int indirectLinkCost;
HopCountLinkWeight(int indirectLinkCost) {
this.indirectLinkCost = indirectLinkCost;
}
@Override
public double weight(TopologyEdge 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;
}
}
// Link weight for preventing traversal over indirect links.
private static class NoIndirectLinksWeight implements LinkWeight {
@Override
public double weight(TopologyEdge edge) {
return edge.link().type() == INDIRECT ? -1 : 1;
}
}
@Override
public String toString() {
return toStringHelper(this)
.add("time", time)
.add("clusters", clusterCount())
.add("devices", deviceCount())
.add("links", linkCount())
.add("pathCount", pathCount())
.toString();
}
}
......
package org.onlab.onos.net.trivial.topology.provider.impl;
package org.onlab.onos.net.trivial.topology.impl;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.topology.TopoEdge;
import org.onlab.onos.net.topology.TopoVertex;
import org.onlab.onos.net.topology.TopologyEdge;
import org.onlab.onos.net.topology.TopologyVertex;
import java.util.Objects;
......@@ -11,11 +11,11 @@ import static com.google.common.base.MoreObjects.toStringHelper;
/**
* Implementation of the topology edge backed by a link.
*/
class DefaultTopoEdge implements TopoEdge {
class DefaultTopologyEdge implements TopologyEdge {
private final Link link;
private final TopoVertex src;
private final TopoVertex dst;
private final TopologyVertex src;
private final TopologyVertex dst;
/**
* Creates a new topology edge.
......@@ -24,7 +24,7 @@ class DefaultTopoEdge implements TopoEdge {
* @param dst destination vertex
* @param link infrastructure link
*/
DefaultTopoEdge(TopoVertex src, TopoVertex dst, Link link) {
DefaultTopologyEdge(TopologyVertex src, TopologyVertex dst, Link link) {
this.src = src;
this.dst = dst;
this.link = link;
......@@ -36,12 +36,12 @@ class DefaultTopoEdge implements TopoEdge {
}
@Override
public TopoVertex src() {
public TopologyVertex src() {
return src;
}
@Override
public TopoVertex dst() {
public TopologyVertex dst() {
return dst;
}
......@@ -52,8 +52,8 @@ class DefaultTopoEdge implements TopoEdge {
@Override
public boolean equals(Object obj) {
if (obj instanceof DefaultTopoEdge) {
final DefaultTopoEdge other = (DefaultTopoEdge) obj;
if (obj instanceof DefaultTopologyEdge) {
final DefaultTopologyEdge other = (DefaultTopologyEdge) obj;
return Objects.equals(this.link, other.link);
}
return false;
......
package org.onlab.onos.net.trivial.topology.impl;
import org.onlab.graph.AdjacencyListsGraph;
import org.onlab.onos.net.topology.TopologyEdge;
import org.onlab.onos.net.topology.TopologyGraph;
import org.onlab.onos.net.topology.TopologyVertex;
import java.util.Set;
/**
* Default implementation of an immutable topology graph based on a generic
* implementation of adjacency lists graph.
*/
public class DefaultTopologyGraph
extends AdjacencyListsGraph<TopologyVertex, TopologyEdge>
implements TopologyGraph {
/**
* Creates a topology graph comprising of the specified vertexes and edges.
*
* @param vertexes set of graph vertexes
* @param edges set of graph edges
*/
public DefaultTopologyGraph(Set<TopologyVertex> vertexes, Set<TopologyEdge> edges) {
super(vertexes, edges);
}
}
package org.onlab.onos.net.trivial.topology.provider.impl;
package org.onlab.onos.net.trivial.topology.impl;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
......@@ -16,7 +16,7 @@ import org.onlab.onos.net.link.LinkListener;
import org.onlab.onos.net.link.LinkService;
import org.onlab.onos.net.provider.AbstractProvider;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.net.topology.TopologyDescription;
import org.onlab.onos.net.topology.GraphDescription;
import org.onlab.onos.net.topology.TopologyProvider;
import org.onlab.onos.net.topology.TopologyProviderRegistry;
import org.onlab.onos.net.topology.TopologyProviderService;
......@@ -32,10 +32,12 @@ import static org.onlab.util.Tools.namedThreads;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Simple implementation of a network topology provider/computor.
* Default implementation of a network topology provider that feeds off
* device and link subsystem events to trigger assembly and computation of
* new topology snapshots.
*/
@Component(immediate = true)
public class SimpleTopologyProvider extends AbstractProvider
public class DefaultTopologyProvider extends AbstractProvider
implements TopologyProvider {
// TODO: make these configurable
......@@ -70,13 +72,13 @@ public class SimpleTopologyProvider extends AbstractProvider
/**
* Creates a provider with the supplier identifier.
*/
public SimpleTopologyProvider() {
public DefaultTopologyProvider() {
super(new ProviderId("org.onlab.onos.provider.topology"));
}
@Activate
public synchronized void activate() {
executor = newFixedThreadPool(MAX_THREADS, namedThreads("topo-compute-%d"));
executor = newFixedThreadPool(MAX_THREADS, namedThreads("topo-build-%d"));
accumulator = new TopologyChangeAccumulator();
providerService = providerRegistry.register(this);
......@@ -90,6 +92,8 @@ public class SimpleTopologyProvider extends AbstractProvider
@Deactivate
public synchronized void deactivate() {
isStarted = false;
deviceService.removeListener(deviceListener);
linkService.removeListener(linkListener);
providerRegistry.unregister(this);
......@@ -98,7 +102,6 @@ public class SimpleTopologyProvider extends AbstractProvider
executor.shutdownNow();
executor = null;
isStarted = false;
log.info("Stopped");
}
......@@ -108,16 +111,18 @@ public class SimpleTopologyProvider extends AbstractProvider
*
* @param reasons events which triggered the topology change
*/
private void triggerTopologyBuild(List<Event> reasons) {
private synchronized void triggerTopologyBuild(List<Event> reasons) {
if (executor != null) {
executor.execute(new TopologyBuilderTask(reasons));
}
}
// Builds the topology using the latest device and link information
// and citing the specified events as reasons for the change.
private void buildTopology(List<Event> reasons) {
if (isStarted) {
TopologyDescription desc =
new DefaultTopologyDescription(System.nanoTime(),
GraphDescription desc =
new DefaultGraphDescription(System.nanoTime(),
deviceService.getDevices(),
linkService.getLinks());
providerService.topologyChanged(desc, reasons);
......
package org.onlab.onos.net.trivial.topology.provider.impl;
package org.onlab.onos.net.trivial.topology.impl;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.topology.TopoVertex;
import org.onlab.onos.net.topology.TopologyVertex;
import java.util.Objects;
/**
* Implementation of the topology vertex backed by a device id.
*/
class DefaultTopoVertex implements TopoVertex {
class DefaultTopologyVertex implements TopologyVertex {
private final DeviceId deviceId;
......@@ -17,7 +17,7 @@ class DefaultTopoVertex implements TopoVertex {
*
* @param deviceId backing infrastructure device identifier
*/
DefaultTopoVertex(DeviceId deviceId) {
DefaultTopologyVertex(DeviceId deviceId) {
this.deviceId = deviceId;
}
......@@ -33,8 +33,8 @@ class DefaultTopoVertex implements TopoVertex {
@Override
public boolean equals(Object obj) {
if (obj instanceof DefaultTopoVertex) {
final DefaultTopoVertex other = (DefaultTopoVertex) obj;
if (obj instanceof DefaultTopologyVertex) {
final DefaultTopologyVertex other = (DefaultTopologyVertex) obj;
return Objects.equals(this.deviceId, other.deviceId);
}
return false;
......
package org.onlab.onos.net.trivial.topology.impl;
import org.onlab.onos.net.DeviceId;
import java.util.Objects;
/**
* Key for filing pre-computed paths between source and destination devices.
*/
class PathKey {
private final DeviceId src;
private final DeviceId dst;
/**
* Creates a path key from the given source/dest pair.
* @param src source device
* @param dst destination device
*/
PathKey(DeviceId src, DeviceId dst) {
this.src = src;
this.dst = dst;
}
@Override
public int hashCode() {
return Objects.hash(src, dst);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PathKey) {
final PathKey other = (PathKey) obj;
return Objects.equals(this.src, other.src) && Objects.equals(this.dst, other.dst);
}
return false;
}
}
......@@ -6,22 +6,22 @@ import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.graph.Graph;
import org.onlab.onos.event.AbstractListenerRegistry;
import org.onlab.onos.event.Event;
import org.onlab.onos.event.EventDeliveryService;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.provider.AbstractProviderRegistry;
import org.onlab.onos.net.provider.AbstractProviderService;
import org.onlab.onos.net.topology.ClusterId;
import org.onlab.onos.net.topology.GraphDescription;
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.Topology;
import org.onlab.onos.net.topology.TopologyCluster;
import org.onlab.onos.net.topology.TopologyDescription;
import org.onlab.onos.net.topology.TopologyEvent;
import org.onlab.onos.net.topology.TopologyGraph;
import org.onlab.onos.net.topology.TopologyListener;
import org.onlab.onos.net.topology.TopologyProvider;
import org.onlab.onos.net.topology.TopologyProviderRegistry;
......@@ -46,6 +46,8 @@ public class SimpleTopologyManager
public static final String TOPOLOGY_NULL = "Topology cannot be null";
private static final String DEVICE_ID_NULL = "Device ID cannot be null";
private static final String CLUSTER_ID_NULL = "Cluster ID cannot be null";
private static final String CLUSTER_NULL = "Topology cluster cannot be null";
public static final String CONNECTION_POINT_NULL = "Connection point cannot be null";
private final Logger log = getLogger(getClass());
......@@ -79,19 +81,49 @@ public class SimpleTopologyManager
@Override
public boolean isLatest(Topology topology) {
checkNotNull(topology, TOPOLOGY_NULL);
return store.isLatest(topology);
return store.isLatest(defaultTopology(topology));
}
// Validates the specified topology and returns it as a default
private DefaultTopology defaultTopology(Topology topology) {
if (topology instanceof DefaultTopology) {
return (DefaultTopology) topology;
}
throw new IllegalArgumentException("Topology class " + topology.getClass() +
" not supported");
}
@Override
public Set<TopologyCluster> getClusters(Topology topology) {
checkNotNull(topology, TOPOLOGY_NULL);
return store.getClusters(topology);
return store.getClusters(defaultTopology(topology));
}
@Override
public TopologyCluster getCluster(Topology topology, ClusterId clusterId) {
checkNotNull(topology, TOPOLOGY_NULL);
checkNotNull(topology, CLUSTER_ID_NULL);
return store.getCluster(defaultTopology(topology), clusterId);
}
@Override
public Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster) {
checkNotNull(topology, TOPOLOGY_NULL);
checkNotNull(topology, CLUSTER_NULL);
return store.getClusterDevices(defaultTopology(topology), cluster);
}
@Override
public Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster) {
checkNotNull(topology, TOPOLOGY_NULL);
checkNotNull(topology, CLUSTER_NULL);
return store.getClusterLinks(defaultTopology(topology), cluster);
}
@Override
public Graph<TopoVertex, TopoEdge> getGraph(Topology topology) {
public TopologyGraph getGraph(Topology topology) {
checkNotNull(topology, TOPOLOGY_NULL);
return store.getGraph(topology);
return store.getGraph(defaultTopology(topology));
}
@Override
......@@ -99,7 +131,7 @@ public class SimpleTopologyManager
checkNotNull(topology, TOPOLOGY_NULL);
checkNotNull(src, DEVICE_ID_NULL);
checkNotNull(dst, DEVICE_ID_NULL);
return store.getPaths(topology, src, dst);
return store.getPaths(defaultTopology(topology), src, dst);
}
@Override
......@@ -108,21 +140,21 @@ public class SimpleTopologyManager
checkNotNull(src, DEVICE_ID_NULL);
checkNotNull(dst, DEVICE_ID_NULL);
checkNotNull(weight, "Link weight cannot be null");
return store.getPaths(topology, src, dst, weight);
return store.getPaths(defaultTopology(topology), src, dst, weight);
}
@Override
public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
checkNotNull(topology, TOPOLOGY_NULL);
checkNotNull(connectPoint, CONNECTION_POINT_NULL);
return store.isInfrastructure(topology, connectPoint);
return store.isInfrastructure(defaultTopology(topology), connectPoint);
}
@Override
public boolean isInBroadcastTree(Topology topology, ConnectPoint connectPoint) {
checkNotNull(topology, TOPOLOGY_NULL);
checkNotNull(connectPoint, CONNECTION_POINT_NULL);
return store.isInBroadcastTree(topology, connectPoint);
return store.isInBroadcastTree(defaultTopology(topology), connectPoint);
}
@Override
......@@ -150,15 +182,14 @@ public class SimpleTopologyManager
}
@Override
public void topologyChanged(TopologyDescription topoDescription,
public void topologyChanged(GraphDescription topoDescription,
List<Event> reasons) {
checkNotNull(topoDescription, "Topology description cannot be null");
log.info("Topology changed due to: {}", // to be removed soon
reasons == null ? "initial compute" : reasons);
TopologyEvent event = store.updateTopology(topoDescription, reasons);
TopologyEvent event = store.updateTopology(provider().id(),
topoDescription, reasons);
if (event != null) {
log.info("Topology changed due to: {}",
log.info("Topology {} changed due to: {}", event.subject(),
reasons == null ? "initial compute" : reasons);
eventDispatcher.post(event);
}
......
package org.onlab.onos.net.trivial.topology.impl;
import org.onlab.graph.Graph;
import org.onlab.onos.event.Event;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.net.topology.ClusterId;
import org.onlab.onos.net.topology.GraphDescription;
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.Topology;
import org.onlab.onos.net.topology.TopologyCluster;
import org.onlab.onos.net.topology.TopologyDescription;
import org.onlab.onos.net.topology.TopologyEvent;
import org.onlab.onos.net.topology.TopologyGraph;
import java.util.List;
import java.util.Set;
......@@ -45,23 +46,56 @@ class SimpleTopologyStore {
}
/**
* Returns the immutable graph view of the current topology.
*
* @param topology topology descriptor
* @return graph view
*/
TopologyGraph getGraph(DefaultTopology topology) {
return topology.getGraph();
}
/**
* Returns the set of topology SCC clusters.
*
* @param topology topology descriptor
* @return set of clusters
*/
Set<TopologyCluster> getClusters(Topology topology) {
return null;
Set<TopologyCluster> getClusters(DefaultTopology topology) {
return topology.getClusters();
}
/**
* Returns the immutable graph view of the current topology.
* Returns the cluster of the specified topology.
*
* @param topology topology descriptor
* @return graph view
* @param clusterId cluster identity
* @return topology cluster
*/
Graph<TopoVertex, TopoEdge> getGraph(Topology topology) {
return null;
TopologyCluster getCluster(DefaultTopology topology, ClusterId clusterId) {
return topology.getCluster(clusterId);
}
/**
* Returns the cluster of the specified topology.
*
* @param topology topology descriptor
* @param cluster topology cluster
* @return set of cluster links
*/
Set<DeviceId> getClusterDevices(DefaultTopology topology, TopologyCluster cluster) {
return topology.getClusterDevices(cluster);
}
/**
* Returns the cluster of the specified topology.
*
* @param topology topology descriptor
* @param cluster topology cluster
* @return set of cluster links
*/
Set<Link> getClusterLinks(DefaultTopology topology, TopologyCluster cluster) {
return topology.getClusterLinks(cluster);
}
/**
......@@ -72,8 +106,8 @@ class SimpleTopologyStore {
* @param dst destination device
* @return set of shortest paths
*/
Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) {
return null;
Set<Path> getPaths(DefaultTopology topology, DeviceId src, DeviceId dst) {
return topology.getPaths(src, dst);
}
/**
......@@ -85,9 +119,9 @@ class SimpleTopologyStore {
* @param weight link weight function
* @return set of shortest paths
*/
Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
Set<Path> getPaths(DefaultTopology topology, DeviceId src, DeviceId dst,
LinkWeight weight) {
return null;
return topology.getPaths(src, dst, weight);
}
/**
......@@ -97,8 +131,8 @@ class SimpleTopologyStore {
* @param connectPoint connection point
* @return true if infrastructure; false otherwise
*/
boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
return false;
boolean isInfrastructure(DefaultTopology topology, ConnectPoint connectPoint) {
return topology.isInfrastructure(connectPoint);
}
/**
......@@ -108,20 +142,36 @@ class SimpleTopologyStore {
* @param connectPoint connection point
* @return true if in broadcast tree; false otherwise
*/
boolean isInBroadcastTree(Topology topology, ConnectPoint connectPoint) {
return false;
boolean isInBroadcastTree(DefaultTopology topology, ConnectPoint connectPoint) {
return topology.isInBroadcastTree(connectPoint);
}
/**
* Generates a new topology snapshot from the specified description.
*
* @param topoDescription topology description
* @param providerId provider identification
* @param graphDescription topology graph description
* @param reasons list of events that triggered the update
* @return topology update event or null if the description is old
*/
TopologyEvent updateTopology(TopologyDescription topoDescription,
TopologyEvent updateTopology(ProviderId providerId,
GraphDescription graphDescription,
List<Event> reasons) {
// First off, make sure that what we're given is indeed newer than
// what we already have.
if (current != null && graphDescription.timestamp() < current.time()) {
return null;
}
// Have the default topology construct self from the description data.
DefaultTopology newTopology =
new DefaultTopology(providerId, graphDescription);
// Promote the new topology to current and return a ready-to-send event.
synchronized (this) {
current = newTopology;
return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED, current);
}
}
}
......
package org.onlab.onos.net.trivial.topology.provider.impl;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
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.graph.TarjanGraphSearch;
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.DefaultTopologyCluster;
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.List;
import java.util.Map;
import java.util.Set;
import static com.google.common.collect.ImmutableSetMultimap.Builder;
import static org.onlab.graph.GraphPathSearch.Result;
import static org.onlab.graph.TarjanGraphSearch.SCCResult;
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 static final TarjanGraphSearch<TopoVertex, TopoEdge> TARJAN =
new TarjanGraphSearch<>();
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;
// Secondary look-up indexes
private ImmutableSetMultimap<ClusterId, DeviceId> devicesByCluster;
private ImmutableSetMultimap<ClusterId, Link> linksByCluster;
private Map<DeviceId, TopologyCluster> clustersByDevice = Maps.newHashMap();
/**
* Creates a topology description to carry topology vitals to the core.
*
* @param nanos time in nanos of when the topology description was created
* @param devices collection of infrastructure devices
* @param links collection of infrastructure links
*/
DefaultTopologyDescription(long nanos, Iterable<Device> devices, Iterable<Link> links) {
this.nanos = nanos;
this.graph = buildGraph(devices, links);
this.results = computeDefaultPaths();
this.clusters = computeClusters();
}
@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 devicesByCluster.get(cluster.id());
}
@Override
public Set<Link> clusterLinks(TopologyCluster cluster) {
return linksByCluster.get(cluster.id());
}
@Override
public TopologyCluster clusterFor(DeviceId deviceId) {
return clustersByDevice.get(deviceId);
}
// 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 static class HopCountLinkWeight implements LinkWeight {
private final int indirectLinkCost;
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;
}
}
// Link weight for preventing traversal over indirect links.
private static class NoIndirectLinksWeight implements LinkWeight {
@Override
public double weight(TopoEdge edge) {
return edge.link().type() == INDIRECT ? -1 : 1;
}
}
// Constructs the topology graph using the supplied devices and links.
private Graph<TopoVertex, TopoEdge> buildGraph(Iterable<Device> devices,
Iterable<Link> links) {
return new AdjacencyListsGraph<>(buildVertexes(devices),
buildEdges(links));
}
// 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 DefaultTopoVertex(device.id());
vertexes.add(vertex);
vertexesById.put(vertex.deviceId(), 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 DefaultTopoEdge(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();
SCCResult<TopoVertex, TopoEdge> result = TARJAN.search(graph, new NoIndirectLinksWeight());
// Extract both vertexes and edges from the results; the lists form
// pairs along the same index.
List<Set<TopoVertex>> clusterVertexes = result.clusterVertexes();
List<Set<TopoEdge>> clusterEdges = result.clusterEdges();
Builder<ClusterId, DeviceId> devicesBuilder = ImmutableSetMultimap.builder();
Builder<ClusterId, Link> linksBuilder = ImmutableSetMultimap.builder();
// Scan over the lists and create a cluster from the results.
for (int i = 0, n = result.clusterCount(); i < n; i++) {
Set<TopoVertex> vertexSet = clusterVertexes.get(i);
Set<TopoEdge> edgeSet = clusterEdges.get(i);
DefaultTopologyCluster cluster =
new DefaultTopologyCluster(ClusterId.clusterId(i),
vertexSet.size(), edgeSet.size(),
findRoot(vertexSet).deviceId());
findClusterDevices(vertexSet, cluster, devicesBuilder);
findClusterLinks(edgeSet, cluster, linksBuilder);
}
return clusters;
}
// Scans through the set of cluster vertexes and puts their devices in a
// multi-map associated with the cluster. It also binds the devices to
// the cluster.
private void findClusterDevices(Set<TopoVertex> vertexSet,
DefaultTopologyCluster cluster,
Builder<ClusterId, DeviceId> builder) {
for (TopoVertex vertex : vertexSet) {
DeviceId deviceId = vertex.deviceId();
builder.put(cluster.id(), deviceId);
clustersByDevice.put(deviceId, cluster);
}
}
// Scans through the set of cluster edges and puts their links in a
// multi-map associated with the cluster.
private void findClusterLinks(Set<TopoEdge> edgeSet,
DefaultTopologyCluster cluster,
Builder<ClusterId, Link> builder) {
for (TopoEdge edge : edgeSet) {
builder.put(cluster.id(), edge.link());
}
}
// Finds the vertex whose device id is the lexicographical minimum in the
// specified set.
private TopoVertex findRoot(Set<TopoVertex> vertexSet) {
TopoVertex minVertex = null;
for (TopoVertex vertex : vertexSet) {
if (minVertex == null ||
minVertex.deviceId().toString()
.compareTo(minVertex.deviceId().toString()) < 0) {
minVertex = vertex;
}
}
return minVertex;
}
// 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 DefaultTopoVertex(id);
vertexesById.put(id, vertex);
}
return vertex;
}
}
<body>
Built-in protocol-agnostic topology builder &amp; provider.
</body>
\ No newline at end of file
......@@ -7,6 +7,7 @@ import org.projectfloodlight.openflow.protocol.OFPacketIn;
import org.projectfloodlight.openflow.protocol.OFPacketOut;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
import org.projectfloodlight.openflow.protocol.match.MatchField;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFPort;
......@@ -83,12 +84,18 @@ public final class DefaultPacketContext implements PacketContext {
@Override
public Integer inPort() {
try {
return pktin.getInPort().getPortNumber();
} catch (UnsupportedOperationException e) {
return pktin.getMatch().get(MatchField.IN_PORT).getPortNumber();
}
}
@Override
public byte[] unparsed() {
return pktin.getData().clone();
}
private OFActionOutput buildOutput(Integer port) {
......
......@@ -250,10 +250,13 @@ public class OpenFlowControllerImpl implements OpenFlowController {
}
OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
if (sw == null) {
sw = getSwitch(dpid);
if (sw == null) {
log.error("Transition to equal called on sw {}, but switch "
+ "was not found in controller-cache", dpid);
return;
}
}
log.info("Transitioned switch {} to EQUAL", dpid);
activeEqualSwitches.put(dpid, sw);
} finally {
......
......@@ -2,10 +2,15 @@ package org.onlab.onos.of.drivers.impl;
import java.util.Collections;
import java.util.List;
import org.onlab.onos.of.controller.Dpid;
import org.onlab.onos.of.controller.driver.AbstractOpenFlowSwitch;
import org.onlab.onos.of.controller.driver.OpenFlowSwitchDriver;
import org.onlab.onos.of.controller.driver.OpenFlowSwitchDriverFactory;
import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -51,8 +56,36 @@ public final class DriverManager implements OpenFlowSwitchDriverFactory {
}
log.warn("DriverManager could not identify switch desc: {}. "
+ "Assigning OFSwitchImplBase", desc);
return null;
+ "Assigning AbstractOpenFlowSwich", desc);
return new AbstractOpenFlowSwitch(dpid) {
@Override
public void write(List<OFMessage> msgs) {
channel.write(msgs);
}
@Override
public void write(OFMessage msg) {
channel.write(Collections.singletonList(msg));
}
@Override
public Boolean supportNxRole() {
return false;
}
@Override
public void startDriverHandshake() {}
@Override
public void processDriverHandshakeMessage(OFMessage m) {}
@Override
public boolean isDriverHandshakeComplete() {
return true;
}
};
}
/**
......
......@@ -320,6 +320,7 @@
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<show>package</show>
<docfilessubdirs>true</docfilessubdirs>
<doctitle>ONOS Java API</doctitle>
<groups>
......
......@@ -60,7 +60,7 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
* Creates an OpenFlow device provider.
*/
public OpenFlowDeviceProvider() {
super(new ProviderId("org.onlab.onos.provider.of.device"));
super(new ProviderId("org.onlab.onos.provider.openflow"));
}
@Activate
......
......@@ -37,7 +37,7 @@ public class OpenFlowHostProvider extends AbstractProvider implements HostProvid
* Creates an OpenFlow host provider.
*/
public OpenFlowHostProvider() {
super(new ProviderId("org.onlab.onos.provider.of.host"));
super(new ProviderId("org.onlab.onos.provider.openflow"));
}
@Activate
......
......@@ -55,7 +55,7 @@ public class OpenFlowLinkProvider extends AbstractProvider implements LinkProvid
* Creates an OpenFlow link provider.
*/
public OpenFlowLinkProvider() {
super(new ProviderId("org.onlab.onos.provider.of.link"));
super(new ProviderId("org.onlab.onos.provider.openflow"));
}
@Activate
......