Dusan Pajin
Committed by Gerrit Code Review

Delta Port Statistics added to DeviceStore, Device Service and CLI

Changed misspelled word in description of the portstats command switch

Change-Id: I131940c83c5cb12080532a4804ec424ca66afa64
......@@ -16,8 +16,14 @@ package org.onosproject.cli.net;
* limitations under the License.
*/
import static org.onosproject.net.DeviceId.deviceId;
import java.util.concurrent.TimeUnit;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.apache.karaf.shell.commands.Option;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.device.PortStatistics;
......@@ -27,7 +33,20 @@ import org.onosproject.net.device.PortStatistics;
*/
@Command(scope = "onos", name = "portstats",
description = "Lists statistics of all ports in the system")
public class DevicePortStatsCommand extends AbstractShellCommand {
public class DevicePortStatsCommand extends DevicesListCommand {
@Option(name = "-d", aliases = "--delta", description = "Show Delta Port Statistics,"
+ "only for the last polling interval",
required = false, multiValued = false)
private boolean delta = false;
@Option(name = "-t", aliases = "--table", description = "Show human readable table format for statistics",
required = false, multiValued = false)
private boolean table = false;
@Argument(index = 0, name = "uri", description = "Device ID",
required = false, multiValued = false)
String uri = null;
private static final String FORMAT =
" port=%s, pktRx=%s, pktTx=%s, bytesRx=%s, bytesTx=%s, pktRxDrp=%s, pktTxDrp=%s, Dur=%s";
......@@ -36,16 +55,138 @@ public class DevicePortStatsCommand extends AbstractShellCommand {
protected void execute() {
DeviceService deviceService = get(DeviceService.class);
deviceService.getDevices().forEach(d ->
printPortStats(d.id(), deviceService.getPortStatistics(d.id()))
);
if (uri == null) {
for (Device d : getSortedDevices(deviceService)) {
if (delta) {
if (table) {
printPortStatsDeltaTable(d.id(), deviceService.getPortDeltaStatistics(d.id()));
} else {
printPortStatsDelta(d.id(), deviceService.getPortDeltaStatistics(d.id()));
}
} else {
printPortStats(d.id(), deviceService.getPortStatistics(d.id()));
}
}
} else {
Device d = deviceService.getDevice(deviceId(uri));
if (d == null) {
error("No such device %s", uri);
} else if (delta) {
if (table) {
printPortStatsDeltaTable(d.id(), deviceService.getPortDeltaStatistics(d.id()));
} else {
printPortStatsDelta(d.id(), deviceService.getPortDeltaStatistics(d.id()));
}
} else {
printPortStats(d.id(), deviceService.getPortStatistics(d.id()));
}
}
}
/**
* Prints Port Statistics.
*
* @param deviceId
* @param portStats
*/
private void printPortStats(DeviceId deviceId, Iterable<PortStatistics> portStats) {
print("deviceId=%s", deviceId);
for (PortStatistics stat : portStats) {
print(FORMAT, stat.port(), stat.packetsReceived(), stat.packetsSent(), stat.bytesReceived(),
stat.bytesSent(), stat.packetsRxDropped(), stat.packetsTxDropped(), stat.durationSec());
}
}
/**
* Prints Port delta statistics.
*
* @param deviceId
* @param portStats
*/
private void printPortStatsDelta(DeviceId deviceId, Iterable<PortStatistics> portStats) {
final String formatDelta = " port=%s, pktRx=%s, pktTx=%s, bytesRx=%s, bytesTx=%s,"
+ " rateRx=%s, rateTx=%s, pktRxDrp=%s, pktTxDrp=%s, interval=%s";
print("deviceId=%s", deviceId);
for (PortStatistics stat : portStats) {
float duration = ((float) stat.durationSec()) +
(((float) stat.durationNano()) / TimeUnit.SECONDS.toNanos(1));
float rateRx = stat.bytesReceived() * 8 / duration;
float rateTx = stat.bytesSent() * 8 / duration;
print(formatDelta, stat.port(),
stat.packetsReceived(),
stat.packetsSent(),
stat.bytesReceived(),
stat.bytesSent(),
String.format("%.1f", rateRx),
String.format("%.1f", rateTx),
stat.packetsRxDropped(),
stat.packetsTxDropped(),
String.format("%.3f", duration));
}
}
/**
* Prints human readable table with delta Port Statistics for specific device.
*
* @param deviceId
* @param portStats
*/
private void printPortStatsDeltaTable(DeviceId deviceId, Iterable<PortStatistics> portStats) {
final String formatDeltaTable = "|%5s | %7s | %7s | %7s | %7s | %7s | %7s | %7s | %7s |%9s |";
print("+---------------------------------------------------------------------------------------------------+");
print("| DeviceId = %s |", deviceId);
print("|---------------------------------------------------------------------------------------------------|");
print("| | Receive | Transmit | Time [s] |");
print("| Port | Packets | Bytes | Rate bps | Drop | Packets | Bytes | Rate bps | Drop | Interval |");
print("|---------------------------------------------------------------------------------------------------|");
for (PortStatistics stat : portStats) {
float duration = ((float) stat.durationSec()) +
(((float) stat.durationNano()) / TimeUnit.SECONDS.toNanos(1));
float rateRx = stat.bytesReceived() * 8 / duration;
float rateTx = stat.bytesSent() * 8 / duration;
print(formatDeltaTable, stat.port(),
humanReadable(stat.packetsReceived()),
humanReadable(stat.bytesReceived()),
humanReadableBps(rateRx),
humanReadable(stat.packetsRxDropped()),
humanReadable(stat.packetsSent()),
humanReadable(stat.bytesSent()),
humanReadableBps(rateTx),
humanReadable(stat.packetsTxDropped()),
String.format("%.3f", duration));
}
print("+---------------------------------------------------------------------------------------------------+");
}
/**
* Converts bytes to human readable string with Kilo, Mega, Giga, etc.
*
* @param bytes
* @return
*/
public static String humanReadable(long bytes) {
int unit = 1000;
if (bytes < unit) {
return String.format("%s ", bytes);
}
int exp = (int) (Math.log(bytes) / Math.log(unit));
Character pre = ("KMGTPE").charAt(exp - 1);
return String.format("%.2f%s", bytes / Math.pow(unit, exp), pre);
}
/**
* Converts bps to human readable format.
*
* @param bps
* @return
*/
public static String humanReadableBps(float bps) {
int unit = 1000;
if (bps < unit) {
return String.format("%.0f ", (float) bps);
}
int exp = (int) (Math.log(bps) / Math.log(unit));
Character pre = ("KMGTPE").charAt(exp - 1);
return String.format("%.2f%s", bps / Math.pow(unit, exp), pre);
}
}
......
......@@ -105,6 +105,14 @@ public interface DeviceService
List<PortStatistics> getPortStatistics(DeviceId deviceId);
/**
* Returns the list of port delta statistics associated with the device.
*
* @param deviceId device identitifer
* @return list of port statistics
*/
List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId);
/**
* Returns the port with the specified number and hosted by the given device.
*
* @param deviceId device identifier
......
......@@ -135,6 +135,14 @@ public interface DeviceStore extends Store<DeviceEvent, DeviceStoreDelegate> {
List<PortStatistics> getPortStatistics(DeviceId deviceId);
/**
* Returns the list of delta port statistics of the specified device.
*
* @param deviceId
* @return list of delta port statistics of all ports of the device
*/
List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId);
/**
* Returns the specified device port.
*
* @param deviceId device identifier
......@@ -159,5 +167,4 @@ public interface DeviceStore extends Store<DeviceEvent, DeviceStoreDelegate> {
*/
DeviceEvent removeDevice(DeviceId deviceId);
}
......
......@@ -75,6 +75,11 @@ public class DeviceServiceAdapter implements DeviceService {
}
@Override
public List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId) {
return null;
}
@Override
public Port getPort(DeviceId deviceId, PortNumber portNumber) {
return null;
}
......
......@@ -37,6 +37,7 @@ import org.onosproject.net.PortNumber;
import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DefaultPortStatistics;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceStore;
......@@ -62,6 +63,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static com.google.common.base.Preconditions.checkArgument;
......@@ -98,6 +100,8 @@ public class SimpleDeviceStore
devicePorts = Maps.newConcurrentMap();
private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, PortStatistics>>
devicePortStats = Maps.newConcurrentMap();
private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, PortStatistics>>
devicePortDeltaStats = Maps.newConcurrentMap();
// Available (=UP) devices
private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
......@@ -421,22 +425,62 @@ public class SimpleDeviceStore
@Override
public DeviceEvent updatePortStatistics(ProviderId providerId, DeviceId deviceId,
Collection<PortStatistics> portStats) {
Collection<PortStatistics> newStatsCollection) {
ConcurrentMap<PortNumber, PortStatistics> statsMap = devicePortStats.get(deviceId);
if (statsMap == null) {
statsMap = Maps.newConcurrentMap();
devicePortStats.put(deviceId, statsMap);
}
ConcurrentMap<PortNumber, PortStatistics> prvStatsMap = devicePortStats.get(deviceId);
ConcurrentMap<PortNumber, PortStatistics> newStatsMap = Maps.newConcurrentMap();
ConcurrentMap<PortNumber, PortStatistics> deltaStatsMap = Maps.newConcurrentMap();
for (PortStatistics stat: portStats) {
PortNumber portNumber = PortNumber.portNumber(stat.port());
statsMap.put(portNumber, stat);
if (prvStatsMap != null) {
for (PortStatistics newStats : newStatsCollection) {
PortNumber port = PortNumber.portNumber(newStats.port());
PortStatistics prvStats = prvStatsMap.get(port);
DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
PortStatistics deltaStats = builder.build();
if (prvStats != null) {
deltaStats = calcDeltaStats(deviceId, prvStats, newStats);
}
deltaStatsMap.put(port, deltaStats);
newStatsMap.put(port, newStats);
}
} else {
for (PortStatistics newStats : newStatsCollection) {
PortNumber port = PortNumber.portNumber(newStats.port());
newStatsMap.put(port, newStats);
}
}
devicePortDeltaStats.put(deviceId, deltaStatsMap);
devicePortStats.put(deviceId, newStatsMap);
return new DeviceEvent(PORT_STATS_UPDATED, devices.get(deviceId), null);
}
public PortStatistics calcDeltaStats(DeviceId deviceId, PortStatistics prvStats, PortStatistics newStats) {
// calculate time difference
long deltaStatsSec, deltaStatsNano;
if (newStats.durationNano() < prvStats.durationNano()) {
deltaStatsNano = newStats.durationNano() - prvStats.durationNano() + TimeUnit.SECONDS.toNanos(1);
deltaStatsSec = newStats.durationSec() - prvStats.durationSec() - 1L;
} else {
deltaStatsNano = newStats.durationNano() - prvStats.durationNano();
deltaStatsSec = newStats.durationSec() - prvStats.durationSec();
}
DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
DefaultPortStatistics deltaStats = builder.setDeviceId(deviceId)
.setPort(newStats.port())
.setPacketsReceived(newStats.packetsReceived() - prvStats.packetsReceived())
.setPacketsSent(newStats.packetsSent() - prvStats.packetsSent())
.setBytesReceived(newStats.bytesReceived() - prvStats.bytesReceived())
.setBytesSent(newStats.bytesSent() - prvStats.bytesSent())
.setPacketsRxDropped(newStats.packetsRxDropped() - prvStats.packetsRxDropped())
.setPacketsTxDropped(newStats.packetsTxDropped() - prvStats.packetsTxDropped())
.setPacketsRxErrors(newStats.packetsRxErrors() - prvStats.packetsRxErrors())
.setPacketsTxErrors(newStats.packetsTxErrors() - prvStats.packetsTxErrors())
.setDurationSec(deltaStatsSec)
.setDurationNano(deltaStatsNano)
.build();
return deltaStats;
}
@Override
public Port getPort(DeviceId deviceId, PortNumber portNumber) {
Map<PortNumber, Port> ports = devicePorts.get(deviceId);
......@@ -453,6 +497,15 @@ public class SimpleDeviceStore
}
@Override
public List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId) {
Map<PortNumber, PortStatistics> portStats = devicePortDeltaStats.get(deviceId);
if (portStats == null) {
return Collections.emptyList();
}
return ImmutableList.copyOf(portStats.values());
}
@Override
public boolean isAvailable(DeviceId deviceId) {
return availableDevices.contains(deviceId);
}
......
......@@ -196,6 +196,13 @@ public class DeviceManager
}
@Override
public List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId) {
checkPermission(Permission.DEVICE_READ);
checkNotNull(deviceId, DEVICE_ID_NULL);
return store.getPortDeltaStatistics(deviceId);
}
@Override
public Port getPort(DeviceId deviceId, PortNumber portNumber) {
checkPermission(Permission.DEVICE_READ);
checkNotNull(deviceId, DEVICE_ID_NULL);
......
......@@ -20,6 +20,7 @@ import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.RandomUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
......@@ -108,7 +109,6 @@ import static org.onlab.util.Tools.minPriority;
import static org.onosproject.cluster.ControllerNodeToNodeId.toNodeId;
import static org.onosproject.net.DefaultAnnotations.merge;
import static org.onosproject.net.device.DeviceEvent.Type.*;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
import static org.onosproject.store.device.impl.GossipDeviceStoreMessageSubjects.*;
import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.PUT;
import static org.slf4j.LoggerFactory.getLogger;
......@@ -139,6 +139,7 @@ public class GossipDeviceStore
private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap();
private EventuallyConsistentMap<DeviceId, Map<PortNumber, PortStatistics>> devicePortStats;
private EventuallyConsistentMap<DeviceId, Map<PortNumber, PortStatistics>> devicePortDeltaStats;
private final EventuallyConsistentMapListener<DeviceId, Map<PortNumber, PortStatistics>>
portStatsListener = new InternalPortStatsListener();
......@@ -246,6 +247,14 @@ public class GossipDeviceStore
.withTimestampProvider((k, v) -> new WallClockTimestamp())
.withTombstonesDisabled()
.build();
devicePortDeltaStats = storageService.<DeviceId, Map<PortNumber, PortStatistics>>
eventuallyConsistentMapBuilder()
.withName("port-stats-delta")
.withSerializer(deviceDataSerializer)
.withAntiEntropyPeriod(5, TimeUnit.SECONDS)
.withTimestampProvider((k, v) -> new WallClockTimestamp())
.withTombstonesDisabled()
.build();
devicePortStats.addListener(portStatsListener);
log.info("Started");
}
......@@ -253,6 +262,7 @@ public class GossipDeviceStore
@Deactivate
public void deactivate() {
devicePortStats.destroy();
devicePortDeltaStats.destroy();
executor.shutdownNow();
backgroundExecutor.shutdownNow();
......@@ -824,18 +834,68 @@ public class GossipDeviceStore
@Override
public DeviceEvent updatePortStatistics(ProviderId providerId, DeviceId deviceId,
Collection<PortStatistics> portStats) {
Map<PortNumber, PortStatistics> statsMap = devicePortStats.get(deviceId);
if (statsMap == null) {
statsMap = Maps.newHashMap();
}
Collection<PortStatistics> newStatsCollection) {
for (PortStatistics stat : portStats) {
PortNumber portNumber = PortNumber.portNumber(stat.port());
statsMap.put(portNumber, stat);
Map<PortNumber, PortStatistics> prvStatsMap = devicePortStats.get(deviceId);
Map<PortNumber, PortStatistics> newStatsMap = Maps.newHashMap();
Map<PortNumber, PortStatistics> deltaStatsMap = Maps.newHashMap();
if (prvStatsMap != null) {
for (PortStatistics newStats : newStatsCollection) {
PortNumber port = PortNumber.portNumber(newStats.port());
PortStatistics prvStats = prvStatsMap.get(port);
DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
PortStatistics deltaStats = builder.build();
if (prvStats != null) {
deltaStats = calcDeltaStats(deviceId, prvStats, newStats);
}
deltaStatsMap.put(port, deltaStats);
newStatsMap.put(port, newStats);
}
} else {
for (PortStatistics newStats : newStatsCollection) {
PortNumber port = PortNumber.portNumber(newStats.port());
newStatsMap.put(port, newStats);
}
}
devicePortDeltaStats.put(deviceId, deltaStatsMap);
devicePortStats.put(deviceId, newStatsMap);
return new DeviceEvent(PORT_STATS_UPDATED, devices.get(deviceId), null);
}
devicePortStats.put(deviceId, statsMap);
return null; // new DeviceEvent(PORT_STATS_UPDATED, devices.get(deviceId), null);
/**
* Calculate delta statistics by subtracting previous from new statistics.
*
* @param deviceId
* @param prvStats
* @param newStats
* @return PortStatistics
*/
public PortStatistics calcDeltaStats(DeviceId deviceId, PortStatistics prvStats, PortStatistics newStats) {
// calculate time difference
long deltaStatsSec, deltaStatsNano;
if (newStats.durationNano() < prvStats.durationNano()) {
deltaStatsNano = newStats.durationNano() - prvStats.durationNano() + TimeUnit.SECONDS.toNanos(1);
deltaStatsSec = newStats.durationSec() - prvStats.durationSec() - 1L;
} else {
deltaStatsNano = newStats.durationNano() - prvStats.durationNano();
deltaStatsSec = newStats.durationSec() - prvStats.durationSec();
}
DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
DefaultPortStatistics deltaStats = builder.setDeviceId(deviceId)
.setPort(newStats.port())
.setPacketsReceived(newStats.packetsReceived() - prvStats.packetsReceived())
.setPacketsSent(newStats.packetsSent() - prvStats.packetsSent())
.setBytesReceived(newStats.bytesReceived() - prvStats.bytesReceived())
.setBytesSent(newStats.bytesSent() - prvStats.bytesSent())
.setPacketsRxDropped(newStats.packetsRxDropped() - prvStats.packetsRxDropped())
.setPacketsTxDropped(newStats.packetsTxDropped() - prvStats.packetsTxDropped())
.setPacketsRxErrors(newStats.packetsRxErrors() - prvStats.packetsRxErrors())
.setPacketsTxErrors(newStats.packetsTxErrors() - prvStats.packetsTxErrors())
.setDurationSec(deltaStatsSec)
.setDurationNano(deltaStatsNano)
.build();
return deltaStats;
}
@Override
......@@ -848,6 +908,15 @@ public class GossipDeviceStore
}
@Override
public List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId) {
Map<PortNumber, PortStatistics> portStats = devicePortDeltaStats.get(deviceId);
if (portStats == null) {
return Collections.emptyList();
}
return ImmutableList.copyOf(portStats.values());
}
@Override
public Port getPort(DeviceId deviceId, PortNumber portNumber) {
Map<PortNumber, Port> ports = devicePorts.get(deviceId);
return ports == null ? null : ports.get(portNumber);
......@@ -934,7 +1003,7 @@ public class GossipDeviceStore
markOfflineInternal(deviceId, timestamp);
descs.clear();
return device == null ? null :
new DeviceEvent(DEVICE_REMOVED, device, null);
new DeviceEvent(DeviceEvent.Type.DEVICE_REMOVED, device, null);
}
}
......