ONOS-1440: Implements port statistics feature, which polls port statistics of all devices every 10 seconds. Also, implemented a simple portstats ONOS CLI command to show the statistics.
Change-Id: I57e046ae2c2463a58b478d3a5b523422cde71ba2
diff --git a/cli/src/main/java/org/onosproject/cli/net/DevicePortStatsCommand.java b/cli/src/main/java/org/onosproject/cli/net/DevicePortStatsCommand.java
new file mode 100644
index 0000000..a244d46
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/net/DevicePortStatsCommand.java
@@ -0,0 +1,51 @@
+package org.onosproject.cli.net;
+
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.device.PortStatistics;
+
+/**
+ * Lists port statistic of all ports in the system.
+ */
+@Command(scope = "onos", name = "portstats",
+ description = "Lists statistics of all ports in the system")
+public class DevicePortStatsCommand extends AbstractShellCommand {
+
+ private static final String FORMAT =
+ " port=%s, pktRx=%s, pktTx=%s, bytesRx=%s, bytesTx=%s, pktRxDrp=%s, pktTxDrp=%s, Dur=%s";
+
+ @Override
+ protected void execute() {
+ DeviceService deviceService = get(DeviceService.class);
+
+ deviceService.getDevices().forEach(d ->
+ printPortStats(d.id(), deviceService.getPortStatistics(d.id()))
+ );
+ }
+
+ 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());
+ }
+ }
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 8234462..87bcbee 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -280,6 +280,10 @@
</command>
<command>
+ <action class="org.onosproject.cli.net.DevicePortStatsCommand"/>
+ </command>
+
+ <command>
<action class="org.onosproject.cli.net.FlowsListCommand"/>
<completers>
<ref component-id="flowRuleStatusCompleter"/>
diff --git a/core/api/src/main/java/org/onosproject/net/device/DefaultPortStatistics.java b/core/api/src/main/java/org/onosproject/net/device/DefaultPortStatistics.java
new file mode 100644
index 0000000..93c91ce
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/device/DefaultPortStatistics.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.device;
+
+import org.onosproject.net.DeviceId;
+
+/**
+ * Default implementation of immutable port statistics.
+ */
+public final class DefaultPortStatistics implements PortStatistics {
+
+ private final DeviceId deviceId;
+ private final int port;
+ private final long packetsReceived;
+ private final long packetsSent;
+ private final long bytesReceived;
+ private final long bytesSent;
+ private final long packetsRxDropped;
+ private final long packetsTxDropped;
+ private final long packetsRxErrors;
+ private final long packetsTxErrors;
+ private final long durationSec;
+ private final long durationNano;
+
+ private DefaultPortStatistics(DeviceId deviceId,
+ int port,
+ long packetsReceived,
+ long packetsSent,
+ long bytesReceived,
+ long bytesSent,
+ long packetsRxDropped,
+ long packetsTxDropped,
+ long packetsRxErrors,
+ long packetsTxErrors,
+ long durationSec,
+ long durationNano) {
+ this.deviceId = deviceId;
+ this.port = port;
+ this.packetsReceived = packetsReceived;
+ this.packetsSent = packetsSent;
+ this.bytesReceived = bytesReceived;
+ this.bytesSent = bytesSent;
+ this.packetsRxDropped = packetsRxDropped;
+ this.packetsTxDropped = packetsTxDropped;
+ this.packetsRxErrors = packetsRxErrors;
+ this.packetsTxErrors = packetsTxErrors;
+ this.durationSec = durationSec;
+ this.durationNano = durationNano;
+ }
+
+ /**
+ * Creates a builder for DefaultPortStatistics object.
+ *
+ * @return builder object for DefaultPortStatistics object
+ */
+ public static DefaultPortStatistics.Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public int port() {
+ return this.port;
+ }
+
+ @Override
+ public long packetsReceived() {
+ return this.packetsReceived;
+ }
+
+ @Override
+ public long packetsSent() {
+ return this.packetsSent;
+ }
+
+ @Override
+ public long bytesReceived() {
+ return this.bytesReceived;
+ }
+
+ @Override
+ public long bytesSent() {
+ return this.bytesSent;
+ }
+
+ @Override
+ public long packetsRxDropped() {
+ return this.packetsRxDropped;
+ }
+
+ @Override
+ public long packetsTxDropped() {
+ return this.packetsTxDropped;
+ }
+
+ @Override
+ public long packetsRxErrors() {
+ return this.packetsRxErrors;
+ }
+
+ @Override
+ public long packetsTxErrors() {
+ return this.packetsTxErrors;
+ }
+
+ @Override
+ public long durationSec() {
+ return this.durationSec;
+ }
+
+ @Override
+ public long durationNano() {
+ return this.durationNano;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("device: " + deviceId + ", ");
+
+ sb.append("port: " + this.port + ", ");
+ sb.append("pktRx: " + this.packetsReceived + ", ");
+ sb.append("pktTx: " + this.packetsSent + ", ");
+ sb.append("byteRx: " + this.bytesReceived + ", ");
+ sb.append("byteTx: " + this.bytesSent + ", ");
+ sb.append("pktRxErr: " + this.packetsRxErrors + ", ");
+ sb.append("pktTxErr: " + this.packetsTxErrors + ", ");
+ sb.append("pktRxDrp: " + this.packetsRxDropped + ", ");
+ sb.append("pktTxDrp: " + this.packetsTxDropped);
+
+ return sb.toString();
+ }
+
+ public static final class Builder {
+
+ DeviceId deviceId;
+ int port;
+ long packetsReceived;
+ long packetsSent;
+ long bytesReceived;
+ long bytesSent;
+ long packetsRxDropped;
+ long packetsTxDropped;
+ long packetsRxErrors;
+ long packetsTxErrors;
+ long durationSec;
+ long durationNano;
+
+ private Builder() {
+
+ }
+
+ /**
+ * Sets port number.
+ *
+ * @param port port number
+ * @return builder object
+ */
+ public Builder setPort(int port) {
+ this.port = port;
+
+ return this;
+ }
+
+ /**
+ * Sets the device identifier.
+ *
+ * @param deviceId device identifier
+ * @return builder object
+ */
+ public Builder setDeviceId(DeviceId deviceId) {
+ this.deviceId = deviceId;
+
+ return this;
+ }
+
+ /**
+ * Sets the number of packet received.
+ *
+ * @param packets number of packets received
+ * @return builder object
+ */
+ public Builder setPacketsReceived(long packets) {
+ packetsReceived = packets;
+
+ return this;
+ }
+
+ /**
+ * Sets the number of packets sent.
+ *
+ * @param packets number of packets sent
+ * @return builder object
+ */
+ public Builder setPacketsSent(long packets) {
+ packetsSent = packets;
+
+ return this;
+ }
+
+ /**
+ * Sets the number of received bytes.
+ *
+ * @param bytes number of received bytes.
+ * @return builder object
+ */
+ public Builder setBytesReceived(long bytes) {
+ bytesReceived = bytes;
+
+ return this;
+ }
+
+ /**
+ * Sets the number of sent bytes.
+ *
+ * @param bytes number of sent bytes
+ * @return builder object
+ */
+ public Builder setBytesSent(long bytes) {
+ bytesSent = bytes;
+
+ return this;
+ }
+
+ /**
+ * Sets the number of packets dropped by RX.
+ *
+ * @param packets number of packets dropped by RX
+ * @return builder object
+ */
+ public Builder setPacketsRxDropped(long packets) {
+ packetsRxDropped = packets;
+
+ return this;
+ }
+
+ /**
+ * Sets the number of packets dropped by TX.
+ *
+ * @param packets
+ * @return builder object
+ */
+ public Builder setPacketsTxDropped(long packets) {
+ packetsTxDropped = packets;
+
+ return this;
+ }
+
+ /**
+ * Sets the number of receive errors.
+ *
+ * @param packets number of receive errors
+ * @return builder object
+ */
+ public Builder setPacketsRxErrors(long packets) {
+ packetsRxErrors = packets;
+
+ return this;
+ }
+
+ /**
+ * Sets the number of transmit errors.
+ *
+ * @param packets number of transmit errors
+ * @return builder object
+ */
+ public Builder setPacketsTxErrors(long packets) {
+ packetsTxErrors = packets;
+
+ return this;
+ }
+
+ /**
+ * Sets the time port has been alive in seconds.
+ *
+ * @param sec time port has been alive in seconds
+ * @return builder object
+ */
+ public Builder setDurationSec(long sec) {
+ durationSec = sec;
+
+ return this;
+ }
+
+ /**
+ * Sets the time port has been alive in nano seconds.
+ *
+ * @param nano time port has been alive in nano seconds
+ * @return builder object
+ */
+ public Builder setDurationNano(long nano) {
+ durationNano = nano;
+
+ return this;
+ }
+
+ /**
+ * Creates a PortStatistics object.
+ *
+ * @return DefaultPortStatistics object
+ */
+ public DefaultPortStatistics build() {
+ return new DefaultPortStatistics(
+ deviceId,
+ port,
+ packetsReceived,
+ packetsSent,
+ bytesReceived,
+ bytesSent,
+ packetsRxDropped,
+ packetsTxDropped,
+ packetsRxErrors,
+ packetsTxErrors,
+ durationSec,
+ durationNano);
+ }
+
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/device/DeviceEvent.java b/core/api/src/main/java/org/onosproject/net/device/DeviceEvent.java
index 395c0eb..420f2da 100644
--- a/core/api/src/main/java/org/onosproject/net/device/DeviceEvent.java
+++ b/core/api/src/main/java/org/onosproject/net/device/DeviceEvent.java
@@ -72,7 +72,12 @@
/**
* Signifies that a port has been removed.
*/
- PORT_REMOVED
+ PORT_REMOVED,
+
+ /*
+ * Signifies that port statistics has been updated.
+ */
+ PORT_STATS_UPDATED
}
/**
diff --git a/core/api/src/main/java/org/onosproject/net/device/DeviceProviderService.java b/core/api/src/main/java/org/onosproject/net/device/DeviceProviderService.java
index f48543c..9a1d923 100644
--- a/core/api/src/main/java/org/onosproject/net/device/DeviceProviderService.java
+++ b/core/api/src/main/java/org/onosproject/net/device/DeviceProviderService.java
@@ -19,6 +19,7 @@
import org.onosproject.net.MastershipRole;
import org.onosproject.net.provider.ProviderService;
+import java.util.Collection;
import java.util.List;
/**
@@ -70,4 +71,12 @@
*/
void receivedRoleReply(DeviceId deviceId, MastershipRole requested, MastershipRole response);
+ /**
+ * Sends statistics about all ports of a device.
+ *
+ * @param deviceId identity of the device
+ * @param portStatistics list of device port statistics
+ */
+ void updatePortStatistics(DeviceId deviceId, Collection<PortStatistics> portStatistics);
+
}
diff --git a/core/api/src/main/java/org/onosproject/net/device/DeviceService.java b/core/api/src/main/java/org/onosproject/net/device/DeviceService.java
index 20b8a0b..1c0f647 100644
--- a/core/api/src/main/java/org/onosproject/net/device/DeviceService.java
+++ b/core/api/src/main/java/org/onosproject/net/device/DeviceService.java
@@ -78,6 +78,14 @@
List<Port> getPorts(DeviceId deviceId);
/**
+ * Returns the list of port statistics associated with the device.
+ *
+ * @param deviceId device identitifer
+ * @return list of port statistics
+ */
+ List<PortStatistics> getPortStatistics(DeviceId deviceId);
+
+ /**
* Returns the port with the specified number and hosted by the given device.
*
* @param deviceId device identifier
diff --git a/core/api/src/main/java/org/onosproject/net/device/DeviceStore.java b/core/api/src/main/java/org/onosproject/net/device/DeviceStore.java
index 4835504..88dc7f9 100644
--- a/core/api/src/main/java/org/onosproject/net/device/DeviceStore.java
+++ b/core/api/src/main/java/org/onosproject/net/device/DeviceStore.java
@@ -22,6 +22,7 @@
import org.onosproject.net.provider.ProviderId;
import org.onosproject.store.Store;
+import java.util.Collection;
import java.util.List;
/**
@@ -114,6 +115,26 @@
List<Port> getPorts(DeviceId deviceId);
/**
+ * Updates the port statistics of the specified device using the give port
+ * statistics.
+ *
+ * @param providerId provider identifier
+ * @param deviceId device identifier
+ * @param portStats list of port statistics
+ * @return ready to send event describing what occurred;
+ */
+ DeviceEvent updatePortStatistics(ProviderId providerId, DeviceId deviceId,
+ Collection<PortStatistics> portStats);
+
+ /**
+ * Returns the list of port statistics of the specified device.
+ *
+ * @param deviceId device identifier
+ * @return list of port statistics of all ports of the device
+ */
+ List<PortStatistics> getPortStatistics(DeviceId deviceId);
+
+ /**
* Returns the specified device port.
*
* @param deviceId device identifier
@@ -137,4 +158,6 @@
* @return null if no such device, or was forwarded to remove master
*/
DeviceEvent removeDevice(DeviceId deviceId);
+
+
}
diff --git a/core/api/src/main/java/org/onosproject/net/device/PortStatistics.java b/core/api/src/main/java/org/onosproject/net/device/PortStatistics.java
new file mode 100644
index 0000000..b72d4e6
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/device/PortStatistics.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.device;
+
+/**
+ * Statistics of a port.
+ */
+public interface PortStatistics {
+
+ /**
+ * Returns the port number.
+ *
+ * @return port number
+ */
+ public int port();
+
+ /**
+ * Returns the number of packets received.
+ *
+ * @return the number of packets received
+ */
+ public long packetsReceived();
+
+ /**
+ * Returns the number of packets sent.
+ *
+ * @return the number of packets sent
+ */
+ public long packetsSent();
+
+ /**
+ * Returns the bytes received.
+ *
+ * @return the bytes received
+ */
+ public long bytesReceived();
+
+ /**
+ * Returns the bytes sent.
+ *
+ * @return the bytes sent
+ */
+ public long bytesSent();
+
+ /**
+ * Returns the number of packets dropped by RX.
+ *
+ * @return the number of packets dropped by RX
+ */
+ public long packetsRxDropped();
+
+ /**
+ * Returns the number of packets dropped by TX.
+ *
+ * @return the number of packets dropped by TX
+ */
+ public long packetsTxDropped();
+
+ /**
+ * Returns the number of transmit errors.
+ *
+ * @return the number of transmit errors
+ */
+ public long packetsRxErrors();
+
+ /**
+ * Returns the number of receive errors.
+ *
+ * @return the number of receive error
+ */
+ public long packetsTxErrors();
+
+ /**
+ * Returns the time port has been alive in seconds.
+ *
+ * @return the time port has been alive in seconds
+ */
+ public long durationSec();
+
+ /**
+ * Returns the time port has been alive in nano seconds.
+ *
+ * @return the time port has been alive in nano seconds
+ */
+ public long durationNano();
+
+}
diff --git a/core/api/src/test/java/org/onosproject/net/device/DeviceServiceAdapter.java b/core/api/src/test/java/org/onosproject/net/device/DeviceServiceAdapter.java
index 422d062..4f72702 100644
--- a/core/api/src/test/java/org/onosproject/net/device/DeviceServiceAdapter.java
+++ b/core/api/src/test/java/org/onosproject/net/device/DeviceServiceAdapter.java
@@ -69,6 +69,11 @@
}
@Override
+ public List<PortStatistics> getPortStatistics(DeviceId deviceId) {
+ return null;
+ }
+
+ @Override
public Port getPort(DeviceId deviceId, PortNumber portNumber) {
return null;
}
diff --git a/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java b/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
index 5cdaf99..842ae10 100644
--- a/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
+++ b/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
@@ -50,10 +50,12 @@
import org.onosproject.net.device.DeviceStore;
import org.onosproject.net.device.DeviceStoreDelegate;
import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.device.PortStatistics;
import org.onosproject.net.provider.AbstractProviderRegistry;
import org.onosproject.net.provider.AbstractProviderService;
import org.slf4j.Logger;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -174,6 +176,12 @@
}
@Override
+ public List<PortStatistics> getPortStatistics(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return store.getPortStatistics(deviceId);
+ }
+
+ @Override
public Port getPort(DeviceId deviceId, PortNumber portNumber) {
checkNotNull(deviceId, DEVICE_ID_NULL);
checkNotNull(portNumber, PORT_NUMBER_NULL);
@@ -463,6 +471,19 @@
}
}
+
+ @Override
+ public void updatePortStatistics(DeviceId deviceId, Collection<PortStatistics> portStatistics) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ checkNotNull(portStatistics,
+ "Port statistics list cannot be null");
+ checkValidity();
+
+ DeviceEvent event = store.updatePortStatistics(this.provider().id(),
+ deviceId, portStatistics);
+
+ post(event);
+ }
}
// Posts the specified event to the local event dispatcher.
diff --git a/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java b/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java
index 325242a..131000b 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java
@@ -53,6 +53,7 @@
import org.onosproject.net.device.DeviceStore;
import org.onosproject.net.device.DeviceStoreDelegate;
import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.device.PortStatistics;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.store.AbstractStore;
import org.onosproject.store.Timestamp;
@@ -121,6 +122,8 @@
// cache of Device and Ports generated by compositing descriptions from providers
private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = Maps.newConcurrentMap();
+ private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, PortStatistics>>
+ devicePortStats = Maps.newConcurrentMap();
// to be updated under Device lock
private final Map<DeviceId, Timestamp> offline = Maps.newHashMap();
@@ -800,6 +803,34 @@
}
@Override
+ public DeviceEvent updatePortStatistics(ProviderId providerId, DeviceId deviceId,
+ Collection<PortStatistics> portStats) {
+
+ ConcurrentMap<PortNumber, PortStatistics> statsMap = devicePortStats.get(deviceId);
+ if (statsMap == null) {
+ statsMap = Maps.newConcurrentMap();
+ devicePortStats.put(deviceId, statsMap);
+ }
+
+ for (PortStatistics stat: portStats) {
+ PortNumber portNumber = PortNumber.portNumber(stat.port());
+ statsMap.put(portNumber, stat);
+ }
+
+ return new DeviceEvent(PORT_STATS_UPDATED, devices.get(deviceId), null);
+ }
+
+ @Override
+ public List<PortStatistics> getPortStatistics(DeviceId deviceId) {
+
+ Map<PortNumber, PortStatistics> portStats = devicePortStats.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);
diff --git a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleDeviceStore.java b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleDeviceStore.java
index 2814be9..d8c55ff 100644
--- a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleDeviceStore.java
+++ b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleDeviceStore.java
@@ -42,6 +42,7 @@
import org.onosproject.net.device.DeviceStore;
import org.onosproject.net.device.DeviceStoreDelegate;
import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.device.PortStatistics;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.store.AbstractStore;
import org.onlab.packet.ChassisId;
@@ -49,6 +50,7 @@
import org.slf4j.Logger;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -94,6 +96,8 @@
private final ConcurrentMap<DeviceId, Device> devices = Maps.newConcurrentMap();
private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>>
devicePorts = Maps.newConcurrentMap();
+ private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, PortStatistics>>
+ devicePortStats = Maps.newConcurrentMap();
// Available (=UP) devices
private final Set<DeviceId> availableDevices = Sets.newConcurrentHashSet();
@@ -416,12 +420,39 @@
}
@Override
+ public DeviceEvent updatePortStatistics(ProviderId providerId, DeviceId deviceId,
+ Collection<PortStatistics> portStats) {
+
+ ConcurrentMap<PortNumber, PortStatistics> statsMap = devicePortStats.get(deviceId);
+ if (statsMap == null) {
+ statsMap = Maps.newConcurrentMap();
+ devicePortStats.put(deviceId, statsMap);
+ }
+
+ for (PortStatistics stat: portStats) {
+ PortNumber portNumber = PortNumber.portNumber(stat.port());
+ statsMap.put(portNumber, stat);
+ }
+
+ return new DeviceEvent(PORT_STATS_UPDATED, devices.get(deviceId), null);
+ }
+
+ @Override
public Port getPort(DeviceId deviceId, PortNumber portNumber) {
Map<PortNumber, Port> ports = devicePorts.get(deviceId);
return ports == null ? null : ports.get(portNumber);
}
@Override
+ public List<PortStatistics> getPortStatistics(DeviceId deviceId) {
+ Map<PortNumber, PortStatistics> portStats = devicePortStats.get(deviceId);
+ if (portStats == null) {
+ return Collections.emptyList();
+ }
+ return ImmutableList.copyOf(portStats.values());
+ }
+
+ @Override
public boolean isAvailable(DeviceId deviceId) {
return availableDevices.contains(deviceId);
}
diff --git a/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java b/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
index 762a8f5..8fcf789 100644
--- a/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
+++ b/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
@@ -47,6 +47,8 @@
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFPacketIn;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
import org.projectfloodlight.openflow.protocol.OFPortStatus;
import org.projectfloodlight.openflow.protocol.OFStatsReply;
import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
@@ -104,6 +106,9 @@
protected Multimap<Dpid, OFGroupDescStatsEntry> fullGroupDescStats =
ArrayListMultimap.create();
+ protected Multimap<Dpid, OFPortStatsEntry> fullPortStats =
+ ArrayListMultimap.create();
+
private final Controller ctrl = new Controller();
@Activate
@@ -216,6 +221,7 @@
Collection<OFFlowStatsEntry> flowStats;
Collection<OFGroupStatsEntry> groupStats;
Collection<OFGroupDescStatsEntry> groupDescStats;
+ Collection<OFPortStatsEntry> portStats;
switch (msg.getType()) {
case PORT_STATUS:
@@ -280,6 +286,9 @@
executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
}
break;
+ case PORT:
+ executorMsgs.submit(new OFMessageHandler(dpid, reply));
+ break;
default:
log.warn("Unsupported stats type : {}", reply.getStatsType());
}
@@ -343,6 +352,15 @@
return null;
}
+ private synchronized Collection<OFPortStatsEntry> publishPortStats(Dpid dpid,
+ OFPortStatsReply reply) {
+ fullPortStats.putAll(dpid, reply.getEntries());
+ if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
+ return fullPortStats.removeAll(dpid);
+ }
+ return null;
+ }
+
@Override
public void setRole(Dpid dpid, RoleState role) {
final OpenFlowSwitch sw = getSwitch(dpid);
diff --git a/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java b/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
index 504d047..d23817f 100644
--- a/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
+++ b/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
@@ -15,6 +15,8 @@
*/
package org.onosproject.provider.of.device.impl;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -29,31 +31,43 @@
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.DeviceProvider;
import org.onosproject.net.device.DeviceProviderRegistry;
import org.onosproject.net.device.DeviceProviderService;
import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.device.PortStatistics;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.openflow.controller.Dpid;
import org.onosproject.openflow.controller.OpenFlowController;
+import org.onosproject.openflow.controller.OpenFlowEventListener;
import org.onosproject.openflow.controller.OpenFlowSwitch;
import org.onosproject.openflow.controller.OpenFlowSwitchListener;
import org.onosproject.openflow.controller.RoleState;
import org.onlab.packet.ChassisId;
import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFPortConfig;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFPortFeatures;
import org.projectfloodlight.openflow.protocol.OFPortReason;
import org.projectfloodlight.openflow.protocol.OFPortState;
+import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.OFStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsType;
import org.projectfloodlight.openflow.protocol.OFVersion;
import org.projectfloodlight.openflow.types.PortSpeed;
import org.slf4j.Logger;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import com.google.common.base.Strings;
@@ -83,7 +97,12 @@
private DeviceProviderService providerService;
- private final OpenFlowSwitchListener listener = new InternalDeviceProvider();
+ private final InternalDeviceProvider listener = new InternalDeviceProvider();
+
+ // TODO: We need to make the poll interval configurable.
+ static final int POLL_INTERVAL = 10;
+
+ private HashMap<Dpid, PortStatsCollector> collectors = Maps.newHashMap();
/**
* Creates an OpenFlow device provider.
@@ -96,6 +115,7 @@
public void activate() {
providerService = providerRegistry.register(this);
controller.addListener(listener);
+ controller.addEventListener(listener);
for (OpenFlowSwitch sw : controller.getSwitches()) {
try {
listener.switchAdded(new Dpid(sw.getId()));
@@ -105,6 +125,9 @@
// disconnect to trigger switch-add later
sw.disconnectSwitch();
}
+ PortStatsCollector psc = new PortStatsCollector(sw, POLL_INTERVAL);
+ psc.start();
+ collectors.put(new Dpid(sw.getId()), psc);
}
LOG.info("Started");
}
@@ -174,7 +197,45 @@
LOG.info("Accepting mastership role change for device {}", deviceId);
}
- private class InternalDeviceProvider implements OpenFlowSwitchListener {
+ private void pushPortMetrics(Dpid dpid, OFPortStatsReply msg) {
+ DeviceId deviceId = DeviceId.deviceId(dpid.uri(dpid));
+
+ Collection<PortStatistics> stats = buildPortStatistics(deviceId, msg);
+
+ providerService.updatePortStatistics(deviceId, stats);
+ }
+
+ private Collection<PortStatistics> buildPortStatistics(DeviceId deviceId, OFPortStatsReply msg) {
+
+ HashSet<PortStatistics> stats = Sets.newHashSet();
+
+ for (OFPortStatsEntry entry: msg.getEntries()) {
+ if (entry.getPortNo().getPortNumber() < 0) {
+ continue;
+ }
+ DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
+ DefaultPortStatistics stat = builder.setDeviceId(deviceId)
+ .setPort(entry.getPortNo().getPortNumber())
+ .setPacketsReceived(entry.getRxPackets().getValue())
+ .setPacketsSent(entry.getTxPackets().getValue())
+ .setBytesReceived(entry.getRxBytes().getValue())
+ .setBytesSent(entry.getTxBytes().getValue())
+ .setPacketsRxDropped(entry.getRxDropped().getValue())
+ .setPacketsTxDropped(entry.getTxDropped().getValue())
+ .setPacketsRxErrors(entry.getRxErrors().getValue())
+ .setPacketsTxErrors(entry.getTxErrors().getValue())
+ .setDurationSec(entry.getDurationSec())
+ .setDurationNano(entry.getDurationNsec())
+ .build();
+
+ stats.add(stat);
+ }
+
+ return Collections.unmodifiableSet(stats);
+
+ }
+
+ private class InternalDeviceProvider implements OpenFlowSwitchListener, OpenFlowEventListener {
@Override
public void switchAdded(Dpid dpid) {
if (providerService == null) {
@@ -201,6 +262,11 @@
cId, annotations);
providerService.deviceConnected(did, description);
providerService.updatePorts(did, buildPortDescriptions(sw.getPorts()));
+
+ PortStatsCollector psc = new PortStatsCollector(
+ controller.getSwitch(dpid), POLL_INTERVAL);
+ psc.start();
+ collectors.put(dpid, psc);
}
@Override
@@ -209,8 +275,12 @@
return;
}
providerService.deviceDisconnected(deviceId(uri(dpid)));
- }
+ PortStatsCollector collector = collectors.remove(dpid);
+ if (collector != null) {
+ collector.stop();
+ }
+ }
@Override
public void switchChanged(Dpid dpid) {
@@ -328,6 +398,19 @@
}
return portSpeed.getSpeedBps() / MBPS;
}
+
+ @Override
+ public void handleMessage(Dpid dpid, OFMessage msg) {
+ switch (msg.getType()) {
+ case STATS_REPLY:
+ if (((OFStatsReply) msg).getStatsType() == OFStatsType.PORT) {
+ pushPortMetrics(dpid, (OFPortStatsReply) msg);
+ }
+ break;
+ default:
+ break;
+ }
+ }
}
}
diff --git a/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/PortStatsCollector.java b/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/PortStatsCollector.java
new file mode 100644
index 0000000..36d7948
--- /dev/null
+++ b/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/PortStatsCollector.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.provider.of.device.impl;
+
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.util.Timer;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFPortStatsRequest;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.slf4j.Logger;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/*
+ * Sends Group Stats Request and collect the group statistics with a time interval.
+ */
+public class PortStatsCollector implements TimerTask {
+
+ // TODO: Refactoring is required using ScheduledExecutorService
+
+ private final HashedWheelTimer timer = Timer.getTimer();
+ private final OpenFlowSwitch sw;
+ private final Logger log = getLogger(getClass());
+ private final int refreshInterval;
+ private final AtomicLong xidAtomic = new AtomicLong(1);
+
+ private Timeout timeout;
+
+ private boolean stopTimer = false;
+
+ /**
+ * Creates a GroupStatsCollector object.
+ *
+ * @param sw Open Flow switch
+ * @param interval time interval for collecting group statistic
+ */
+ public PortStatsCollector(OpenFlowSwitch sw, int interval) {
+ this.sw = sw;
+ this.refreshInterval = interval;
+ }
+
+ @Override
+ public void run(Timeout timeout) throws Exception {
+ log.trace("Collecting stats for {}", sw.getStringId());
+
+ sendPortStatistic();
+
+ if (!this.stopTimer) {
+ log.trace("Scheduling stats collection in {} seconds for {}",
+ this.refreshInterval, this.sw.getStringId());
+ timeout.getTimer().newTimeout(this, refreshInterval,
+ TimeUnit.SECONDS);
+ }
+ }
+
+ private void sendPortStatistic() {
+ if (log.isTraceEnabled()) {
+ log.trace("sendGroupStatistics {}:{}", sw.getStringId(), sw.getRole());
+ }
+ if (sw.getRole() != RoleState.MASTER) {
+ return;
+ }
+ Long statsXid = xidAtomic.getAndIncrement();
+ OFPortStatsRequest statsRequest = sw.factory().buildPortStatsRequest()
+ .setPortNo(OFPort.ANY)
+ .setXid(statsXid)
+ .build();
+ sw.sendMsg(statsRequest);
+ }
+
+ /**
+ * Starts the collector.
+ */
+ public void start() {
+ log.info("Starting Port Stats collection thread for {}", sw.getStringId());
+ timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Stops the collector.
+ */
+ public void stop() {
+ log.info("Stopping Port Stats collection thread for {}", sw.getStringId());
+ this.stopTimer = true;
+ timeout.cancel();
+ }
+}
diff --git a/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java b/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java
index c83da17..f8b0905 100644
--- a/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java
+++ b/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java
@@ -23,6 +23,7 @@
import static org.onosproject.net.MastershipRole.*;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -41,6 +42,7 @@
import org.onosproject.net.device.DeviceProviderRegistry;
import org.onosproject.net.device.DeviceProviderService;
import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.device.PortStatistics;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.openflow.controller.Dpid;
import org.onosproject.openflow.controller.OpenFlowController;
@@ -217,6 +219,11 @@
roles.put(requested, Dpid.dpid(deviceId.uri()));
}
+ @Override
+ public void updatePortStatistics(DeviceId deviceId, Collection<PortStatistics> portStatistics) {
+
+ }
+
}
}