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()
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(),
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 annotations optional key/value annotations map
*
*/
@Deprecated
public DefaultGraphDescription(long nanos, Iterable<Device> devices,
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={}",
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,6 +216,7 @@ public class DefaultTopologyProvider extends AbstractProvider
if (isStarted) {
GraphDescription desc =
new DefaultGraphDescription(System.nanoTime(),
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);
}
}
......
......@@ -61,6 +61,11 @@ public class TopologyResourceTest extends ResourceTest {
}
@Override
public long creationTime() {
return 22222L;
}
@Override
public long computeCost() {
return 0;
}
......