ONOS-7077 Openflow 1.5 OXS and stat trigger support

Change-Id: I006bcd3d8eac451a780c7e5c69a12298ead14281
diff --git a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
index 6c720c8..8bde2f6 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
@@ -17,6 +17,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 import org.onlab.packet.EthType;
@@ -49,6 +50,7 @@
     private final List<Instruction> all;
     private final Instructions.TableTypeTransition table;
     private final Instructions.MetadataInstruction meta;
+    private final Instructions.StatTriggerInstruction statTrigger;
 
     private final boolean hasClear;
 
@@ -69,6 +71,7 @@
         this.table = null;
         this.meta = null;
         this.meter = null;
+        this.statTrigger = null;
     }
 
     /**
@@ -80,11 +83,13 @@
      * @param clear instruction to clear the deferred actions list
      */
     private DefaultTrafficTreatment(List<Instruction> deferred,
-                                   List<Instruction> immediate,
-                                   Instructions.TableTypeTransition table,
-                                   boolean clear,
-                                   Instructions.MetadataInstruction meta,
-                                   Instructions.MeterInstruction meter) {
+                                    List<Instruction> immediate,
+                                    Instructions.TableTypeTransition table,
+                                    boolean clear,
+                                    Instructions.MetadataInstruction meta,
+                                    Instructions.MeterInstruction meter,
+                                    Instructions.StatTriggerInstruction statTrigger
+                                    ) {
         this.immediate = ImmutableList.copyOf(checkNotNull(immediate));
         this.deferred = ImmutableList.copyOf(checkNotNull(deferred));
         this.all = new ImmutableList.Builder<Instruction>()
@@ -95,6 +100,7 @@
         this.meta = meta;
         this.hasClear = clear;
         this.meter = meter;
+        this.statTrigger = statTrigger;
     }
 
     @Override
@@ -128,6 +134,11 @@
     }
 
     @Override
+    public Instructions.StatTriggerInstruction statTrigger() {
+        return statTrigger;
+    }
+
+    @Override
     public Instructions.MeterInstruction metered() {
         return meter;
     }
@@ -191,6 +202,7 @@
                 .add("transition", table == null ? "None" : table.toString())
                 .add("meter", meter == null ? "None" : meter.toString())
                 .add("cleared", hasClear)
+                .add("StatTrigger", statTrigger)
                 .add("metadata", meta)
                 .toString();
     }
@@ -209,6 +221,8 @@
 
         Instructions.MeterInstruction meter;
 
+        Instructions.StatTriggerInstruction statTrigger;
+
         List<Instruction> deferred = new ArrayList<>();
 
         List<Instruction> immediate = new ArrayList<>();
@@ -259,6 +273,9 @@
                 case METER:
                     meter = (Instructions.MeterInstruction) instruction;
                     break;
+                case STAT_TRIGGER:
+                    statTrigger = (Instructions.StatTriggerInstruction) instruction;
+                    break;
                 default:
                     throw new IllegalArgumentException("Unknown instruction type: " +
                                                                instruction.type());
@@ -493,6 +510,12 @@
         }
 
         @Override
+        public TrafficTreatment.Builder statTrigger(Map<StatTriggerField, Long> statTriggerFieldMap,
+                                                    StatTriggerFlag statTriggerFlag) {
+            return add(Instructions.statTrigger(statTriggerFieldMap, statTriggerFlag));
+        }
+
+        @Override
         public TrafficTreatment.Builder addTreatment(TrafficTreatment treatment) {
             List<Instruction> previous = current;
             deferred();
@@ -516,7 +539,7 @@
                 immediate();
                 noAction();
             }
-            return new DefaultTrafficTreatment(deferred, immediate, table, clear, meta, meter);
+            return new DefaultTrafficTreatment(deferred, immediate, table, clear, meta, meter, statTrigger);
         }
 
     }
diff --git a/core/api/src/main/java/org/onosproject/net/flow/StatTriggerField.java b/core/api/src/main/java/org/onosproject/net/flow/StatTriggerField.java
new file mode 100644
index 0000000..f946542
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/flow/StatTriggerField.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.flow;
+
+/**
+ * Stat fields are supported default by OXS.
+ */
+public enum StatTriggerField {
+    /** Time flow entry has been alive. Unit indicates nanoseconds. */
+    DURATION,
+    /** Time flow entry has been idle. Unit indicates nanoseconds. */
+    IDLE_TIME,
+    /** Number of aggregated flow entries. */
+    FLOW_COUNT,
+    /** Number of packets in flow entry. */
+    PACKET_COUNT,
+    /** Number of bytes in flow entry. */
+    BYTE_COUNT
+}
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/net/flow/StatTriggerFlag.java b/core/api/src/main/java/org/onosproject/net/flow/StatTriggerFlag.java
new file mode 100644
index 0000000..7ed650c
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/flow/StatTriggerFlag.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.flow;
+
+/**
+ * Stat Trigger Flags.
+ */
+public enum StatTriggerFlag {
+    /** Trigger for all multiples of thresholds. */
+    PERIODIC,
+    /** Trigger on only first reach threshold. */
+    ONLY_FIRST
+}
diff --git a/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java b/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
index 9ddabe1..057f0f8 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
@@ -16,6 +16,7 @@
 package org.onosproject.net.flow;
 
 import java.util.List;
+import java.util.Map;
 
 import com.google.common.annotations.Beta;
 import org.onlab.packet.EthType;
@@ -81,6 +82,12 @@
     Instructions.MetadataInstruction writeMetadata();
 
     /**
+     * Returns the stat trigger instruction if there is one.
+     * @return a stat trigger instruction; may be null.
+     */
+    Instructions.StatTriggerInstruction statTrigger();
+
+    /**
      * Returns the meter instruction if there is one.
      *
      * @return a meter instruction that may be null
@@ -422,6 +429,16 @@
         Builder extension(ExtensionTreatment extension, DeviceId deviceId);
 
         /**
+         * Add stat trigger instruction.
+         *
+         * @param statTriggerFieldMap defines stat trigger constraints
+         * @param statTriggerFlag describes which circumstances that start will be triggered
+         * @return a treatment builder
+         */
+        Builder statTrigger(Map<StatTriggerField, Long> statTriggerFieldMap,
+                            StatTriggerFlag statTriggerFlag);
+
+        /**
          * Add all instructions from another treatment.
          *
          * @param treatment another treatment
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java
index c3c9b60..66fbb18 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java
@@ -96,7 +96,12 @@
         /**
          * Signifies that an extension instruction will be used.
          */
-        EXTENSION
+        EXTENSION,
+
+        /**
+         * Signifies that statistics will be triggered.
+         */
+        STAT_TRIGGER
     }
 
     /**
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
index 460976a..9bd7df5 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
@@ -16,6 +16,7 @@
 package org.onosproject.net.flow.instructions;
 
 import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableMap;
 import org.onlab.packet.EthType;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
@@ -28,6 +29,8 @@
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.OduSignalId;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.StatTriggerField;
+import org.onosproject.net.flow.StatTriggerFlag;
 import org.onosproject.net.flow.instructions.L0ModificationInstruction.ModOchSignalInstruction;
 import org.onosproject.net.flow.instructions.L1ModificationInstruction.ModOduSignalIdInstruction;
 import org.onosproject.net.flow.instructions.L3ModificationInstruction.L3SubType;
@@ -42,6 +45,7 @@
 import org.onosproject.net.meter.MeterId;
 import org.onosproject.net.pi.runtime.PiTableAction;
 
+import java.util.Map;
 import java.util.Objects;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
@@ -498,6 +502,20 @@
     }
 
     /**
+     * Creates a stat trigger instruction.
+     *
+     * @param statTriggerMap map keeps stat trigger threshold
+     * @param flag stat trigger flag
+     * @return stat trigger instruction
+     */
+    public static StatTriggerInstruction statTrigger(Map<StatTriggerField, Long> statTriggerMap,
+                                                     StatTriggerFlag flag) {
+        checkNotNull(statTriggerMap, "Stat trigger map cannot be null");
+        checkNotNull(flag, "Stat trigger flag  cannot be null");
+        return new StatTriggerInstruction(statTriggerMap, flag);
+    }
+
+    /**
      *  No Action instruction.
      */
     public static final class NoActionInstruction implements Instruction {
@@ -865,6 +883,68 @@
         }
     }
 
+    public static class StatTriggerInstruction implements Instruction {
+        private Map<StatTriggerField, Long> statTriggerFieldMap;
+        private StatTriggerFlag statTriggerFlag;
+
+
+        StatTriggerInstruction(Map<StatTriggerField, Long> statTriggerMap,
+                                      StatTriggerFlag flag) {
+            this.statTriggerFieldMap = ImmutableMap.copyOf(statTriggerMap);
+            this.statTriggerFlag = flag;
+        }
+
+        public Map<StatTriggerField, Long> getStatTriggerFieldMap() {
+            return statTriggerFieldMap;
+        }
+
+        public StatTriggerFlag getStatTriggerFlag() {
+            return statTriggerFlag;
+        }
+
+        public Long getStatValue(StatTriggerField field) {
+            return statTriggerFieldMap.get(field);
+        }
+
+        @Override
+        public Type type() {
+            return Type.STAT_TRIGGER;
+        }
+
+        @Override
+        public String toString() {
+            return "StatTriggerInstruction{" +
+                    "statTriggerFieldMap=" + statTriggerFieldMap +
+                    ", statTriggerFlag=" + statTriggerFlag +
+                    '}';
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            StatTriggerInstruction that = (StatTriggerInstruction) o;
+
+            if (!Objects.equals(statTriggerFieldMap, that.statTriggerFieldMap)) {
+                return false;
+            }
+
+            return statTriggerFlag == that.statTriggerFlag;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = statTriggerFieldMap != null ? statTriggerFieldMap.hashCode() : 0;
+            result = 31 * result + (statTriggerFlag != null ? statTriggerFlag.hashCode() : 0);
+            return result;
+        }
+    }
+
 }
 
 
diff --git a/core/api/src/test/java/org/onosproject/net/flow/instructions/InstructionsTest.java b/core/api/src/test/java/org/onosproject/net/flow/instructions/InstructionsTest.java
index 9eada31..ddf6500 100644
--- a/core/api/src/test/java/org/onosproject/net/flow/instructions/InstructionsTest.java
+++ b/core/api/src/test/java/org/onosproject/net/flow/instructions/InstructionsTest.java
@@ -31,6 +31,8 @@
 import org.onosproject.net.Lambda;
 import org.onosproject.net.OduSignalId;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.StatTriggerField;
+import org.onosproject.net.flow.StatTriggerFlag;
 import org.onosproject.net.meter.MeterId;
 import org.onosproject.net.pi.runtime.PiAction;
 import org.onosproject.net.pi.runtime.PiActionId;
@@ -38,7 +40,9 @@
 import org.onosproject.net.pi.runtime.PiActionParamId;
 import org.onosproject.net.pi.runtime.PiTableAction;
 
+import java.util.EnumMap;
 import java.util.List;
+import java.util.Map;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.equalTo;
@@ -829,6 +833,49 @@
                                meterInstruction2);
     }
 
+    private long packetCountValue1 = 5L;
+    private long byteCountValue1 = 10L;
+    private long packetCountValue2 = 10L;
+    private long byteCountValue2 = 5L;
+    private StatTriggerFlag flag1 = StatTriggerFlag.ONLY_FIRST;
+    private StatTriggerFlag flag2 = StatTriggerFlag.PERIODIC;
+    Map<StatTriggerField, Long> statTriggerFieldMap1 = new EnumMap<StatTriggerField, Long>(StatTriggerField.class) {
+        {
+            put(StatTriggerField.BYTE_COUNT, packetCountValue1);
+            put(StatTriggerField.PACKET_COUNT, byteCountValue1);
+        }
+    };
+    Map<StatTriggerField, Long> statTriggerFieldMap2 = new EnumMap<StatTriggerField, Long>(StatTriggerField.class) {
+        {
+            put(StatTriggerField.BYTE_COUNT, packetCountValue2);
+            put(StatTriggerField.PACKET_COUNT, byteCountValue2);
+        }
+    };
+
+    final Instruction statInstruction1 = Instructions.statTrigger(statTriggerFieldMap1, flag1);
+    final Instruction statInstruction1Same = Instructions.statTrigger(statTriggerFieldMap1, flag1);
+    final Instruction statInstruction2 = Instructions.statTrigger(statTriggerFieldMap2, flag2);
+
+    @Test
+    public void testStatTriggerTrafficMethod() {
+        final Instruction instruction = Instructions.statTrigger(statTriggerFieldMap1, flag1);
+        final Instructions.StatTriggerInstruction statTriggerInstruction =
+                checkAndConvert(instruction,
+                        Instruction.Type.STAT_TRIGGER,
+                        Instructions.StatTriggerInstruction.class);
+        assertThat(statTriggerInstruction.getStatTriggerFieldMap(), is(equalTo(statTriggerFieldMap1)));
+        assertThat(statTriggerInstruction.getStatTriggerFlag(), is(equalTo(flag1)));
+        assertThat(statTriggerInstruction.getStatTriggerFieldMap(), is(not(equalTo(statTriggerFieldMap2))));
+        assertThat(statTriggerInstruction.getStatTriggerFlag(), is(not(equalTo(flag2))));
+    }
+
+    @Test
+    public void testStatTriggerTrafficInstructionEquals() {
+        checkEqualsAndToString(statInstruction1,
+                statInstruction1Same,
+                statInstruction2);
+    }
+
     //  TableTypeTransition
 
     private final Instruction transitionInstruction1 = Instructions.transition(1);
diff --git a/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java b/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
index 352e2f5..a05745c 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java
@@ -113,6 +113,11 @@
         }
 
         @Override
+        public Instructions.StatTriggerInstruction statTrigger() {
+            return null;
+        }
+
+        @Override
         public Instructions.MeterInstruction metered() {
             return null;
         }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
index a9571f9..ff55e49 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java
@@ -17,6 +17,7 @@
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Maps;
 import org.onlab.osgi.DefaultServiceDirectory;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.packet.EthType;
@@ -37,6 +38,8 @@
 import org.onosproject.net.OduSignalId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.StatTriggerField;
+import org.onosproject.net.flow.StatTriggerFlag;
 import org.onosproject.net.flow.instructions.ExtensionTreatment;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
@@ -48,10 +51,12 @@
 import org.onosproject.net.meter.MeterId;
 import org.slf4j.Logger;
 
+import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import static org.onlab.util.Tools.nullIsIllegal;
+import static org.onosproject.codec.impl.InstructionCodec.STAT_PACKET_COUNT;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -262,6 +267,58 @@
                 + subType + " is not supported");
     }
 
+    private Instruction decodeStatTrigger() {
+        String statTriggerFlag = nullIsIllegal(json.get(InstructionCodec.STAT_TRIGGER_FLAG),
+                InstructionCodec.STAT_TRIGGER_FLAG + InstructionCodec.ERROR_MESSAGE).asText();
+
+        StatTriggerFlag flag = null;
+
+        if (statTriggerFlag.equals(StatTriggerFlag.ONLY_FIRST.name())) {
+            flag = StatTriggerFlag.ONLY_FIRST;
+        } else if (statTriggerFlag.equals(StatTriggerFlag.PERIODIC.name())) {
+            flag = StatTriggerFlag.PERIODIC;
+        } else {
+            throw new IllegalArgumentException("statTriggerFlag "
+                    + statTriggerFlag + " is not supported");
+        }
+        if (!json.has(InstructionCodec.STAT_THRESHOLDS)) {
+            throw new IllegalArgumentException("statThreshold is not added");
+        }
+        JsonNode statThresholdsNode = nullIsIllegal(json.get(InstructionCodec.STAT_THRESHOLDS),
+                InstructionCodec.STAT_THRESHOLDS + InstructionCodec.ERROR_MESSAGE);
+        Map<StatTriggerField, Long> statThresholdMap = getStatThreshold(statThresholdsNode);
+        if (statThresholdMap.isEmpty()) {
+            throw new IllegalArgumentException("statThreshold must have at least one property");
+        }
+        return Instructions.statTrigger(statThresholdMap, flag);
+    }
+
+    private Map<StatTriggerField, Long> getStatThreshold(JsonNode statThresholdNode) {
+        Map<StatTriggerField, Long> statThresholdMap = Maps.newEnumMap(StatTriggerField.class);
+        for (JsonNode jsonNode : statThresholdNode) {
+            if (jsonNode.hasNonNull(InstructionCodec.STAT_BYTE_COUNT)) {
+                JsonNode byteCountNode = jsonNode.get(InstructionCodec.STAT_BYTE_COUNT);
+                if (!byteCountNode.isNull() && byteCountNode.isNumber()) {
+                    statThresholdMap.put(StatTriggerField.BYTE_COUNT, byteCountNode.asLong());
+                }
+            } else if (jsonNode.hasNonNull(STAT_PACKET_COUNT)) {
+                JsonNode packetCount = jsonNode.get(STAT_PACKET_COUNT);
+                if (!packetCount.isNull() && packetCount.isNumber()) {
+                    statThresholdMap.put(StatTriggerField.PACKET_COUNT, packetCount.asLong());
+                }
+            } else if (jsonNode.hasNonNull(InstructionCodec.STAT_DURATION)) {
+                JsonNode duration = jsonNode.get(InstructionCodec.STAT_DURATION);
+                if (!duration.isNull() && duration.isNumber()) {
+                    statThresholdMap.put(StatTriggerField.DURATION, duration.asLong());
+                }
+            } else {
+                log.error("Unsupported stat {}", jsonNode.toString());
+            }
+        }
+
+        return statThresholdMap;
+    }
+
     /**
      * Decodes a extension instruction.
      *
@@ -391,6 +448,8 @@
             return decodeL4();
         } else if (type.equals(Instruction.Type.EXTENSION.name())) {
             return decodeExtension();
+        } else if (type.equals(Instruction.Type.STAT_TRIGGER.name())) {
+            return decodeStatTrigger();
         }
         throw new IllegalArgumentException("Instruction type "
                 + type + " is not supported");
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
index 1f741b5..9762643 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java
@@ -59,6 +59,11 @@
     protected static final String TRIBUTARY_SLOT_BITMAP = "tributarySlotBitmap";
     protected static final String EXTENSION = "extension";
     protected static final String DEVICE_ID = "deviceId";
+    protected static final String STAT_TRIGGER_FLAG = "statTriggerFlag";
+    protected static final String STAT_THRESHOLDS = "statThreshold";
+    protected static final String STAT_BYTE_COUNT = "byteCount";
+    protected static final String STAT_PACKET_COUNT = "packetCount";
+    protected static final String STAT_DURATION = "duration";
 
     protected static final String MISSING_MEMBER_MESSAGE =
             " member is required in Instruction";
diff --git a/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java b/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java
index 825ef1a..7785a38 100644
--- a/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java
@@ -664,6 +664,11 @@
         }
 
         @Override
+        public Instructions.StatTriggerInstruction statTrigger() {
+            return null;
+        }
+
+        @Override
         public Instructions.MeterInstruction metered() {
             return null;
         }
diff --git a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
index d5deae1..6c4ad07 100644
--- a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
+++ b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
@@ -100,16 +100,18 @@
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowId;
 import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleExtPayLoad;
+import org.onosproject.net.flow.IndexTableId;
+import org.onosproject.net.flow.StatTriggerField;
+import org.onosproject.net.flow.StatTriggerFlag;
+import org.onosproject.net.flow.StoredFlowEntry;
+import org.onosproject.net.flow.TableId;
+import org.onosproject.net.flow.TableStatisticsEntry;
 import org.onosproject.net.flow.oldbatch.FlowRuleBatchEntry;
 import org.onosproject.net.flow.oldbatch.FlowRuleBatchEvent;
 import org.onosproject.net.flow.oldbatch.FlowRuleBatchOperation;
 import org.onosproject.net.flow.oldbatch.FlowRuleBatchRequest;
-import org.onosproject.net.flow.FlowRuleEvent;
-import org.onosproject.net.flow.FlowRuleExtPayLoad;
-import org.onosproject.net.flow.IndexTableId;
-import org.onosproject.net.flow.StoredFlowEntry;
-import org.onosproject.net.flow.TableId;
-import org.onosproject.net.flow.TableStatisticsEntry;
 import org.onosproject.net.flow.criteria.ArpHaCriterion;
 import org.onosproject.net.flow.criteria.ArpOpCriterion;
 import org.onosproject.net.flow.criteria.ArpPaCriterion;
@@ -355,6 +357,9 @@
             .nextId(KryoNamespace.INITIAL_ID + BASIC_MAX_SIZE + MISC_MAX_SIZE)
             .register(
                     Instructions.MeterInstruction.class,
+                    Instructions.StatTriggerInstruction.class,
+                    StatTriggerFlag.class,
+                    StatTriggerField.class,
                     MeterId.class,
                     Version.class,
                     ControllerNode.State.class,
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowRuleManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowRuleManagerTest.java
index c447755..584401e 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowRuleManagerTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowRuleManagerTest.java
@@ -465,6 +465,11 @@
         }
 
         @Override
+        public Instructions.StatTriggerInstruction statTrigger() {
+            return null;
+        }
+
+        @Override
         public Instructions.MeterInstruction metered() {
             return null;
         }
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java
index bf0af04..9bd2146 100644
--- a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java
@@ -70,14 +70,24 @@
         if (isBuilt.getAndSet(true)) {
             return;
         }
-        OFPacketOut.Builder builder = sw.factory().buildPacketOut();
         OFAction act = buildOutput(outPort.getPortNumber());
-        pktout = builder.setXid(pktin.getXid())
-                .setInPort(pktinInPort())
-                .setBufferId(OFBufferId.NO_BUFFER)
-                .setData(pktin.getData())
+        pktout = createOFPacketOut(pktin.getData(), act, pktin.getXid());
+    }
+
+    private OFPacketOut createOFPacketOut(byte[] data, OFAction act, long xid) {
+        OFPacketOut.Builder builder = sw.factory().buildPacketOut();
+        if (sw.factory().getVersion().getWireVersion() <= OFVersion.OF_14.getWireVersion()) {
+            return builder.setXid(xid)
+                    .setInPort(pktinInPort())
+                    .setBufferId(OFBufferId.NO_BUFFER)
+                    .setData(data)
 //                .setBufferId(pktin.getBufferId())
+                    .setActions(Collections.singletonList(act)).build();
+        }
+        return builder.setXid(xid)
+                .setBufferId(OFBufferId.NO_BUFFER)
                 .setActions(Collections.singletonList(act))
+                .setData(data)
                 .build();
     }
 
@@ -86,14 +96,8 @@
         if (isBuilt.getAndSet(true)) {
             return;
         }
-        OFPacketOut.Builder builder = sw.factory().buildPacketOut();
         OFAction act = buildOutput(outPort.getPortNumber());
-        pktout = builder.setXid(pktin.getXid())
-                .setBufferId(OFBufferId.NO_BUFFER)
-                .setInPort(pktinInPort())
-                .setActions(Collections.singletonList(act))
-                .setData(ethFrame.serialize())
-                .build();
+        pktout = createOFPacketOut(ethFrame.serialize(), act, pktin.getXid());
     }
 
     @Override
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
index b50bdbc..05b68a4 100644
--- a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
@@ -49,6 +49,8 @@
 import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
 import org.projectfloodlight.openflow.protocol.OFExperimenter;
 import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFFlowLightweightStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFFlowLightweightStatsReply;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
 import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
@@ -60,6 +62,8 @@
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
 import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.OFQueueStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFQueueStatsReply;
 import org.projectfloodlight.openflow.protocol.OFStatsReply;
 import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
 import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
@@ -88,8 +92,6 @@
 import static org.onosproject.net.Device.Type.CONTROLLER;
 import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
 import static org.onosproject.openflow.controller.Dpid.dpid;
-import org.projectfloodlight.openflow.protocol.OFQueueStatsEntry;
-import org.projectfloodlight.openflow.protocol.OFQueueStatsReply;
 
 
 @Component(immediate = true)
@@ -161,6 +163,9 @@
     protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
             ArrayListMultimap.create();
 
+    protected Multimap<Dpid, OFFlowLightweightStatsEntry> fullFlowLightweightStats =
+            ArrayListMultimap.create();
+
     protected Multimap<Dpid, OFTableStatsEntry> fullTableStats =
             ArrayListMultimap.create();
 
@@ -422,7 +427,17 @@
                     executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
                 }
                 break;
-
+            case FLOW_LIGHTWEIGHT:
+                Collection<OFFlowLightweightStatsEntry> flowLightweightStats =
+                        publishFlowStatsLightweight(dpid, (OFFlowLightweightStatsReply) reply);
+                if (flowLightweightStats != null) {
+                    OFFlowLightweightStatsReply.Builder rep =
+                            OFFactories.getFactory(reply.getVersion()).buildFlowLightweightStatsReply();
+                    rep.setEntries(ImmutableList.copyOf(flowLightweightStats));
+                    rep.setXid(reply.getXid());
+                    executorMsgs.execute(new OFMessageHandler(dpid, rep.build()));
+                }
+                break;
             case TABLE:
                 Collection<OFTableStatsEntry> tableStats = publishTableStats(dpid, (OFTableStatsReply) reply);
                 if (tableStats != null) {
@@ -529,6 +544,17 @@
         return null;
     }
 
+    private synchronized Collection<OFFlowLightweightStatsEntry> publishFlowStatsLightweight(
+            Dpid dpid,
+            OFFlowLightweightStatsReply reply) {
+        //TODO: Get rid of synchronized
+        fullFlowLightweightStats.putAll(dpid, reply.getEntries());
+        if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
+            return fullFlowLightweightStats.removeAll(dpid);
+        }
+        return null;
+    }
+
     private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid,
                                                                        OFTableStatsReply reply) {
         //TODO: Get rid of synchronized
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
index 4ec4a69..a281927 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
@@ -134,8 +134,9 @@
             return new FlowModBuilderVer10(flowRule, factory, xid, driverService);
         case OF_13:
         case OF_14:
-        case OF_15:
             return new FlowModBuilderVer13(flowRule, factory, xid, driverService);
+        case OF_15:
+            return new FlowModBuilderVer15(flowRule, factory, xid, driverService);
         default:
             throw new UnsupportedOperationException(
                     "No flow mod builder for protocol version " + factory.getVersion());
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
index 5381af7..ad601c9 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
@@ -100,10 +100,10 @@
  */
 public class FlowModBuilderVer13 extends FlowModBuilder {
 
-    private final Logger log = LoggerFactory.getLogger(getClass());
-    private static final int OFPCML_NO_BUFFER = 0xffff;
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+    protected static final int OFPCML_NO_BUFFER = 0xffff;
 
-    private final TrafficTreatment treatment;
+    protected final TrafficTreatment treatment;
 
     /**
      * Constructor for a flow mod builder for OpenFlow 1.3.
@@ -295,24 +295,24 @@
         return actions;
     }
 
-    private OFInstruction buildTableGoto(Instructions.TableTypeTransition i) {
+    protected OFInstruction buildTableGoto(Instructions.TableTypeTransition i) {
         OFInstruction instruction = factory().instructions().gotoTable(
                 TableId.of(i.tableId()));
         return instruction;
     }
 
-    private OFInstruction buildMetadata(Instructions.MetadataInstruction m) {
+    protected OFInstruction buildMetadata(Instructions.MetadataInstruction m) {
         OFInstruction instruction = factory().instructions().writeMetadata(
                 U64.of(m.metadata()), U64.of(m.metadataMask()));
         return instruction;
     }
 
-    private OFInstruction buildMeter(Instructions.MeterInstruction metered) {
+    protected OFInstruction buildMeter(Instructions.MeterInstruction metered) {
         return factory().instructions().meter(metered.meterId().id());
     }
 
 
-    private OFAction buildL0Modification(Instruction i) {
+    protected OFAction buildL0Modification(Instruction i) {
         L0ModificationInstruction l0m = (L0ModificationInstruction) i;
         OFOxm<?> oxm = null;
         switch (l0m.subtype()) {
@@ -340,7 +340,7 @@
         return null;
     }
 
-    private OFAction buildL1Modification(Instruction i) {
+    protected OFAction buildL1Modification(Instruction i) {
         L1ModificationInstruction l1m = (L1ModificationInstruction) i;
         OFOxm<?> oxm = null;
         switch (l1m.subtype()) {
@@ -365,7 +365,7 @@
         return null;
     }
 
-    private OFAction buildL2Modification(Instruction i) {
+    protected OFAction buildL2Modification(Instruction i) {
         L2ModificationInstruction l2m = (L2ModificationInstruction) i;
         ModEtherInstruction eth;
         OFOxm<?> oxm = null;
@@ -430,7 +430,7 @@
         return null;
     }
 
-    private OFAction buildL3Modification(Instruction i) {
+    protected OFAction buildL3Modification(Instruction i) {
         L3ModificationInstruction l3m = (L3ModificationInstruction) i;
         ModIPInstruction ip;
         Ip4Address ip4;
@@ -493,7 +493,7 @@
         return null;
     }
 
-    private OFAction buildL4Modification(Instruction i) {
+    protected OFAction buildL4Modification(Instruction i) {
         L4ModificationInstruction l4m = (L4ModificationInstruction) i;
         ModTransportPortInstruction tp;
         OFOxm<?> oxm = null;
@@ -525,7 +525,7 @@
         return null;
     }
 
-    private OFAction buildExtensionAction(ExtensionTreatment i) {
+    protected OFAction buildExtensionAction(ExtensionTreatment i) {
         if (!driverService.isPresent()) {
             log.error("No driver service present");
             return null;
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer15.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer15.java
new file mode 100644
index 0000000..7ba893a
--- /dev/null
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer15.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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 com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.StatTriggerField;
+import org.onosproject.net.flow.StatTriggerFlag;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFFlowAdd;
+import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
+import org.projectfloodlight.openflow.protocol.OFOxsList;
+import org.projectfloodlight.openflow.protocol.OFStatTriggerFlags;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActionGroup;
+import org.projectfloodlight.openflow.protocol.action.OFActionMeter;
+import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetQueue;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.oxs.OFOxs;
+import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFGroup;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U64;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.projectfloodlight.openflow.protocol.OFStatTriggerFlags.ONLY_FIRST;
+import static org.projectfloodlight.openflow.protocol.OFStatTriggerFlags.PERIODIC;
+
+/**
+ * Flow mod builder for OpenFlow 1.5+.
+ */
+public class FlowModBuilderVer15 extends FlowModBuilderVer13 {
+
+    /**
+     * Constructor for a flow mod builder for OpenFlow 1.5.
+     *
+     * @param flowRule      the flow rule to transform into a flow mod
+     * @param factory       the OpenFlow factory to use to build the flow mod
+     * @param xid           the transaction ID
+     * @param driverService the device driver service
+     */
+    protected FlowModBuilderVer15(FlowRule flowRule, OFFactory factory,
+                                  Optional<Long> xid,
+                                  Optional<DriverService> driverService) {
+        super(flowRule, factory, xid, driverService);
+    }
+
+    @Override
+    public OFFlowMod buildFlowAdd() {
+        Match match = buildMatch();
+        List<OFAction> deferredActions = buildActions(treatment.deferred(), false);
+        List<OFAction> immediateActions = buildActions(treatment.immediate(), true);
+        List<OFInstruction> instructions = Lists.newLinkedList();
+
+        if (treatment.clearedDeferred()) {
+            instructions.add(factory().instructions().clearActions());
+        }
+        if (!immediateActions.isEmpty()) {
+            instructions.add(factory().instructions().applyActions(immediateActions));
+        }
+        if (!deferredActions.isEmpty()) {
+            instructions.add(factory().instructions().writeActions(deferredActions));
+        }
+        if (treatment.tableTransition() != null) {
+            instructions.add(buildTableGoto(treatment.tableTransition()));
+        }
+        if (treatment.writeMetadata() != null) {
+            instructions.add(buildMetadata(treatment.writeMetadata()));
+        }
+        if (treatment.statTrigger() != null) {
+            instructions.add(buildStatTrigger(treatment.statTrigger()));
+        }
+
+
+
+        long cookie = flowRule().id().value();
+
+        OFFlowAdd fm = factory().buildFlowAdd()
+                .setXid(xid)
+                .setCookie(U64.of(cookie))
+                .setBufferId(OFBufferId.NO_BUFFER)
+                .setInstructions(instructions)
+                .setMatch(match)
+                .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
+                .setPriority(flowRule().priority())
+                .setTableId(TableId.of(flowRule().tableId()))
+                .setHardTimeout(flowRule().hardTimeout())
+                .build();
+
+        return fm;
+    }
+
+    @Override
+    public OFFlowMod buildFlowMod() {
+        Match match = buildMatch();
+        List<OFAction> deferredActions = buildActions(treatment.deferred(), false);
+        List<OFAction> immediateActions = buildActions(treatment.immediate(), true);
+        List<OFInstruction> instructions = Lists.newLinkedList();
+
+
+        if (!immediateActions.isEmpty()) {
+            instructions.add(factory().instructions().applyActions(immediateActions));
+        }
+        if (treatment.clearedDeferred()) {
+            instructions.add(factory().instructions().clearActions());
+        }
+        if (!deferredActions.isEmpty()) {
+            instructions.add(factory().instructions().writeActions(deferredActions));
+        }
+        if (treatment.tableTransition() != null) {
+            instructions.add(buildTableGoto(treatment.tableTransition()));
+        }
+        if (treatment.writeMetadata() != null) {
+            instructions.add(buildMetadata(treatment.writeMetadata()));
+        }
+        if (treatment.statTrigger() != null) {
+            instructions.add(buildStatTrigger(treatment.statTrigger()));
+        }
+
+        long cookie = flowRule().id().value();
+
+        OFFlowMod fm = factory().buildFlowModify()
+                .setXid(xid)
+                .setCookie(U64.of(cookie))
+                .setBufferId(OFBufferId.NO_BUFFER)
+                .setInstructions(instructions)
+                .setMatch(match)
+                .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
+                .setPriority(flowRule().priority())
+                .setTableId(TableId.of(flowRule().tableId()))
+                .setHardTimeout(flowRule().hardTimeout())
+                .build();
+
+        return fm;
+    }
+
+    private List<OFAction> buildActions(List<Instruction> treatments, Boolean immediateActions) {
+        if (treatment == null) {
+            return Collections.emptyList();
+        }
+
+        boolean tableFound = false;
+        List<OFAction> actions = new LinkedList<>();
+
+        //Meter action handling
+        if (null != treatment.metered() && immediateActions) {
+            OFAction meterAction = buildMultipleMeterAction(treatment.metered());
+            actions.add(meterAction);
+        }
+
+        for (Instruction i : treatments) {
+            switch (i.type()) {
+                case NOACTION:
+                    return Collections.emptyList();
+                case L0MODIFICATION:
+                    actions.add(buildL0Modification(i));
+                    break;
+                case L1MODIFICATION:
+                    actions.add(buildL1Modification(i));
+                    break;
+                case L2MODIFICATION:
+                    actions.add(buildL2Modification(i));
+                    break;
+                case L3MODIFICATION:
+                    actions.add(buildL3Modification(i));
+                    break;
+                case L4MODIFICATION:
+                    actions.add(buildL4Modification(i));
+                    break;
+                case OUTPUT:
+                    Instructions.OutputInstruction out = (Instructions.OutputInstruction) i;
+                    OFActionOutput.Builder action = factory().actions().buildOutput()
+                            .setPort(OFPort.of((int) out.port().toLong()));
+                    if (out.port().equals(PortNumber.CONTROLLER)) {
+                        action.setMaxLen(OFPCML_NO_BUFFER);
+                    }
+                    actions.add(action.build());
+                    break;
+                case GROUP:
+                    Instructions.GroupInstruction group = (Instructions.GroupInstruction) i;
+                    OFActionGroup.Builder groupBuilder = factory().actions().buildGroup()
+                            .setGroup(OFGroup.of(group.groupId().id()));
+                    actions.add(groupBuilder.build());
+                    break;
+                case QUEUE:
+                    Instructions.SetQueueInstruction queue = (Instructions.SetQueueInstruction) i;
+                    OFActionSetQueue.Builder queueBuilder = factory().actions().buildSetQueue()
+                            .setQueueId(queue.queueId());
+                    actions.add(queueBuilder.build());
+                    break;
+                case TABLE:
+                    //FIXME: should not occur here.
+                    tableFound = true;
+                    break;
+                case EXTENSION:
+                    actions.add(buildExtensionAction(((Instructions.ExtensionInstructionWrapper) i)
+                            .extensionInstruction()));
+                    break;
+                default:
+                    log.warn("Instruction type {} not yet implemented.", i.type());
+            }
+        }
+
+        if (tableFound && actions.isEmpty()) {
+            // handles the case where there are no actions, but there is
+            // a goto instruction for the next table
+            return Collections.emptyList();
+        }
+        return actions;
+    }
+
+    private OFOxsList getOFOxsList(Map<StatTriggerField, Long> statTriggerMap) {
+        OFFactory factory = factory();
+        List<OFOxs<?>> ofOxsList = Lists.newArrayList();
+        for (Map.Entry<StatTriggerField, Long> entry : statTriggerMap.entrySet()) {
+            switch (entry.getKey()) {
+                case DURATION:
+                    ofOxsList.add(factory.oxss().buildDuration().setValue(U64.of(entry.getValue())).build());
+                    break;
+                case IDLE_TIME:
+                    ofOxsList.add(factory.oxss().buildIdleTime().setValue(U64.of(entry.getValue())).build());
+                    break;
+                case BYTE_COUNT:
+                    ofOxsList.add(factory.oxss().buildByteCount().setValue(U64.of(entry.getValue())).build());
+                    break;
+                case FLOW_COUNT:
+                    ofOxsList.add(factory.oxss().buildFlowCount().setValue(U32.of(entry.getValue())).build());
+                    break;
+                case PACKET_COUNT:
+                    ofOxsList.add(factory.oxss().buildPacketCount().setValue(U64.of(entry.getValue())).build());
+                    break;
+                default:
+                    log.warn("Unsupported Stat Trigger field");
+                    break;
+            }
+        }
+        return OFOxsList.ofList(ofOxsList);
+    }
+
+    private Set<OFStatTriggerFlags> getStatTriggerFlag(StatTriggerFlag flag) {
+        Set<OFStatTriggerFlags> statTriggerFlagsSet = Sets.newHashSet();
+        switch (flag) {
+            case PERIODIC:
+                statTriggerFlagsSet.add(PERIODIC);
+                break;
+            case ONLY_FIRST:
+                statTriggerFlagsSet.add(ONLY_FIRST);
+                break;
+            default:
+                break;
+        }
+        return statTriggerFlagsSet;
+    }
+
+    /**
+     * Meter action builder.
+     *
+     * @param meterInstruction meter instruction
+     * @return meter action
+     */
+    protected OFAction buildMultipleMeterAction(Instructions.MeterInstruction meterInstruction) {
+        OFActionMeter.Builder meterBuilder = factory().actions().buildMeter()
+                .setMeterId(meterInstruction.meterId().id());
+        return meterBuilder.build();
+    }
+
+    protected OFInstruction buildStatTrigger(Instructions.StatTriggerInstruction s) {
+        OFInstruction instruction = factory().instructions().statTrigger(getStatTriggerFlag(s.getStatTriggerFlag()),
+                getOFOxsList(s.getStatTriggerFieldMap()));
+        return instruction;
+    }
+}
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
index 9dc03cd..7f472ad 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
@@ -63,6 +63,7 @@
 import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
 import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFFlowLightweightStatsReply;
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
@@ -142,6 +143,7 @@
 
     private final Timer timer = new Timer("onos-openflow-collector");
 
+
     // Old simple collector set
     private final Map<Dpid, FlowStatsCollector> simpleCollectors = Maps.newConcurrentMap();
 
@@ -441,6 +443,8 @@
                         pushFlowMetrics(dpid, (OFFlowStatsReply) msg);
                     } else if (((OFStatsReply) msg).getStatsType() == OFStatsType.TABLE) {
                         pushTableStatistics(dpid, (OFTableStatsReply) msg);
+                    } else if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW_LIGHTWEIGHT) {
+                        pushFlowLightWeightMetrics(dpid, (OFFlowLightweightStatsReply) msg);
                     }
                     break;
                 case BARRIER_REPLY:
@@ -653,6 +657,40 @@
             providerService.pushTableStatistics(did, tableStatsEntries);
         }
 
+        private void pushFlowLightWeightMetrics(Dpid dpid, OFFlowLightweightStatsReply replies) {
+
+            DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
+            NewAdaptiveFlowStatsCollector afsc = afsCollectors.get(dpid);
+            if (adaptiveFlowSampling && afsc != null)  {
+                List<FlowEntry> flowEntries = replies.getEntries().stream()
+                        .map(entry -> new FlowEntryBuilder(did, entry, driverService).withSetAfsc(afsc).build())
+                        .collect(Collectors.toList());
+
+                // Check that OFFlowStatsReply Xid is same with the one of OFFlowStatsRequest?
+                if (afsc.getFlowMissingXid() != NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID) {
+                    log.debug("OpenFlowRuleProvider:pushFlowMetrics, flowMissingXid={}, "
+                                    + "OFFlowStatsReply Xid={}, for {}",
+                            afsc.getFlowMissingXid(), replies.getXid(), dpid);
+                    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);
+                }
+            } else {
+                List<FlowEntry> flowEntries = replies.getEntries().stream()
+                        .map(entry -> new FlowEntryBuilder(did, entry, driverService).build())
+                        .collect(Collectors.toList());
+                // call existing entire flow stats update with flowMissing synchronization
+                providerService.pushFlowMetrics(did, flowEntries);
+            }
+        }
+
         private TableStatisticsEntry buildTableStatistics(DeviceId deviceId,
                                                           OFTableStatsEntry ofEntry) {
             TableStatisticsEntry entry = null;
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java
index 083a74c..c16c34f 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowEntryBuilder.java
@@ -16,6 +16,7 @@
 package org.onosproject.provider.of.flow.util;
 
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import org.onlab.packet.EthType;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
@@ -42,6 +43,8 @@
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowEntry.FlowEntryState;
 import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.StatTriggerField;
+import org.onosproject.net.flow.StatTriggerFlag;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes;
@@ -49,11 +52,14 @@
 import org.onosproject.openflow.controller.ExtensionSelectorInterpreter;
 import org.onosproject.openflow.controller.ExtensionTreatmentInterpreter;
 import org.onosproject.provider.of.flow.impl.NewAdaptiveFlowStatsCollector;
+import org.projectfloodlight.openflow.protocol.OFFlowLightweightStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFMatchV3;
 import org.projectfloodlight.openflow.protocol.OFObject;
+import org.projectfloodlight.openflow.protocol.OFOxsList;
+import org.projectfloodlight.openflow.protocol.OFStatTriggerFlags;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.action.OFActionCircuit;
@@ -74,12 +80,14 @@
 import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionGotoTable;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionStatTrigger;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteActions;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteMetadata;
 import org.projectfloodlight.openflow.protocol.match.Match;
 import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigid;
+import org.projectfloodlight.openflow.protocol.oxs.OFOxs;
 import org.projectfloodlight.openflow.protocol.ver13.OFFactoryVer13;
 import org.projectfloodlight.openflow.types.CircuitSignalID;
 import org.projectfloodlight.openflow.types.IPv4Address;
@@ -97,9 +105,15 @@
 import org.slf4j.LoggerFactory;
 
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import static java.util.concurrent.TimeUnit.NANOSECONDS;
 import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.onosproject.net.flow.StatTriggerField.*;
+import static org.onosproject.net.flow.StatTriggerField.IDLE_TIME;
+import static org.onosproject.net.flow.StatTriggerFlag.ONLY_FIRST;
+import static org.onosproject.net.flow.StatTriggerFlag.PERIODIC;
 import static org.onosproject.net.flow.criteria.Criteria.*;
 import static org.onosproject.net.flow.instructions.Instructions.modL0Lambda;
 import static org.onosproject.net.flow.instructions.Instructions.modL1OduSignalId;
@@ -111,6 +125,7 @@
     private final OFFlowStatsEntry stat;
     private final OFFlowRemoved removed;
     private final OFFlowMod flowMod;
+    private final OFFlowLightweightStatsEntry lightWeightStat;
 
     private final Match match;
 
@@ -120,7 +135,7 @@
 
     private final DeviceId deviceId;
 
-    public enum FlowType { STAT, REMOVED, MOD }
+    public enum FlowType { STAT, LIGHTWEIGHT_STAT, REMOVED, MOD }
 
     private final FlowType type;
 
@@ -140,6 +155,21 @@
         this.type = FlowType.STAT;
         this.driverService = driverService;
         this.afsc = null;
+        this.lightWeightStat = null;
+    }
+
+    public FlowEntryBuilder(DeviceId deviceId, OFFlowLightweightStatsEntry lightWeightStat,
+                            DriverService driverService) {
+        this.stat = null;
+        this.match = lightWeightStat.getMatch();
+        this.instructions = null;
+        this.deviceId = deviceId;
+        this.removed = null;
+        this.flowMod = null;
+        this.type = FlowType.LIGHTWEIGHT_STAT;
+        this.driverService = driverService;
+        this.afsc = null;
+        this.lightWeightStat = lightWeightStat;
     }
 
     public FlowEntryBuilder(DeviceId deviceId, OFFlowRemoved removed, DriverService driverService) {
@@ -152,6 +182,7 @@
         this.type = FlowType.REMOVED;
         this.driverService = driverService;
         this.afsc = null;
+        this.lightWeightStat = null;
     }
 
     public FlowEntryBuilder(DeviceId deviceId, OFFlowMod fm, DriverService driverService) {
@@ -164,6 +195,7 @@
         this.removed = null;
         this.driverService = driverService;
         this.afsc = null;
+        this.lightWeightStat = null;
     }
 
     public FlowEntryBuilder withSetAfsc(NewAdaptiveFlowStatsCollector afsc) {
@@ -172,83 +204,16 @@
     }
 
     public FlowEntry build(FlowEntryState... state) {
-        FlowRule.Builder builder;
         try {
             switch (this.type) {
                 case STAT:
-                    builder = DefaultFlowRule.builder()
-                            .forDevice(deviceId)
-                            .withSelector(buildSelector())
-                            .withTreatment(buildTreatment())
-                            .withPriority(stat.getPriority())
-                            .withIdleTimeout(stat.getIdleTimeout())
-                            .withCookie(stat.getCookie().getValue());
-                    if (stat.getVersion() != OFVersion.OF_10) {
-                        builder.forTable(stat.getTableId().getValue());
-                    }
-
-                    if (afsc != null) {
-                        FlowEntry.FlowLiveType liveType = afsc.calFlowLiveType(stat.getDurationSec());
-                        return new DefaultFlowEntry(builder.build(), FlowEntryState.ADDED,
-                                                    SECONDS.toNanos(stat.getDurationSec())
-                                                            + stat.getDurationNsec(), NANOSECONDS,
-                                                    liveType,
-                                                    stat.getPacketCount().getValue(),
-                                                    stat.getByteCount().getValue());
-                    } else {
-                        return new DefaultFlowEntry(builder.build(), FlowEntryState.ADDED,
-                                                    stat.getDurationSec(),
-                                                    stat.getPacketCount().getValue(),
-                                                    stat.getByteCount().getValue());
-                    }
-
+                    return createFlowEntryFromStat();
+                case LIGHTWEIGHT_STAT:
+                    return createFlowEntryFromLightweightStat();
                 case REMOVED:
-                    builder = DefaultFlowRule.builder()
-                            .forDevice(deviceId)
-                            .withSelector(buildSelector())
-                            .withPriority(removed.getPriority())
-                            .withIdleTimeout(removed.getIdleTimeout())
-                            .withCookie(removed.getCookie().getValue())
-                            .withReason(FlowRule.FlowRemoveReason.parseShort((short) removed.getReason().ordinal()));
-
-                    if (removed.getVersion() != OFVersion.OF_10) {
-                        builder.forTable(removed.getTableId().getValue());
-                    }
-
-                    if (afsc != null) {
-                        FlowEntry.FlowLiveType liveType = afsc.calFlowLiveType(removed.getDurationSec());
-                        return new DefaultFlowEntry(builder.build(), FlowEntryState.REMOVED,
-                                                    SECONDS.toNanos(removed.getDurationSec())
-                                                            + removed.getDurationNsec(), NANOSECONDS,
-                                                    liveType,
-                                                    removed.getPacketCount().getValue(),
-                                                    removed.getByteCount().getValue());
-                    } else {
-                        return new DefaultFlowEntry(builder.build(), FlowEntryState.REMOVED,
-                                                    removed.getDurationSec(),
-                                                    removed.getPacketCount().getValue(),
-                                                    removed.getByteCount().getValue());
-                    }
-
+                    return createFlowEntryForFlowRemoved();
                 case MOD:
-                    FlowEntryState flowState = state.length > 0 ? state[0] : FlowEntryState.FAILED;
-                    builder = DefaultFlowRule.builder()
-                            .forDevice(deviceId)
-                            .withSelector(buildSelector())
-                            .withTreatment(buildTreatment())
-                            .withPriority(flowMod.getPriority())
-                            .withIdleTimeout(flowMod.getIdleTimeout())
-                            .withCookie(flowMod.getCookie().getValue());
-                    if (flowMod.getVersion() != OFVersion.OF_10) {
-                        builder.forTable(flowMod.getTableId().getValue());
-                    }
-
-                    if (afsc != null) {
-                        FlowEntry.FlowLiveType liveType = FlowEntry.FlowLiveType.IMMEDIATE;
-                        return new DefaultFlowEntry(builder.build(), flowState, 0, liveType, 0, 0);
-                    } else {
-                        return new DefaultFlowEntry(builder.build(), flowState, 0, 0, 0);
-                    }
+                    return createFlowEntryForFlowMod(state);
                 default:
                     log.error("Unknown flow type : {}", this.type);
                     return null;
@@ -260,6 +225,143 @@
 
     }
 
+    private FlowEntry createFlowEntryFromStat() {
+
+        FlowRule.Builder builder = DefaultFlowRule.builder()
+                .forDevice(deviceId)
+                .withSelector(buildSelector())
+                .withTreatment(buildTreatment())
+                .withPriority(stat.getPriority())
+                .withIdleTimeout(stat.getIdleTimeout())
+                .withCookie(stat.getCookie().getValue());
+        if (stat.getVersion() != OFVersion.OF_10) {
+            builder.forTable(stat.getTableId().getValue());
+        }
+        if (stat.getVersion().getWireVersion() < OFVersion.OF_15.getWireVersion()) {
+            if (afsc != null) {
+                FlowEntry.FlowLiveType liveType = afsc.calFlowLiveType(stat.getDurationSec());
+                return new DefaultFlowEntry(builder.build(), FlowEntryState.ADDED,
+                        SECONDS.toNanos(stat.getDurationSec())
+                                + stat.getDurationNsec(), NANOSECONDS,
+                        liveType,
+                        stat.getPacketCount().getValue(),
+                        stat.getByteCount().getValue());
+            } else {
+                return new DefaultFlowEntry(builder.build(), FlowEntryState.ADDED,
+                        stat.getDurationSec(),
+                        stat.getPacketCount().getValue(),
+                        stat.getByteCount().getValue());
+            }
+        }
+        FlowStatParser statParser = new FlowStatParser(stat.getStats());
+        if (afsc != null && statParser.isDurationReceived()) {
+            FlowEntry.FlowLiveType liveType = afsc.calFlowLiveType(statParser.getDuration());
+            return new DefaultFlowEntry(builder.build(), FlowEntryState.ADDED,
+                    SECONDS.toNanos(statParser.getDuration())
+                            + SECONDS.toNanos(statParser.getDuration()), NANOSECONDS,
+                    liveType,
+                    statParser.getPacketCount(),
+                    statParser.getByteCount());
+        } else {
+            return new DefaultFlowEntry(builder.build(), FlowEntryState.ADDED,
+                    statParser.getDuration(),
+                    statParser.getPacketCount(),
+                    statParser.getByteCount());
+        }
+
+    }
+
+    private FlowEntry createFlowEntryForFlowMod(FlowEntryState ...state) {
+        FlowEntryState flowState = state.length > 0 ? state[0] : FlowEntryState.FAILED;
+        FlowRule.Builder builder = DefaultFlowRule.builder()
+                .forDevice(deviceId)
+                .withSelector(buildSelector())
+                .withTreatment(buildTreatment())
+                .withPriority(flowMod.getPriority())
+                .withIdleTimeout(flowMod.getIdleTimeout())
+                .withCookie(flowMod.getCookie().getValue());
+        if (flowMod.getVersion() != OFVersion.OF_10) {
+            builder.forTable(flowMod.getTableId().getValue());
+        }
+
+        if (afsc != null) {
+            FlowEntry.FlowLiveType liveType = FlowEntry.FlowLiveType.IMMEDIATE;
+            return new DefaultFlowEntry(builder.build(), flowState, 0, liveType, 0, 0);
+        } else {
+            return new DefaultFlowEntry(builder.build(), flowState, 0, 0, 0);
+        }
+    }
+
+    private FlowEntry createFlowEntryForFlowRemoved() {
+        FlowRule.Builder builder = DefaultFlowRule.builder()
+                .forDevice(deviceId)
+                .withSelector(buildSelector())
+                .withPriority(removed.getPriority())
+                .withIdleTimeout(removed.getIdleTimeout())
+                .withCookie(removed.getCookie().getValue())
+                .withReason(FlowRule.FlowRemoveReason.parseShort((short) removed.getReason().ordinal()));
+
+        if (removed.getVersion() != OFVersion.OF_10) {
+            builder.forTable(removed.getTableId().getValue());
+        }
+        if (removed.getVersion().getWireVersion() < OFVersion.OF_15.getWireVersion()) {
+            if (afsc != null) {
+                FlowEntry.FlowLiveType liveType = afsc.calFlowLiveType(removed.getDurationSec());
+                return new DefaultFlowEntry(builder.build(), FlowEntryState.REMOVED,
+                        SECONDS.toNanos(removed.getDurationSec())
+                                + removed.getDurationNsec(), NANOSECONDS,
+                        liveType,
+                        removed.getPacketCount().getValue(),
+                        removed.getByteCount().getValue());
+            } else {
+                return new DefaultFlowEntry(builder.build(), FlowEntryState.REMOVED,
+                        removed.getDurationSec(),
+                        removed.getPacketCount().getValue(),
+                        removed.getByteCount().getValue());
+            }
+        }
+        FlowStatParser statParser = new FlowStatParser(removed.getStats());
+        if (afsc != null && statParser.isDurationReceived()) {
+            FlowEntry.FlowLiveType liveType = afsc.calFlowLiveType(statParser.getDuration());
+            return new DefaultFlowEntry(builder.build(), FlowEntryState.REMOVED,
+                    SECONDS.toNanos(statParser.getDuration())
+                            + SECONDS.toNanos(statParser.getDuration()), NANOSECONDS,
+                    liveType,
+                    statParser.getPacketCount(),
+                    statParser.getByteCount());
+        } else {
+            return new DefaultFlowEntry(builder.build(), FlowEntryState.REMOVED,
+                    statParser.getDuration(),
+                    statParser.getPacketCount(),
+                    statParser.getByteCount());
+        }
+    }
+
+    private FlowEntry createFlowEntryFromLightweightStat() {
+        FlowRule.Builder builder = DefaultFlowRule.builder()
+                .forDevice(deviceId)
+                .withSelector(buildSelector())
+                .withPriority(lightWeightStat.getPriority())
+                .withIdleTimeout(0)
+                .withCookie(0);
+        FlowStatParser flowLightweightStatParser = new FlowStatParser(lightWeightStat.getStats());
+        builder.forTable(lightWeightStat.getTableId().getValue());
+        if (afsc != null && flowLightweightStatParser.isDurationReceived()) {
+            FlowEntry.FlowLiveType liveType = afsc.calFlowLiveType(flowLightweightStatParser.getDuration());
+            return new DefaultFlowEntry(builder.build(), FlowEntryState.ADDED,
+                    SECONDS.toNanos(flowLightweightStatParser.getDuration())
+                            + flowLightweightStatParser.getDuration(), NANOSECONDS,
+                    liveType,
+                    flowLightweightStatParser.getPacketCount(),
+                    flowLightweightStatParser.getByteCount());
+        } else {
+            return new DefaultFlowEntry(builder.build(), FlowEntryState.ADDED,
+                    flowLightweightStatParser.getDuration(),
+                    flowLightweightStatParser.getPacketCount(),
+                    flowLightweightStatParser.getByteCount());
+        }
+    }
+
     private List<OFInstruction> getInstructions(OFFlowMod entry) {
         switch (entry.getVersion()) {
             case OF_10:
@@ -321,6 +423,10 @@
                 case CLEAR_ACTIONS:
                     builder.wipeDeferred();
                     break;
+                case STAT_TRIGGER:
+                    OFInstructionStatTrigger statTrigger = (OFInstructionStatTrigger) in;
+                    buildStatTrigger(statTrigger.getThresholds(), statTrigger.getFlags(), builder);
+                    break;
                 case EXPERIMENTER:
                     break;
                 case METER:
@@ -333,6 +439,57 @@
         return builder.build();
     }
 
+    private TrafficTreatment.Builder buildStatTrigger(OFOxsList oxsList,
+                                                      Set<OFStatTriggerFlags> flagsSet,
+                                                      TrafficTreatment.Builder builder) {
+        Map<StatTriggerField, Long> statTriggerMap = Maps.newEnumMap(StatTriggerField.class);
+        for (OFOxs<?> ofOxs : oxsList) {
+            switch (ofOxs.getStatField().id) {
+                case DURATION:
+                    U64 durationType = (U64) ofOxs.getValue();
+                    statTriggerMap.put(DURATION, durationType.getValue());
+                    break;
+                case FLOW_COUNT:
+                    U32 flowCount = (U32) ofOxs.getValue();
+                    statTriggerMap.put(FLOW_COUNT, flowCount.getValue());
+                    break;
+                case PACKET_COUNT:
+                    U64 packetCount = (U64) ofOxs.getValue();
+                    statTriggerMap.put(PACKET_COUNT, packetCount.getValue());
+                    break;
+                case BYTE_COUNT:
+                    U64 byteCount = (U64) ofOxs.getValue();
+                    statTriggerMap.put(BYTE_COUNT, byteCount.getValue());
+                    break;
+                case IDLE_TIME:
+                    U64 idleTime = (U64) ofOxs.getValue();
+                    statTriggerMap.put(IDLE_TIME, idleTime.getValue());
+                    break;
+                default:
+                    log.warn("getStatField not supported {}", ofOxs.getStatField().id);
+                    break;
+            }
+        }
+        StatTriggerFlag flag = null;
+        for (OFStatTriggerFlags flags : flagsSet) {
+            switch (flags) {
+                case PERIODIC:
+                    flag = PERIODIC;
+                    break;
+                case ONLY_FIRST:
+                    flag = ONLY_FIRST;
+                    break;
+                default:
+                    log.warn("flag not supported {}", flags);
+                    break;
+            }
+        }
+        if (!statTriggerMap.isEmpty() && flag != null) {
+            builder.add(Instructions.statTrigger(statTriggerMap, flag));
+        }
+        return builder;
+    }
+
     /**
      * Configures traffic treatment builder with a given collection of actions.
      *
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowStatParser.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowStatParser.java
new file mode 100644
index 0000000..dfea390
--- /dev/null
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/util/FlowStatParser.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014-present Open Networking Foundation
+ *
+ * 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.util;
+
+import org.projectfloodlight.openflow.protocol.stat.Stat;
+import org.projectfloodlight.openflow.protocol.stat.StatField;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U64;
+
+/**
+ * FlowStatParser helps to parse OXS which is added in OPF 1.5.
+ */
+public final class FlowStatParser {
+    private final Stat stat;
+
+
+    private long duration;
+    private long idleTime;
+    private long flowCount;
+    private long packetCount;
+    private long byteCount;
+    private boolean isDurationReceived;
+
+    public FlowStatParser(Stat stat) {
+        this.stat = stat;
+        parseStats();
+    }
+
+    public Stat getStat() {
+        return stat;
+    }
+
+    private void parseStats() {
+        U64 durationOfValue = this.stat.get(StatField.DURATION);
+        U64 byteCountOfValue = this.stat.get(StatField.BYTE_COUNT);
+        U32 flowCountOfValue = this.stat.get(StatField.FLOW_COUNT);
+        U64 idleTimeOfValue = this.stat.get(StatField.IDLE_TIME);
+        U64 packetCountOfValue = this.stat.get(StatField.PACKET_COUNT);
+
+        isDurationReceived = durationOfValue != null;
+        duration = durationOfValue != null ? durationOfValue.getValue() : 0;
+        byteCount = byteCountOfValue != null ? byteCountOfValue.getValue() : 0;
+        idleTime = idleTimeOfValue != null ? idleTimeOfValue.getValue() : 0;
+        flowCount = flowCountOfValue != null ? flowCountOfValue.getValue() : 0;
+        packetCount = packetCountOfValue != null ? packetCountOfValue.getValue() : 0;
+    }
+
+
+    public long getByteCount() {
+        return byteCount;
+    }
+
+    public long getDuration() {
+        return duration;
+    }
+
+    public long getFlowCount() {
+        return flowCount;
+    }
+
+    public long getPacketCount() {
+        return packetCount;
+    }
+
+    public long getIdleTime() {
+        return idleTime;
+    }
+
+    public boolean isDurationReceived() {
+        return isDurationReceived;
+    }
+}
diff --git a/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProvider.java b/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProvider.java
index 3d0dacd..bcacb29 100644
--- a/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProvider.java
+++ b/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProvider.java
@@ -339,8 +339,9 @@
             meter.setLife(stat.getDurationSec());
             meter.setProcessedBytes(stat.getByteInCount().getValue());
             meter.setProcessedPackets(stat.getPacketInCount().getValue());
-            meter.setReferenceCount(stat.getFlowCount());
-
+            if (stat.getVersion().getWireVersion() < OFVersion.OF_15.getWireVersion()) {
+                meter.setReferenceCount(stat.getFlowCount());
+            }
             // marks the meter as seen on the dataplane
             pendingOperations.invalidate(stat.getMeterId());
             return meter;
diff --git a/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProvider.java b/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProvider.java
index 0eb0c04..c5e2a87 100644
--- a/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProvider.java
+++ b/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProvider.java
@@ -40,6 +40,7 @@
 import org.onosproject.openflow.controller.PacketListener;
 import org.projectfloodlight.openflow.protocol.OFPacketOut;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFVersion;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.ver10.OFFactoryVer10;
 import org.projectfloodlight.openflow.types.OFBufferId;
@@ -136,12 +137,14 @@
                 .buildOutput()
                 .setPort(out)
                 .build();
-        return builder
-                .setBufferId(OFBufferId.NO_BUFFER)
-                .setInPort(OFPort.CONTROLLER)
+        builder.setBufferId(OFBufferId.NO_BUFFER)
                 .setActions(Collections.singletonList(act))
-                .setData(eth)
-                .build();
+                .setData(eth);
+        if (sw.factory().getVersion().getWireVersion() <= OFVersion.OF_14.getWireVersion()) {
+            builder.setInPort(OFPort.CONTROLLER);
+        }
+
+        return builder.build();
     }
 
     /**