[SDFAB-102] Backport changes required for policies to fabric.p4 (Redirect)

Change-Id: I357c908d31abad9c3f8d74723d937ea948e54808
diff --git a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/NextObjectiveTranslator.java b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/NextObjectiveTranslator.java
index 8ecffb2..47b7ef6 100644
--- a/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/NextObjectiveTranslator.java
+++ b/pipelines/fabric/impl/src/main/java/org/onosproject/pipelines/fabric/impl/behaviour/pipeliner/NextObjectiveTranslator.java
@@ -17,6 +17,7 @@
 package org.onosproject.pipelines.fabric.impl.behaviour.pipeliner;
 
 import com.google.common.collect.Lists;
+import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
@@ -28,6 +29,7 @@
 import org.onosproject.net.flow.criteria.PiCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
 import org.onosproject.net.flowobjective.DefaultNextTreatment;
 import org.onosproject.net.flowobjective.NextObjective;
@@ -57,6 +59,7 @@
 import java.util.stream.Collectors;
 
 import static java.lang.String.format;
+import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.MPLS_LABEL;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_ID;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_POP;
 import static org.onosproject.pipelines.fabric.impl.behaviour.FabricUtils.criterion;
@@ -103,13 +106,55 @@
         }
 
         if (!isGroupModifyOp(obj)) {
-            // Generate next VLAN rules.
+            // Generate next MPLS and VLAN rules.
+            nextMpls(obj, resultBuilder);
             nextVlan(obj, resultBuilder);
         }
 
         return resultBuilder.build();
     }
 
+    private void nextMpls(NextObjective obj,
+                          ObjectiveTranslation.Builder resultBuilder)
+            throws FabricPipelinerException {
+        // Next objective can contain only one mpls push and one mpls label
+        // instruction. Pipeliner does not support other configurations.
+
+        final List<List<ModMplsLabelInstruction>> mplsInstructions = defaultNextTreatments(
+                obj.nextTreatments(), false).stream()
+                .map(defaultNextTreatment -> l2Instructions(defaultNextTreatment.treatment(), MPLS_LABEL)
+                        .stream().map(v -> (ModMplsLabelInstruction) v)
+                        .collect(Collectors.toList()))
+                .filter(l -> !l.isEmpty())
+                .collect(Collectors.toList());
+
+        if (mplsInstructions.isEmpty()) {
+            // No need to apply next mpls table
+            return;
+        }
+
+        // We expect one mpls label for each treatment and the label has to be the same
+        final Set<MplsLabel> mplsLabels = mplsInstructions.stream()
+                .flatMap(Collection::stream)
+                .map(ModMplsLabelInstruction::label)
+                .collect(Collectors.toSet());
+        if (obj.nextTreatments().size() != mplsInstructions.size() ||
+                mplsLabels.size() != 1) {
+            throw new FabricPipelinerException(
+                    "Inconsistent MPLS_LABEL instructions, cannot process " +
+                            "next_mpls rule. It is required that all " +
+                            "treatments have the same MPLS_LABEL instructions.");
+        }
+        final TrafficSelector selector = nextIdSelector(obj.id());
+        final TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setMpls(mplsLabels.iterator().next())
+                .build();
+
+        resultBuilder.addFlowRule(flowRule(
+                obj, FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_MPLS,
+                selector, treatment));
+    }
+
     private void nextVlan(NextObjective obj,
                           ObjectiveTranslation.Builder resultBuilder)
             throws FabricPipelinerException {
@@ -169,7 +214,7 @@
         final TrafficTreatment treatment = treatmentBuilder.build();
 
         resultBuilder.addFlowRule(flowRule(
-                obj, FabricConstants.FABRIC_INGRESS_NEXT_NEXT_VLAN,
+                obj, FabricConstants.FABRIC_INGRESS_PRE_NEXT_NEXT_VLAN,
                 selector, treatment));
     }
 
@@ -258,7 +303,7 @@
             if (obj.meta() != null && obj.meta().getCriterion(Criterion.Type.VLAN_VID) != null) {
                 egressVlan(outPort, obj, popVlanInst, resultBuilder);
             } else {
-                log.warn("NextObjective {} is trying to program {} without {} information",
+                log.debug("NextObjective {} is trying to program {} without {} information",
                         obj, FabricConstants.FABRIC_EGRESS_EGRESS_NEXT_EGRESS_VLAN,
                         obj.meta() == null ? "metadata" : "vlanId");
             }