aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/providers/openflow
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/providers/openflow')
-rw-r--r--framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java227
-rw-r--r--framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceValueMapper.java73
-rw-r--r--framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java3
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java5
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java2
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java4
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java881
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java218
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java95
9 files changed, 1427 insertions, 81 deletions
diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
index cb19dc52..4fa961f8 100644
--- a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
+++ b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
@@ -15,10 +15,24 @@
*/
package org.onosproject.provider.of.device.impl;
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.get;
+import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.net.Port.Type.COPPER;
+import static org.onosproject.net.Port.Type.FIBER;
+import static org.onosproject.openflow.controller.Dpid.dpid;
+import static org.onosproject.openflow.controller.Dpid.uri;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -28,15 +42,17 @@ import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.ChassisId;
import org.onlab.util.Frequency;
-import org.onosproject.cfg.ComponentConfigService;
import org.onlab.util.Spectrum;
+import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.ChannelSpacing;
import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.GridType;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.OchSignal;
+import org.onosproject.net.OduCltPort;
import org.onosproject.net.OduSignalType;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
@@ -49,6 +65,7 @@ import org.onosproject.net.device.DeviceProvider;
import org.onosproject.net.device.DeviceProviderRegistry;
import org.onosproject.net.device.DeviceProviderService;
import org.onosproject.net.device.OchPortDescription;
+import org.onosproject.net.device.OduCltPortDescription;
import org.onosproject.net.device.OmsPortDescription;
import org.onosproject.net.device.PortDescription;
import org.onosproject.net.device.PortStatistics;
@@ -64,13 +81,19 @@ import org.onosproject.openflow.controller.PortDescPropertyType;
import org.onosproject.openflow.controller.RoleState;
import org.osgi.service.component.ComponentContext;
import org.projectfloodlight.openflow.protocol.OFCalientPortDescStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFExpPort;
+import org.projectfloodlight.openflow.protocol.OFExpPortDescPropOpticalTransport;
+import org.projectfloodlight.openflow.protocol.OFExpPortOpticalTransportLayerEntry;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFObject;
import org.projectfloodlight.openflow.protocol.OFPortConfig;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFPortDescPropOpticalTransport;
import org.projectfloodlight.openflow.protocol.OFPortFeatures;
import org.projectfloodlight.openflow.protocol.OFPortOptical;
+import org.projectfloodlight.openflow.protocol.OFPortOpticalTransportLayerClass;
+import org.projectfloodlight.openflow.protocol.OFPortOpticalTransportSignalType;
import org.projectfloodlight.openflow.protocol.OFPortReason;
import org.projectfloodlight.openflow.protocol.OFPortState;
import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
@@ -83,23 +106,10 @@ 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.Dictionary;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Strings.isNullOrEmpty;
-import static org.onlab.util.Tools.get;
-import static org.onosproject.net.DeviceId.deviceId;
-import static org.onosproject.net.Port.Type.COPPER;
-import static org.onosproject.net.Port.Type.FIBER;
-import static org.onosproject.openflow.controller.Dpid.dpid;
-import static org.onosproject.openflow.controller.Dpid.uri;
-import static org.slf4j.LoggerFactory.getLogger;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
/**
* Provider which uses an OpenFlow controller to detect network
@@ -109,7 +119,11 @@ import static org.slf4j.LoggerFactory.getLogger;
public class OpenFlowDeviceProvider extends AbstractProvider implements DeviceProvider {
private static final Logger LOG = getLogger(OpenFlowDeviceProvider.class);
+
private static final long MBPS = 1_000 * 1_000;
+ private static final Frequency FREQ100 = Frequency.ofGHz(100);
+ private static final Frequency FREQ193_1 = Frequency.ofTHz(193.1);
+ private static final Frequency FREQ4_4 = Frequency.ofTHz(4.4);
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceProviderRegistry providerRegistry;
@@ -145,27 +159,16 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
providerService = providerRegistry.register(this);
controller.addListener(listener);
controller.addEventListener(listener);
- for (OpenFlowSwitch sw : controller.getSwitches()) {
- try {
- listener.switchAdded(new Dpid(sw.getId()));
- } catch (Exception e) {
- LOG.warn("Failed initially adding {} : {}", sw.getStringId(), e.getMessage());
- LOG.debug("Error details:", e);
- // disconnect to trigger switch-add later
- sw.disconnectSwitch();
- }
- PortStatsCollector psc = new PortStatsCollector(sw, portStatsPollFrequency);
- psc.start();
- collectors.put(new Dpid(sw.getId()), psc);
- }
+ connectInitialDevices();
LOG.info("Started");
}
@Deactivate
public void deactivate(ComponentContext context) {
cfgService.unregisterProperties(getClass(), false);
- providerRegistry.unregister(this);
controller.removeListener(listener);
+ disconnectDevices();
+ providerRegistry.unregister(this);
collectors.values().forEach(PortStatsCollector::stop);
providerService = null;
LOG.info("Stopped");
@@ -191,13 +194,31 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
LOG.info("Settings: portStatsPollFrequency={}", portStatsPollFrequency);
}
+ private void connectInitialDevices() {
+ for (OpenFlowSwitch sw : controller.getSwitches()) {
+ try {
+ listener.switchAdded(new Dpid(sw.getId()));
+ } catch (Exception e) {
+ LOG.warn("Failed initially adding {} : {}", sw.getStringId(), e.getMessage());
+ LOG.debug("Error details:", e);
+ // disconnect to trigger switch-add later
+ sw.disconnectSwitch();
+ }
+ PortStatsCollector psc = new PortStatsCollector(sw, portStatsPollFrequency);
+ psc.start();
+ collectors.put(new Dpid(sw.getId()), psc);
+ }
+ }
+
+ private void disconnectDevices() {
+ // Only disconnect the devices for which we are currently master.
+ controller.getMasterSwitches().forEach(sw -> listener.switchRemoved(new Dpid(sw.getId())));
+ }
+
@Override
public boolean isReachable(DeviceId deviceId) {
OpenFlowSwitch sw = controller.getSwitch(dpid(deviceId.uri()));
- if (sw == null || !sw.isConnected()) {
- return false;
- }
- return true;
+ return sw != null && sw.isConnected();
}
@Override
@@ -302,8 +323,9 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
ChassisId cId = new ChassisId(dpid.value());
SparseAnnotations annotations = DefaultAnnotations.builder()
- .set("protocol", sw.factory().getVersion().toString())
- .set("channelId", sw.channelId())
+ .set(AnnotationKeys.PROTOCOL, sw.factory().getVersion().toString())
+ .set(AnnotationKeys.CHANNEL_ID, sw.channelId())
+ .set(AnnotationKeys.MANAGEMENT_ADDRESS, sw.channelId().split(":")[0])
.build();
DeviceDescription description =
@@ -386,18 +408,27 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
*/
private List<PortDescription> buildPortDescriptions(OpenFlowSwitch sw) {
final List<PortDescription> portDescs = new ArrayList<>(sw.getPorts().size());
- sw.getPorts().forEach(port -> portDescs.add(buildPortDescription(port)));
+ if (!(Device.Type.ROADM.equals(sw.deviceType()))) {
+ sw.getPorts().forEach(port -> portDescs.add(buildPortDescription(port)));
+ }
OpenFlowOpticalSwitch opsw;
switch (sw.deviceType()) {
case ROADM:
opsw = (OpenFlowOpticalSwitch) sw;
+ List<OFPortDesc> ports = opsw.getPorts();
+ LOG.debug("SW ID {} , ETH- ODU CLT Ports {}", opsw.getId(), ports);
+ // ODU client ports are reported as ETH
+ ports.forEach(port -> portDescs.add(buildOduCltPortDescription(port)));
+
opsw.getPortTypes().forEach(type -> {
- opsw.getPortsOf(type).forEach(
- op -> {
- portDescs.add(buildPortDescription(type, (OFPortOptical) op));
- }
- );
+ List<? extends OFObject> portsOf = opsw.getPortsOf(type);
+ LOG.debug("Ports Of{}", portsOf);
+ portsOf.forEach(
+ op -> {
+ portDescs.add(buildPortDescription(type, (OFObject) op));
+ }
+ );
});
break;
case FIBER_SWITCH:
@@ -417,6 +448,105 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
return portDescs;
}
+ private PortDescription buildOduCltPortDescription(OFPortDesc port) {
+ PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
+ boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN) &&
+ !port.getConfig().contains(OFPortConfig.PORT_DOWN);
+ Long portSpeed = portSpeed(port);
+ OduCltPort.SignalType sigType = null;
+
+ switch (portSpeed.toString()) {
+ case "1":
+ sigType = OduCltPort.SignalType.CLT_1GBE;
+ break;
+ case "10":
+ sigType = OduCltPort.SignalType.CLT_10GBE;
+ break;
+ case "40":
+ sigType = OduCltPort.SignalType.CLT_40GBE;
+ break;
+ case "100":
+ sigType = OduCltPort.SignalType.CLT_100GBE;
+ break;
+ default:
+ throw new RuntimeException("Un recognize OduClt speed: " + portSpeed.toString());
+ }
+
+ SparseAnnotations annotations = buildOduCltAnnotation(port);
+ return new OduCltPortDescription(portNo, enabled, sigType, annotations);
+ }
+
+ private SparseAnnotations buildOduCltAnnotation(OFPortDesc port) {
+ SparseAnnotations annotations = null;
+ String portName = Strings.emptyToNull(port.getName());
+ if (portName != null) {
+ annotations = DefaultAnnotations.builder()
+ .set(AnnotationKeys.PORT_NAME, portName)
+ .set(AnnotationKeys.STATIC_PORT, Boolean.TRUE.toString()).build();
+ }
+ return annotations;
+ }
+
+ private PortDescription buildPortDescription(PortDescPropertyType ptype, OFObject port) {
+ if (port instanceof OFPortOptical) {
+ return buildPortDescription(ptype, (OFPortOptical) port);
+ }
+ return buildPortDescription(ptype, (OFExpPort) port);
+ }
+
+ /**
+ * Build a portDescription from a given a port description describing some
+ * Optical port.
+ *
+ * @param ptype description property type.
+ * @param port the port to build from.
+ * @return portDescription for the port.
+ */
+ private PortDescription buildPortDescription(PortDescPropertyType ptype, OFExpPort port) {
+ PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
+ boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN)
+ && !port.getConfig().contains(OFPortConfig.PORT_DOWN);
+ SparseAnnotations annotations = makePortNameAnnotation(port.getName());
+
+ OFExpPortDescPropOpticalTransport firstProp = port.getProperties().get(0);
+ OFPortOpticalTransportSignalType sigType = firstProp.getPortSignalType();
+
+ DefaultPortDescription portDes = null;
+ switch (sigType) {
+ case OMSN:
+ portDes = new OmsPortDescription(portNo, enabled, FREQ193_1, FREQ193_1.add(FREQ4_4),
+ FREQ100, annotations);
+ break;
+ case OCH:
+ OFExpPortOpticalTransportLayerEntry entry = firstProp.getFeatures().get(0).getValue().get(0);
+ OFPortOpticalTransportLayerClass layerClass = entry.getLayerClass();
+ if (!OFPortOpticalTransportLayerClass.ODU.equals(layerClass)) {
+ LOG.error("Unsupported layer Class {} ", layerClass);
+ return null;
+ }
+
+ // convert to ONOS OduSignalType
+ OduSignalType oduSignalType = OpenFlowDeviceValueMapper.
+ lookupOduSignalType((byte) entry.getSignalType());
+ //OchSignal is needed for OchPortDescription constructor,
+ //yet not relevant for tunable OCH port, creating with default parameters
+ OchSignal signalId = new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, 1, 1);
+
+ portDes = new OchPortDescription(portNo, enabled,
+ oduSignalType, true, signalId, annotations);
+
+ break;
+ case OTU2:
+ case OTU4:
+ LOG.error("Signal tpye OTU2/4 not supported yet ", port.toString());
+ break;
+ default:
+ break;
+ }
+
+ return portDes;
+ }
+
/**
* Creates an annotation for the port name if one is available.
*
@@ -565,5 +695,4 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
}
}
}
-
}
diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceValueMapper.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceValueMapper.java
new file mode 100644
index 00000000..7bdf06fd
--- /dev/null
+++ b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceValueMapper.java
@@ -0,0 +1,73 @@
+/*
+ * 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.onosproject.net.OduSignalType;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.EnumHashBiMap;
+
+/**
+ * Collection of helper methods to convert protocol agnostic models to values used in OpenFlow spec.
+ */
+final class OpenFlowDeviceValueMapper {
+
+ // prohibit instantiation
+ private OpenFlowDeviceValueMapper() {}
+
+ private static final BiMap<OduSignalType, Byte> ODU_SIGNAL_TYPES = EnumHashBiMap.create(OduSignalType.class);
+ static {
+ // See ONF "Optical Transport Protocol Extensions Version 1.0" for the following values
+ ODU_SIGNAL_TYPES.put(OduSignalType.ODU1, (byte) 1); // OFPODUT_ODU1 of enum ofp_odu_signal_type
+ ODU_SIGNAL_TYPES.put(OduSignalType.ODU2, (byte) 2); // OFPODUT_ODU2 of enum ofp_odu_signal_type
+ ODU_SIGNAL_TYPES.put(OduSignalType.ODU3, (byte) 3); // OFPODUT_ODU3 of enum ofp_odu_signal_type
+ ODU_SIGNAL_TYPES.put(OduSignalType.ODU4, (byte) 4); // OFPODUT_ODU4 of enum ofp_odu_signal_type
+ ODU_SIGNAL_TYPES.put(OduSignalType.ODU0, (byte) 10); // OFPODUT_ODU0 of enum ofp_odu_signal_type
+ ODU_SIGNAL_TYPES.put(OduSignalType.ODU2e, (byte) 11); // OFPODUT_ODU2E of enum ofp_odu_signal_type
+ }
+
+ /**
+ * Looks up the specified input value to the corresponding value with the specified map.
+ *
+ * @param map bidirectional mapping
+ * @param input input value
+ * @param cls class of output value
+ * @param <I> type of input value
+ * @param <O> type of output value
+ * @return the corresponding value stored in the specified map
+ */
+ private static <I, O> O lookup(BiMap<I, O> map, I input, Class<O> cls) {
+ if (!map.containsKey(input)) {
+ throw new RuntimeException(
+ String.format("No mapping found for %s when converting to %s", input, cls.getName()));
+ }
+
+ return map.get(input);
+ }
+
+ /**
+ * Looks up the the corresponding {@link OduSignalType} instance
+ * from the specified byte value for ODU signal type defined in
+ * ONF "Optical Transport Protocol Extensions Version 1.0".
+ *
+ * @param signalType byte value as ODU (Optical channel Data Unit) signal type defined the spec
+ * @return the corresponding OchSignalType instance
+ */
+ static OduSignalType lookupOduSignalType(byte signalType) {
+ return lookup(ODU_SIGNAL_TYPES.inverse(), signalType, OduSignalType.class);
+ }
+
+}
diff --git a/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java b/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java
index 7b4d7922..d0838bb8 100644
--- a/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java
+++ b/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java
@@ -16,6 +16,7 @@
package org.onosproject.provider.of.device.impl;
import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
@@ -236,7 +237,7 @@ public class OpenFlowDeviceProviderTest {
@Override
public Iterable<OpenFlowSwitch> getMasterSwitches() {
- return null;
+ return ImmutableSet.of();
}
@Override
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
index f238bdb1..cf918605 100644
--- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
@@ -221,11 +221,6 @@ public class FlowEntryBuilder {
private TrafficTreatment buildTreatment() {
TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
- // If this is a drop rule
- if (instructions.size() == 0) {
- builder.drop();
- return builder.build();
- }
for (OFInstruction in : instructions) {
switch (in.getType()) {
case GOTO_TABLE:
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
index c9de4500..f77819d5 100644
--- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
@@ -142,7 +142,7 @@ public class FlowModBuilderVer10 extends FlowModBuilder {
for (Instruction i : treatment.immediate()) {
switch (i.type()) {
case DROP:
- log.warn("Saw drop action; assigning drop action");
+ case NOACTION:
return Collections.emptyList();
case L2MODIFICATION:
act = buildL2Modification(i);
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
index 8918d337..cc265758 100644
--- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
@@ -123,6 +123,9 @@ public class FlowModBuilderVer13 extends FlowModBuilder {
if (treatment.writeMetadata() != null) {
instructions.add(buildMetadata(treatment.writeMetadata()));
}
+ if (treatment.metered() != null) {
+ instructions.add(buildMeter(treatment.metered()));
+ }
long cookie = flowRule().id().value();
@@ -212,6 +215,7 @@ public class FlowModBuilderVer13 extends FlowModBuilder {
for (Instruction i : treatments) {
switch (i.type()) {
case DROP:
+ case NOACTION:
return Collections.emptyList();
case L0MODIFICATION:
actions.add(buildL0Modification(i));
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java
new file mode 100644
index 00000000..a81367cd
--- /dev/null
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java
@@ -0,0 +1,881 @@
+/*
+ * 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.flow.impl;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import org.onosproject.net.flow.DefaultTypedFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowId;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.StoredFlowEntry;
+import org.onosproject.net.flow.TypedStoredFlowEntry;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TableId;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.flow.TypedStoredFlowEntry.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Efficiently and adaptively collects flow statistics for the specified switch.
+ */
+public class NewAdaptiveFlowStatsCollector {
+
+ private final Logger log = getLogger(getClass());
+
+ private final OpenFlowSwitch sw;
+
+ private ScheduledExecutorService adaptiveFlowStatsScheduler =
+ Executors.newScheduledThreadPool(4, groupedThreads("onos/flow", "device-stats-collector-%d"));
+ private ScheduledFuture<?> calAndShortFlowsThread;
+ private ScheduledFuture<?> midFlowsThread;
+ private ScheduledFuture<?> longFlowsThread;
+
+ // Task that calculates all flowEntries' FlowLiveType and collects stats IMMEDIATE flows every calAndPollInterval
+ private CalAndShortFlowsTask calAndShortFlowsTask;
+ // Task that collects stats MID flows every 2*calAndPollInterval
+ private MidFlowsTask midFlowsTask;
+ // Task that collects stats LONG flows every 3*calAndPollInterval
+ private LongFlowsTask longFlowsTask;
+
+ private static final int CAL_AND_POLL_TIMES = 1; // must be always 0
+ private static final int MID_POLL_TIMES = 2; // variable greater or equal than 1
+ private static final int LONG_POLL_TIMES = 3; // variable greater or equal than MID_POLL_TIMES
+ //TODO: make ENTIRE_POLL_TIMES configurable with enable or disable
+ // must be variable greater or equal than common multiple of MID_POLL_TIMES and LONG_POLL_TIMES
+ private static final int ENTIRE_POLL_TIMES = 6;
+
+ private static final int DEFAULT_CAL_AND_POLL_FREQUENCY = 5;
+ private static final int MIN_CAL_AND_POLL_FREQUENCY = 2;
+ private static final int MAX_CAL_AND_POLL_FREQUENCY = 60;
+
+ private int calAndPollInterval; // CAL_AND_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
+ private int midPollInterval; // MID_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
+ private int longPollInterval; // LONG_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
+ // only used for checking condition at each task if it collects entire flows from a given switch or not
+ private int entirePollInterval; // ENTIRE_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
+
+ // Number of call count of each Task,
+ // for undoing collection except only entire flows collecting task in CalAndShortFlowsTask
+ private int callCountCalAndShortFlowsTask = 0; // increased CAL_AND_POLL_TIMES whenever Task is called
+ private int callCountMidFlowsTask = 0; // increased MID_POLL_TIMES whenever Task is called
+ private int callCountLongFlowsTask = 0; // increased LONG_POLL_TIMES whenever Task is called
+
+ private InternalDeviceFlowTable deviceFlowTable = new InternalDeviceFlowTable();
+
+ private boolean isFirstTimeStart = true;
+
+ public static final long NO_FLOW_MISSING_XID = (-1);
+ private long flowMissingXid = NO_FLOW_MISSING_XID;
+
+ /**
+ * Creates a new adaptive collector for the given switch and default cal_and_poll frequency.
+ *
+ * @param sw switch to pull
+ * @param pollInterval cal and immediate poll frequency in seconds
+ */
+ NewAdaptiveFlowStatsCollector(OpenFlowSwitch sw, int pollInterval) {
+ this.sw = sw;
+
+ initMemberVars(pollInterval);
+ }
+
+ // check calAndPollInterval validity and set all pollInterval values and finally initialize each task call count
+ private void initMemberVars(int pollInterval) {
+ if (pollInterval < MIN_CAL_AND_POLL_FREQUENCY) {
+ this.calAndPollInterval = MIN_CAL_AND_POLL_FREQUENCY;
+ } else if (pollInterval >= MAX_CAL_AND_POLL_FREQUENCY) {
+ this.calAndPollInterval = MAX_CAL_AND_POLL_FREQUENCY;
+ } else {
+ this.calAndPollInterval = pollInterval;
+ }
+
+ calAndPollInterval = CAL_AND_POLL_TIMES * calAndPollInterval;
+ midPollInterval = MID_POLL_TIMES * calAndPollInterval;
+ longPollInterval = LONG_POLL_TIMES * calAndPollInterval;
+ entirePollInterval = ENTIRE_POLL_TIMES * calAndPollInterval;
+
+ callCountCalAndShortFlowsTask = 0;
+ callCountMidFlowsTask = 0;
+ callCountLongFlowsTask = 0;
+
+ flowMissingXid = NO_FLOW_MISSING_XID;
+ }
+
+ /**
+ * Adjusts adaptive poll frequency.
+ *
+ * @param pollInterval poll frequency in seconds
+ */
+ synchronized void adjustCalAndPollInterval(int pollInterval) {
+ initMemberVars(pollInterval);
+
+ if (calAndShortFlowsThread != null) {
+ calAndShortFlowsThread.cancel(false);
+ }
+ if (midFlowsThread != null) {
+ midFlowsThread.cancel(false);
+ }
+ if (longFlowsThread != null) {
+ longFlowsThread.cancel(false);
+ }
+
+ calAndShortFlowsTask = new CalAndShortFlowsTask();
+ calAndShortFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ calAndShortFlowsTask,
+ 0,
+ calAndPollInterval,
+ TimeUnit.SECONDS);
+
+ midFlowsTask = new MidFlowsTask();
+ midFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ midFlowsTask,
+ 0,
+ midPollInterval,
+ TimeUnit.SECONDS);
+
+ longFlowsTask = new LongFlowsTask();
+ longFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ longFlowsTask,
+ 0,
+ longPollInterval,
+ TimeUnit.SECONDS);
+
+ log.debug("calAndPollInterval=" + calAndPollInterval + "is adjusted");
+ }
+
+ private class CalAndShortFlowsTask implements Runnable {
+ @Override
+ public void run() {
+ if (sw.getRole() == RoleState.MASTER) {
+ log.trace("CalAndShortFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());
+
+ if (isFirstTimeStart) {
+ // isFirstTimeStart, get entire flow stats from a given switch sw
+ log.trace("CalAndShortFlowsTask Collecting Entire AdaptiveStats at first time start for {}",
+ sw.getStringId());
+ ofFlowStatsRequestAllSend();
+
+ callCountCalAndShortFlowsTask += CAL_AND_POLL_TIMES;
+ isFirstTimeStart = false;
+ } else if (callCountCalAndShortFlowsTask == ENTIRE_POLL_TIMES) {
+ // entire_poll_times, get entire flow stats from a given switch sw
+ log.trace("CalAndShortFlowsTask Collecting Entire AdaptiveStats for {}", sw.getStringId());
+ ofFlowStatsRequestAllSend();
+
+ callCountCalAndShortFlowsTask = CAL_AND_POLL_TIMES;
+ //TODO: check flows deleted in switch, but exist in controller flow table, then remove them
+ //
+ } else {
+ calAndShortFlowsTaskInternal();
+ callCountCalAndShortFlowsTask += CAL_AND_POLL_TIMES;
+ }
+ }
+ }
+ }
+
+ // send openflow flow stats request message with getting all flow entries to a given switch sw
+ private void ofFlowStatsRequestAllSend() {
+ OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest()
+ .setMatch(sw.factory().matchWildcardAll())
+ .setTableId(TableId.ALL)
+ .setOutPort(OFPort.NO_MASK)
+ .build();
+
+ synchronized (this) {
+ // set the request xid to check the reply in OpenFlowRuleProvider
+ // After processing the reply of this request message,
+ // this must be set to NO_FLOW_MISSING_XID(-1) by provider
+ setFlowMissingXid(request.getXid());
+ log.debug("ofFlowStatsRequestAllSend,Request={},for {}", request.toString(), sw.getStringId());
+
+ sw.sendMsg(request);
+ }
+ }
+
+ // send openflow flow stats request message with getting the specific flow entry(fe) to a given switch sw
+ private void ofFlowStatsRequestFlowSend(FlowEntry fe) {
+ // set find match
+ Match match = FlowModBuilder.builder(fe, sw.factory(), Optional.empty()).buildMatch();
+ // set find tableId
+ TableId tableId = TableId.of(fe.tableId());
+ // set output port
+ Instruction ins = fe.treatment().allInstructions().stream()
+ .filter(i -> (i.type() == Instruction.Type.OUTPUT))
+ .findFirst()
+ .orElse(null);
+ OFPort ofPort = OFPort.NO_MASK;
+ if (ins != null) {
+ Instructions.OutputInstruction out = (Instructions.OutputInstruction) ins;
+ ofPort = OFPort.of((int) ((out.port().toLong())));
+ }
+
+ OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest()
+ .setMatch(match)
+ .setTableId(tableId)
+ .setOutPort(ofPort)
+ .build();
+
+ synchronized (this) {
+ if (getFlowMissingXid() != NO_FLOW_MISSING_XID) {
+ log.debug("ofFlowStatsRequestFlowSend: previous FlowStatsRequestAll does not be processed yet,"
+ + " set no flow missing xid anyway, for {}",
+ sw.getStringId());
+ setFlowMissingXid(NO_FLOW_MISSING_XID);
+ }
+
+ sw.sendMsg(request);
+ }
+ }
+
+ private void calAndShortFlowsTaskInternal() {
+ deviceFlowTable.checkAndMoveLiveFlowAll();
+
+ deviceFlowTable.getShortFlows().forEach(fe -> {
+ ofFlowStatsRequestFlowSend(fe);
+ });
+ }
+
+ private class MidFlowsTask implements Runnable {
+ @Override
+ public void run() {
+ if (sw.getRole() == RoleState.MASTER) {
+ log.trace("MidFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());
+
+ // skip collecting because CalAndShortFlowsTask collects entire flow stats from a given switch sw
+ if (callCountMidFlowsTask == ENTIRE_POLL_TIMES) {
+ callCountMidFlowsTask = MID_POLL_TIMES;
+ } else {
+ midFlowsTaskInternal();
+ callCountMidFlowsTask += MID_POLL_TIMES;
+ }
+ }
+ }
+ }
+
+ private void midFlowsTaskInternal() {
+ deviceFlowTable.getMidFlows().forEach(fe -> {
+ ofFlowStatsRequestFlowSend(fe);
+ });
+ }
+
+ private class LongFlowsTask implements Runnable {
+ @Override
+ public void run() {
+ if (sw.getRole() == RoleState.MASTER) {
+ log.trace("LongFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());
+
+ // skip collecting because CalAndShortFlowsTask collects entire flow stats from a given switch sw
+ if (callCountLongFlowsTask == ENTIRE_POLL_TIMES) {
+ callCountLongFlowsTask = LONG_POLL_TIMES;
+ } else {
+ longFlowsTaskInternal();
+ callCountLongFlowsTask += LONG_POLL_TIMES;
+ }
+ }
+ }
+ }
+
+ private void longFlowsTaskInternal() {
+ deviceFlowTable.getLongFlows().forEach(fe -> {
+ ofFlowStatsRequestFlowSend(fe);
+ });
+ }
+
+ /**
+ * start adaptive flow statistic collection.
+ *
+ */
+ public synchronized void start() {
+ log.debug("Starting AdaptiveStats collection thread for {}", sw.getStringId());
+ callCountCalAndShortFlowsTask = 0;
+ callCountMidFlowsTask = 0;
+ callCountLongFlowsTask = 0;
+
+ isFirstTimeStart = true;
+
+ // Initially start polling quickly. Then drop down to configured value
+ calAndShortFlowsTask = new CalAndShortFlowsTask();
+ calAndShortFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ calAndShortFlowsTask,
+ 1,
+ calAndPollInterval,
+ TimeUnit.SECONDS);
+
+ midFlowsTask = new MidFlowsTask();
+ midFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ midFlowsTask,
+ 1,
+ midPollInterval,
+ TimeUnit.SECONDS);
+
+ longFlowsTask = new LongFlowsTask();
+ longFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ longFlowsTask,
+ 1,
+ longPollInterval,
+ TimeUnit.SECONDS);
+
+ log.info("Started");
+ }
+
+ /**
+ * stop adaptive flow statistic collection.
+ *
+ */
+ public synchronized void stop() {
+ log.debug("Stopping AdaptiveStats collection thread for {}", sw.getStringId());
+ if (calAndShortFlowsThread != null) {
+ calAndShortFlowsThread.cancel(true);
+ }
+ if (midFlowsThread != null) {
+ midFlowsThread.cancel(true);
+ }
+ if (longFlowsThread != null) {
+ longFlowsThread.cancel(true);
+ }
+
+ adaptiveFlowStatsScheduler.shutdownNow();
+
+ isFirstTimeStart = false;
+
+ log.info("Stopped");
+ }
+
+ /**
+ * add typed flow entry from flow rule into the internal flow table.
+ *
+ * @param flowRules the flow rules
+ *
+ */
+ public synchronized void addWithFlowRule(FlowRule... flowRules) {
+ for (FlowRule fr : flowRules) {
+ // First remove old entry unconditionally, if exist
+ deviceFlowTable.remove(fr);
+
+ // add new flow entry, we suppose IMMEDIATE_FLOW
+ TypedStoredFlowEntry newFlowEntry = new DefaultTypedFlowEntry(fr,
+ FlowLiveType.IMMEDIATE_FLOW);
+ deviceFlowTable.addWithCalAndSetFlowLiveType(newFlowEntry);
+ }
+ }
+
+ /**
+ * add or update typed flow entry from flow entry into the internal flow table.
+ *
+ * @param flowEntries the flow entries
+ *
+ */
+ public synchronized void addOrUpdateFlows(FlowEntry... flowEntries) {
+ for (FlowEntry fe : flowEntries) {
+ // check if this new rule is an update to an existing entry
+ TypedStoredFlowEntry stored = deviceFlowTable.getFlowEntry(fe);
+
+ if (stored != null) {
+ // duplicated flow entry is collected!, just skip
+ if (fe.bytes() == stored.bytes() && fe.packets() == stored.packets()
+ && fe.life() == stored.life()) {
+ log.debug("addOrUpdateFlows:, FlowId=" + Long.toHexString(fe.id().value())
+ + ",is DUPLICATED stats collection, just skip."
+ + " AdaptiveStats collection thread for {}",
+ sw.getStringId());
+
+ stored.setLastSeen();
+ continue;
+ } else if (fe.life() < stored.life()) {
+ // Invalid updates the stats values, i.e., bytes, packets, durations ...
+ log.debug("addOrUpdateFlows():" +
+ " Invalid Flow Update! The new life is SMALLER than the previous one, jus skip." +
+ " new flowId=" + Long.toHexString(fe.id().value()) +
+ ", old flowId=" + Long.toHexString(stored.id().value()) +
+ ", new bytes=" + fe.bytes() + ", old bytes=" + stored.bytes() +
+ ", new life=" + fe.life() + ", old life=" + stored.life() +
+ ", new lastSeen=" + fe.lastSeen() + ", old lastSeen=" + stored.lastSeen());
+ // go next
+ stored.setLastSeen();
+ continue;
+ }
+
+ // update now
+ stored.setLife(fe.life());
+ stored.setPackets(fe.packets());
+ stored.setBytes(fe.bytes());
+ stored.setLastSeen();
+ if (stored.state() == FlowEntry.FlowEntryState.PENDING_ADD) {
+ // flow is really RULE_ADDED
+ stored.setState(FlowEntry.FlowEntryState.ADDED);
+ }
+ // flow is RULE_UPDATED, skip adding and just updating flow live table
+ //deviceFlowTable.calAndSetFlowLiveType(stored);
+ continue;
+ }
+
+ // add new flow entry, we suppose IMMEDIATE_FLOW
+ TypedStoredFlowEntry newFlowEntry = new DefaultTypedFlowEntry(fe,
+ FlowLiveType.IMMEDIATE_FLOW);
+ deviceFlowTable.addWithCalAndSetFlowLiveType(newFlowEntry);
+ }
+ }
+
+ /**
+ * remove typed flow entry from the internal flow table.
+ *
+ * @param flowRules the flow entries
+ *
+ */
+ public synchronized void removeFlows(FlowRule... flowRules) {
+ for (FlowRule rule : flowRules) {
+ deviceFlowTable.remove(rule);
+ }
+ }
+
+ // same as removeFlows() function
+ /**
+ * remove typed flow entry from the internal flow table.
+ *
+ * @param flowRules the flow entries
+ *
+ */
+ public void flowRemoved(FlowRule... flowRules) {
+ removeFlows(flowRules);
+ }
+
+ // same as addOrUpdateFlows() function
+ /**
+ * add or update typed flow entry from flow entry into the internal flow table.
+ *
+ * @param flowEntries the flow entry list
+ *
+ */
+ public void pushFlowMetrics(List<FlowEntry> flowEntries) {
+ flowEntries.forEach(fe -> {
+ addOrUpdateFlows(fe);
+ });
+ }
+
+ /**
+ * returns flowMissingXid that indicates the execution of flowMissing process or not(NO_FLOW_MISSING_XID(-1)).
+ *
+ */
+ public long getFlowMissingXid() {
+ return flowMissingXid;
+ }
+
+ /**
+ * set flowMissingXid, namely OFFlowStatsRequest match any ALL message Id.
+ *
+ * @param flowMissingXid the OFFlowStatsRequest message Id
+ *
+ */
+ public void setFlowMissingXid(long flowMissingXid) {
+ this.flowMissingXid = flowMissingXid;
+ }
+
+ private class InternalDeviceFlowTable {
+
+ private final Map<FlowId, Set<TypedStoredFlowEntry>>
+ flowEntries = Maps.newConcurrentMap();
+
+ private final Set<StoredFlowEntry> shortFlows = new HashSet<>();
+ private final Set<StoredFlowEntry> midFlows = new HashSet<>();
+ private final Set<StoredFlowEntry> longFlows = new HashSet<>();
+
+ // Assumed latency adjustment(default=500 millisecond) between FlowStatsRequest and Reply
+ private final long latencyFlowStatsRequestAndReplyMillis = 500;
+
+
+ // Statistics for table operation
+ private long addCount = 0, addWithSetFlowLiveTypeCount = 0;
+ private long removeCount = 0;
+
+ /**
+ * Resets all count values with zero.
+ *
+ */
+ public void resetAllCount() {
+ addCount = 0;
+ addWithSetFlowLiveTypeCount = 0;
+ removeCount = 0;
+ }
+
+ // get set of flow entries for the given flowId
+ private Set<TypedStoredFlowEntry> getFlowEntriesInternal(FlowId flowId) {
+ return flowEntries.computeIfAbsent(flowId, id -> Sets.newCopyOnWriteArraySet());
+ }
+
+ // get flow entry for the given flow rule
+ private TypedStoredFlowEntry getFlowEntryInternal(FlowRule rule) {
+ Set<TypedStoredFlowEntry> flowEntries = getFlowEntriesInternal(rule.id());
+ return flowEntries.stream()
+ .filter(entry -> Objects.equal(entry, rule))
+ .findAny()
+ .orElse(null);
+ }
+
+ // get the flow entries for all flows in flow table
+ private Set<TypedStoredFlowEntry> getFlowEntriesInternal() {
+ Set<TypedStoredFlowEntry> result = Sets.newHashSet();
+
+ flowEntries.values().forEach(result::addAll);
+ return result;
+ }
+
+ /**
+ * Gets the number of flow entry in flow table.
+ *
+ * @return the number of flow entry.
+ *
+ */
+ public long getFlowCount() {
+ return flowEntries.values().stream().mapToLong(Set::size).sum();
+ }
+
+ /**
+ * Gets the number of flow entry in flow table.
+ *
+ * @param rule the flow rule
+ * @return the typed flow entry.
+ *
+ */
+ public TypedStoredFlowEntry getFlowEntry(FlowRule rule) {
+ checkNotNull(rule);
+
+ return getFlowEntryInternal(rule);
+ }
+
+ /**
+ * Gets the all typed flow entries in flow table.
+ *
+ * @return the set of typed flow entry.
+ *
+ */
+ public Set<TypedStoredFlowEntry> getFlowEntries() {
+ return getFlowEntriesInternal();
+ }
+
+ /**
+ * Gets the short typed flow entries in flow table.
+ *
+ * @return the set of typed flow entry.
+ *
+ */
+ public Set<StoredFlowEntry> getShortFlows() {
+ return ImmutableSet.copyOf(shortFlows); //Sets.newHashSet(shortFlows);
+ }
+
+ /**
+ * Gets the mid typed flow entries in flow table.
+ *
+ * @return the set of typed flow entry.
+ *
+ */
+ public Set<StoredFlowEntry> getMidFlows() {
+ return ImmutableSet.copyOf(midFlows); //Sets.newHashSet(midFlows);
+ }
+
+ /**
+ * Gets the long typed flow entries in flow table.
+ *
+ * @return the set of typed flow entry.
+ *
+ */
+ public Set<StoredFlowEntry> getLongFlows() {
+ return ImmutableSet.copyOf(longFlows); //Sets.newHashSet(longFlows);
+ }
+
+ /**
+ * Add typed flow entry into table only.
+ *
+ * @param rule the flow rule
+ *
+ */
+ public synchronized void add(TypedStoredFlowEntry rule) {
+ checkNotNull(rule);
+
+ //rule have to be new DefaultTypedFlowEntry
+ boolean result = getFlowEntriesInternal(rule.id()).add(rule);
+
+ if (result) {
+ addCount++;
+ }
+ }
+
+ /**
+ * Calculates and set the flow live type at the first time,
+ * and then add it into a corresponding typed flow table.
+ *
+ * @param rule the flow rule
+ *
+ */
+ public void calAndSetFlowLiveType(TypedStoredFlowEntry rule) {
+ checkNotNull(rule);
+
+ calAndSetFlowLiveTypeInternal(rule);
+ }
+
+ /**
+ * Add the typed flow entry into table, and calculates and set the flow live type,
+ * and then add it into a corresponding typed flow table.
+ *
+ * @param rule the flow rule
+ *
+ */
+ public synchronized void addWithCalAndSetFlowLiveType(TypedStoredFlowEntry rule) {
+ checkNotNull(rule);
+
+ //rule have to be new DefaultTypedFlowEntry
+ boolean result = getFlowEntriesInternal(rule.id()).add(rule);
+ if (result) {
+ calAndSetFlowLiveTypeInternal(rule);
+ addWithSetFlowLiveTypeCount++;
+ } else {
+ log.debug("addWithCalAndSetFlowLiveType, FlowId=" + Long.toHexString(rule.id().value())
+ + " ADD Failed, cause it may already exists in table !!!,"
+ + " AdaptiveStats collection thread for {}",
+ sw.getStringId());
+ }
+ }
+
+ // In real, calculates and set the flow live type at the first time,
+ // and then add it into a corresponding typed flow table
+ private void calAndSetFlowLiveTypeInternal(TypedStoredFlowEntry rule) {
+ long life = rule.life();
+ FlowLiveType prevFlowLiveType = rule.flowLiveType();
+
+ if (life >= longPollInterval) {
+ rule.setFlowLiveType(FlowLiveType.LONG_FLOW);
+ longFlows.add(rule);
+ } else if (life >= midPollInterval) {
+ rule.setFlowLiveType(FlowLiveType.MID_FLOW);
+ midFlows.add(rule);
+ } else if (life >= calAndPollInterval) {
+ rule.setFlowLiveType(FlowLiveType.SHORT_FLOW);
+ shortFlows.add(rule);
+ } else if (life >= 0) {
+ rule.setFlowLiveType(FlowLiveType.IMMEDIATE_FLOW);
+ } else { // life < 0
+ rule.setFlowLiveType(FlowLiveType.UNKNOWN_FLOW);
+ }
+
+ if (rule.flowLiveType() != prevFlowLiveType) {
+ switch (prevFlowLiveType) {
+ // delete it from previous flow table
+ case SHORT_FLOW:
+ shortFlows.remove(rule);
+ break;
+ case MID_FLOW:
+ midFlows.remove(rule);
+ break;
+ case LONG_FLOW:
+ longFlows.remove(rule);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+
+ // check the flow live type based on current time, then set and add it into corresponding table
+ private boolean checkAndMoveLiveFlowInternal(TypedStoredFlowEntry fe, long cTime) {
+ long curTime = (cTime > 0 ? cTime : System.currentTimeMillis());
+ // For latency adjustment(default=500 millisecond) between FlowStatsRequest and Reply
+ long fromLastSeen = ((curTime - fe.lastSeen() + latencyFlowStatsRequestAndReplyMillis) / 1000);
+ // fe.life() unit is SECOND!
+ long liveTime = fe.life() + fromLastSeen;
+
+
+ switch (fe.flowLiveType()) {
+ case IMMEDIATE_FLOW:
+ if (liveTime >= longPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.LONG_FLOW);
+ longFlows.add(fe);
+ } else if (liveTime >= midPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.MID_FLOW);
+ midFlows.add(fe);
+ } else if (liveTime >= calAndPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.SHORT_FLOW);
+ shortFlows.add(fe);
+ }
+ break;
+ case SHORT_FLOW:
+ if (liveTime >= longPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.LONG_FLOW);
+ shortFlows.remove(fe);
+ longFlows.add(fe);
+ } else if (liveTime >= midPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.MID_FLOW);
+ shortFlows.remove(fe);
+ midFlows.add(fe);
+ }
+ break;
+ case MID_FLOW:
+ if (liveTime >= longPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.LONG_FLOW);
+ midFlows.remove(fe);
+ longFlows.add(fe);
+ }
+ break;
+ case LONG_FLOW:
+ if (fromLastSeen > entirePollInterval) {
+ log.trace("checkAndMoveLiveFlowInternal, flow is already removed at switch.");
+ return false;
+ }
+ break;
+ case UNKNOWN_FLOW: // Unknown flow is an internal error flow type, just fall through
+ default :
+ // Error Unknown Live Type
+ log.error("checkAndMoveLiveFlowInternal, Unknown Live Type error!"
+ + "AdaptiveStats collection thread for {}",
+ sw.getStringId());
+ return false;
+ }
+
+ log.debug("checkAndMoveLiveFlowInternal, FlowId=" + Long.toHexString(fe.id().value())
+ + ", state=" + fe.state()
+ + ", After liveType=" + fe.flowLiveType()
+ + ", liveTime=" + liveTime
+ + ", life=" + fe.life()
+ + ", bytes=" + fe.bytes()
+ + ", packets=" + fe.packets()
+ + ", fromLastSeen=" + fromLastSeen
+ + ", priority=" + fe.priority()
+ + ", selector=" + fe.selector().criteria()
+ + ", treatment=" + fe.treatment()
+ + " AdaptiveStats collection thread for {}",
+ sw.getStringId());
+
+ return true;
+ }
+
+ /**
+ * Check and move live type for all type flow entries in table at every calAndPollInterval time.
+ *
+ */
+ public void checkAndMoveLiveFlowAll() {
+ Set<TypedStoredFlowEntry> typedFlowEntries = getFlowEntriesInternal();
+
+ long calCurTime = System.currentTimeMillis();
+ typedFlowEntries.forEach(fe -> {
+ if (!checkAndMoveLiveFlowInternal(fe, calCurTime)) {
+ remove(fe);
+ }
+ });
+
+ // print table counts for debug
+ if (log.isDebugEnabled()) {
+ synchronized (this) {
+ long totalFlowCount = getFlowCount();
+ long shortFlowCount = shortFlows.size();
+ long midFlowCount = midFlows.size();
+ long longFlowCount = longFlows.size();
+ long immediateFlowCount = totalFlowCount - shortFlowCount - midFlowCount - longFlowCount;
+ long calTotalCount = addCount + addWithSetFlowLiveTypeCount - removeCount;
+
+ log.debug("--------------------------------------------------------------------------- for {}",
+ sw.getStringId());
+ log.debug("checkAndMoveLiveFlowAll, Total Flow_Count=" + totalFlowCount
+ + ", add - remove_Count=" + calTotalCount
+ + ", IMMEDIATE_FLOW_Count=" + immediateFlowCount
+ + ", SHORT_FLOW_Count=" + shortFlowCount
+ + ", MID_FLOW_Count=" + midFlowCount
+ + ", LONG_FLOW_Count=" + longFlowCount
+ + ", add_Count=" + addCount
+ + ", addWithSetFlowLiveType_Count=" + addWithSetFlowLiveTypeCount
+ + ", remove_Count=" + removeCount
+ + " AdaptiveStats collection thread for {}", sw.getStringId());
+ log.debug("--------------------------------------------------------------------------- for {}",
+ sw.getStringId());
+ if (totalFlowCount != calTotalCount) {
+ log.error("checkAndMoveLiveFlowAll, Real total flow count and "
+ + "calculated total flow count do NOT match, something is wrong internally "
+ + "or check counter value bound is over!");
+ }
+ if (immediateFlowCount < 0) {
+ log.error("checkAndMoveLiveFlowAll, IMMEDIATE_FLOW count is negative, "
+ + "something is wrong internally "
+ + "or check counter value bound is over!");
+ }
+ }
+ }
+ log.trace("checkAndMoveLiveFlowAll, AdaptiveStats for {}", sw.getStringId());
+ }
+
+ /**
+ * Remove the typed flow entry from table.
+ *
+ * @param rule the flow rule
+ *
+ */
+ public synchronized void remove(FlowRule rule) {
+ checkNotNull(rule);
+
+ TypedStoredFlowEntry removeStore = getFlowEntryInternal(rule);
+ if (removeStore != null) {
+ removeLiveFlowsInternal((TypedStoredFlowEntry) removeStore);
+ boolean result = getFlowEntriesInternal(rule.id()).remove(removeStore);
+
+ if (result) {
+ removeCount++;
+ }
+ }
+ }
+
+ // Remove the typed flow entry from corresponding table
+ private void removeLiveFlowsInternal(TypedStoredFlowEntry fe) {
+ switch (fe.flowLiveType()) {
+ case IMMEDIATE_FLOW:
+ // do nothing
+ break;
+ case SHORT_FLOW:
+ shortFlows.remove(fe);
+ break;
+ case MID_FLOW:
+ midFlows.remove(fe);
+ break;
+ case LONG_FLOW:
+ longFlows.remove(fe);
+ break;
+ default: // error in Flow Live Type
+ log.error("removeLiveFlowsInternal, Unknown Live Type error!");
+ break;
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
index de079e03..6374ca55 100644
--- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Open Networking Laboratory
+ * 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.
@@ -21,6 +21,7 @@ import com.google.common.cache.RemovalCause;
import com.google.common.cache.RemovalNotification;
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;
@@ -32,6 +33,7 @@ import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.flow.CompletedBatchOperation;
+import org.onosproject.net.flow.DefaultTableStatisticsEntry;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleBatchEntry;
@@ -40,6 +42,7 @@ import org.onosproject.net.flow.FlowRuleExtPayLoad;
import org.onosproject.net.flow.FlowRuleProvider;
import org.onosproject.net.flow.FlowRuleProviderRegistry;
import org.onosproject.net.flow.FlowRuleProviderService;
+import org.onosproject.net.flow.TableStatisticsEntry;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.net.statistic.DefaultLoad;
@@ -58,6 +61,8 @@ import org.projectfloodlight.openflow.protocol.OFErrorType;
import org.projectfloodlight.openflow.protocol.OFFlowMod;
import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
+import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
+import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFPortStatus;
import org.projectfloodlight.openflow.protocol.OFStatsReply;
@@ -70,12 +75,14 @@ import java.util.Collections;
import java.util.Dictionary;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.Timer;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
+import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.onlab.util.Tools.get;
import static org.slf4j.LoggerFactory.getLogger;
@@ -99,11 +106,16 @@ public class OpenFlowRuleProvider extends AbstractProvider
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ComponentConfigService cfgService;
- private static final int DEFAULT_POLL_FREQUENCY = 10;
+ private static final int DEFAULT_POLL_FREQUENCY = 5;
@Property(name = "flowPollFrequency", intValue = DEFAULT_POLL_FREQUENCY,
label = "Frequency (in seconds) for polling flow statistics")
private int flowPollFrequency = DEFAULT_POLL_FREQUENCY;
+ private static final boolean DEFAULT_ADAPTIVE_FLOW_SAMPLING = true;
+ @Property(name = "adaptiveFlowSampling", boolValue = DEFAULT_ADAPTIVE_FLOW_SAMPLING,
+ label = "Adaptive Flow Sampling is on or off")
+ private boolean adaptiveFlowSampling = DEFAULT_ADAPTIVE_FLOW_SAMPLING;
+
private FlowRuleProviderService providerService;
private final InternalFlowProvider listener = new InternalFlowProvider();
@@ -111,7 +123,12 @@ public class OpenFlowRuleProvider extends AbstractProvider
private Cache<Long, InternalCacheEntry> pendingBatches;
private final Timer timer = new Timer("onos-openflow-collector");
+ private final Map<Dpid, FlowStatsCollector> simpleCollectors = Maps.newHashMap();
+
+ // NewAdaptiveFlowStatsCollector Set
+ private final Map<Dpid, NewAdaptiveFlowStatsCollector> afsCollectors = Maps.newHashMap();
private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
+ private final Map<Dpid, TableStatisticsCollector> tableStatsCollectors = Maps.newHashMap();
/**
* Creates an OpenFlow host provider.
@@ -128,9 +145,11 @@ public class OpenFlowRuleProvider extends AbstractProvider
controller.addEventListener(listener);
pendingBatches = createBatchCache();
+
createCollectors();
- log.info("Started");
+ log.info("Started with flowPollFrequency = {}, adaptiveFlowSampling = {}",
+ flowPollFrequency, adaptiveFlowSampling);
}
@Deactivate
@@ -161,6 +180,20 @@ public class OpenFlowRuleProvider extends AbstractProvider
}
log.info("Settings: flowPollFrequency={}", flowPollFrequency);
+
+ boolean newAdaptiveFlowSampling;
+ String s = get(properties, "adaptiveFlowSampling");
+ newAdaptiveFlowSampling = isNullOrEmpty(s) ? adaptiveFlowSampling : Boolean.parseBoolean(s.trim());
+
+ if (newAdaptiveFlowSampling != adaptiveFlowSampling) {
+ // stop previous collector
+ stopCollectors();
+ adaptiveFlowSampling = newAdaptiveFlowSampling;
+ // create new collectors
+ createCollectors();
+ }
+
+ log.info("Settings: adaptiveFlowSampling={}", adaptiveFlowSampling);
}
private Cache<Long, InternalCacheEntry> createBatchCache() {
@@ -179,19 +212,43 @@ public class OpenFlowRuleProvider extends AbstractProvider
}
private void createCollector(OpenFlowSwitch sw) {
- FlowStatsCollector fsc = new FlowStatsCollector(timer, sw, flowPollFrequency);
- fsc.start();
- collectors.put(new Dpid(sw.getId()), fsc);
+ if (adaptiveFlowSampling) {
+ // NewAdaptiveFlowStatsCollector Constructor
+ NewAdaptiveFlowStatsCollector fsc = new NewAdaptiveFlowStatsCollector(sw, flowPollFrequency);
+ fsc.start();
+ afsCollectors.put(new Dpid(sw.getId()), fsc);
+ } else {
+ FlowStatsCollector fsc = new FlowStatsCollector(timer, sw, flowPollFrequency);
+ fsc.start();
+ simpleCollectors.put(new Dpid(sw.getId()), fsc);
+ }
+ TableStatisticsCollector tsc = new TableStatisticsCollector(timer, sw, flowPollFrequency);
+ tsc.start();
+ tableStatsCollectors.put(new Dpid(sw.getId()), tsc);
}
private void stopCollectors() {
- collectors.values().forEach(FlowStatsCollector::stop);
- collectors.clear();
+ if (adaptiveFlowSampling) {
+ // NewAdaptiveFlowStatsCollector Destructor
+ afsCollectors.values().forEach(NewAdaptiveFlowStatsCollector::stop);
+ afsCollectors.clear();
+ } else {
+ simpleCollectors.values().forEach(FlowStatsCollector::stop);
+ simpleCollectors.clear();
+ }
+ tableStatsCollectors.values().forEach(TableStatisticsCollector::stop);
+ tableStatsCollectors.clear();
}
private void adjustRate() {
DefaultLoad.setPollInterval(flowPollFrequency);
- collectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency));
+ if (adaptiveFlowSampling) {
+ // NewAdaptiveFlowStatsCollector calAndPollInterval
+ afsCollectors.values().forEach(fsc -> fsc.adjustCalAndPollInterval(flowPollFrequency));
+ } else {
+ simpleCollectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency));
+ }
+ tableStatsCollectors.values().forEach(tsc -> tsc.adjustPollInterval(flowPollFrequency));
}
@Override
@@ -202,8 +259,9 @@ public class OpenFlowRuleProvider extends AbstractProvider
}
private void applyRule(FlowRule flowRule) {
- OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId()
- .uri()));
+ Dpid dpid = Dpid.dpid(flowRule.deviceId().uri());
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+
FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad();
if (hasPayload(flowRuleExtPayLoad)) {
OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad());
@@ -212,6 +270,14 @@ public class OpenFlowRuleProvider extends AbstractProvider
}
sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
Optional.empty()).buildFlowAdd());
+
+ if (adaptiveFlowSampling) {
+ // Add TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+ NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid);
+ if (collector != null) {
+ collector.addWithFlowRule(flowRule);
+ }
+ }
}
@Override
@@ -222,8 +288,9 @@ public class OpenFlowRuleProvider extends AbstractProvider
}
private void removeRule(FlowRule flowRule) {
- OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId()
- .uri()));
+ Dpid dpid = Dpid.dpid(flowRule.deviceId().uri());
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+
FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad();
if (hasPayload(flowRuleExtPayLoad)) {
OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad());
@@ -232,6 +299,14 @@ public class OpenFlowRuleProvider extends AbstractProvider
}
sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
Optional.empty()).buildFlowDel());
+
+ if (adaptiveFlowSampling) {
+ // Remove TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+ NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid);
+ if (collector != null) {
+ collector.removeFlows(flowRule);
+ }
+ }
}
@Override
@@ -242,11 +317,12 @@ public class OpenFlowRuleProvider extends AbstractProvider
@Override
public void executeBatch(FlowRuleBatchOperation batch) {
+ checkNotNull(batch);
pendingBatches.put(batch.id(), new InternalCacheEntry(batch));
- OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(batch.deviceId()
- .uri()));
+ Dpid dpid = Dpid.dpid(batch.deviceId().uri());
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
OFFlowMod mod;
for (FlowRuleBatchEntry fbe : batch.getOperations()) {
// flow is the third party privacy flow
@@ -257,21 +333,35 @@ public class OpenFlowRuleProvider extends AbstractProvider
sw.sendMsg(msg);
continue;
}
- FlowModBuilder builder = FlowModBuilder.builder(fbe.target(), sw
- .factory(), Optional.of(batch.id()));
+ FlowModBuilder builder =
+ FlowModBuilder.builder(fbe.target(), sw.factory(), Optional.of(batch.id()));
+ NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid);
switch (fbe.operator()) {
case ADD:
mod = builder.buildFlowAdd();
+ if (adaptiveFlowSampling && collector != null) {
+ // Add TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+ collector.addWithFlowRule(fbe.target());
+ }
break;
case REMOVE:
mod = builder.buildFlowDel();
+ if (adaptiveFlowSampling && collector != null) {
+ // Remove TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+ collector.removeFlows(fbe.target());
+ }
break;
case MODIFY:
mod = builder.buildFlowMod();
+ if (adaptiveFlowSampling && collector != null) {
+ // Add or Update TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+ // afsCollectors.get(dpid).addWithFlowRule(fbe.target()); //check if add is good or not
+ collector.addOrUpdateFlows((FlowEntry) fbe.target());
+ }
break;
default:
log.error("Unsupported batch operation {}; skipping flowmod {}",
- fbe.operator(), fbe);
+ fbe.operator(), fbe);
continue;
}
sw.sendMsg(mod);
@@ -292,14 +382,28 @@ public class OpenFlowRuleProvider extends AbstractProvider
@Override
public void switchAdded(Dpid dpid) {
+
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+
createCollector(controller.getSwitch(dpid));
}
@Override
public void switchRemoved(Dpid dpid) {
- FlowStatsCollector collector = collectors.remove(dpid);
- if (collector != null) {
- collector.stop();
+ if (adaptiveFlowSampling) {
+ NewAdaptiveFlowStatsCollector collector = afsCollectors.remove(dpid);
+ if (collector != null) {
+ collector.stop();
+ }
+ } else {
+ FlowStatsCollector collector = simpleCollectors.remove(dpid);
+ if (collector != null) {
+ collector.stop();
+ }
+ }
+ TableStatisticsCollector tsc = tableStatsCollectors.remove(dpid);
+ if (tsc != null) {
+ tsc.stop();
}
}
@@ -321,10 +425,20 @@ public class OpenFlowRuleProvider extends AbstractProvider
FlowEntry fr = new FlowEntryBuilder(dpid, removed).build();
providerService.flowRemoved(fr);
+
+ if (adaptiveFlowSampling) {
+ // Removed TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+ NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid);
+ if (collector != null) {
+ collector.flowRemoved(fr);
+ }
+ }
break;
case STATS_REPLY:
if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW) {
pushFlowMetrics(dpid, (OFFlowStatsReply) msg);
+ } else if (((OFStatsReply) msg).getStatsType() == OFStatsType.TABLE) {
+ pushTableStatistics(dpid, (OFTableStatsReply) msg);
}
break;
case BARRIER_REPLY:
@@ -370,11 +484,10 @@ public class OpenFlowRuleProvider extends AbstractProvider
+ " tell us which one.");
}
}
- break;
+
default:
log.debug("Unhandled message type: {}", msg.getType());
}
-
}
@Override
@@ -386,13 +499,68 @@ public class OpenFlowRuleProvider extends AbstractProvider
private void pushFlowMetrics(Dpid dpid, OFFlowStatsReply replies) {
DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
- OpenFlowSwitch sw = controller.getSwitch(dpid);
List<FlowEntry> flowEntries = replies.getEntries().stream()
.map(entry -> new FlowEntryBuilder(dpid, entry).build())
.collect(Collectors.toList());
- providerService.pushFlowMetrics(did, flowEntries);
+ if (adaptiveFlowSampling) {
+ NewAdaptiveFlowStatsCollector afsc = afsCollectors.get(dpid);
+
+ synchronized (afsc) {
+ if (afsc.getFlowMissingXid() != NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID) {
+ log.debug("OpenFlowRuleProvider:pushFlowMetrics, flowMissingXid={}, "
+ + "OFFlowStatsReply Xid={}, for {}",
+ afsc.getFlowMissingXid(), replies.getXid(), dpid);
+ }
+
+ // Check that OFFlowStatsReply Xid is same with the one of OFFlowStatsRequest?
+ if (afsc.getFlowMissingXid() != NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID) {
+ if (afsc.getFlowMissingXid() == replies.getXid()) {
+ // call entire flow stats update with flowMissing synchronization.
+ // used existing pushFlowMetrics
+ providerService.pushFlowMetrics(did, flowEntries);
+ }
+ // reset flowMissingXid to NO_FLOW_MISSING_XID
+ afsc.setFlowMissingXid(NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID);
+
+ } else {
+ // call individual flow stats update
+ providerService.pushFlowMetricsWithoutFlowMissing(did, flowEntries);
+ }
+
+ // Update TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector
+ afsc.pushFlowMetrics(flowEntries);
+ }
+ } else {
+ // call existing entire flow stats update with flowMissing synchronization
+ providerService.pushFlowMetrics(did, flowEntries);
+ }
+ }
+
+ private void pushTableStatistics(Dpid dpid, OFTableStatsReply replies) {
+
+ DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
+ List<TableStatisticsEntry> tableStatsEntries = replies.getEntries().stream()
+ .map(entry -> buildTableStatistics(did, entry))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ providerService.pushTableStatistics(did, tableStatsEntries);
+ }
+
+ private TableStatisticsEntry buildTableStatistics(DeviceId deviceId,
+ OFTableStatsEntry ofEntry) {
+ TableStatisticsEntry entry = null;
+ if (ofEntry != null) {
+ entry = new DefaultTableStatisticsEntry(deviceId,
+ ofEntry.getTableId().getValue(),
+ ofEntry.getActiveCount(),
+ ofEntry.getLookupCount().getValue(),
+ ofEntry.getMatchedCount().getValue());
+ }
+
+ return entry;
+
}
}
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java
new file mode 100644
index 00000000..922a470a
--- /dev/null
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java
@@ -0,0 +1,95 @@
+/*
+ * 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.flow.impl;
+
+import org.onlab.util.SharedExecutors;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFTableStatsRequest;
+import org.slf4j.Logger;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Collects Table statistics for the specified switch.
+ */
+class TableStatisticsCollector {
+
+ private final Logger log = getLogger(getClass());
+
+ public static final int SECONDS = 1000;
+
+ private final OpenFlowSwitch sw;
+ private Timer timer;
+ private TimerTask task;
+
+ private int pollInterval;
+
+ /**
+ * Creates a new table statistics collector for the given switch and poll frequency.
+ *
+ * @param timer timer to use for scheduling
+ * @param sw switch to pull
+ * @param pollInterval poll frequency in seconds
+ */
+ TableStatisticsCollector(Timer timer, OpenFlowSwitch sw, int pollInterval) {
+ this.timer = timer;
+ this.sw = sw;
+ this.pollInterval = pollInterval;
+ }
+
+ /**
+ * Adjusts poll frequency.
+ *
+ * @param pollInterval poll frequency in seconds
+ */
+ synchronized void adjustPollInterval(int pollInterval) {
+ this.pollInterval = pollInterval;
+ task.cancel();
+ task = new InternalTimerTask();
+ timer.scheduleAtFixedRate(task, pollInterval * SECONDS, pollInterval * 1000);
+ }
+
+ private class InternalTimerTask extends TimerTask {
+ @Override
+ public void run() {
+ if (sw.getRole() == RoleState.MASTER) {
+ log.trace("Collecting stats for {}", sw.getStringId());
+ OFTableStatsRequest request = sw.factory().buildTableStatsRequest()
+ .build();
+ sw.sendMsg(request);
+ }
+ }
+ }
+
+ public synchronized void start() {
+ // Initially start polling quickly. Then drop down to configured value
+ log.debug("Starting Table Stats collection thread for {}", sw.getStringId());
+ task = new InternalTimerTask();
+ SharedExecutors.getTimer().scheduleAtFixedRate(task, 1 * SECONDS,
+ pollInterval * SECONDS);
+ }
+
+ public synchronized void stop() {
+ log.debug("Stopping Table Stats collection thread for {}", sw.getStringId());
+ task.cancel();
+ task = null;
+ }
+
+}