Abhishek Dwaraki
Committed by Ray Milkey

ONOS-400 Topology creation and up time formatting fixes

Change-Id: Iaf6d4dbbc1c7eaae9465a2d931d40f07a75ad07d
......@@ -15,7 +15,11 @@
*/
package org.onosproject.cli.net;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.onosproject.cli.AbstractShellCommand;
......@@ -23,18 +27,20 @@ import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyProvider;
import org.onosproject.net.topology.TopologyService;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Lists summary of the current topology.
*/
@Command(scope = "onos", name = "topology",
description = "Lists summary of the current 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";
private static final String FMT = "created=%s, uptime=%s, devices=%d, links=%d, clusters=%d";
@Option(name = "-r", aliases = "--recompute", description = "Trigger topology re-computation",
required = false, multiValued = false)
@Option(name = "-r", aliases = "--recompute",
description = "Trigger topology re-computation", required = false,
multiValued = false)
private boolean recompute = false;
protected TopologyService service;
......@@ -51,19 +57,75 @@ public class TopologyCommand extends AbstractShellCommand {
@Override
protected void execute() {
init();
long topologyUptime =
Math.max(0, (System.currentTimeMillis() - topology.creationTime()));
if (recompute) {
get(TopologyProvider.class).triggerRecompute();
} else if (outputJson()) {
print("%s", new ObjectMapper().createObjectNode()
.put("time", topology.time())
.put("deviceCount", topology.deviceCount())
.put("linkCount", topology.linkCount())
.put("clusterCount", topology.clusterCount()));
print("%s",
new ObjectMapper()
.createObjectNode()
.put("time", topology.time())
.put("created", formatCreationTime(topology.creationTime()))
.put("uptime", formatElapsedTime(topologyUptime))
.put("deviceCount", topology.deviceCount())
.put("linkCount", topology.linkCount())
.put("clusterCount", topology.clusterCount()));
} else {
print(FMT, topology.time(), topology.deviceCount(), topology.linkCount(),
topology.clusterCount());
print(FMT, formatCreationTime(topology.creationTime()),
formatElapsedTime(topologyUptime),
topology.deviceCount(), topology.linkCount(),
topology.clusterCount());
}
}
/**
* Converts millis to a formatted elapsed time string.
*
* @param millis Duration in millis to convert to a string
*
* @return Formatted string: "D days, H hrs, M mins, S secs".
*/
private static String formatElapsedTime(long millis) {
if (millis < 0) {
throw new IllegalArgumentException("Interval less than zero. "
+ "Possible unsynchronized timestamps");
}
final long days = TimeUnit.MILLISECONDS.toDays(millis);
millis -= TimeUnit.DAYS.toMillis(days);
final long hours = TimeUnit.MILLISECONDS.toHours(millis);
millis -= TimeUnit.HOURS.toMillis(hours);
final long minutes = TimeUnit.MILLISECONDS.toMinutes(millis);
millis -= TimeUnit.MINUTES.toMillis(minutes);
final long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
final StringBuilder topologyUptimeString = new StringBuilder(64);
topologyUptimeString.append(days);
topologyUptimeString.append(" days, ");
topologyUptimeString.append(hours);
topologyUptimeString.append(" hrs, ");
topologyUptimeString.append(minutes);
topologyUptimeString.append(" mins, ");
topologyUptimeString.append(seconds);
topologyUptimeString.append(" secs");
return (topologyUptimeString.toString());
}
/**
* Converts millis to a formatted Date String.
*
* @param millis Duration in millis to convert to a string
*
* @return Formatted string: yyyy-MM-dd HH:mm:ss.
*/
private static String formatCreationTime(long millis) {
final DateFormat dateFormatter =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(millis);
return (dateFormatter.format(calendar.getTime()));
}
}
......
......@@ -34,30 +34,60 @@ import com.google.common.collect.Maps;
* Default implementation of an immutable topology graph data carrier.
*/
public class DefaultGraphDescription extends AbstractDescription
implements GraphDescription {
implements GraphDescription {
private static final Logger log = getLogger(DefaultGraphDescription.class);
private final long nanos;
private final long creationTime;
private final ImmutableSet<TopologyVertex> vertexes;
private final ImmutableSet<TopologyEdge> edges;
private final Map<DeviceId, TopologyVertex> vertexesById = Maps.newHashMap();
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
* @param nanos time in nanos of when the topology description was created
*
* @param devices collection of infrastructure devices
*
* @param links collection of infrastructure links
*
* @param annotations optional key/value annotations map
*
*/
@Deprecated
public DefaultGraphDescription(long nanos, Iterable<Device> devices,
Iterable<Link> links,
SparseAnnotations... annotations) {
Iterable<Link> links,
SparseAnnotations... annotations) {
this(nanos, System.currentTimeMillis(), devices, links, annotations);
}
/**
* 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 millis time in millis of when the topology description was created
*
* @param devices collection of infrastructure devices
*
* @param links collection of infrastructure links
*
* @param annotations optional key/value annotations map
*
*/
public DefaultGraphDescription(long nanos, long millis,
Iterable<Device> devices,
Iterable<Link> links,
SparseAnnotations... annotations) {
super(annotations);
this.nanos = nanos;
this.creationTime = millis;
this.vertexes = buildVertexes(devices);
this.edges = buildEdges(links);
vertexesById.clear();
......@@ -69,6 +99,11 @@ public class DefaultGraphDescription extends AbstractDescription
}
@Override
public long creationTime() {
return creationTime;
}
@Override
public ImmutableSet<TopologyVertex> vertexes() {
return vertexes;
}
......@@ -79,7 +114,8 @@ public class DefaultGraphDescription extends AbstractDescription
}
// Builds a set of topology vertexes from the specified list of devices
private ImmutableSet<TopologyVertex> buildVertexes(Iterable<Device> devices) {
private ImmutableSet<TopologyVertex>
buildVertexes(Iterable<Device> devices) {
ImmutableSet.Builder<TopologyVertex> vertexes = ImmutableSet.builder();
for (Device device : devices) {
TopologyVertex vertex = new DefaultTopologyVertex(device.id());
......@@ -95,7 +131,8 @@ public class DefaultGraphDescription extends AbstractDescription
for (Link link : links) {
try {
edges.add(new DefaultTopologyEdge(vertexOf(link.src()),
vertexOf(link.dst()), link));
vertexOf(link.dst()),
link));
} catch (IllegalArgumentException e) {
log.debug("Ignoring {}, missing vertex", link);
}
......
......@@ -15,9 +15,10 @@
*/
package org.onosproject.net.topology;
import com.google.common.collect.ImmutableSet;
import org.onosproject.net.Description;
import com.google.common.collect.ImmutableSet;
/**
* Describes attribute(s) of a network graph.
*/
......@@ -32,6 +33,14 @@ public interface GraphDescription extends Description {
long timestamp();
/**
* Returns the creation timestamp of the graph description. This is
* expressed in system millis to allow proper date and time formatting.
*
* @return graph description creation timestamp in millis
*/
long creationTime();
/**
* Returns the set of topology graph vertexes.
*
* @return set of graph vertexes
......@@ -46,4 +55,3 @@ public interface GraphDescription extends Description {
ImmutableSet<TopologyEdge> edges();
}
......
......@@ -23,16 +23,24 @@ import org.onosproject.net.Provided;
public interface Topology extends Provided {
/**
* Returns the time, specified in system nanos of when the topology
* became available.
* Returns the time, specified in system nanos of when the topology became
* available.
*
* @return time in system nanos
*/
long time();
/**
* Returns the time, specified in system nanos of how long the topology
* took to compute.
* Returns the time, specified in system millis of when the topology became
* available.
*
* @return time in system nanos
*/
long creationTime();
/**
* Returns the time, specified in system nanos of how long the topology took
* to compute.
*
* @return elapsed time in system nanos
*/
......@@ -53,7 +61,6 @@ public interface Topology extends Provided {
*/
int deviceCount();
/**
* Returns the number of infrastructure links in the topology.
*
......
......@@ -15,7 +15,21 @@
*/
package org.onosproject.net.topology.impl;
import com.google.common.collect.ImmutableList;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.concurrent.Executors.newFixedThreadPool;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Collections;
import java.util.Dictionary;
import java.util.List;
import java.util.Timer;
import java.util.concurrent.ExecutorService;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -42,28 +56,16 @@ import org.onosproject.net.topology.TopologyProviderService;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import java.util.Collections;
import java.util.Dictionary;
import java.util.List;
import java.util.Timer;
import java.util.concurrent.ExecutorService;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.concurrent.Executors.newFixedThreadPool;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
import static org.onosproject.net.device.DeviceEvent.Type.*;
import static org.slf4j.LoggerFactory.getLogger;
import com.google.common.collect.ImmutableList;
/**
* Default implementation of a network topology provider that feeds off
* device and link subsystem events to trigger assembly and computation of
* new topology snapshots.
* 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)
@Service
public class DefaultTopologyProvider extends AbstractProvider
implements TopologyProvider {
public class DefaultTopologyProvider extends AbstractProvider implements TopologyProvider {
private static final int MAX_THREADS = 8;
private static final int DEFAULT_MAX_EVENTS = 1000;
......@@ -71,7 +73,8 @@ public class DefaultTopologyProvider extends AbstractProvider
private static final int DEFAULT_MAX_BATCH_MS = 50;
// FIXME: Replace with a system-wide timer instance;
// TODO: Convert to use HashedWheelTimer or produce a variant of that; then decide which we want to adopt
// TODO: Convert to use HashedWheelTimer or produce a variant of that; then
// decide which we want to adopt
private static final Timer TIMER = new Timer("onos-topo-event-batching");
@Property(name = "maxEvents", intValue = DEFAULT_MAX_EVENTS,
......@@ -100,8 +103,8 @@ public class DefaultTopologyProvider extends AbstractProvider
private volatile boolean isStarted = false;
private TopologyProviderService providerService;
private DeviceListener deviceListener = new InternalDeviceListener();
private LinkListener linkListener = new InternalLinkListener();
private final DeviceListener deviceListener = new InternalDeviceListener();
private final LinkListener linkListener = new InternalLinkListener();
private Accumulator<Event> accumulator;
private ExecutorService executor;
......@@ -115,7 +118,8 @@ public class DefaultTopologyProvider extends AbstractProvider
@Activate
public synchronized void activate(ComponentContext context) {
executor = newFixedThreadPool(MAX_THREADS, groupedThreads("onos/topo", "build-%d"));
executor = newFixedThreadPool(MAX_THREADS,
groupedThreads("onos/topo", "build-%d"));
accumulator = new TopologyChangeAccumulator();
logConfig("Configured");
......@@ -171,21 +175,23 @@ public class DefaultTopologyProvider extends AbstractProvider
newMaxIdleMs = DEFAULT_MAX_IDLE_MS;
}
if (newMaxEvents != maxEvents || newMaxBatchMs != maxBatchMs || newMaxIdleMs != maxIdleMs) {
if ((newMaxEvents != maxEvents) || (newMaxBatchMs != maxBatchMs)
|| (newMaxIdleMs != maxIdleMs)) {
maxEvents = newMaxEvents;
maxBatchMs = newMaxBatchMs;
maxIdleMs = newMaxIdleMs;
accumulator = maxEvents > 1 ? new TopologyChangeAccumulator() : null;
accumulator = maxEvents > 1 ? new TopologyChangeAccumulator()
: null;
logConfig("Reconfigured");
}
}
private void logConfig(String prefix) {
log.info("{} with maxEvents = {}; maxBatchMs = {}; maxIdleMs = {}; accumulator={}",
prefix, maxEvents, maxBatchMs, maxIdleMs, accumulator != null);
log.info(
"{} with maxEvents = {}; maxBatchMs = {}; maxIdleMs = {}; accumulator={}",
prefix, maxEvents, maxBatchMs, maxIdleMs, accumulator != null);
}
@Override
public void triggerRecompute() {
triggerTopologyBuild(Collections.<Event>emptyList());
......@@ -195,7 +201,8 @@ public class DefaultTopologyProvider extends AbstractProvider
* Triggers assembly of topology data citing the specified events as the
* reason.
*
* @param reasons events which triggered the topology change
* @param reasons
* events which triggered the topology change
*/
private synchronized void triggerTopologyBuild(List<Event> reasons) {
if (executor != null) {
......@@ -209,8 +216,9 @@ public class DefaultTopologyProvider extends AbstractProvider
if (isStarted) {
GraphDescription desc =
new DefaultGraphDescription(System.nanoTime(),
deviceService.getAvailableDevices(),
linkService.getActiveLinks());
System.currentTimeMillis(),
deviceService.getAvailableDevices(),
linkService.getActiveLinks());
providerService.topologyChanged(desc, reasons);
}
}
......@@ -228,8 +236,8 @@ public class DefaultTopologyProvider extends AbstractProvider
@Override
public void event(DeviceEvent event) {
DeviceEvent.Type type = event.type();
if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
type == DEVICE_AVAILABILITY_CHANGED) {
if ((type == DEVICE_ADDED) || (type == DEVICE_REMOVED) ||
(type == DEVICE_AVAILABILITY_CHANGED)) {
processEvent(event);
}
}
......@@ -268,7 +276,8 @@ public class DefaultTopologyProvider extends AbstractProvider
try {
buildTopology(reasons);
} catch (Exception e) {
log.warn("Unable to compute topology due to: {}", e.getMessage());
log.warn("Unable to compute topology due to: {}",
e.getMessage());
log.debug("Unable to compute topology", e);
}
}
......
......@@ -15,11 +15,18 @@
*/
package org.onosproject.store.topology.impl;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import static com.google.common.base.MoreObjects.toStringHelper;
import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
import static org.onosproject.net.Link.State.ACTIVE;
import static org.onosproject.net.Link.State.INACTIVE;
import static org.onosproject.net.Link.Type.INDIRECT;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.onlab.graph.DijkstraGraphSearch;
import org.onlab.graph.GraphPathSearch;
import org.onlab.graph.GraphPathSearch.Result;
......@@ -43,32 +50,25 @@ import org.onosproject.net.topology.TopologyEdge;
import org.onosproject.net.topology.TopologyGraph;
import org.onosproject.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.ALL_PATHS;
import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
import static org.onosproject.net.Link.State.ACTIVE;
import static org.onosproject.net.Link.State.INACTIVE;
import static org.onosproject.net.Link.Type.INDIRECT;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSetMultimap.Builder;
// FIXME: Move to onos-core-common when ready
/**
* Default implementation of the topology descriptor. This carries the
* backing topology data.
* Default implementation of the topology descriptor. This carries the backing
* topology data.
*/
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 DijkstraGraphSearch<TopologyVertex, TopologyEdge> DIJKSTRA = new DijkstraGraphSearch<>();
private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN = new TarjanGraphSearch<>();
private final long time;
private final long creationTime;
private final long computeCost;
private final TopologyGraph graph;
......@@ -83,16 +83,19 @@ public class DefaultTopology extends AbstractModel implements Topology {
/**
* Creates a topology descriptor attributed to the specified provider.
*
* @param providerId identity of the provider
* @param description data describing the new topology
* @param providerId
* identity of the provider
* @param description
* data describing the new topology
*/
DefaultTopology(ProviderId providerId, GraphDescription description) {
super(providerId);
this.time = description.timestamp();
this.creationTime = description.creationTime();
// Build the graph
this.graph = new DefaultTopologyGraph(description.vertexes(),
description.edges());
description.edges());
this.clusterResults = Suppliers.memoize(() -> searchForClusters());
this.clusters = Suppliers.memoize(() -> buildTopologyClusters());
......@@ -101,7 +104,8 @@ public class DefaultTopology extends AbstractModel implements Topology {
this.weight = new HopCountLinkWeight(graph.getVertexes().size());
this.broadcastSets = Suppliers.memoize(() -> buildBroadcastSets());
this.infrastructurePoints = Suppliers.memoize(() -> findInfrastructurePoints());
this.infrastructurePoints = Suppliers
.memoize(() -> findInfrastructurePoints());
this.computeCost = Math.max(0, System.nanoTime() - time);
}
......@@ -111,6 +115,11 @@ public class DefaultTopology extends AbstractModel implements Topology {
}
@Override
public long creationTime() {
return creationTime;
}
@Override
public long computeCost() {
return computeCost;
}
......@@ -164,6 +173,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Returns the specified topology cluster.
*
* @param clusterId cluster identifier
*
* @return topology cluster
*/
TopologyCluster getCluster(ClusterId clusterId) {
......@@ -174,6 +184,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Returns the topology cluster that contains the given device.
*
* @param deviceId device identifier
*
* @return topology cluster
*/
TopologyCluster getCluster(DeviceId deviceId) {
......@@ -184,6 +195,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Returns the set of cluster devices.
*
* @param cluster topology cluster
*
* @return cluster devices
*/
Set<DeviceId> getClusterDevices(TopologyCluster cluster) {
......@@ -194,6 +206,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Returns the set of cluster links.
*
* @param cluster topology cluster
*
* @return cluster links
*/
Set<Link> getClusterLinks(TopologyCluster cluster) {
......@@ -204,6 +217,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Indicates whether the given point is an infrastructure link end-point.
*
* @param connectPoint connection point
*
* @return true if infrastructure
*/
boolean isInfrastructure(ConnectPoint connectPoint) {
......@@ -214,6 +228,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Indicates whether the given point is part of a broadcast set.
*
* @param connectPoint connection point
*
* @return true if in broadcast set
*/
boolean isBroadcastPoint(ConnectPoint connectPoint) {
......@@ -225,19 +240,21 @@ public class DefaultTopology extends AbstractModel implements Topology {
// 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());
throw new IllegalArgumentException("No cluster found for device "
+ connectPoint.deviceId());
}
// If the broadcast set is null or empty, or if the point explicitly
// belongs to it, return true;
Set<ConnectPoint> points = broadcastSets.get().get(cluster.id());
return points == null || points.isEmpty() || points.contains(connectPoint);
return (points == null) || points.isEmpty() || points.contains(connectPoint);
}
/**
* Returns the size of the cluster broadcast set.
*
* @param clusterId cluster identifier
*
* @return size of the cluster broadcast set
*/
int broadcastSetSize(ClusterId clusterId) {
......@@ -249,7 +266,9 @@ public class DefaultTopology extends AbstractModel implements Topology {
* destination devices.
*
* @param src source device
*
* @param dst destination device
*
* @return set of shortest paths
*/
Set<Path> getPaths(DeviceId src, DeviceId dst) {
......@@ -260,9 +279,12 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Computes on-demand the set of shortest paths between source and
* destination devices.
*
* @param src source device
* @param dst destination device
* @param src source device
*
* @param dst destination device
*
* @param weight link weight function
*
* @return set of shortest paths
*/
Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeight weight) {
......@@ -283,7 +305,6 @@ public class DefaultTopology extends AbstractModel implements Topology {
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<>();
......@@ -293,7 +314,6 @@ public class DefaultTopology extends AbstractModel implements Topology {
return new DefaultPath(CORE_PROVIDER_ID, links, path.cost());
}
// Searches for SCC clusters in the network topology graph using Tarjan
// algorithm.
private SCCResult<TopologyVertex, TopologyEdge> searchForClusters() {
......@@ -315,9 +335,10 @@ public class DefaultTopology extends AbstractModel implements Topology {
Set<TopologyEdge> edgeSet = clusterEdges.get(i);
ClusterId cid = ClusterId.clusterId(i);
DefaultTopologyCluster cluster =
new DefaultTopologyCluster(cid, vertexSet.size(), edgeSet.size(),
findRoot(vertexSet));
DefaultTopologyCluster cluster = new DefaultTopologyCluster(cid,
vertexSet.size(),
edgeSet.size(),
findRoot(vertexSet));
clusterBuilder.put(cid, cluster);
}
return clusterBuilder.build();
......@@ -328,9 +349,8 @@ public class DefaultTopology extends AbstractModel implements Topology {
private TopologyVertex findRoot(Set<TopologyVertex> vertexSet) {
TopologyVertex minVertex = null;
for (TopologyVertex vertex : vertexSet) {
if (minVertex == null ||
minVertex.deviceId().toString()
.compareTo(minVertex.deviceId().toString()) < 0) {
if ((minVertex == null) || (minVertex.deviceId()
.toString().compareTo(minVertex.deviceId().toString()) < 0)) {
minVertex = vertex;
}
}
......@@ -339,7 +359,8 @@ public class DefaultTopology extends AbstractModel implements Topology {
// Processes a map of broadcast sets for each cluster.
private ImmutableSetMultimap<ClusterId, ConnectPoint> buildBroadcastSets() {
Builder<ClusterId, ConnectPoint> builder = ImmutableSetMultimap.builder();
Builder<ClusterId, ConnectPoint> builder = ImmutableSetMultimap
.builder();
for (TopologyCluster cluster : clusters.get().values()) {
addClusterBroadcastSet(cluster, builder);
}
......@@ -349,11 +370,9 @@ public class DefaultTopology extends AbstractModel implements Topology {
// 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) {
private void addClusterBroadcastSet(TopologyCluster cluster, Builder<ClusterId, ConnectPoint> builder) {
// Use the graph root search results to build the broadcast set.
Result<TopologyVertex, TopologyEdge> result =
DIJKSTRA.search(graph, cluster.root(), null, weight, 1);
Result<TopologyVertex, TopologyEdge> result = DIJKSTRA.search(graph, cluster.root(), null, weight, 1);
for (Map.Entry<TopologyVertex, Set<TopologyEdge>> entry : result.parents().entrySet()) {
TopologyVertex vertex = entry.getKey();
......@@ -389,9 +408,12 @@ public class DefaultTopology extends AbstractModel implements Topology {
// Builds cluster-devices, cluster-links and device-cluster indexes.
private ClusterIndexes 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();
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.get().values()) {
......@@ -428,8 +450,9 @@ public class DefaultTopology extends AbstractModel implements Topology {
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().state() == ACTIVE ?
(edge.link().type() == INDIRECT ? indirectLinkCost : 1) : -1;
return edge.link().state() ==
ACTIVE ? (edge.link().type() ==
INDIRECT ? indirectLinkCost : 1) : -1;
}
}
......@@ -437,7 +460,8 @@ public class DefaultTopology extends AbstractModel implements Topology {
private static class NoIndirectLinksWeight implements LinkWeight {
@Override
public double weight(TopologyEdge edge) {
return edge.link().state() == INACTIVE || edge.link().type() == INDIRECT ? -1 : 1;
return (edge.link().state() == INACTIVE)
|| (edge.link().type() == INDIRECT) ? -1 : 1;
}
}
......@@ -446,9 +470,10 @@ public class DefaultTopology extends AbstractModel implements Topology {
final ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
final ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
public ClusterIndexes(ImmutableMap<DeviceId, TopologyCluster> clustersByDevice,
ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster,
ImmutableSetMultimap<TopologyCluster, Link> linksByCluster) {
public ClusterIndexes(
ImmutableMap<DeviceId, TopologyCluster> clustersByDevice,
ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster,
ImmutableSetMultimap<TopologyCluster, Link> linksByCluster) {
this.clustersByDevice = clustersByDevice;
this.devicesByCluster = devicesByCluster;
this.linksByCluster = linksByCluster;
......@@ -459,10 +484,10 @@ public class DefaultTopology extends AbstractModel implements Topology {
public String toString() {
return toStringHelper(this)
.add("time", time)
.add("creationTime", creationTime)
.add("computeCost", computeCost)
.add("clusters", clusterCount())
.add("devices", deviceCount())
.add("links", linkCount())
.toString();
.add("links", linkCount()).toString();
}
}
......
......@@ -15,11 +15,18 @@
*/
package org.onosproject.store.trivial.impl;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import static com.google.common.base.MoreObjects.toStringHelper;
import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
import static org.onosproject.net.Link.State.ACTIVE;
import static org.onosproject.net.Link.State.INACTIVE;
import static org.onosproject.net.Link.Type.INDIRECT;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.onlab.graph.DijkstraGraphSearch;
import org.onlab.graph.GraphPathSearch;
import org.onlab.graph.GraphPathSearch.Result;
......@@ -43,32 +50,25 @@ import org.onosproject.net.topology.TopologyEdge;
import org.onosproject.net.topology.TopologyGraph;
import org.onosproject.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.ALL_PATHS;
import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
import static org.onosproject.net.Link.State.ACTIVE;
import static org.onosproject.net.Link.State.INACTIVE;
import static org.onosproject.net.Link.Type.INDIRECT;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSetMultimap.Builder;
// FIXME: Move to onos-core-common when ready
/**
* Default implementation of the topology descriptor. This carries the
* backing topology data.
* Default implementation of the topology descriptor. This carries the backing
* topology data.
*/
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 DijkstraGraphSearch<TopologyVertex, TopologyEdge> DIJKSTRA = new DijkstraGraphSearch<>();
private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN = new TarjanGraphSearch<>();
private final long time;
private final long creationTime;
private final long computeCost;
private final TopologyGraph graph;
......@@ -83,16 +83,19 @@ public class DefaultTopology extends AbstractModel implements Topology {
/**
* Creates a topology descriptor attributed to the specified provider.
*
* @param providerId identity of the provider
* @param description data describing the new topology
* @param providerId
* identity of the provider
* @param description
* data describing the new topology
*/
DefaultTopology(ProviderId providerId, GraphDescription description) {
super(providerId);
this.time = description.timestamp();
this.creationTime = description.creationTime();
// Build the graph
this.graph = new DefaultTopologyGraph(description.vertexes(),
description.edges());
description.edges());
this.clusterResults = Suppliers.memoize(() -> searchForClusters());
this.clusters = Suppliers.memoize(() -> buildTopologyClusters());
......@@ -101,7 +104,8 @@ public class DefaultTopology extends AbstractModel implements Topology {
this.weight = new HopCountLinkWeight(graph.getVertexes().size());
this.broadcastSets = Suppliers.memoize(() -> buildBroadcastSets());
this.infrastructurePoints = Suppliers.memoize(() -> findInfrastructurePoints());
this.infrastructurePoints = Suppliers
.memoize(() -> findInfrastructurePoints());
this.computeCost = Math.max(0, System.nanoTime() - time);
}
......@@ -111,6 +115,11 @@ public class DefaultTopology extends AbstractModel implements Topology {
}
@Override
public long creationTime() {
return creationTime;
}
@Override
public long computeCost() {
return computeCost;
}
......@@ -164,6 +173,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Returns the specified topology cluster.
*
* @param clusterId cluster identifier
*
* @return topology cluster
*/
TopologyCluster getCluster(ClusterId clusterId) {
......@@ -174,6 +184,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Returns the topology cluster that contains the given device.
*
* @param deviceId device identifier
*
* @return topology cluster
*/
TopologyCluster getCluster(DeviceId deviceId) {
......@@ -184,6 +195,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Returns the set of cluster devices.
*
* @param cluster topology cluster
*
* @return cluster devices
*/
Set<DeviceId> getClusterDevices(TopologyCluster cluster) {
......@@ -194,6 +206,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Returns the set of cluster links.
*
* @param cluster topology cluster
*
* @return cluster links
*/
Set<Link> getClusterLinks(TopologyCluster cluster) {
......@@ -204,6 +217,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Indicates whether the given point is an infrastructure link end-point.
*
* @param connectPoint connection point
*
* @return true if infrastructure
*/
boolean isInfrastructure(ConnectPoint connectPoint) {
......@@ -214,6 +228,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Indicates whether the given point is part of a broadcast set.
*
* @param connectPoint connection point
*
* @return true if in broadcast set
*/
boolean isBroadcastPoint(ConnectPoint connectPoint) {
......@@ -225,19 +240,21 @@ public class DefaultTopology extends AbstractModel implements Topology {
// 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());
throw new IllegalArgumentException("No cluster found for device "
+ connectPoint.deviceId());
}
// If the broadcast set is null or empty, or if the point explicitly
// belongs to it, return true;
Set<ConnectPoint> points = broadcastSets.get().get(cluster.id());
return points == null || points.isEmpty() || points.contains(connectPoint);
return (points == null) || points.isEmpty() || points.contains(connectPoint);
}
/**
* Returns the size of the cluster broadcast set.
*
* @param clusterId cluster identifier
*
* @return size of the cluster broadcast set
*/
int broadcastSetSize(ClusterId clusterId) {
......@@ -249,7 +266,9 @@ public class DefaultTopology extends AbstractModel implements Topology {
* destination devices.
*
* @param src source device
*
* @param dst destination device
*
* @return set of shortest paths
*/
Set<Path> getPaths(DeviceId src, DeviceId dst) {
......@@ -260,9 +279,12 @@ public class DefaultTopology extends AbstractModel implements Topology {
* Computes on-demand the set of shortest paths between source and
* destination devices.
*
* @param src source device
* @param dst destination device
* @param src source device
*
* @param dst destination device
*
* @param weight link weight function
*
* @return set of shortest paths
*/
Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeight weight) {
......@@ -283,7 +305,6 @@ public class DefaultTopology extends AbstractModel implements Topology {
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<>();
......@@ -293,7 +314,6 @@ public class DefaultTopology extends AbstractModel implements Topology {
return new DefaultPath(CORE_PROVIDER_ID, links, path.cost());
}
// Searches for SCC clusters in the network topology graph using Tarjan
// algorithm.
private SCCResult<TopologyVertex, TopologyEdge> searchForClusters() {
......@@ -315,9 +335,10 @@ public class DefaultTopology extends AbstractModel implements Topology {
Set<TopologyEdge> edgeSet = clusterEdges.get(i);
ClusterId cid = ClusterId.clusterId(i);
DefaultTopologyCluster cluster =
new DefaultTopologyCluster(cid, vertexSet.size(), edgeSet.size(),
findRoot(vertexSet));
DefaultTopologyCluster cluster = new DefaultTopologyCluster(cid,
vertexSet.size(),
edgeSet.size(),
findRoot(vertexSet));
clusterBuilder.put(cid, cluster);
}
return clusterBuilder.build();
......@@ -328,9 +349,8 @@ public class DefaultTopology extends AbstractModel implements Topology {
private TopologyVertex findRoot(Set<TopologyVertex> vertexSet) {
TopologyVertex minVertex = null;
for (TopologyVertex vertex : vertexSet) {
if (minVertex == null ||
minVertex.deviceId().toString()
.compareTo(minVertex.deviceId().toString()) < 0) {
if ((minVertex == null) || (minVertex.deviceId().toString()
.compareTo(minVertex.deviceId().toString()) < 0)) {
minVertex = vertex;
}
}
......@@ -349,8 +369,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
// 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) {
private void addClusterBroadcastSet(TopologyCluster cluster, Builder<ClusterId, ConnectPoint> builder) {
// Use the graph root search results to build the broadcast set.
Result<TopologyVertex, TopologyEdge> result =
DIJKSTRA.search(graph, cluster.root(), null, weight, 1);
......@@ -389,9 +408,12 @@ public class DefaultTopology extends AbstractModel implements Topology {
// Builds cluster-devices, cluster-links and device-cluster indexes.
private ClusterIndexes 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();
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.get().values()) {
......@@ -411,8 +433,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
// Finalize all indexes.
return new ClusterIndexes(clusterBuilder.build(),
devicesBuilder.build(),
linksBuilder.build());
devicesBuilder.build(), linksBuilder.build());
}
// Link weight for measuring link cost as hop count with indirect links
......@@ -428,8 +449,9 @@ public class DefaultTopology extends AbstractModel implements Topology {
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().state() == ACTIVE ?
(edge.link().type() == INDIRECT ? indirectLinkCost : 1) : -1;
return edge.link().state() ==
ACTIVE ? (edge.link().type() ==
INDIRECT ? indirectLinkCost : 1) : -1;
}
}
......@@ -437,7 +459,8 @@ public class DefaultTopology extends AbstractModel implements Topology {
private static class NoIndirectLinksWeight implements LinkWeight {
@Override
public double weight(TopologyEdge edge) {
return edge.link().state() == INACTIVE || edge.link().type() == INDIRECT ? -1 : 1;
return (edge.link().state() == INACTIVE)
|| (edge.link().type() == INDIRECT) ? -1 : 1;
}
}
......@@ -446,9 +469,10 @@ public class DefaultTopology extends AbstractModel implements Topology {
final ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
final ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
public ClusterIndexes(ImmutableMap<DeviceId, TopologyCluster> clustersByDevice,
ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster,
ImmutableSetMultimap<TopologyCluster, Link> linksByCluster) {
public ClusterIndexes(
ImmutableMap<DeviceId, TopologyCluster> clustersByDevice,
ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster,
ImmutableSetMultimap<TopologyCluster, Link> linksByCluster) {
this.clustersByDevice = clustersByDevice;
this.devicesByCluster = devicesByCluster;
this.linksByCluster = linksByCluster;
......@@ -459,10 +483,10 @@ public class DefaultTopology extends AbstractModel implements Topology {
public String toString() {
return toStringHelper(this)
.add("time", time)
.add("created", creationTime)
.add("computeCost", computeCost)
.add("clusters", clusterCount())
.add("devices", deviceCount())
.add("links", linkCount())
.toString();
.add("links", linkCount()).toString();
}
}
......
......@@ -61,6 +61,11 @@ public class TopologyResourceTest extends ResourceTest {
}
@Override
public long creationTime() {
return 22222L;
}
@Override
public long computeCost() {
return 0;
}
......