Improve fabric.p4 to reduce pipeline resources and refactor pipeconf impl

This patch affects both the P4 pipeline implementation and the
Java pipeconf.

P4 PIPELINE
- Less tables and smarter use of metadata to reduce inter-tables
dependencies and favor parallel execution of tables.
- Removed unused actions / renamed existing ones to make forwarding
behavior clearer (e.g. ingress_port_vlan table)
- Remove co-existence of simple and hansed table. Hashed should be the
default one, but implementations that do not support action profiles
might compile fabric.p4 to use the simple one.
- Use @name annotations for match fields to make control plane
independent of table implementation.
- Use @hidden to avoid showing actions and table on the p4info that
cannot be controlled at runtime.
- First attempt to support double VLAN cross-connect (xconnect table).
- New design has been tested with "fabric-refactoring" branch of
fabric-p4test:
github.com/opennetworkinglab/fabric-p4test/tree/fabric-refactoring

JAVA PIPECONF
This patch brings a major refactoring that reflects the experience
gathered in the past months of working on fabric.p4 and reasoning on its
pipeconf implementation. Indeed, the FlowObjective API is
under-specified and sometimes ambiguous which makes the process of
creating and maintaining a pipeliner implementation tedious. This
refactoring brings a simplified implementation by removing unused/
unnecessary functionalities and by recognizing commonality when possible
(e.g. by means of abstract and utility classes). It also makes design
patterns more explicit and consistent. Overall, the goal is to reduce
technical debt and to make it easier to support new features as we
evolve fabric.p4

Changes include:
- Changes in pipeliner/interpreter to reflect new pipeline design.
- By default translate objective treatment to PiAction. This favors
debuggability of flow rules in ONOS.
- Support new NextObjective’s NextTreatment class.
- Remove lots of unused/unnecessary code (e.g. async callback handling
for pending objective install status in pipeliner as current
implementation was always returning success)
- Gather commonality in abstract classes and simplify implementation
for objective translator (filtering, forwarding, next)
- New implementation of ForwardingFunctionTypes (FFT) that looks at
criterion instance values along with their types (to avoid relying on
case-specific if-else conditions to recognize variants of an FFT)
- Adaptive translation of NextObjective based on presence of simple or
hashed table.
- Support DENY FilteringObjective

Also:
- Fix onos-p4-gen-constants to avoid generating conflicting
PiMatchFieldId variable names.
- Install Graphviz tools in p4vm to generate p4c graphs
- Generate p4c graphs by default when compiling fabric.p4
- Use more compact Hex string when printing PI values

Change-Id: Ife79e44054dc5bc48833f95d0551a7370150eac5
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java
index a629718..217a736 100644
--- a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/FabricInterpreterTest.java
@@ -54,17 +54,17 @@
      * Map treatment to push_internal_vlan action.
      */
     @Test
-    public void testFilteringTreatment1() throws Exception {
+    public void testFilteringTreatmentPermitWithInternalVlan() throws Exception {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .pushVlan()
                 .setVlanId(VLAN_100)
                 .build();
         PiAction mappedAction = interpreter.mapTreatment(treatment,
                                                    FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN);
-        PiActionParam param = new PiActionParam(FabricConstants.NEW_VLAN_ID,
+        PiActionParam param = new PiActionParam(FabricConstants.VLAN_ID,
                                                 ImmutableByteSequence.copyFrom(VLAN_100.toShort()));
         PiAction expectedAction = PiAction.builder()
-                .withId(FabricConstants.FABRIC_INGRESS_FILTERING_PUSH_INTERNAL_VLAN)
+                .withId(FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT_WITH_INTERNAL_VLAN)
                 .withParameter(param)
                 .build();
 
@@ -72,35 +72,15 @@
     }
 
     /**
-     * Map treatment to set_vlan action.
+     * Map treatment to permit action.
      */
     @Test
-    public void testFilteringTreatment2() throws Exception {
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setVlanId(VLAN_100)
-                .build();
-        PiAction mappedAction = interpreter.mapTreatment(treatment,
-                                                         FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN);
-        PiActionParam param = new PiActionParam(FabricConstants.NEW_VLAN_ID,
-                                                ImmutableByteSequence.copyFrom(VLAN_100.toShort()));
-        PiAction expectedAction = PiAction.builder()
-                .withId(FabricConstants.FABRIC_INGRESS_FILTERING_SET_VLAN)
-                .withParameter(param)
-                .build();
-
-        assertEquals(expectedAction, mappedAction);
-    }
-
-    /**
-     * Map treatment to nop action.
-     */
-    @Test
-    public void testFilteringTreatment3() throws Exception {
+    public void testFilteringTreatmentPermit() throws Exception {
         TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
         PiAction mappedAction = interpreter.mapTreatment(treatment,
                                                          FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN);
         PiAction expectedAction = PiAction.builder()
-                .withId(FabricConstants.FABRIC_INGRESS_FILTERING_NOP_INGRESS_PORT_VLAN)
+                .withId(FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT)
                 .build();
 
         assertEquals(expectedAction, mappedAction);
@@ -112,37 +92,43 @@
      * Map treatment to duplicate_to_controller action.
      */
     @Test
-    public void testForwardingTreatment1() throws Exception {
+    public void testAclTreatmentCloneToCpu() throws Exception {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .punt()
                 .build();
         PiAction mappedAction = interpreter.mapTreatment(treatment,
-                                                         FabricConstants.FABRIC_INGRESS_FORWARDING_ACL);
+                                                         FabricConstants.FABRIC_INGRESS_ACL_ACL);
         PiAction expectedAction = PiAction.builder()
-                .withId(FabricConstants.FABRIC_INGRESS_FORWARDING_CLONE_TO_CPU)
+                .withId(FabricConstants.FABRIC_INGRESS_ACL_CLONE_TO_CPU)
                 .build();
 
         assertEquals(expectedAction, mappedAction);
     }
 
     /**
-     * Map empty treatment for forwarding block to nop action.
+     * Map empty treatment for routing v4 table.
      */
     @Test
-    public void testEmptyForwardingTreatment() throws Exception {
+    public void testRoutingV4TreatmentEmpty() throws Exception {
         TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
-
-        PiAction mappedAction = interpreter.mapTreatment(treatment,
-                                                         FabricConstants.FABRIC_INGRESS_FORWARDING_ROUTING_V4);
+        PiAction mappedAction = interpreter.mapTreatment(
+                treatment, FabricConstants.FABRIC_INGRESS_FORWARDING_ROUTING_V4);
         PiAction expectedAction = PiAction.builder()
                 .withId(FabricConstants.FABRIC_INGRESS_FORWARDING_NOP_ROUTING_V4)
                 .build();
         assertEquals(expectedAction, mappedAction);
+    }
 
-        mappedAction = interpreter.mapTreatment(treatment,
-                                                FabricConstants.FABRIC_INGRESS_FORWARDING_ACL);
-        expectedAction = PiAction.builder()
-                .withId(FabricConstants.FABRIC_INGRESS_FORWARDING_NOP_ACL)
+    /**
+     * Map empty treatment for ACL table.
+     */
+    @Test
+    public void testAclTreatmentEmpty() throws Exception {
+        TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+        PiAction mappedAction = interpreter.mapTreatment(
+                treatment, FabricConstants.FABRIC_INGRESS_ACL_ACL);
+        PiAction expectedAction = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_ACL_NOP_ACL)
                 .build();
         assertEquals(expectedAction, mappedAction);
     }
@@ -153,47 +139,64 @@
      * Map treatment to output action.
      */
     @Test
-    public void testNextTreatment1() throws Exception {
+    public void testNextTreatmentSimpleOutput() throws Exception {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .setOutput(PORT_1)
                 .build();
-        PiAction mappedAction = interpreter.mapTreatment(treatment,
-                                                         FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE);
-        short portNumVal = (short) PORT_1.toLong();
-        PiActionParam param = new PiActionParam(FabricConstants.PORT_NUM,
-                                                ImmutableByteSequence.copyFrom(portNumVal));
+        PiAction mappedAction = interpreter.mapTreatment(
+                treatment, FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE);
+        PiActionParam param = new PiActionParam(FabricConstants.PORT_NUM, PORT_1.toLong());
         PiAction expectedAction = PiAction.builder()
                 .withId(FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_SIMPLE)
                 .withParameter(param)
                 .build();
-
         assertEquals(expectedAction, mappedAction);
     }
 
     /**
-     * Map treatment to output_ecmp action.
+     * Map treatment for hashed table to routing v4 action.
      */
     @Test
-    public void testNextTreatment2() throws Exception {
+    public void testNextTreatmentHashedRoutingV4() throws Exception {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .setEthSrc(SRC_MAC)
                 .setEthDst(DST_MAC)
                 .setOutput(PORT_1)
                 .build();
-        PiAction mappedAction = interpreter.mapTreatment(treatment,
-                                                         FabricConstants.FABRIC_INGRESS_NEXT_HASHED);
-        short portNumVal = (short) PORT_1.toLong();
-        PiActionParam ethSrcParam = new PiActionParam(FabricConstants.SMAC,
-                                                      ImmutableByteSequence.copyFrom(SRC_MAC.toBytes()));
-        PiActionParam ethDstParam = new PiActionParam(FabricConstants.DMAC,
-                                                      ImmutableByteSequence.copyFrom(DST_MAC.toBytes()));
-        PiActionParam portParam = new PiActionParam(FabricConstants.PORT_NUM,
-                                                ImmutableByteSequence.copyFrom(portNumVal));
+        PiAction mappedAction = interpreter.mapTreatment(
+                treatment, FabricConstants.FABRIC_INGRESS_NEXT_HASHED);
+        PiActionParam ethSrcParam = new PiActionParam(FabricConstants.SMAC, SRC_MAC.toBytes());
+        PiActionParam ethDstParam = new PiActionParam(FabricConstants.DMAC, DST_MAC.toBytes());
+        PiActionParam portParam = new PiActionParam(FabricConstants.PORT_NUM, PORT_1.toLong());
         PiAction expectedAction = PiAction.builder()
-                .withId(FabricConstants.FABRIC_INGRESS_NEXT_L3_ROUTING_HASHED)
+                .withId(FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_HASHED)
                 .withParameters(ImmutableList.of(ethSrcParam, ethDstParam, portParam))
                 .build();
+        assertEquals(expectedAction, mappedAction);
+    }
 
+    /**
+     * Map treatment for hashed table to routing v4 action.
+     */
+    @Test
+    public void testNextTreatmentHashedRoutingMpls() throws Exception {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setEthSrc(SRC_MAC)
+                .setEthDst(DST_MAC)
+                .setOutput(PORT_1)
+                .pushMpls()
+                .setMpls(MPLS_10)
+                .build();
+        PiAction mappedAction = interpreter.mapTreatment(
+                treatment, FabricConstants.FABRIC_INGRESS_NEXT_HASHED);
+        PiActionParam ethSrcParam = new PiActionParam(FabricConstants.SMAC, SRC_MAC.toBytes());
+        PiActionParam ethDstParam = new PiActionParam(FabricConstants.DMAC, DST_MAC.toBytes());
+        PiActionParam portParam = new PiActionParam(FabricConstants.PORT_NUM, PORT_1.toLong());
+        PiActionParam mplsParam = new PiActionParam(FabricConstants.LABEL, MPLS_10.toInt());
+        PiAction expectedAction = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_NEXT_MPLS_ROUTING_HASHED)
+                .withParameters(ImmutableList.of(ethSrcParam, ethDstParam, portParam, mplsParam))
+                .build();
         assertEquals(expectedAction, mappedAction);
     }
 
@@ -204,50 +207,14 @@
     public void testNextTreatment3() throws Exception {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .setVlanId(VLAN_100)
-                .setOutput(PORT_1)
                 .build();
-        PiAction mappedAction = interpreter.mapTreatment(treatment,
-                                                         FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE);
-        short portNumVal = (short) PORT_1.toLong();
-        PiActionParam portParam = new PiActionParam(FabricConstants.PORT_NUM,
-                                                    ImmutableByteSequence.copyFrom(portNumVal));
-        short vlanVal = VLAN_100.toShort();
-        PiActionParam vlanParam = new PiActionParam(FabricConstants.NEW_VLAN_ID,
-                                                    ImmutableByteSequence.copyFrom(vlanVal));
-
+        PiAction mappedAction = interpreter.mapTreatment(
+                treatment, FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN);
+        PiActionParam vlanParam = new PiActionParam(
+                FabricConstants.VLAN_ID, VLAN_100.toShort());
         PiAction expectedAction = PiAction.builder()
-                .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_VLAN_OUTPUT)
-                .withParameters(ImmutableList.of(vlanParam, portParam))
-                .build();
-
-        assertEquals(expectedAction, mappedAction);
-    }
-
-    @Test
-    public void testMplsRoutingTreatment() throws Exception {
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setEthDst(DST_MAC)
-                .setEthSrc(SRC_MAC)
-                .pushMpls()
-                .copyTtlOut()
-                .setMpls(MPLS_10)
-                .setOutput(PORT_1)
-                .build();
-        PiAction mappedAction = interpreter.mapTreatment(treatment,
-                                                         FabricConstants.FABRIC_INGRESS_NEXT_HASHED);
-        short portNumVal = (short) PORT_1.toLong();
-        PiActionParam ethSrcParam = new PiActionParam(FabricConstants.SMAC,
-                                                      ImmutableByteSequence.copyFrom(SRC_MAC.toBytes()));
-        PiActionParam ethDstParam = new PiActionParam(FabricConstants.DMAC,
-                                                      ImmutableByteSequence.copyFrom(DST_MAC.toBytes()));
-        PiActionParam portParam = new PiActionParam(FabricConstants.PORT_NUM,
-                                                    ImmutableByteSequence.copyFrom(portNumVal));
-        ImmutableByteSequence mplsVal =
-                ImmutableByteSequence.copyFrom(MPLS_10.toInt()).fit(20);
-        PiActionParam mplsParam = new PiActionParam(FabricConstants.LABEL, mplsVal);
-        PiAction expectedAction = PiAction.builder()
-                .withId(FabricConstants.FABRIC_INGRESS_NEXT_MPLS_ROUTING_V4_HASHED)
-                .withParameters(ImmutableList.of(ethSrcParam, ethDstParam, portParam, mplsParam))
+                .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_VLAN)
+                .withParameter(vlanParam)
                 .build();
         assertEquals(expectedAction, mappedAction);
     }
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipelinerTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipelinerTest.java
index 9ceb869..3bdab99 100644
--- a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipelinerTest.java
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipelinerTest.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.pipelines.fabric.pipeliner;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.MacAddress;
@@ -33,71 +34,71 @@
 import org.onosproject.net.flowobjective.DefaultFilteringObjective;
 import org.onosproject.net.flowobjective.FilteringObjective;
 import org.onosproject.net.flowobjective.ObjectiveError;
-import org.onosproject.net.group.GroupDescription;
 import org.onosproject.net.pi.runtime.PiAction;
 import org.onosproject.net.pi.runtime.PiActionParam;
 import org.onosproject.pipelines.fabric.FabricConstants;
 
-import java.util.List;
-
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 
 /**
  * Test cases for fabric.p4 pipeline filtering control block.
  */
 public class FabricFilteringPipelinerTest extends FabricPipelinerTest {
 
+    private FilteringObjectiveTranslator translator;
+
+    @Before
+    public void setup() {
+        super.doSetup();
+        translator = new FilteringObjectiveTranslator(DEVICE_ID, capabilitiesHashed);
+    }
+
     /**
      * Creates one rule for ingress_port_vlan table and 3 rules for
      * fwd_classifier table (IPv4, IPv6 and MPLS unicast) when
      * the condition is VLAN + MAC.
      */
     @Test
-    public void testRouterMacAndVlanFilter() {
+    public void testRouterMacAndVlanFilter() throws FabricPipelinerException {
         FilteringObjective filteringObjective = buildFilteringObjective(ROUTER_MAC);
-        PipelinerTranslationResult result = pipeliner.pipelinerFilter.filter(filteringObjective);
-
-        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
-        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
-
-        assertTrue(groupsInstalled.isEmpty());
+        ObjectiveTranslation actualTranslation = translator.translate(filteringObjective);
 
         // in port vlan flow rule
-        FlowRule actualFlowRule = flowRulesInstalled.get(0);
-        FlowRule flowRuleExpected =
+        FlowRule inportFlowRuleExpected =
                 buildExpectedVlanInPortRule(PORT_1,
                                             VlanId.NONE,
                                             VLAN_100,
                                             FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN);
-        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
 
         // forwarding classifier ipv4
-        actualFlowRule = flowRulesInstalled.get(1);
-        flowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
+        FlowRule classifierV4FlowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
                                                           ROUTER_MAC,
                                                           null,
                                                           Ethernet.TYPE_IPV4,
-                                                          FabricFilteringPipeliner.FWD_IPV4_ROUTING);
-        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+                                                          FilteringObjectiveTranslator.FWD_IPV4_ROUTING);
 
         // forwarding classifier ipv6
-        actualFlowRule = flowRulesInstalled.get(2);
-        flowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
+        FlowRule classifierV6FlowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
                                                           ROUTER_MAC,
                                                           null,
                                                           Ethernet.TYPE_IPV6,
-                                                          FabricFilteringPipeliner.FWD_IPV6_ROUTING);
-        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+                                                          FilteringObjectiveTranslator.FWD_IPV6_ROUTING);
 
         // forwarding classifier mpls
-        actualFlowRule = flowRulesInstalled.get(3);
-        flowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
+        FlowRule classifierMplsFlowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
                                                           ROUTER_MAC,
                                                           null,
                                                           Ethernet.MPLS_UNICAST,
-                                                          FabricFilteringPipeliner.FWD_MPLS);
-        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+                                                          FilteringObjectiveTranslator.FWD_MPLS);
+
+        ObjectiveTranslation expectedTranslation = ObjectiveTranslation.builder()
+                .addFlowRule(inportFlowRuleExpected)
+                .addFlowRule(classifierV4FlowRuleExpected)
+                .addFlowRule(classifierV6FlowRuleExpected)
+                .addFlowRule(classifierMplsFlowRuleExpected)
+                .build();
+
+        assertEquals(expectedTranslation, actualTranslation);
     }
 
     /**
@@ -106,7 +107,7 @@
      * multicast mac address.
      */
     @Test
-    public void testIpv4MulticastFwdClass() {
+    public void testIpv4MulticastFwdClass() throws FabricPipelinerException {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .pushVlan()
                 .setVlanId(VLAN_100)
@@ -121,29 +122,28 @@
                 .fromApp(APP_ID)
                 .makePermanent()
                 .add();
-        PipelinerTranslationResult result = pipeliner.pipelinerFilter.filter(filteringObjective);
-        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
-        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
-
-        assertTrue(groupsInstalled.isEmpty());
+        ObjectiveTranslation actualTranslation = translator.translate(filteringObjective);
 
         // in port vlan flow rule
-        FlowRule actualFlowRule = flowRulesInstalled.get(0);
-        FlowRule flowRuleExpected =
+        FlowRule inportFlowRuleExpected =
                 buildExpectedVlanInPortRule(PORT_1,
                                             VlanId.NONE,
                                             VLAN_100,
                                             FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN);
-        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
 
         // forwarding classifier
-        actualFlowRule = flowRulesInstalled.get(1);
-        flowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
+        FlowRule classifierFlowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
                                                           MacAddress.IPV4_MULTICAST,
                                                           MacAddress.IPV4_MULTICAST_MASK,
                                                           Ethernet.TYPE_IPV4,
-                                                          FabricFilteringPipeliner.FWD_IPV4_ROUTING);
-        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+                                                          FilteringObjectiveTranslator.FWD_IPV4_ROUTING);
+
+        ObjectiveTranslation expectedTranslation = ObjectiveTranslation.builder()
+                .addFlowRule(inportFlowRuleExpected)
+                .addFlowRule(classifierFlowRuleExpected)
+                .build();
+
+        assertEquals(expectedTranslation, actualTranslation);
     }
 
     /**
@@ -152,7 +152,7 @@
      * multicast mac address.
      */
     @Test
-    public void testIpv6MulticastFwdClass() {
+    public void testIpv6MulticastFwdClass() throws FabricPipelinerException {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .pushVlan()
                 .setVlanId(VLAN_100)
@@ -167,29 +167,27 @@
                 .fromApp(APP_ID)
                 .makePermanent()
                 .add();
-        PipelinerTranslationResult result = pipeliner.pipelinerFilter.filter(filteringObjective);
-        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
-        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
-
-        assertTrue(groupsInstalled.isEmpty());
+        ObjectiveTranslation actualTranslation = translator.translate(filteringObjective);
 
         // in port vlan flow rule
-        FlowRule actualFlowRule = flowRulesInstalled.get(0);
-        FlowRule flowRuleExpected =
+        FlowRule inportFlowRuleExpected =
                 buildExpectedVlanInPortRule(PORT_1,
                                             VlanId.NONE,
                                             VLAN_100,
                                             FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN);
-        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
 
-        // forwarding classifier
-        actualFlowRule = flowRulesInstalled.get(1);
-        flowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
+        FlowRule classifierFlowRuleExpected = buildExpectedFwdClassifierRule(PORT_1,
                                                           MacAddress.IPV6_MULTICAST,
                                                           MacAddress.IPV6_MULTICAST_MASK,
                                                           Ethernet.TYPE_IPV6,
-                                                          FabricFilteringPipeliner.FWD_IPV6_ROUTING);
-        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
+                                                          FilteringObjectiveTranslator.FWD_IPV6_ROUTING);
+
+        ObjectiveTranslation expectedTranslation = ObjectiveTranslation.builder()
+                .addFlowRule(inportFlowRuleExpected)
+                .addFlowRule(classifierFlowRuleExpected)
+                .build();
+
+        assertEquals(expectedTranslation, actualTranslation);
     }
 
     /**
@@ -198,51 +196,65 @@
      * The packet will be handled by bridging table by default.
      */
     @Test
-    public void testFwdBridging() {
+    public void testFwdBridging() throws Exception {
         FilteringObjective filteringObjective = buildFilteringObjective(null);
-        PipelinerTranslationResult result = pipeliner.pipelinerFilter.filter(filteringObjective);
-        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
-        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
-
-        assertTrue(groupsInstalled.isEmpty());
+        ObjectiveTranslation actualTranslation = translator.translate(filteringObjective);
 
         // in port vlan flow rule
-        FlowRule actualFlowRule = flowRulesInstalled.get(0);
         FlowRule flowRuleExpected =
                 buildExpectedVlanInPortRule(PORT_1,
                                             VlanId.NONE,
                                             VLAN_100,
                                             FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN);
-        assertTrue(flowRuleExpected.exactMatch(actualFlowRule));
 
         // No rules in forwarding classifier, will do default action: set fwd type to bridging
+
+        ObjectiveTranslation expectedTranslation = ObjectiveTranslation.builder()
+                .addFlowRule(flowRuleExpected)
+                .build();
+
+        assertEquals(expectedTranslation, actualTranslation);
     }
 
     /**
-     * We supports only PERMIT type of filtering objective.
+     * Test DENY objective.
      */
     @Test
-    public void testUnsupportedObjective() {
+    public void testDenyObjective() throws FabricPipelinerException {
         FilteringObjective filteringObjective = DefaultFilteringObjective.builder()
                 .deny()
                 .withKey(Criteria.matchInPort(PORT_1))
-                .addCondition(Criteria.matchVlanId(VLAN_100))
+                .addCondition(Criteria.matchVlanId(VlanId.NONE))
                 .fromApp(APP_ID)
                 .makePermanent()
+                .withPriority(PRIORITY)
                 .add();
 
-        PipelinerTranslationResult result = pipeliner.pipelinerFilter.filter(filteringObjective);
-        pipeliner.pipelinerFilter.filter(filteringObjective);
+        ObjectiveTranslation actualTranslation = translator.translate(filteringObjective);
 
-        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
-        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
+                .matchInPort(PORT_1)
+                .matchPi(VLAN_INVALID);
+        PiAction piAction = PiAction.builder()
+                    .withId(FabricConstants.FABRIC_INGRESS_FILTERING_DENY)
+                    .build();
+        FlowRule expectedFlowRule = DefaultFlowRule.builder()
+                .withPriority(PRIORITY)
+                .withSelector(selector.build())
+                .withTreatment(DefaultTrafficTreatment.builder()
+                                       .piTableAction(piAction).build())
+                .fromApp(APP_ID)
+                .forDevice(DEVICE_ID)
+                .makePermanent()
+                .forTable(FabricConstants.FABRIC_INGRESS_FILTERING_INGRESS_PORT_VLAN)
+                .build();
 
-        assertTrue(flowRulesInstalled.isEmpty());
-        assertTrue(groupsInstalled.isEmpty());
+        ObjectiveTranslation expectedTranslation = ObjectiveTranslation.builder()
+                .addFlowRule(expectedFlowRule)
+                .build();
 
-        assertTrue(result.error().isPresent());
-        ObjectiveError error = result.error().get();
-        assertEquals(ObjectiveError.UNSUPPORTED, error);
+        assertEquals(expectedTranslation, actualTranslation);
+
     }
 
     /**
@@ -258,12 +270,8 @@
                 .makePermanent()
                 .add();
 
-        PipelinerTranslationResult result = pipeliner.pipelinerFilter.filter(filteringObjective);
-        pipeliner.pipelinerFilter.filter(filteringObjective);
-
-        assertTrue(result.error().isPresent());
-        ObjectiveError error = result.error().get();
-        assertEquals(ObjectiveError.BADPARAMS, error);
+        ObjectiveTranslation result1 = translator.translate(filteringObjective);
+        assertError(ObjectiveError.BADPARAMS, result1);
 
         // Filtering objective should use in_port as key
         filteringObjective = DefaultFilteringObjective.builder()
@@ -275,16 +283,17 @@
                 .makePermanent()
                 .add();
 
-        result = pipeliner.pipelinerFilter.filter(filteringObjective);
-        pipeliner.pipelinerFilter.filter(filteringObjective);
-
-        assertTrue(result.error().isPresent());
-        error = result.error().get();
-        assertEquals(ObjectiveError.BADPARAMS, error);
+        ObjectiveTranslation result2 = translator.translate(filteringObjective);
+        assertError(ObjectiveError.BADPARAMS, result2);
     }
 
     /* Utilities */
 
+    private void assertError(ObjectiveError error, ObjectiveTranslation actualTranslation) {
+        ObjectiveTranslation expectedTranslation = ObjectiveTranslation.ofError(error);
+        assertEquals(expectedTranslation, actualTranslation);
+    }
+
     private FilteringObjective buildFilteringObjective(MacAddress dstMac) {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .pushVlan()
@@ -311,20 +320,27 @@
 
         TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
                 .matchInPort(inPort);
-        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+        PiAction piAction;
         if (vlanId == null || vlanId.equals(VlanId.NONE)) {
             selector.matchPi(VLAN_INVALID);
-            treatment.pushVlan();
-            treatment.setVlanId(internalVlan);
+            piAction = PiAction.builder()
+                    .withId(FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT_WITH_INTERNAL_VLAN)
+                    .withParameter(new PiActionParam(
+                            FabricConstants.VLAN_ID, internalVlan.toShort()))
+                    .build();
         } else {
             selector.matchPi(VLAN_VALID);
             selector.matchVlanId(vlanId);
+            piAction = PiAction.builder()
+                    .withId(FabricConstants.FABRIC_INGRESS_FILTERING_PERMIT)
+                    .build();
         }
 
         return DefaultFlowRule.builder()
                 .withPriority(PRIORITY)
                 .withSelector(selector.build())
-                .withTreatment(treatment.build())
+                .withTreatment(DefaultTrafficTreatment.builder()
+                                       .piTableAction(piAction).build())
                 .fromApp(APP_ID)
                 .forDevice(DEVICE_ID)
                 .makePermanent()
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipelineTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipelineTest.java
index bb06dd3..9c04d8f 100644
--- a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipelineTest.java
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipelineTest.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.pipelines.fabric.pipeliner;
 
+import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.onlab.packet.Ethernet;
@@ -49,13 +50,20 @@
  */
 public class FabricForwardingPipelineTest extends FabricPipelinerTest {
 
+    private ForwardingObjectiveTranslator translator;
+
+    @Before
+    public void setup() {
+        super.doSetup();
+        translator = new ForwardingObjectiveTranslator(DEVICE_ID, capabilitiesHashed);
+    }
+
     /**
      * Test versatile flag of forwarding objective with ARP match.
      */
     @Test
     public void testAclArp() {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .wipeDeferred()
                 .punt()
                 .build();
         // ARP
@@ -71,7 +79,7 @@
                 .withTreatment(treatment)
                 .add();
 
-        PipelinerTranslationResult result = pipeliner.pipelinerForward.forward(fwd);
+        ObjectiveTranslation result = translator.translate(fwd);
 
         List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
         List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
@@ -79,13 +87,17 @@
         assertTrue(groupsInstalled.isEmpty());
 
         FlowRule actualFlowRule = flowRulesInstalled.get(0);
+        PiAction piAction = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_ACL_CLONE_TO_CPU)
+                .build();
         FlowRule expectedFlowRule = DefaultFlowRule.builder()
                 .forDevice(DEVICE_ID)
-                .forTable(FabricConstants.FABRIC_INGRESS_FORWARDING_ACL)
+                .forTable(FabricConstants.FABRIC_INGRESS_ACL_ACL)
                 .withPriority(PRIORITY)
                 .makePermanent()
                 .withSelector(selector)
-                .withTreatment(treatment)
+                .withTreatment(DefaultTrafficTreatment.builder()
+                                       .piTableAction(piAction).build())
                 .fromApp(APP_ID)
                 .build();
 
@@ -117,7 +129,7 @@
                 .withTreatment(treatment)
                 .add();
 
-        PipelinerTranslationResult result = pipeliner.pipelinerForward.forward(fwd);
+        ObjectiveTranslation result = translator.translate(fwd);
 
         List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
         List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
@@ -125,13 +137,17 @@
         assertTrue(groupsInstalled.isEmpty());
 
         FlowRule actualFlowRule = flowRulesInstalled.get(0);
+        PiAction piAction = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_ACL_PUNT_TO_CPU)
+                .build();
         FlowRule expectedFlowRule = DefaultFlowRule.builder()
                 .forDevice(DEVICE_ID)
-                .forTable(FabricConstants.FABRIC_INGRESS_FORWARDING_ACL)
+                .forTable(FabricConstants.FABRIC_INGRESS_ACL_ACL)
                 .withPriority(PRIORITY)
                 .makePermanent()
                 .withSelector(selector)
-                .withTreatment(treatment)
+                .withTreatment(DefaultTrafficTreatment.builder()
+                                       .piTableAction(piAction).build())
                 .fromApp(APP_ID)
                 .build();
 
@@ -142,7 +158,7 @@
      * Test programming L2 unicast rule to bridging table.
      */
     @Test
-    public void testL2Unicast() {
+    public void testL2Unicast() throws FabricPipelinerException {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchVlanId(VLAN_100)
                 .matchEthDst(HOST_MAC)
@@ -152,7 +168,7 @@
     }
 
     @Test
-    public void testL2Broadcast() {
+    public void testL2Broadcast() throws FabricPipelinerException {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchVlanId(VLAN_100)
                 .build();
@@ -161,7 +177,7 @@
     }
 
     @Test
-    public void testIPv4Unicast() {
+    public void testIPv4Unicast() throws FabricPipelinerException {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPDst(IPV4_UNICAST_ADDR)
@@ -174,7 +190,7 @@
     }
 
     @Test
-    public void testIPv4UnicastWithNoNextId() {
+    public void testIPv4UnicastWithNoNextId() throws FabricPipelinerException {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPDst(IPV4_UNICAST_ADDR)
@@ -188,7 +204,7 @@
 
     @Test
     @Ignore
-    public void testIPv4Multicast() {
+    public void testIPv4Multicast() throws FabricPipelinerException {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchVlanId(VLAN_100)
@@ -203,7 +219,7 @@
 
     @Test
     @Ignore
-    public void testIPv6Unicast() {
+    public void testIPv6Unicast() throws FabricPipelinerException {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV6)
                 .matchIPDst(IPV6_UNICAST_ADDR)
@@ -218,7 +234,7 @@
 
     @Test
     @Ignore
-    public void testIPv6Multicast() {
+    public void testIPv6Multicast() throws FabricPipelinerException {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV6)
                 .matchVlanId(VLAN_100)
@@ -232,7 +248,7 @@
     }
 
     @Test
-    public void testMpls() {
+    public void testMpls() throws FabricPipelinerException {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.MPLS_UNICAST)
                 .matchMplsLabel(MPLS_10)
@@ -255,11 +271,16 @@
     }
 
     private void testSpecificForward(PiTableId expectedTableId, TrafficSelector expectedSelector,
-                                     TrafficSelector selector, Integer nextId) {
+                                     TrafficSelector selector, Integer nextId) throws FabricPipelinerException {
         TrafficTreatment setNextIdTreatment;
         if (nextId == null) {
             // Ref: RoutingRulePopulator.java->revokeIpRuleForRouter
-            setNextIdTreatment = DefaultTrafficTreatment.builder().build();
+
+            setNextIdTreatment = DefaultTrafficTreatment.builder().
+                    piTableAction(PiAction.builder()
+                                          .withId(FabricConstants.FABRIC_INGRESS_FORWARDING_NOP_ROUTING_V4)
+                                          .build())
+                    .build();
         } else {
             PiActionParam nextIdParam = new PiActionParam(FabricConstants.NEXT_ID, nextId);
             PiAction.Builder setNextIdAction = PiAction.builder()
@@ -283,7 +304,8 @@
     }
 
     private void testSpecificForward(PiTableId expectedTableId, TrafficSelector expectedSelector,
-                                     TrafficSelector selector, Integer nextId, TrafficTreatment treatment) {
+                                     TrafficSelector selector, Integer nextId, TrafficTreatment treatment)
+            throws FabricPipelinerException {
         ForwardingObjective.Builder fwd = DefaultForwardingObjective.builder()
                 .withSelector(selector)
                 .withPriority(PRIORITY)
@@ -296,14 +318,7 @@
             fwd.nextStep(nextId);
         }
 
-        PipelinerTranslationResult result = pipeliner.pipelinerForward.forward(fwd.add());
-
-        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
-        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
-        assertEquals(1, flowRulesInstalled.size());
-        assertTrue(groupsInstalled.isEmpty());
-
-        FlowRule actualFlowRule = flowRulesInstalled.get(0);
+        ObjectiveTranslation actualTranslation = translator.translate(fwd.add());
 
         FlowRule expectedFlowRule = DefaultFlowRule.builder()
                 .forDevice(DEVICE_ID)
@@ -315,7 +330,11 @@
                 .fromApp(APP_ID)
                 .build();
 
-        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
+        ObjectiveTranslation expectedTranslation = ObjectiveTranslation.builder()
+                .addFlowRule(expectedFlowRule)
+                .build();
+
+        assertEquals(expectedTranslation, actualTranslation);
     }
 
     private TrafficSelector buildExpectedSelector(TrafficSelector selector) {
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipelinerTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipelinerTest.java
index 1aab0d5..081f68f 100644
--- a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipelinerTest.java
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricNextPipelinerTest.java
@@ -17,12 +17,8 @@
 package org.onosproject.pipelines.fabric.pipeliner;
 
 import com.google.common.collect.ImmutableList;
-import org.easymock.EasyMock;
+import org.junit.Before;
 import org.junit.Test;
-import org.onlab.junit.TestUtils;
-import org.onlab.util.ImmutableByteSequence;
-import org.onosproject.net.behaviour.DefaultNextGroup;
-import org.onosproject.net.behaviour.NextGroup;
 import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -31,9 +27,7 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.PiCriterion;
 import org.onosproject.net.flowobjective.DefaultNextObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveStore;
 import org.onosproject.net.flowobjective.NextObjective;
-import org.onosproject.net.flowobjective.Objective;
 import org.onosproject.net.group.DefaultGroupBucket;
 import org.onosproject.net.group.DefaultGroupDescription;
 import org.onosproject.net.group.DefaultGroupKey;
@@ -48,34 +42,44 @@
 import org.onosproject.pipelines.fabric.FabricConstants;
 
 import java.util.List;
-import java.util.Map;
 import java.util.stream.Collectors;
 
-import static org.easymock.EasyMock.*;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 
 /**
  * Test cases for fabric.p4 pipeline next control block.
  */
 public class FabricNextPipelinerTest extends FabricPipelinerTest {
+
+    private NextObjectiveTranslator translatorHashed;
+    private NextObjectiveTranslator translatorSimple;
+
     private FlowRule vlanMetaFlowRule;
 
-    public FabricNextPipelinerTest() {
+    @Before
+    public void setup() {
+        super.doSetup();
+
+        translatorHashed = new NextObjectiveTranslator(DEVICE_ID, capabilitiesHashed);
+        translatorSimple = new NextObjectiveTranslator(DEVICE_ID, capabilitiesSimple);
+
         PiCriterion nextIdCriterion = PiCriterion.builder()
-                .matchExact(FabricConstants.FABRIC_METADATA_NEXT_ID, NEXT_ID_1)
+                .matchExact(FabricConstants.HDR_NEXT_ID, NEXT_ID_1)
                 .build();
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchPi(nextIdCriterion)
                 .build();
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setVlanId(VLAN_100)
+        PiAction piAction = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_VLAN)
+                .withParameter(new PiActionParam(FabricConstants.VLAN_ID, VLAN_100.toShort()))
                 .build();
-
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .piTableAction(piAction)
+                .build();
         vlanMetaFlowRule = DefaultFlowRule.builder()
                 .withSelector(selector)
                 .withTreatment(treatment)
-                .forTable(FabricConstants.FABRIC_INGRESS_NEXT_VLAN_META)
+                .forTable(FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN)
                 .makePermanent()
                 // FIXME: currently next objective doesn't support priority, ignore this
                 .withPriority(0)
@@ -88,53 +92,81 @@
      * Test program output rule for Simple table.
      */
     @Test
-    public void testSimpleOutput() {
+    public void testSimpleOutput() throws FabricPipelinerException {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .setOutput(PORT_1)
                 .build();
-        testSimple(treatment);
+        PiAction piAction = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_SIMPLE)
+                .withParameter(new PiActionParam(
+                        FabricConstants.PORT_NUM, PORT_1.toLong()))
+                .build();
+        testSimple(treatment, piAction);
     }
 
     /**
      * Test program set vlan and output rule for Simple table.
      */
     @Test
-    public void testSimpleOutputWithVlanTranslation() {
+    public void testSimpleOutputWithVlanTranslation() throws FabricPipelinerException {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .setVlanId(VLAN_100)
                 .setOutput(PORT_1)
                 .build();
-        testSimple(treatment);
+        PiAction piAction = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_NEXT_OUTPUT_SIMPLE)
+                .withParameter(new PiActionParam(
+                        FabricConstants.PORT_NUM, PORT_1.toLong()))
+                .build();
+        testSimple(treatment, piAction);
     }
 
     /**
      * Test program set mac and output rule for Simple table.
      */
     @Test
-    public void testSimpleOutputWithMacTranslation() {
+    public void testSimpleOutputWithMacTranslation() throws FabricPipelinerException {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .setEthSrc(ROUTER_MAC)
                 .setEthDst(HOST_MAC)
                 .setOutput(PORT_1)
                 .build();
-        testSimple(treatment);
+        PiAction piAction = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_SIMPLE)
+                .withParameter(new PiActionParam(
+                        FabricConstants.SMAC, ROUTER_MAC.toBytes()))
+                .withParameter(new PiActionParam(
+                        FabricConstants.DMAC, HOST_MAC.toBytes()))
+                .withParameter(new PiActionParam(
+                        FabricConstants.PORT_NUM, PORT_1.toLong()))
+                .build();
+        testSimple(treatment, piAction);
     }
 
     /**
      * Test program set mac, set vlan, and output rule for Simple table.
      */
     @Test
-    public void testSimpleOutputWithVlanAndMacTranslation() {
+    public void testSimpleOutputWithVlanAndMacTranslation() throws FabricPipelinerException {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .setEthSrc(ROUTER_MAC)
                 .setEthDst(HOST_MAC)
                 .setVlanId(VLAN_100)
                 .setOutput(PORT_1)
                 .build();
-        testSimple(treatment);
+        PiAction piAction = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_SIMPLE)
+                .withParameter(new PiActionParam(
+                        FabricConstants.SMAC, ROUTER_MAC.toBytes()))
+                .withParameter(new PiActionParam(
+                        FabricConstants.DMAC, HOST_MAC.toBytes()))
+                .withParameter(new PiActionParam(
+                        FabricConstants.PORT_NUM, PORT_1.toLong()))
+                .build();
+        testSimple(treatment, piAction);
     }
 
-    private void testSimple(TrafficTreatment treatment) {
+    private void testSimple(TrafficTreatment treatment, PiAction piAction) throws FabricPipelinerException {
         NextObjective nextObjective = DefaultNextObjective.builder()
                 .withId(NEXT_ID_1)
                 .withPriority(PRIORITY)
@@ -145,26 +177,15 @@
                 .fromApp(APP_ID)
                 .add();
 
-        PipelinerTranslationResult result = pipeliner.pipelinerNext.next(nextObjective);
-
-        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
-        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
-        assertEquals(2, flowRulesInstalled.size());
-        assertTrue(groupsInstalled.isEmpty());
+        ObjectiveTranslation actualTranslation = translatorSimple.translate(nextObjective);
 
         // Simple table
         PiCriterion nextIdCriterion = PiCriterion.builder()
-                .matchExact(FabricConstants.FABRIC_METADATA_NEXT_ID, NEXT_ID_1)
+                .matchExact(FabricConstants.HDR_NEXT_ID, NEXT_ID_1)
                 .build();
         TrafficSelector nextIdSelector = DefaultTrafficSelector.builder()
                 .matchPi(nextIdCriterion)
                 .build();
-
-        // VLAN meta table
-        FlowRule actualFlowRule = flowRulesInstalled.get(0);
-        assertTrue(actualFlowRule.exactMatch(vlanMetaFlowRule));
-
-        actualFlowRule = flowRulesInstalled.get(1);
         FlowRule expectedFlowRule = DefaultFlowRule.builder()
                 .forDevice(DEVICE_ID)
                 .fromApp(APP_ID)
@@ -173,9 +194,16 @@
                 .withPriority(0)
                 .forTable(FabricConstants.FABRIC_INGRESS_NEXT_SIMPLE)
                 .withSelector(nextIdSelector)
-                .withTreatment(treatment)
+                .withTreatment(DefaultTrafficTreatment.builder()
+                                       .piTableAction(piAction).build())
                 .build();
-        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
+
+        ObjectiveTranslation expectedTranslation = ObjectiveTranslation.builder()
+                .addFlowRule(vlanMetaFlowRule)
+                .addFlowRule(expectedFlowRule)
+                .build();
+
+        assertEquals(expectedTranslation, actualTranslation);
     }
 
     /**
@@ -183,15 +211,29 @@
      */
     @Test
     public void testHashedOutput() throws Exception {
+        PiAction piAction1 = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_HASHED)
+                .withParameter(new PiActionParam(
+                        FabricConstants.SMAC, ROUTER_MAC.toBytes()))
+                .withParameter(new PiActionParam(
+                        FabricConstants.DMAC, HOST_MAC.toBytes()))
+                .withParameter(new PiActionParam(
+                        FabricConstants.PORT_NUM, PORT_1.toLong()))
+                .build();
+        PiAction piAction2 = PiAction.builder()
+                .withId(FabricConstants.FABRIC_INGRESS_NEXT_ROUTING_HASHED)
+                .withParameter(new PiActionParam(
+                        FabricConstants.SMAC, ROUTER_MAC.toBytes()))
+                .withParameter(new PiActionParam(
+                        FabricConstants.DMAC, HOST_MAC.toBytes()))
+                .withParameter(new PiActionParam(
+                        FabricConstants.PORT_NUM, PORT_1.toLong()))
+                .build();
         TrafficTreatment treatment1 = DefaultTrafficTreatment.builder()
-                .setEthSrc(ROUTER_MAC)
-                .setEthDst(HOST_MAC)
-                .setOutput(PORT_1)
+                .piTableAction(piAction1)
                 .build();
         TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
-                .setEthSrc(ROUTER_MAC)
-                .setEthDst(HOST_MAC)
-                .setOutput(PORT_2)
+                .piTableAction(piAction2)
                 .build();
 
         NextObjective nextObjective = DefaultNextObjective.builder()
@@ -205,17 +247,11 @@
                 .fromApp(APP_ID)
                 .add();
 
-        PipelinerTranslationResult result = pipeliner.pipelinerNext.next(nextObjective);
+        ObjectiveTranslation actualTranslation = translatorHashed.doTranslate(nextObjective);
 
-        // Should generates 2 flows and 1 group
-        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
-        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
-        assertEquals(2, flowRulesInstalled.size());
-        assertEquals(1, groupsInstalled.size());
-
-        // Hashed table
+        // Expected hashed table flow rule.
         PiCriterion nextIdCriterion = PiCriterion.builder()
-                .matchExact(FabricConstants.FABRIC_METADATA_NEXT_ID, NEXT_ID_1)
+                .matchExact(FabricConstants.HDR_NEXT_ID, NEXT_ID_1)
                 .build();
         TrafficSelector nextIdSelector = DefaultTrafficSelector.builder()
                 .matchPi(nextIdCriterion)
@@ -224,12 +260,6 @@
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .piTableAction(actionGroupId)
                 .build();
-
-        // VLAN meta table
-        FlowRule actualFlowRule = flowRulesInstalled.get(0);
-        assertTrue(actualFlowRule.exactMatch(vlanMetaFlowRule));
-
-        actualFlowRule = flowRulesInstalled.get(1);
         FlowRule expectedFlowRule = DefaultFlowRule.builder()
                 .forDevice(DEVICE_ID)
                 .fromApp(APP_ID)
@@ -240,18 +270,15 @@
                 .withSelector(nextIdSelector)
                 .withTreatment(treatment)
                 .build();
-        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
 
-        // Group
-        GroupDescription actualGroup = groupsInstalled.get(0);
+        // Expected group
         List<TrafficTreatment> treatments = ImmutableList.of(treatment1, treatment2);
-
         List<GroupBucket> buckets = treatments.stream()
                 .map(DefaultGroupBucket::createSelectGroupBucket)
                 .collect(Collectors.toList());
         GroupBuckets groupBuckets = new GroupBuckets(buckets);
         PiGroupKey groupKey = new PiGroupKey(FabricConstants.FABRIC_INGRESS_NEXT_HASHED,
-                                             FabricConstants.FABRIC_INGRESS_NEXT_ECMP_SELECTOR,
+                                             FabricConstants.FABRIC_INGRESS_NEXT_HASHED_SELECTOR,
                                              NEXT_ID_1);
         GroupDescription expectedGroup = new DefaultGroupDescription(
                 DEVICE_ID,
@@ -261,7 +288,14 @@
                 NEXT_ID_1,
                 APP_ID
         );
-        assertEquals(expectedGroup, actualGroup);
+
+        ObjectiveTranslation expectedTranslation = ObjectiveTranslation.builder()
+                .addFlowRule(expectedFlowRule)
+                .addFlowRule(vlanMetaFlowRule)
+                .addGroup(expectedGroup)
+                .build();
+
+        assertEquals(expectedTranslation, actualTranslation);
 
     }
 
@@ -269,7 +303,7 @@
      * Test program output group for Broadcast table.
      */
     @Test
-    public void testBroadcastOutput() {
+    public void testBroadcastOutput() throws FabricPipelinerException {
         TrafficTreatment treatment1 = DefaultTrafficTreatment.builder()
                 .setOutput(PORT_1)
                 .build();
@@ -288,35 +322,31 @@
                 .fromApp(APP_ID)
                 .add();
 
-        PipelinerTranslationResult result = pipeliner.pipelinerNext.next(nextObjective);
+        ObjectiveTranslation actualTranslation = translatorHashed.doTranslate(nextObjective);
 
-        // Should generate 1 flow, 1 group and 2 buckets in it
-        List<FlowRule> flowRulesInstalled = (List<FlowRule>) result.flowRules();
-        List<GroupDescription> groupsInstalled = (List<GroupDescription>) result.groups();
-        assertEquals(3, flowRulesInstalled.size());
-        assertEquals(1, groupsInstalled.size());
-        // FIXME: expected should be 2
-        // But we are adding an extra bucket to clone the pkt to the CPU
-        assertEquals(3, groupsInstalled.get(0).buckets().buckets().size());
+        // Should generate 3 flows:
+        // - Multicast table flow that matches on next-id and set multicast group (1)
+        // - Egress VLAN pop handling for treatment2 (0)
+        // - Next VLAN flow (2)
+        // And 2 groups:
+        // - Multicast group
 
-        //create the expected flow rule
+        // Expected multicast table flow rule.
         PiCriterion nextIdCriterion = PiCriterion.builder()
-                .matchExact(FabricConstants.FABRIC_METADATA_NEXT_ID, NEXT_ID_1)
+                .matchExact(FabricConstants.HDR_NEXT_ID, NEXT_ID_1)
                 .build();
         TrafficSelector nextIdSelector = DefaultTrafficSelector.builder()
                 .matchPi(nextIdCriterion)
                 .build();
-
-        PiActionParam groupIdParam = new PiActionParam(FabricConstants.GID,
-                                                       ImmutableByteSequence.copyFrom(NEXT_ID_1));
         PiAction setMcGroupAction = PiAction.builder()
-                .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_MCAST_GROUP)
-                .withParameter(groupIdParam)
+                .withId(FabricConstants.FABRIC_INGRESS_NEXT_SET_MCAST_GROUP_ID)
+                .withParameter(new PiActionParam(
+                        FabricConstants.GROUP_ID, NEXT_ID_1))
                 .build();
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .piTableAction(setMcGroupAction)
                 .build();
-        FlowRule expectedFlowRule = DefaultFlowRule.builder()
+        FlowRule expectedHashedFlowRule = DefaultFlowRule.builder()
                 .forDevice(DEVICE_ID)
                 .fromApp(APP_ID)
                 .makePermanent()
@@ -326,24 +356,19 @@
                 .withTreatment(treatment)
                 .build();
 
-        // VLAN meta table
-        FlowRule vmFlowRule = flowRulesInstalled.get(0);
-        assertTrue(vmFlowRule.exactMatch(vlanMetaFlowRule));
-
-        FlowRule actualFlowRule = flowRulesInstalled.get(1);
-        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
-
-        //prepare expected egress rule for the egress vlan pipeline
+        // Expected egress VLAN POP flow rule.
         PiCriterion egressVlanTableMatch = PiCriterion.builder()
-                .matchExact(FabricConstants.STANDARD_METADATA_EGRESS_PORT,
-                            (short) PORT_2.toLong())
+                .matchExact(FabricConstants.HDR_EG_PORT, PORT_2.toLong())
                 .build();
         TrafficSelector selectorForEgressVlan = DefaultTrafficSelector.builder()
                 .matchPi(egressVlanTableMatch)
                 .matchVlanId(VLAN_100)
                 .build();
+        PiAction piActionForEgressVlan = PiAction.builder()
+                .withId(FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_POP_VLAN)
+                .build();
         TrafficTreatment treatmentForEgressVlan = DefaultTrafficTreatment.builder()
-                .popVlan()
+                .piTableAction(piActionForEgressVlan)
                 .build();
         FlowRule expectedEgressVlanRule = DefaultFlowRule.builder()
                 .withSelector(selectorForEgressVlan)
@@ -354,78 +379,40 @@
                 .forDevice(DEVICE_ID)
                 .fromApp(APP_ID)
                 .build();
-        //egress vlan table
-        FlowRule actualEgressVlanFlowRule = flowRulesInstalled.get(2);
-        assertTrue(expectedEgressVlanRule.exactMatch(actualEgressVlanFlowRule));
 
-        //create the expected group
-        GroupDescription actualGroup = groupsInstalled.get(0);
-        TrafficTreatment groupTreatment1 = DefaultTrafficTreatment.builder()
+        // Expected ALL group.
+        TrafficTreatment allGroupTreatment1 = DefaultTrafficTreatment.builder()
                 .setOutput(PORT_1)
                 .build();
-        TrafficTreatment groupTreatment2 = DefaultTrafficTreatment.builder()
+        TrafficTreatment allGroupTreatment2 = DefaultTrafficTreatment.builder()
                 .setOutput(PORT_2)
                 .build();
-        List<TrafficTreatment> treatments = ImmutableList.of(groupTreatment1, groupTreatment2);
-
-        List<GroupBucket> buckets = treatments.stream()
+        List<TrafficTreatment> allTreatments = ImmutableList.of(
+                allGroupTreatment1, allGroupTreatment2);
+        List<GroupBucket> allBuckets = allTreatments.stream()
                 .map(DefaultGroupBucket::createAllGroupBucket)
                 .collect(Collectors.toList());
-
         // FIXME: remove when we implement proper clone to CPU behavior
-        buckets.add(DefaultGroupBucket.createAllGroupBucket(
+        allBuckets.add(DefaultGroupBucket.createAllGroupBucket(
                 DefaultTrafficTreatment.builder().punt().build()));
-
-        GroupBuckets groupBuckets = new GroupBuckets(buckets);
-
-        GroupKey groupKey = new DefaultGroupKey(FabricPipeliner.KRYO.serialize(NEXT_ID_1));
-
-        GroupDescription expectedGroup = new DefaultGroupDescription(
+        GroupBuckets allGroupBuckets = new GroupBuckets(allBuckets);
+        GroupKey allGroupKey = new DefaultGroupKey(FabricPipeliner.KRYO.serialize(NEXT_ID_1));
+        GroupDescription expectedAllGroup = new DefaultGroupDescription(
                 DEVICE_ID,
                 GroupDescription.Type.ALL,
-                groupBuckets,
-                groupKey,
+                allGroupBuckets,
+                allGroupKey,
                 NEXT_ID_1,
                 APP_ID
         );
-        assertEquals(expectedGroup, actualGroup);
-    }
 
-    /**
-     * Test removing next objective, and expect the next objective data will be removed from the store.
-     */
-    @Test
-    public void testRemoveNextObjective() {
-        // Initialize mock objects
-        NextObjective mockNextObjective = DefaultNextObjective.builder()
-                .fromApp(APP_ID)
-                .withId(NEXT_ID_1)
-                .makePermanent()
-                .withPriority(PRIORITY)
-                .withType(NextObjective.Type.SIMPLE)
-                .remove();
-        FlowObjectiveStore mockFlowObjStore = EasyMock.createNiceMock(FlowObjectiveStore.class);
-        FabricNextPipeliner mockNextPipeliner = EasyMock.createNiceMock(FabricNextPipeliner.class);
-        PipelinerTranslationResult mockResult = PipelinerTranslationResult.builder().build(); // empty result
-        NextGroup mockNextGroup = new DefaultNextGroup(null);
-        expect(mockNextPipeliner.next(mockNextObjective)).andReturn(mockResult).once();
-        expect(mockFlowObjStore.getNextGroup(mockNextObjective.id())).andReturn(mockNextGroup).once();
-        expect(mockFlowObjStore.removeNextGroup(mockNextObjective.id())).andReturn(mockNextGroup).once();
-        replay(mockNextPipeliner, mockFlowObjStore);
+        ObjectiveTranslation expectedTranslation = ObjectiveTranslation.builder()
+                .addFlowRule(expectedHashedFlowRule)
+                .addFlowRule(vlanMetaFlowRule)
+                .addFlowRule(expectedEgressVlanRule)
+                .addGroup(expectedAllGroup)
+                .build();
 
-        // Initialize the pipeliner
-        FabricPipeliner thePipeliner = new FabricPipeliner();
-        thePipeliner.pipelinerNext = mockNextPipeliner;
-        TestUtils.setField(thePipeliner, "flowObjectiveStore", mockFlowObjStore);
-        // execute removing process
-        thePipeliner.next(mockNextObjective);
-        Map<Objective, FabricPipeliner.PendingInstallObjective> pios =
-                TestUtils.getField(thePipeliner, "pendingInstallObjectives");
-        FabricPipeliner.PendingInstallObjective pio = pios.get(mockNextObjective);
-        pio.callback.accept(null); // applying successful result
-
-        // "removeNextGroup" method should be called once, or failed if not.
-        verify(mockNextPipeliner, mockFlowObjStore);
-
+        assertEquals(expectedTranslation, actualTranslation);
     }
 }
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipelinerTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipelinerTest.java
index c361377..a5704da 100644
--- a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipelinerTest.java
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/FabricPipelinerTest.java
@@ -16,10 +16,7 @@
 
 package org.onosproject.pipelines.fabric.pipeliner;
 
-import org.junit.Before;
 import org.junit.Test;
-import org.onlab.junit.TestUtils;
-import org.onlab.osgi.ServiceDirectory;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.MplsLabel;
@@ -28,15 +25,11 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
-import org.onosproject.net.behaviour.PipelinerContext;
-import org.onosproject.net.driver.Driver;
-import org.onosproject.net.driver.DriverHandler;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.criteria.PiCriterion;
-import org.onosproject.net.group.GroupService;
+import org.onosproject.pipelines.fabric.FabricCapabilities;
 import org.onosproject.pipelines.fabric.FabricConstants;
-import org.onosproject.pipelines.fabric.FabricInterpreter;
 
 import static org.easymock.EasyMock.createNiceMock;
 import static org.easymock.EasyMock.expect;
@@ -60,36 +53,23 @@
     static final TrafficSelector VLAN_META = DefaultTrafficSelector.builder()
             .matchVlanId(VLAN_100)
             .build();
-
     static final PiCriterion VLAN_VALID = PiCriterion.builder()
-            .matchExact(FabricConstants.HDR_VLAN_TAG_IS_VALID, new byte[]{1})
+            .matchExact(FabricConstants.HDR_VLAN_IS_VALID, new byte[]{1})
             .build();
     static final PiCriterion VLAN_INVALID = PiCriterion.builder()
-            .matchExact(FabricConstants.HDR_VLAN_TAG_IS_VALID, new byte[]{0})
+            .matchExact(FabricConstants.HDR_VLAN_IS_VALID, new byte[]{0})
             .build();
 
-    FabricPipeliner pipeliner;
-    FabricInterpreter interpreter;
+    FabricCapabilities capabilitiesHashed;
+    FabricCapabilities capabilitiesSimple;
 
-    @Before
-    public void setup() {
-        pipeliner = new FabricPipeliner();
-
-        GroupService mockGroupService = createNiceMock(GroupService.class);
-        ServiceDirectory serviceDirectory = createNiceMock(ServiceDirectory.class);
-        PipelinerContext pipelinerContext = createNiceMock(PipelinerContext.class);
-        DriverHandler driverHandler = createNiceMock(DriverHandler.class);
-        Driver mockDriver = createNiceMock(Driver.class);
-        expect(mockDriver.getProperty("supportTableCounters")).andReturn("true").anyTimes();
-        expect(mockDriver.getProperty("noHashedTable")).andReturn("false").anyTimes();
-        expect(driverHandler.driver()).andReturn(mockDriver).anyTimes();
-        expect(pipelinerContext.directory()).andReturn(serviceDirectory).anyTimes();
-        expect(serviceDirectory.get(GroupService.class)).andReturn(mockGroupService).anyTimes();
-        replay(serviceDirectory, pipelinerContext, driverHandler, mockDriver);
-        TestUtils.setField(pipeliner, "handler", driverHandler);
-
-        pipeliner.init(DEVICE_ID, pipelinerContext);
-        interpreter = new FabricInterpreter();
+    void doSetup() {
+        this.capabilitiesHashed = createNiceMock(FabricCapabilities.class);
+        this.capabilitiesSimple = createNiceMock(FabricCapabilities.class);
+        expect(capabilitiesHashed.hasHashedTable()).andReturn(true).anyTimes();
+        expect(capabilitiesSimple.hasHashedTable()).andReturn(false).anyTimes();
+        replay(capabilitiesHashed);
+        replay(capabilitiesSimple);
     }
 
     @Test
diff --git a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionTypeTest.java b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionTypeTest.java
index 1ef11c2..2121b4f 100644
--- a/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionTypeTest.java
+++ b/pipelines/fabric/src/test/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionTypeTest.java
@@ -39,6 +39,7 @@
     private static final ApplicationId APP_ID = TestApplicationId.create("ForwardingFunctionTypeTest");
     private static final VlanId VLAN_100 = VlanId.vlanId((short) 100);
     private static final MacAddress MAC_ADDR = MacAddress.valueOf("00:00:00:00:00:01");
+    private static final MacAddress MAC_NONE = MacAddress.NONE;
     private static final IpPrefix IPV4_UNICAST_ADDR = IpPrefix.valueOf("10.0.0.1/32");
     private static final IpPrefix IPV4_MCAST_ADDR = IpPrefix.valueOf("224.0.0.1/32");
     private static final IpPrefix IPV6_UNICAST_ADDR = IpPrefix.valueOf("2000::1/32");
@@ -67,6 +68,15 @@
     }
 
     @Test
+    public void testL2BroadcastWithMacNone() {
+        selector = DefaultTrafficSelector.builder()
+                .matchVlanId(VLAN_100)
+                .matchEthDst(MAC_NONE)
+                .build();
+        testFft(selector, ForwardingFunctionType.L2_BROADCAST);
+    }
+
+    @Test
     public void testIpv4Unicast() {
         selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
@@ -112,7 +122,7 @@
                 .matchMplsLabel(MPLS_10)
                 .matchMplsBos(true)
                 .build();
-        testFft(selector, ForwardingFunctionType.MPLS);
+        testFft(selector, ForwardingFunctionType.MPLS_SEGMENT_ROUTING);
     }
 
     private void testFft(TrafficSelector selector, ForwardingFunctionType expectedFft) {