[ONOS-7242] Support MPLS by fabric.p4 pipeliner

Change-Id: I56a8f266e6d0afe5ad6737b8d0e399758fb75378
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 76bd030..fdda55f 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
@@ -20,6 +20,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
 import org.onlab.util.ImmutableByteSequence;
 import org.onosproject.net.PortNumber;
@@ -38,6 +39,7 @@
     private static final PortNumber PORT_1 = PortNumber.portNumber(1);
     private static final MacAddress SRC_MAC = MacAddress.valueOf("00:00:00:00:00:01");
     private static final MacAddress DST_MAC = MacAddress.valueOf("00:00:00:00:00:02");
+    private static final MplsLabel MPLS_10 = MplsLabel.mplsLabel(10);
 
     private FabricInterpreter interpreter;
 
@@ -198,4 +200,33 @@
 
         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.TBL_HASHED_ID);
+        short portNumVal = (short) PORT_1.toLong();
+        PiActionParam ethSrcParam = new PiActionParam(FabricConstants.ACT_PRM_SMAC_ID,
+                                                      ImmutableByteSequence.copyFrom(SRC_MAC.toBytes()));
+        PiActionParam ethDstParam = new PiActionParam(FabricConstants.ACT_PRM_DMAC_ID,
+                                                      ImmutableByteSequence.copyFrom(DST_MAC.toBytes()));
+        PiActionParam portParam = new PiActionParam(FabricConstants.ACT_PRM_PORT_NUM_ID,
+                                                    ImmutableByteSequence.copyFrom(portNumVal));
+        ImmutableByteSequence mplsVal =
+                ImmutableByteSequence.fit(ImmutableByteSequence.copyFrom(MPLS_10.toInt()), 20);
+        PiActionParam mplsParam = new PiActionParam(FabricConstants.ACT_PRM_LABEL_ID, mplsVal);
+        PiAction expectedAction = PiAction.builder()
+                .withId(FabricConstants.ACT_MPLS_ROUTING_V4_ID)
+                .withParameters(ImmutableList.of(ethSrcParam, ethDstParam, portParam, mplsParam))
+                .build();
+        assertEquals(expectedAction, mappedAction);
+    }
 }
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 0332e63..1602c58 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
@@ -210,13 +210,46 @@
     }
 
     @Test
-    @Ignore
     public void testMpls() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.MPLS_UNICAST)
+                .matchMplsLabel(MPLS_10)
+                .matchMplsBos(true)
+                .build();
+        TrafficSelector expectedSelector = DefaultTrafficSelector.builder()
+                .matchMplsLabel(MPLS_10)
+                .build();
 
+        PiActionParam nextIdParam = new PiActionParam(FabricConstants.ACT_PRM_NEXT_ID_ID,
+                                                      ImmutableByteSequence.copyFrom(NEXT_ID_1.byteValue()));
+        PiAction setNextIdAction = PiAction.builder()
+                .withId(FabricConstants.ACT_POP_MPLS_AND_NEXT_ID)
+                .withParameter(nextIdParam)
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .piTableAction(setNextIdAction)
+                .build();
+        testSpecificForward(FabricConstants.TBL_MPLS_ID, expectedSelector, selector, NEXT_ID_1, treatment);
     }
 
     private void testSpecificForward(PiTableId expectedTableId, TrafficSelector expectedSelector,
                                      TrafficSelector selector, Integer nextId) {
+        PiActionParam nextIdParam = new PiActionParam(FabricConstants.ACT_PRM_NEXT_ID_ID,
+                                                      ImmutableByteSequence.copyFrom(nextId.byteValue()));
+        PiAction setNextIdAction = PiAction.builder()
+                .withId(FabricConstants.ACT_SET_NEXT_ID_ID)
+                .withParameter(nextIdParam)
+                .build();
+        TrafficTreatment setNextIdTreatment = DefaultTrafficTreatment.builder()
+                .piTableAction(setNextIdAction)
+                .build();
+
+        testSpecificForward(expectedTableId, expectedSelector, selector, nextId, setNextIdTreatment);
+
+    }
+
+    private void testSpecificForward(PiTableId expectedTableId, TrafficSelector expectedSelector,
+                                     TrafficSelector selector, Integer nextId, TrafficTreatment treatment) {
         ForwardingObjective fwd = DefaultForwardingObjective.builder()
                 .withSelector(selector)
                 .withPriority(PRIORITY)
@@ -234,15 +267,6 @@
         assertTrue(groupsInstalled.isEmpty());
 
         FlowRule actualFlowRule = flowRulesInstalled.get(0);
-        PiActionParam nextIdParam = new PiActionParam(FabricConstants.ACT_PRM_NEXT_ID_ID,
-                                                      ImmutableByteSequence.copyFrom(nextId.byteValue()));
-        PiAction setNextIdAction = PiAction.builder()
-                .withId(FabricConstants.ACT_SET_NEXT_ID_ID)
-                .withParameter(nextIdParam)
-                .build();
-        TrafficTreatment setNextIdTreatment = DefaultTrafficTreatment.builder()
-                .piTableAction(setNextIdAction)
-                .build();
 
         FlowRule expectedFlowRule = DefaultFlowRule.builder()
                 .forDevice(DEVICE_ID)
@@ -250,7 +274,7 @@
                 .withPriority(PRIORITY)
                 .makePermanent()
                 .withSelector(expectedSelector)
-                .withTreatment(setNextIdTreatment)
+                .withTreatment(treatment)
                 .fromApp(APP_ID)
                 .build();
 
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 3fe196b..8493ca6 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
@@ -16,6 +16,7 @@
 
 package org.onosproject.pipelines.fabric.pipeliner;
 
+import com.google.common.collect.ImmutableList;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.onlab.util.ImmutableByteSequence;
@@ -28,15 +29,24 @@
 import org.onosproject.net.flow.criteria.PiCriterion;
 import org.onosproject.net.flowobjective.DefaultNextObjective;
 import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
 import org.onosproject.net.group.GroupDescription;
 import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionGroupId;
 import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.net.pi.runtime.PiGroupKey;
 import org.onosproject.pipelines.fabric.FabricConstants;
 
 import java.util.List;
+import java.util.stream.Collectors;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.onosproject.pipelines.fabric.FabricConstants.ACT_PRF_ECMP_SELECTOR_ID;
+import static org.onosproject.pipelines.fabric.FabricConstants.TBL_HASHED_ID;
 
 /**
  * Test cases for fabric.p4 pipeline next control block.
@@ -96,20 +106,39 @@
         assertEquals(2, flowRulesInstalled.size());
         assertTrue(groupsInstalled.isEmpty());
 
-        FlowRule actualFlowRule;
-        FlowRule expectedFlowRule;
+        verifyNextIdMapping(flowRulesInstalled.get(0), NEXT_TYPE_SIMPLE);
 
-        // Next id mapping table
-        actualFlowRule = flowRulesInstalled.get(0);
-        byte[] nextIdVal = new byte[]{NEXT_ID_1.byteValue()};
+        // Simple table
         PiCriterion nextIdCriterion = PiCriterion.builder()
-                .matchExact(FabricConstants.HF_FABRIC_METADATA_NEXT_ID_ID, nextIdVal)
+                .matchExact(FabricConstants.HF_FABRIC_METADATA_NEXT_ID_ID, NEXT_ID_1)
+                .build();
+        TrafficSelector nextIdSelector = DefaultTrafficSelector.builder()
+                .matchPi(nextIdCriterion)
+                .build();
+        FlowRule actualFlowRule = flowRulesInstalled.get(1);
+        FlowRule expectedFlowRule = DefaultFlowRule.builder()
+                .forDevice(DEVICE_ID)
+                .fromApp(APP_ID)
+                .makePermanent()
+                // FIXME: currently next objective doesn't support priority, ignore this
+                .withPriority(0)
+                .forTable(FabricConstants.TBL_SIMPLE_ID)
+                .withSelector(nextIdSelector)
+                .withTreatment(treatment)
+                .build();
+        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
+    }
+
+    private void verifyNextIdMapping(FlowRule flowRule, byte nextType) {
+        FlowRule expectedFlowRule;
+        PiCriterion nextIdCriterion = PiCriterion.builder()
+                .matchExact(FabricConstants.HF_FABRIC_METADATA_NEXT_ID_ID, NEXT_ID_1)
                 .build();
         TrafficSelector nextIdSelector = DefaultTrafficSelector.builder()
                 .matchPi(nextIdCriterion)
                 .build();
         PiActionParam setNextToSimpleParam = new PiActionParam(FabricConstants.ACT_PRM_NEXT_TYPE_ID,
-                                                               ImmutableByteSequence.copyFrom(NEXT_TYPE_SIMPLE));
+                                                               ImmutableByteSequence.copyFrom(nextType));
         PiAction setNextToSimpleAction = PiAction.builder()
                 .withId(FabricConstants.ACT_SET_NEXT_TYPE_ID)
                 .withParameter(setNextToSimpleParam)
@@ -127,29 +156,87 @@
                 .withSelector(nextIdSelector)
                 .withTreatment(setNextTypeTreatment)
                 .build();
-        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
-
-        // Simple table
-        actualFlowRule = flowRulesInstalled.get(1);
-        expectedFlowRule = DefaultFlowRule.builder()
-                .forDevice(DEVICE_ID)
-                .fromApp(APP_ID)
-                .makePermanent()
-                // FIXME: currently next objective doesn't support priority, ignore this
-                .withPriority(0)
-                .forTable(FabricConstants.TBL_SIMPLE_ID)
-                .withSelector(nextIdSelector)
-                .withTreatment(treatment)
-                .build();
-        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
+        assertTrue(expectedFlowRule.exactMatch(flowRule));
     }
 
     /**
      * Test program ecmp output group for Hashed table.
      */
     @Test
-    @Ignore
-    public void testHashedOutput() {
+    public void testHashedOutput() throws Exception {
+        TrafficTreatment treatment1 = DefaultTrafficTreatment.builder()
+                .setEthSrc(ROUTER_MAC)
+                .setEthDst(HOST_MAC)
+                .setOutput(PORT_1)
+                .build();
+        TrafficTreatment treatment2 = DefaultTrafficTreatment.builder()
+                .setEthSrc(ROUTER_MAC)
+                .setEthDst(HOST_MAC)
+                .setOutput(PORT_2)
+                .build();
+
+        NextObjective nextObjective = DefaultNextObjective.builder()
+                .withId(NEXT_ID_1)
+                .withPriority(PRIORITY)
+                .addTreatment(treatment1)
+                .addTreatment(treatment2)
+                .withType(NextObjective.Type.HASHED)
+                .makePermanent()
+                .fromApp(APP_ID)
+                .add();
+
+        PipelinerTranslationResult result = pipeliner.pipelinerNext.next(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());
+
+        verifyNextIdMapping(flowRulesInstalled.get(0), NEXT_TYPE_HASHED);
+
+        // Hashed table
+        PiCriterion nextIdCriterion = PiCriterion.builder()
+                .matchExact(FabricConstants.HF_FABRIC_METADATA_NEXT_ID_ID, NEXT_ID_1)
+                .build();
+        TrafficSelector nextIdSelector = DefaultTrafficSelector.builder()
+                .matchPi(nextIdCriterion)
+                .build();
+        PiActionGroupId actionGroupId = PiActionGroupId.of(NEXT_ID_1);
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .piTableAction(actionGroupId)
+                .build();
+        FlowRule actualFlowRule = flowRulesInstalled.get(1);
+        FlowRule expectedFlowRule = DefaultFlowRule.builder()
+                .forDevice(DEVICE_ID)
+                .fromApp(APP_ID)
+                .makePermanent()
+                // FIXME: currently next objective doesn't support priority, ignore this
+                .withPriority(0)
+                .forTable(TBL_HASHED_ID)
+                .withSelector(nextIdSelector)
+                .withTreatment(treatment)
+                .build();
+        assertTrue(expectedFlowRule.exactMatch(actualFlowRule));
+
+        // Group
+        GroupDescription actualGroup = groupsInstalled.get(0);
+        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(TBL_HASHED_ID, ACT_PRF_ECMP_SELECTOR_ID, NEXT_ID_1);
+        GroupDescription expectedGroup = new DefaultGroupDescription(
+                DEVICE_ID,
+                GroupDescription.Type.SELECT,
+                groupBuckets,
+                groupKey,
+                NEXT_ID_1,
+                APP_ID
+        );
+        assertEquals(expectedGroup, actualGroup);
 
     }
 
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 30610ed..fb92bf9 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
@@ -29,6 +29,7 @@
 import org.onosproject.net.behaviour.PipelinerContext;
 import org.onosproject.net.flow.criteria.PiCriterion;
 import org.onosproject.pipelines.fabric.FabricConstants;
+import org.onosproject.pipelines.fabric.FabricInterpreter;
 
 import static org.easymock.EasyMock.createNiceMock;
 import static org.easymock.EasyMock.expect;
@@ -39,6 +40,7 @@
     static final DeviceId DEVICE_ID = DeviceId.deviceId("device:bmv2:11");
     static final int PRIORITY = 100;
     static final PortNumber PORT_1 = PortNumber.portNumber(1);
+    static final PortNumber PORT_2 = PortNumber.portNumber(2);
     static final VlanId VLAN_100 = VlanId.vlanId("100");
     static final MacAddress HOST_MAC = MacAddress.valueOf("00:00:00:00:00:01");
     static final MacAddress ROUTER_MAC = MacAddress.valueOf("00:00:00:00:02:01");
@@ -71,6 +73,7 @@
             .build();
 
     FabricPipeliner pipeliner;
+    FabricInterpreter interpreter;
 
     @Before
     public void setup() {
@@ -82,5 +85,6 @@
         replay(serviceDirectory, pipelinerContext);
 
         pipeliner.init(DEVICE_ID, pipelinerContext);
+        interpreter = new FabricInterpreter();
     }
 }
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 44ec9bc..5e77906 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
@@ -106,7 +106,6 @@
     }
 
     @Test
-    @Ignore
     public void testMplsUnicast() {
         selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.MPLS_UNICAST)