tom

Check-point before refactoring the topology provider stuff.

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;
import org.apache.karaf.shell.console.Completer;
import org.apache.karaf.shell.console.completer.StringsCompleter;
import org.onlab.onos.GreetService;
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.
*/
public class NameCompleter 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();
SortedSet<String> strings = delegate.getStrings();
while (it.hasNext()) {
strings.add(it.next());
}
// Now let the completer do the work for figuring out what to offer.
return delegate.complete(buffer, cursor, candidates);
}
}
......@@ -30,18 +30,9 @@
<ref component-id="deviceIdCompleter"/>
</completers>
</command>
<command>
<action class="org.onlab.onos.cli.GreetCommand"/>
<completers>
<ref component-id="nameCompleter"/>
</completers>
</command>
</command-bundle>
<bean id="deviceIdCompleter" class="org.onlab.onos.cli.net.DeviceIdCompleter"/>
<bean id="roleCompleter" class="org.onlab.onos.cli.net.RoleCompleter"/>
<bean id="nameCompleter" class="org.onlab.onos.cli.NameCompleter"/>
</blueprint>
......
package org.onlab.onos.net.topology;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
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;
import static org.onlab.graph.GraphPathSearch.Result;
/**
* Describes attribute(s) of a network topology.
......@@ -22,50 +24,39 @@ public interface TopologyDescription extends Description {
long timestamp();
/**
* Returns the topology graph.
* Returns the topology graph in immutable form.
*
* @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.
* Returns an immutable map of path search results for each source device.
*
* @param srcDeviceId source device identifier
* @return path search result for the given source node
* @return map of path search result for each source node
*/
GraphPathSearch.Result pathResults(DeviceId srcDeviceId);
ImmutableMap<DeviceId, Result<TopoVertex, TopoEdge>> pathsBySource();
/**
* Returns the set of topology SCC clusters.
*
* @return set of SCC clusters
*/
Set<TopologyCluster> clusters();
ImmutableSet<TopologyCluster> clusters();
/**
* Returns the set of devices contained by the specified topology cluster.
* Returns an immutable set multi-map of devices for each cluster.
*
* @return set of devices that belong to the specified cluster
* @return set multi-map of devices that belong to each cluster
*/
Set<DeviceId> clusterDevices(TopologyCluster cluster);
ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster();
/**
* Returns the set of infrastructure links contained by the specified cluster.
* Returns an immutable set multi-map of links for each cluster.
*
* @return set of links that form the given cluster
* @return set multi-map of links that belong to each 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);
ImmutableSetMultimap<TopologyCluster, Link> linksByCluster();
}
......
......@@ -21,6 +21,7 @@ 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
*/
......
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.Graph;
import org.onlab.graph.GraphPathSearch;
import org.onlab.onos.net.AbstractModel;
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.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 java.util.Set;
/**
* Default implementation of the topology descriptor. This carries the
......@@ -11,29 +26,33 @@ import org.onlab.onos.net.topology.Topology;
public class DefaultTopology extends AbstractModel implements Topology {
private final long time;
private final int clusterCount;
private final int deviceCount;
private final int linkCount;
private final int pathCount;
private final Graph<TopoVertex, TopoEdge> graph;
private final ImmutableMap<DeviceId, GraphPathSearch.Result<TopoVertex, TopoEdge>> results;
private final ImmutableSet<TopologyCluster> clusters;
private final ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
private final ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
private final ImmutableSet connectPoints;
/**
* 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 providerId identity of the provider
* @param description data describing the new topology
*/
DefaultTopology(ProviderId providerId, long time, int clusterCount,
int deviceCount, int linkCount, int pathCount) {
DefaultTopology(ProviderId providerId, TopologyDescription description) {
super(providerId);
this.time = time;
this.clusterCount = clusterCount;
this.deviceCount = deviceCount;
this.linkCount = linkCount;
this.pathCount = pathCount;
this.time = description.timestamp();
this.graph = description.graph();
this.results = description.pathsBySource();
this.clusters = description.clusters();
this.devicesByCluster = description.devicesByCluster();
this.linksByCluster = description.linksByCluster();
this.connectPoints = ImmutableSet.of();
this.pathCount = 0;
}
@Override
......@@ -43,17 +62,17 @@ 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
......@@ -61,4 +80,23 @@ public class DefaultTopology extends AbstractModel implements Topology {
return pathCount;
}
Set<TopologyCluster> getClusters() {
return clusters;
}
Graph<TopoVertex, TopoEdge> getGraph() {
return graph;
}
boolean isInfrastructure(ConnectPoint connectPoint) {
return connectPoints.contains(connectPoint);
}
boolean isInBroadcastTree(ConnectPoint connectPoint) {
return false;
}
Set<Path> getPaths(DeviceId src, DeviceId dst) {
return null; // pointToPointPaths.get(key(src, dst));
}
}
......
......@@ -79,19 +79,28 @@ 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 Graph<TopoVertex, TopoEdge> getGraph(Topology topology) {
checkNotNull(topology, TOPOLOGY_NULL);
return store.getGraph(topology);
return store.getGraph(defaultTopology(topology));
}
@Override
......@@ -99,7 +108,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 +117,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
......@@ -156,7 +165,8 @@ public class SimpleTopologyManager
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: {}",
reasons == null ? "initial compute" : reasons);
......
......@@ -5,6 +5,7 @@ import org.onlab.onos.event.Event;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.net.topology.LinkWeight;
import org.onlab.onos.net.topology.TopoEdge;
import org.onlab.onos.net.topology.TopoVertex;
......@@ -50,8 +51,8 @@ class SimpleTopologyStore {
* @param topology topology descriptor
* @return set of clusters
*/
Set<TopologyCluster> getClusters(Topology topology) {
return null;
Set<TopologyCluster> getClusters(DefaultTopology topology) {
return topology.getClusters();
}
/**
......@@ -60,8 +61,8 @@ class SimpleTopologyStore {
* @param topology topology descriptor
* @return graph view
*/
Graph<TopoVertex, TopoEdge> getGraph(Topology topology) {
return null;
Graph<TopoVertex, TopoEdge> getGraph(DefaultTopology topology) {
return topology.getGraph();
}
/**
......@@ -72,8 +73,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,7 +86,7 @@ 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;
}
......@@ -97,8 +98,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 +109,34 @@ 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 providerId provider identification
* @param topoDescription topology 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,
TopologyDescription topoDescription,
List<Event> reasons) {
return null;
// First off, make sure that what we're given is indeed newer than
// what we already have.
if (current != null && topoDescription.timestamp() < current.time()) {
return null;
}
// Have the default topology construct self from the description data.
DefaultTopology newTopology =
new DefaultTopology(providerId, topoDescription);
// Promote the new topology to current and return a ready-to-send event.
current = newTopology;
return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED, current);
}
}
......
package org.onlab.onos.net.trivial.topology.provider.impl;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Maps;
......@@ -26,6 +27,7 @@ import java.util.Map;
import java.util.Set;
import static com.google.common.collect.ImmutableSetMultimap.Builder;
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;
......@@ -43,13 +45,12 @@ class DefaultTopologyDescription implements TopologyDescription {
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 ImmutableMap<DeviceId, Result<TopoVertex, TopoEdge>> results;
// Secondary look-up indexes
private ImmutableSetMultimap<ClusterId, DeviceId> devicesByCluster;
private ImmutableSetMultimap<ClusterId, Link> linksByCluster;
private Map<DeviceId, TopologyCluster> clustersByDevice = Maps.newHashMap();
// Cluster-related structures
private final ImmutableSet<TopologyCluster> clusters;
private ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
private ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
/**
* Creates a topology description to carry topology vitals to the core.
......@@ -76,31 +77,25 @@ class DefaultTopologyDescription implements TopologyDescription {
}
@Override
public Result<TopoVertex, TopoEdge> pathResults(DeviceId srcDeviceId) {
return results.get(srcDeviceId);
}
@Override
public Set<TopologyCluster> clusters() {
return ImmutableSet.copyOf(clusters.values());
public ImmutableMap<DeviceId, Result<TopoVertex, TopoEdge>> pathsBySource() {
return results;
}
@Override
public Set<DeviceId> clusterDevices(TopologyCluster cluster) {
return devicesByCluster.get(cluster.id());
public ImmutableSet<TopologyCluster> clusters() {
return clusters;
}
@Override
public Set<Link> clusterLinks(TopologyCluster cluster) {
return linksByCluster.get(cluster.id());
public ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster() {
return devicesByCluster;
}
@Override
public TopologyCluster clusterFor(DeviceId deviceId) {
return clustersByDevice.get(deviceId);
public ImmutableSetMultimap<TopologyCluster, Link> linksByCluster() {
return linksByCluster;
}
// 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 {
......@@ -156,29 +151,31 @@ class DefaultTopologyDescription implements TopologyDescription {
// 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() {
private ImmutableMap<DeviceId, Result<TopoVertex, TopoEdge>> computeDefaultPaths() {
LinkWeight weight = new HopCountLinkWeight(graph.getVertexes().size());
Map<DeviceId, Result<TopoVertex, TopoEdge>> results = Maps.newHashMap();
ImmutableMap.Builder<DeviceId, Result<TopoVertex, TopoEdge>> results =
ImmutableMap.builder();
// 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;
return results.build();
}
// 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());
private ImmutableSet<TopologyCluster> computeClusters() {
ImmutableSet.Builder<TopologyCluster> clusterBuilder = ImmutableSet.builder();
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();
Builder<TopologyCluster, DeviceId> devicesBuilder = ImmutableSetMultimap.builder();
Builder<TopologyCluster, 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++) {
......@@ -189,10 +186,11 @@ class DefaultTopologyDescription implements TopologyDescription {
new DefaultTopologyCluster(ClusterId.clusterId(i),
vertexSet.size(), edgeSet.size(),
findRoot(vertexSet).deviceId());
clusterBuilder.add(cluster);
findClusterDevices(vertexSet, cluster, devicesBuilder);
findClusterLinks(edgeSet, cluster, linksBuilder);
}
return clusters;
return clusterBuilder.build();
}
// Scans through the set of cluster vertexes and puts their devices in a
......@@ -200,11 +198,10 @@ class DefaultTopologyDescription implements TopologyDescription {
// the cluster.
private void findClusterDevices(Set<TopoVertex> vertexSet,
DefaultTopologyCluster cluster,
Builder<ClusterId, DeviceId> builder) {
Builder<TopologyCluster, DeviceId> builder) {
for (TopoVertex vertex : vertexSet) {
DeviceId deviceId = vertex.deviceId();
builder.put(cluster.id(), deviceId);
clustersByDevice.put(deviceId, cluster);
builder.put(cluster, deviceId);
}
}
......@@ -212,9 +209,9 @@ class DefaultTopologyDescription implements TopologyDescription {
// multi-map associated with the cluster.
private void findClusterLinks(Set<TopoEdge> edgeSet,
DefaultTopologyCluster cluster,
Builder<ClusterId, Link> builder) {
Builder<TopologyCluster, Link> builder) {
for (TopoEdge edge : edgeSet) {
builder.put(cluster.id(), edge.link());
builder.put(cluster, edge.link());
}
}
......
......@@ -90,6 +90,8 @@ public class SimpleTopologyProvider extends AbstractProvider
@Deactivate
public synchronized void deactivate() {
isStarted = false;
deviceService.removeListener(deviceListener);
linkService.removeListener(linkListener);
providerRegistry.unregister(this);
......@@ -98,7 +100,6 @@ public class SimpleTopologyProvider extends AbstractProvider
executor.shutdownNow();
executor = null;
isStarted = false;
log.info("Stopped");
}
......@@ -108,7 +109,7 @@ 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) {
executor.execute(new TopologyBuilderTask(reasons));
}
......