Traffic Treatements now support deferred, immediate, table, and clear instructions.

By default, treatments are all immediate. Treatments will be deferred if the builder
predicated by deferred(). Subsequent treatments will be deferred until immediate is called
on the builder again. Multiple calls to deferred and immediate are permitted.

Change-Id: I76b3a44f2219fc1e72a7fb41b72d7bd602be85b7
diff --git a/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java
index c1b4a18..e5de812 100644
--- a/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java
+++ b/apps/bgprouter/src/main/java/org/onosproject/bgprouter/BgpRouter.java
@@ -34,6 +34,7 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -322,7 +323,7 @@
         private static final int HIGHEST_PRIORITY = 0xffff;
         private Set<InterfaceIpAddress> intfIps = new HashSet<InterfaceIpAddress>();
         private Set<MacAddress> intfMacs = new HashSet<MacAddress>();
-        private Set<VlanId> intfVlans = new HashSet<VlanId>();
+        private Map<PortNumber, VlanId> portVlanPair = Maps.newHashMap();
 
         public void provision(boolean install, Set<Interface> intfs) {
             getIntefaceConfig(intfs);
@@ -341,7 +342,7 @@
             for (Interface intf : intfs) {
                 intfIps.addAll(intf.ipAddresses());
                 intfMacs.add(intf.mac());
-                intfVlans.add(intf.vlan());
+                portVlanPair.put(intf.connectPoint().port(), intf.vlan());
             }
         }
 
@@ -447,14 +448,15 @@
             FlowRule rule;
 
             //Interface Vlans
-            for (VlanId vid : intfVlans) {
-                log.debug("adding rule for VLAN: {}", vid);
+            for (Map.Entry<PortNumber, VlanId> portVlan : portVlanPair.entrySet()) {
+                log.debug("adding rule for VLAN: {}", portVlan);
                 selector = DefaultTrafficSelector.builder();
                 treatment = DefaultTrafficTreatment.builder();
 
-                selector.matchVlanId(vid);
-                treatment.stripVlan();
+                selector.matchVlanId(portVlan.getValue());
+                selector.matchInPort(portVlan.getKey());
                 treatment.transition(Type.ETHER);
+                treatment.deferred().popVlan();
 
                 rule = new DefaultFlowRule(deviceId, selector.build(),
                                            treatment.build(), CONTROLLER_PRIORITY, appId,
diff --git a/cli/src/main/java/org/onosproject/cli/net/FlowsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/FlowsListCommand.java
index 0eb6c37..4ba6a5d 100644
--- a/cli/src/main/java/org/onosproject/cli/net/FlowsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/FlowsListCommand.java
@@ -195,7 +195,7 @@
                       f.bytes(), f.packets(), f.life(), f.priority(), f.type(),
                       coreService.getAppId(f.appId()).name());
                 print(SFMT, f.selector().criteria());
-                print(TFMT, f.treatment().instructions());
+                print(TFMT, f.treatment());
             }
         }
     }
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 75f7f7f..aa5af74 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,7 +17,7 @@
 
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableList;
-
+import com.google.common.collect.Lists;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.MplsLabel;
@@ -27,7 +27,6 @@
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
 
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
 
@@ -36,7 +35,11 @@
  */
 public final class DefaultTrafficTreatment implements TrafficTreatment {
 
-    private final List<Instruction> instructions;
+    private final List<Instruction> immediate;
+    private final List<Instruction> deferred;
+    private final Instructions.TableTypeTransition table;
+
+    private final boolean hasClear;
 
     /**
      * Creates a new traffic treatment from the specified list of instructions.
@@ -44,12 +47,46 @@
      * @param instructions treatment instructions
      */
     private DefaultTrafficTreatment(List<Instruction> instructions) {
-        this.instructions = ImmutableList.copyOf(instructions);
+        this.immediate = ImmutableList.copyOf(instructions);
+        this.deferred = ImmutableList.of();
+        this.hasClear = false;
+        this.table = null;
+    }
+
+    private DefaultTrafficTreatment(List<Instruction> deferred,
+                                   List<Instruction> immediate,
+                                   Instructions.TableTypeTransition table,
+                                   boolean clear) {
+        this.immediate = ImmutableList.copyOf(immediate);
+        this.deferred = ImmutableList.copyOf(deferred);
+        this.table = table;
+        this.hasClear = clear;
+
     }
 
     @Override
     public List<Instruction> instructions() {
-        return instructions;
+        return immediate;
+    }
+
+    @Override
+    public List<Instruction> deferred() {
+        return deferred;
+    }
+
+    @Override
+    public List<Instruction> immediate() {
+        return immediate;
+    }
+
+    @Override
+    public Instructions.TableTypeTransition tableTransition() {
+        return table;
+    }
+
+    @Override
+    public Boolean clearedDeferred() {
+        return hasClear;
     }
 
     /**
@@ -75,7 +112,7 @@
     //FIXME: Order of instructions may affect hashcode
     @Override
     public int hashCode() {
-        return Objects.hash(instructions);
+        return Objects.hash(immediate, deferred, table);
     }
 
     @Override
@@ -85,7 +122,9 @@
         }
         if (obj instanceof DefaultTrafficTreatment) {
             DefaultTrafficTreatment that = (DefaultTrafficTreatment) obj;
-            return Objects.equals(instructions, that.instructions);
+            return Objects.equals(immediate, that.immediate) &&
+                    Objects.equals(deferred, that.deferred) &&
+                    Objects.equals(table, that.table);
 
         }
         return false;
@@ -94,7 +133,10 @@
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
-                .add("instructions", instructions)
+                .add("immediate", immediate)
+                .add("deferred", deferred)
+                .add("transition", table == null ? "None" : table.toString())
+                .add("cleared", hasClear)
                 .toString();
     }
 
@@ -106,19 +148,22 @@
 
         boolean drop = false;
 
-        List<Instruction> outputs = new LinkedList<>();
+        boolean clear = false;
 
-        // TODO: should be a list of instructions based on group objects
-        List<Instruction> groups = new LinkedList<>();
+        Instructions.TableTypeTransition table;
 
-        // TODO: should be a list of instructions based on modification objects
-        List<Instruction> modifications = new LinkedList<>();
+        List<Instruction> deferred = Lists.newLinkedList();
+
+        List<Instruction> immediate = Lists.newLinkedList();
+
+        List<Instruction> current = immediate;
 
         // Creates a new builder
         private Builder() {
         }
 
         // Creates a new builder based off an existing treatment
+        //FIXME only works for immediate instruction sets.
         private Builder(TrafficTreatment treatment) {
             for (Instruction instruction : treatment.instructions()) {
                 add(instruction);
@@ -127,30 +172,26 @@
 
         @Override
         public Builder add(Instruction instruction) {
-            if (drop) {
-                return this;
-            }
+
             switch (instruction.type()) {
                 case DROP:
-                    drop = true;
-                    break;
-                case TABLE:
                 case OUTPUT:
-                    outputs.add(instruction);
-                    break;
+                case GROUP:
                 case L0MODIFICATION:
                 case L2MODIFICATION:
                 case L3MODIFICATION:
-                    // TODO: enforce modification order if any
-                    modifications.add(instruction);
+                    current.add(instruction);
                     break;
-                case GROUP:
-                    groups.add(instruction);
+                case TABLE:
+                    table = (Instructions.TableTypeTransition) instruction;
                     break;
                 default:
                     throw new IllegalArgumentException("Unknown instruction type: " +
                                                                instruction.type());
+
+
             }
+
             return this;
         }
 
@@ -254,27 +295,40 @@
         }
 
         @Override
-        public TrafficTreatment.Builder transition(FlowRule.Type type) {
-            return add(Instructions.transition(type));
-        }
-
-        @Override
         public Builder popVlan() {
             return add(Instructions.popVlan());
         }
 
         @Override
+        public Builder transition(FlowRule.Type type) {
+            return add(Instructions.transition(type));
+        }
+
+        @Override
+        public Builder immediate() {
+            current = immediate;
+            return this;
+        }
+
+        @Override
+        public Builder deferred() {
+            current = deferred;
+            return this;
+        }
+
+        @Override
+        public Builder wipeDeferred() {
+            clear = true;
+            return this;
+        }
+
+        @Override
         public TrafficTreatment build() {
-
-            //If we are dropping should we just return an empty list?
-            List<Instruction> instructions = new LinkedList<Instruction>();
-            instructions.addAll(modifications);
-            instructions.addAll(groups);
-            if (!drop) {
-                instructions.addAll(outputs);
+            if (deferred.size() == 0 && immediate.size() == 0
+                    && table == null && !clear) {
+                drop();
             }
-
-            return new DefaultTrafficTreatment(instructions);
+            return new DefaultTrafficTreatment(deferred, immediate, table, clear);
         }
 
     }
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 1d2f7f4..b44411b 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
@@ -25,6 +25,7 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
+import org.onosproject.net.flow.instructions.Instructions;
 
 /**
  * Abstraction of network traffic treatment.
@@ -36,9 +37,37 @@
      *
      * @return list of treatment instructions
      */
+    @Deprecated
     List<Instruction> instructions();
 
     /**
+     * Returns the list of treatment instructions that will be applied
+     * further down the pipeline.
+     * @return list of treatment instructions
+     */
+    List<Instruction> deferred();
+
+    /**
+     * Returns the list of treatment instructions that will be applied
+     * immediately.
+     * @return list of treatment instructions
+     */
+    List<Instruction> immediate();
+
+    /**
+     * Returns the next table in the pipeline.
+     * @return a table transition; may be null.
+     */
+    Instructions.TableTypeTransition tableTransition();
+
+    /**
+     * Whether the deferred treatment instructions will be cleared
+     * by the device.
+     * @return a boolean
+     */
+    Boolean clearedDeferred();
+
+    /**
      * Builder of traffic treatment entities.
      */
     public interface Builder {
@@ -218,6 +247,25 @@
         public Builder popVlan();
 
         /**
+         * Any instructions preceded by this method call will be deferred.
+         * @return a treatment builder
+         */
+        public Builder deferred();
+
+        /**
+         * Any instructions preceded by this method call will be immediate.
+         * @return a treatment builder
+         */
+        public Builder immediate();
+
+
+        /**
+         * Instructs the device to clear the deferred instructions set.
+         * @return a treatment builder
+         */
+        public Builder wipeDeferred();
+
+        /**
          * Builds an immutable traffic treatment descriptor.
          *
          * @return traffic treatment
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 afbae7d..47e473f 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
@@ -295,7 +295,7 @@
 
         @Override
         public String toString() {
-            return toStringHelper(type()).toString();
+            return toStringHelper(type().toString()).toString();
 
         }
 
diff --git a/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficTreatmentTest.java b/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficTreatmentTest.java
index 7d18749..1086585 100644
--- a/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficTreatmentTest.java
+++ b/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficTreatmentTest.java
@@ -15,17 +15,16 @@
  */
 package org.onosproject.net.flow;
 
-import java.util.List;
-
+import com.google.common.testing.EqualsTester;
 import org.junit.Test;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
 
-import com.google.common.testing.EqualsTester;
+import java.util.List;
 
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -83,7 +82,7 @@
         builder1.add(instruction1);
 
         final List<Instruction> instructions2 = builder1.build().instructions();
-        assertThat(instructions2, hasSize(8));
+        assertThat(instructions2, hasSize(11));
     }
 
     /**
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 3951f0f..df673e1 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
@@ -45,6 +45,7 @@
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.criteria.Criterion.Type;
 import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
 import org.onosproject.net.resource.Bandwidth;
 import org.onosproject.net.resource.BandwidthResourceRequest;
 import org.onosproject.net.resource.Lambda;
@@ -96,6 +97,26 @@
         public List<Instruction> instructions() {
             return new ArrayList<>();
         }
+
+        @Override
+        public List<Instruction> deferred() {
+            return null;
+        }
+
+        @Override
+        public List<Instruction> immediate() {
+            return null;
+        }
+
+        @Override
+        public Instructions.TableTypeTransition tableTransition() {
+            return null;
+        }
+
+        @Override
+        public Boolean clearedDeferred() {
+            return null;
+        }
     }
 
     /**
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 a22dd08..d40a95d 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
@@ -63,6 +63,7 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.store.trivial.impl.SimpleFlowRuleStore;
@@ -562,6 +563,26 @@
         }
 
         @Override
+        public List<Instruction> deferred() {
+            return null;
+        }
+
+        @Override
+        public List<Instruction> immediate() {
+            return null;
+        }
+
+        @Override
+        public Instructions.TableTypeTransition tableTransition() {
+            return null;
+        }
+
+        @Override
+        public Boolean clearedDeferred() {
+            return null;
+        }
+
+        @Override
         public int hashCode() {
             return testval;
         }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/statistic/impl/DistributedStatisticStore.java b/core/store/dist/src/main/java/org/onosproject/store/statistic/impl/DistributedStatisticStore.java
index 64ebf06..24222ce 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/statistic/impl/DistributedStatisticStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/statistic/impl/DistributedStatisticStore.java
@@ -16,13 +16,14 @@
 package org.onosproject.store.statistic.impl;
 
 import com.google.common.collect.Sets;
-
+import org.apache.commons.collections.ListUtils;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
@@ -39,12 +40,12 @@
 import org.onosproject.store.flow.ReplicaInfoService;
 import org.onosproject.store.serializers.KryoNamespaces;
 import org.onosproject.store.serializers.KryoSerializer;
-import org.onlab.util.KryoNamespace;
 import org.slf4j.Logger;
 
 import java.io.IOException;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -305,7 +306,9 @@
     }
 
     private PortNumber getOutput(FlowRule rule) {
-        for (Instruction i : rule.treatment().instructions()) {
+        List<Instruction> all = ListUtils.union(rule.treatment().immediate(),
+                                                rule.treatment().deferred());
+        for (Instruction i : all) {
             if (i.type() == Instruction.Type.OUTPUT) {
                 Instructions.OutputInstruction out = (Instructions.OutputInstruction) i;
                 return out.port();
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
index 9cfe5d8..79efd7c 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
@@ -16,7 +16,6 @@
 package org.onosproject.provider.of.flow.impl;
 
 import com.google.common.collect.Lists;
-
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.Ip6Address;
@@ -41,7 +40,6 @@
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
-import org.projectfloodlight.openflow.protocol.OFInstructionType;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.action.OFActionCircuit;
 import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter;
@@ -57,10 +55,12 @@
 import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteActions;
 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.OFOxmOchSigidBasic;
+import org.projectfloodlight.openflow.protocol.ver13.OFFactoryVer13;
 import org.projectfloodlight.openflow.types.IPv4Address;
 import org.projectfloodlight.openflow.types.IPv6Address;
 import org.projectfloodlight.openflow.types.Masked;
@@ -81,7 +81,12 @@
     private final OFFlowMod flowMod;
 
     private final Match match;
-    private final List<OFAction> actions;
+
+    /*
+    All actions are contained in an OFInstruction. For OF1.0
+     the instruction type is apply instruction (immediate set in ONOS speak)
+     */
+    private final List<OFInstruction> instructions;
 
     private final Dpid dpid;
 
@@ -94,7 +99,7 @@
     public FlowEntryBuilder(Dpid dpid, OFFlowStatsEntry entry) {
         this.stat = entry;
         this.match = entry.getMatch();
-        this.actions = getActions(entry);
+        this.instructions = getInstructions(entry);
         this.dpid = dpid;
         this.removed = null;
         this.flowMod = null;
@@ -104,7 +109,7 @@
     public FlowEntryBuilder(Dpid dpid, OFFlowStatsEntry entry, Type tableType) {
         this.stat = entry;
         this.match = entry.getMatch();
-        this.actions = getActions(entry);
+        this.instructions = getInstructions(entry);
         this.dpid = dpid;
         this.removed = null;
         this.flowMod = null;
@@ -117,7 +122,7 @@
         this.removed = removed;
 
         this.dpid = dpid;
-        this.actions = null;
+        this.instructions = null;
         this.stat = null;
         this.flowMod = null;
         this.type = FlowType.REMOVED;
@@ -127,7 +132,7 @@
     public FlowEntryBuilder(Dpid dpid, OFFlowMod fm) {
         this.match = fm.getMatch();
         this.dpid = dpid;
-        this.actions = fm.getActions();
+        this.instructions = getInstructions(fm);
         this.type = FlowType.MOD;
         this.flowMod = fm;
         this.stat = null;
@@ -164,21 +169,30 @@
 
     }
 
-    private List<OFAction> getActions(OFFlowStatsEntry entry) {
+    private List<OFInstruction> getInstructions(OFFlowMod entry) {
         switch (entry.getVersion()) {
             case OF_10:
-                return entry.getActions();
+                return Lists.newArrayList(
+                        OFFactoryVer13.INSTANCE.instructions().applyActions(entry.getActions()));
             case OF_11:
             case OF_12:
             case OF_13:
-                List<OFInstruction> ins = entry.getInstructions();
-                for (OFInstruction in : ins) {
-                    if (in.getType().equals(OFInstructionType.APPLY_ACTIONS)) {
-                        OFInstructionApplyActions apply = (OFInstructionApplyActions) in;
-                        return apply.getActions();
-                    }
-                }
-                return Lists.newLinkedList();
+                return entry.getInstructions();
+            default:
+                log.warn("Unknown OF version {}", entry.getVersion());
+        }
+        return Lists.newLinkedList();
+    }
+
+    private List<OFInstruction> getInstructions(OFFlowStatsEntry entry) {
+        switch (entry.getVersion()) {
+            case OF_10:
+                return Lists.newArrayList(
+                        OFFactoryVer13.INSTANCE.instructions().applyActions(entry.getActions()));
+            case OF_11:
+            case OF_12:
+            case OF_13:
+                return entry.getInstructions();
             default:
                 log.warn("Unknown OF version {}", entry.getVersion());
         }
@@ -188,111 +202,145 @@
     private TrafficTreatment buildTreatment() {
         TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
         // If this is a drop rule
-        if (actions.size() == 0) {
+        if (instructions.size() == 0) {
             builder.drop();
             return builder.build();
         }
-        for (OFAction act : actions) {
-            switch (act.getType()) {
-            case OUTPUT:
-                OFActionOutput out = (OFActionOutput) act;
-                builder.setOutput(
-                        PortNumber.portNumber(out.getPort().getPortNumber()));
-                break;
-            case SET_VLAN_VID:
-                OFActionSetVlanVid vlan = (OFActionSetVlanVid) act;
-                builder.setVlanId(VlanId.vlanId(vlan.getVlanVid().getVlan()));
-                break;
-            case SET_VLAN_PCP:
-                OFActionSetVlanPcp pcp = (OFActionSetVlanPcp) act;
-                builder.setVlanPcp(pcp.getVlanPcp().getValue());
-                break;
-            case SET_DL_DST:
-                OFActionSetDlDst dldst = (OFActionSetDlDst) act;
-                builder.setEthDst(
-                        MacAddress.valueOf(dldst.getDlAddr().getLong()));
-                break;
-            case SET_DL_SRC:
-                OFActionSetDlSrc dlsrc = (OFActionSetDlSrc) act;
-                builder.setEthSrc(
-                        MacAddress.valueOf(dlsrc.getDlAddr().getLong()));
-
-                break;
-            case SET_NW_DST:
-                OFActionSetNwDst nwdst = (OFActionSetNwDst) act;
-                IPv4Address di = nwdst.getNwAddr();
-                builder.setIpDst(Ip4Address.valueOf(di.getInt()));
-                break;
-            case SET_NW_SRC:
-                OFActionSetNwSrc nwsrc = (OFActionSetNwSrc) act;
-                IPv4Address si = nwsrc.getNwAddr();
-                builder.setIpSrc(Ip4Address.valueOf(si.getInt()));
-                break;
-            case EXPERIMENTER:
-                OFActionExperimenter exp = (OFActionExperimenter) act;
-                if (exp.getExperimenter() == 0x80005A06 ||
-                        exp.getExperimenter() == 0x748771) {
-                    OFActionCircuit ct = (OFActionCircuit) exp;
-                    builder.setLambda(((OFOxmOchSigidBasic) ct.getField()).getValue().getChannelNumber());
-                } else {
-                    log.warn("Unsupported OFActionExperimenter {}", exp.getExperimenter());
-                }
-                break;
-            case SET_FIELD:
-                OFActionSetField setField = (OFActionSetField) act;
-                handleSetField(builder, setField.getField());
-                break;
-            case POP_MPLS:
-                OFActionPopMpls popMpls = (OFActionPopMpls) act;
-                builder.popMpls((short) popMpls.getEthertype().getValue());
-                break;
-            case PUSH_MPLS:
-                builder.pushMpls();
-                break;
-            case COPY_TTL_IN:
-                builder.copyTtlIn();
-                break;
-            case COPY_TTL_OUT:
-                builder.copyTtlOut();
-                break;
-            case DEC_MPLS_TTL:
-                builder.decMplsTtl();
-                break;
-            case DEC_NW_TTL:
-                builder.decNwTtl();
-                break;
-            case GROUP:
-                OFActionGroup group = (OFActionGroup) act;
-                builder.group(new DefaultGroupId(group.getGroup().getGroupNumber()));
-                break;
-            case POP_VLAN:
-                builder.popVlan();
-                break;
-            case STRIP_VLAN:
-                builder.stripVlan();
-                break;
-            case SET_TP_DST:
-            case SET_TP_SRC:
-            case POP_PBB:
-            case PUSH_PBB:
-            case PUSH_VLAN:
-            case SET_MPLS_LABEL:
-            case SET_MPLS_TC:
-            case SET_MPLS_TTL:
-            case SET_NW_ECN:
-            case SET_NW_TOS:
-            case SET_NW_TTL:
-            case SET_QUEUE:
-
-            case ENQUEUE:
-            default:
-                log.warn("Action type {} not yet implemented.", act.getType());
+        for (OFInstruction in : instructions) {
+            switch (in.getType()) {
+                case GOTO_TABLE:
+                    builder.transition(tableType);
+                    break;
+                case WRITE_METADATA:
+                    break;
+                case WRITE_ACTIONS:
+                    builder.deferred();
+                    buildActions(((OFInstructionWriteActions) in).getActions(),
+                                 builder);
+                    break;
+                case APPLY_ACTIONS:
+                    builder.immediate();
+                    buildActions(((OFInstructionApplyActions) in).getActions(),
+                                 builder);
+                    break;
+                case CLEAR_ACTIONS:
+                    builder.wipeDeferred();
+                    break;
+                case EXPERIMENTER:
+                    break;
+                case METER:
+                    break;
+                default:
+                    log.warn("Unknown instructions type {}", in.getType());
             }
         }
 
         return builder.build();
     }
 
+    private TrafficTreatment.Builder buildActions(List<OFAction> actions,
+                                                  TrafficTreatment.Builder builder) {
+        for (OFAction act : actions) {
+            switch (act.getType()) {
+                case OUTPUT:
+                    OFActionOutput out = (OFActionOutput) act;
+                    builder.setOutput(
+                            PortNumber.portNumber(out.getPort().getPortNumber()));
+                    break;
+                case SET_VLAN_VID:
+                    OFActionSetVlanVid vlan = (OFActionSetVlanVid) act;
+                    builder.setVlanId(VlanId.vlanId(vlan.getVlanVid().getVlan()));
+                    break;
+                case SET_VLAN_PCP:
+                    OFActionSetVlanPcp pcp = (OFActionSetVlanPcp) act;
+                    builder.setVlanPcp(pcp.getVlanPcp().getValue());
+                    break;
+                case SET_DL_DST:
+                    OFActionSetDlDst dldst = (OFActionSetDlDst) act;
+                    builder.setEthDst(
+                            MacAddress.valueOf(dldst.getDlAddr().getLong()));
+                    break;
+                case SET_DL_SRC:
+                    OFActionSetDlSrc dlsrc = (OFActionSetDlSrc) act;
+                    builder.setEthSrc(
+                            MacAddress.valueOf(dlsrc.getDlAddr().getLong()));
+
+                    break;
+                case SET_NW_DST:
+                    OFActionSetNwDst nwdst = (OFActionSetNwDst) act;
+                    IPv4Address di = nwdst.getNwAddr();
+                    builder.setIpDst(Ip4Address.valueOf(di.getInt()));
+                    break;
+                case SET_NW_SRC:
+                    OFActionSetNwSrc nwsrc = (OFActionSetNwSrc) act;
+                    IPv4Address si = nwsrc.getNwAddr();
+                    builder.setIpSrc(Ip4Address.valueOf(si.getInt()));
+                    break;
+                case EXPERIMENTER:
+                    OFActionExperimenter exp = (OFActionExperimenter) act;
+                    if (exp.getExperimenter() == 0x80005A06 ||
+                            exp.getExperimenter() == 0x748771) {
+                        OFActionCircuit ct = (OFActionCircuit) exp;
+                        builder.setLambda(((OFOxmOchSigidBasic) ct.getField()).getValue().getChannelNumber());
+                    } else {
+                        log.warn("Unsupported OFActionExperimenter {}", exp.getExperimenter());
+                    }
+                    break;
+                case SET_FIELD:
+                    OFActionSetField setField = (OFActionSetField) act;
+                    handleSetField(builder, setField.getField());
+                    break;
+                case POP_MPLS:
+                    OFActionPopMpls popMpls = (OFActionPopMpls) act;
+                    builder.popMpls((short) popMpls.getEthertype().getValue());
+                    break;
+                case PUSH_MPLS:
+                    builder.pushMpls();
+                    break;
+                case COPY_TTL_IN:
+                    builder.copyTtlIn();
+                    break;
+                case COPY_TTL_OUT:
+                    builder.copyTtlOut();
+                    break;
+                case DEC_MPLS_TTL:
+                    builder.decMplsTtl();
+                    break;
+                case DEC_NW_TTL:
+                    builder.decNwTtl();
+                    break;
+                case GROUP:
+                    OFActionGroup group = (OFActionGroup) act;
+                    builder.group(new DefaultGroupId(group.getGroup().getGroupNumber()));
+                    break;
+                case POP_VLAN:
+                    builder.popVlan();
+                    break;
+                case STRIP_VLAN:
+                    builder.stripVlan();
+                    break;
+                case SET_TP_DST:
+                case SET_TP_SRC:
+                case POP_PBB:
+                case PUSH_PBB:
+                case PUSH_VLAN:
+                case SET_MPLS_LABEL:
+                case SET_MPLS_TC:
+                case SET_MPLS_TTL:
+                case SET_NW_ECN:
+                case SET_NW_TOS:
+                case SET_NW_TTL:
+                case SET_QUEUE:
+
+                case ENQUEUE:
+                default:
+                    log.warn("Action type {} not yet implemented.", act.getType());
+            }
+        }
+        return builder;
+    }
+
+
     private void handleSetField(TrafficTreatment.Builder builder, OFOxm<?> oxm) {
         switch (oxm.getMatchField().id) {
         case VLAN_PCP:
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 ff4ae57..c2cfe28 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
@@ -205,7 +205,7 @@
                                        OFVlanVidMatch.PRESENT);
                 } else {
                     mBuilder.setExact(MatchField.VLAN_VID,
-                            OFVlanVidMatch.ofVlanVid(VlanVid.ofVlan(vid.vlanId().toShort())));
+                                      OFVlanVidMatch.ofVlanVid(VlanVid.ofVlan(vid.vlanId().toShort())));
                 }
                 break;
             case VLAN_PCP:
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 c8b63b4..d253aef 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
@@ -15,6 +15,7 @@
  */
 package org.onosproject.provider.of.flow.impl;
 
+import com.google.common.collect.Lists;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip6Address;
 import org.onosproject.net.PortNumber;
@@ -95,18 +96,22 @@
     @Override
     public OFFlowAdd buildFlowAdd() {
         Match match = buildMatch();
-        List<OFAction> actions = buildActions();
-        List<OFInstruction> instructions = buildInstructions();
+        List<OFAction> deferredActions = buildActions(treatment.deferred());
+        List<OFAction> immediateActions = buildActions(treatment.immediate());
+        List<OFInstruction> instructions = Lists.newLinkedList();
 
-        // FIXME had to revert back to using apply-actions instead of
-        // write-actions because LINC-OE apparently doesn't support
-        // write-actions. I would prefer to change this back in the future
-        // because apply-actions is an optional instruction in OF 1.3.
 
-        if (actions != null) {
-            OFInstruction applyActions =
-                    factory().instructions().applyActions(actions);
-            instructions.add(applyActions);
+        if (immediateActions.size() > 0) {
+            instructions.add(factory().instructions().applyActions(immediateActions));
+        }
+        if (treatment.clearedDeferred()) {
+            instructions.add(factory().instructions().clearActions());
+        }
+        if (deferredActions.size() > 0) {
+            instructions.add(factory().instructions().writeActions(deferredActions));
+        }
+        if (treatment.tableTransition() != null) {
+            instructions.add(buildTableGoto(treatment.tableTransition()));
         }
 
         long cookie = flowRule().id().value();
@@ -128,13 +133,22 @@
     @Override
     public OFFlowMod buildFlowMod() {
         Match match = buildMatch();
-        List<OFAction> actions = buildActions();
-        List<OFInstruction> instructions = buildInstructions();
+        List<OFAction> deferredActions = buildActions(treatment.deferred());
+        List<OFAction> immediateActions = buildActions(treatment.immediate());
+        List<OFInstruction> instructions = Lists.newLinkedList();
 
-        if (actions != null) {
-            OFInstruction applyActions =
-                    factory().instructions().applyActions(actions);
-            instructions.add(applyActions);
+
+        if (immediateActions.size() > 0) {
+            instructions.add(factory().instructions().applyActions(immediateActions));
+        }
+        if (treatment.clearedDeferred()) {
+            instructions.add(factory().instructions().clearActions());
+        }
+        if (deferredActions.size() > 0) {
+            instructions.add(factory().instructions().writeActions(deferredActions));
+        }
+        if (treatment.tableTransition() != null) {
+            instructions.add(buildTableGoto(treatment.tableTransition()));
         }
 
         long cookie = flowRule().id().value();
@@ -189,13 +203,13 @@
         return instructions;
     }
 
-    private List<OFAction> buildActions() {
+    private List<OFAction> buildActions(List<Instruction> treatments) {
         List<OFAction> actions = new LinkedList<>();
         boolean tableFound = false;
         if (treatment == null) {
             return actions;
         }
-        for (Instruction i : treatment.instructions()) {
+        for (Instruction i : treatments) {
             switch (i.type()) {
                 case DROP:
                     log.warn("Saw drop action; assigning drop action");
@@ -320,7 +334,7 @@
                 return factory().actions().popMpls(EthType.of(popHeaderInstructions
                                                               .ethernetType()));
             case STRIP_VLAN:
-                return factory().actions().stripVlan();
+                return factory().actions().popVlan();
             case MPLS_LABEL:
                 ModMplsLabelInstruction mplsLabel =
                         (ModMplsLabelInstruction) l2m;
diff --git a/web/api/src/test/java/org/onosproject/codec/impl/IntentJsonMatcher.java b/web/api/src/test/java/org/onosproject/codec/impl/IntentJsonMatcher.java
index 9dddd4d..5ae8f20 100644
--- a/web/api/src/test/java/org/onosproject/codec/impl/IntentJsonMatcher.java
+++ b/web/api/src/test/java/org/onosproject/codec/impl/IntentJsonMatcher.java
@@ -380,7 +380,7 @@
 
         for (Instruction instruction : instructions) {
             boolean instructionFound = false;
-            for (int instructionIndex = 0; instructionIndex < jsonCriteria.size(); instructionIndex++) {
+            for (int instructionIndex = 0; instructionIndex < jsonInstructions.size(); instructionIndex++) {
                 final InstructionJsonMatcher instructionMatcher =
                         InstructionJsonMatcher.matchesInstruction(instruction);
                 if (instructionMatcher.matches(jsonInstructions.get(instructionIndex))) {