Implements [CORD-587] and [CORD-588]

Changes:
- Add termination in SR app;
- Add termination in the drivers

Change-Id: Ia9bb31c2c2e20acab8d6bfe27113f7421a8b83da
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2Pipeline.java
index e32f5d5..9da1e27 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2Pipeline.java
@@ -129,6 +129,11 @@
     protected static final int MPLS_TUNNEL_ID_BASE = 0x10000;
     protected static final int MPLS_TUNNEL_ID_MAX = 0x1FFFF;
 
+    protected static final int MPLS_UNI_PORT_MAX = 0x0000FFFF;
+
+    protected static final int MPLS_NNI_PORT_BASE = 0x00020000;
+    protected static final int MPLS_NNI_PORT_MAX = 0x0002FFFF;
+
     private final Logger log = getLogger(getClass());
     protected ServiceDirectory serviceDirectory;
     protected FlowRuleService flowRuleService;
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa3Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa3Pipeline.java
index 38fe384..db01906 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa3Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa3Pipeline.java
@@ -21,6 +21,8 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.driver.extensions.Ofdpa3MatchMplsL2Port;
 import org.onosproject.driver.extensions.Ofdpa3MatchOvid;
+import org.onosproject.driver.extensions.Ofdpa3PopCw;
+import org.onosproject.driver.extensions.Ofdpa3PopL2Header;
 import org.onosproject.driver.extensions.Ofdpa3SetMplsL2Port;
 import org.onosproject.driver.extensions.Ofdpa3SetMplsType;
 import org.onosproject.driver.extensions.Ofdpa3SetOvid;
@@ -40,8 +42,11 @@
 import org.onosproject.net.flow.criteria.PortCriterion;
 import org.onosproject.net.flow.criteria.TunnelIdCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
 import org.onosproject.net.flowobjective.FilteringObjective;
 import org.onosproject.net.flowobjective.ForwardingObjective;
 import org.onosproject.net.flowobjective.ObjectiveError;
@@ -146,7 +151,7 @@
                 return;
             }
             // 0x0000XXXX is UNI interface.
-            if (portCriterion.port().toLong() > 0x0000FFFF) {
+            if (portCriterion.port().toLong() > MPLS_UNI_PORT_MAX) {
                 log.error("Filering Objective invalid logical port {}",
                           portCriterion.port().toLong());
                 fail(filteringObjective, ObjectiveError.BADPARAMS);
@@ -273,23 +278,104 @@
     @Override
     protected Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
         // We use the tunnel id to identify pw related flows.
+        // Looking for the fwd objective of the initiation.
         TunnelIdCriterion tunnelIdCriterion = (TunnelIdCriterion) fwd.selector()
                 .getCriterion(TUNNEL_ID);
         if (tunnelIdCriterion != null) {
-            return processPwVersatile(fwd);
+            return processInitPwVersatile(fwd);
+        }
+        // Looking for the fwd objective of the termination.
+        ModTunnelIdInstruction modTunnelIdInstruction = getModTunnelIdInstruction(fwd.treatment());
+        OutputInstruction outputInstruction = getOutputInstruction(fwd.treatment());
+        if (modTunnelIdInstruction != null && outputInstruction != null) {
+            return processTermPwVersatile(fwd, modTunnelIdInstruction, outputInstruction);
         }
         // If it is not a pseudo wire flow we fall back
         // to the OFDPA 2.0 pipeline.
         return super.processVersatile(fwd);
     }
 
+    private Collection<FlowRule> processTermPwVersatile(ForwardingObjective forwardingObjective,
+                                                        ModTunnelIdInstruction modTunnelIdInstruction,
+                                                        OutputInstruction outputInstruction) {
+        TrafficTreatment.Builder flowTreatment;
+        TrafficSelector.Builder flowSelector;
+        // We divide the mpls actions from the tunnel actions. We need
+        // this to order the actions in the final treatment.
+        TrafficTreatment.Builder mplsTreatment = DefaultTrafficTreatment.builder();
+        createMplsTreatment(forwardingObjective.treatment(), mplsTreatment);
+        // The match of the forwarding objective is ready to go.
+        flowSelector = DefaultTrafficSelector.builder(forwardingObjective.selector());
+        // We verify the tunnel id and mpls port are correct.
+        long tunnelId = MPLS_TUNNEL_ID_BASE | modTunnelIdInstruction.tunnelId();
+        if (tunnelId > MPLS_TUNNEL_ID_MAX) {
+            log.error("Pw Versatile Forwarding Objective must include tunnel id < {}",
+                      MPLS_TUNNEL_ID_MAX);
+            fail(forwardingObjective, ObjectiveError.BADPARAMS);
+            return Collections.emptySet();
+        }
+        // 0x0002XXXX is NNI interface.
+        int mplsLogicalPort = ((int) outputInstruction.port().toLong()) | MPLS_NNI_PORT_BASE;
+        if (mplsLogicalPort > MPLS_NNI_PORT_MAX) {
+            log.error("Pw Versatile Forwarding Objective invalid logical port {}",
+                      mplsLogicalPort);
+            fail(forwardingObjective, ObjectiveError.BADPARAMS);
+            return Collections.emptySet();
+        }
+        // Next id cannot be null.
+        if (forwardingObjective.nextId() == null) {
+            log.error("Pw Versatile Forwarding Objective must contain nextId ",
+                      forwardingObjective.nextId());
+            fail(forwardingObjective, ObjectiveError.BADPARAMS);
+            return Collections.emptySet();
+        }
+        // We retrieve the l2 interface group and point the mpls
+        // flow to this.
+        NextGroup next = getGroupForNextObjective(forwardingObjective.nextId());
+        if (next == null) {
+            log.warn("next-id:{} not found in dev:{}", forwardingObjective.nextId(), deviceId);
+            fail(forwardingObjective, ObjectiveError.GROUPMISSING);
+            return Collections.emptySet();
+        }
+        List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
+        Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
+        if (group == null) {
+            log.warn("Group with key:{} for next-id:{} not found in dev:{}",
+                     gkeys.get(0).peekFirst(), forwardingObjective.nextId(), deviceId);
+            fail(forwardingObjective, ObjectiveError.GROUPMISSING);
+            return Collections.emptySet();
+        }
+        // We prepare the treatment for the mpls flow table.
+        // The order of the actions has to be strictly this
+        // according to the OFDPA 2.0 specification.
+        flowTreatment = DefaultTrafficTreatment.builder(mplsTreatment.build());
+        flowTreatment.extension(new Ofdpa3PopCw(), deviceId);
+        flowTreatment.popVlan();
+        flowTreatment.extension(new Ofdpa3PopL2Header(), deviceId);
+        flowTreatment.setTunnelId(tunnelId);
+        flowTreatment.extension(new Ofdpa3SetMplsL2Port(mplsLogicalPort), deviceId);
+        flowTreatment.extension(new Ofdpa3SetMplsType(VPWS), deviceId);
+        flowTreatment.transition(MPLS_TYPE_TABLE);
+        flowTreatment.deferred().group(group.id());
+        // We prepare the flow rule for the mpls table.
+        FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+                .fromApp(forwardingObjective.appId())
+                .withPriority(forwardingObjective.priority())
+                .forDevice(deviceId)
+                .withSelector(flowSelector.build())
+                .withTreatment(flowTreatment.build())
+                .makePermanent()
+                .forTable(MPLS_TABLE_1);
+        return Collections.singletonList(ruleBuilder.build());
+    }
+
     /**
      * Helper method to process the pw forwarding objectives.
      *
      * @param forwardingObjective the fw objective to process
      * @return a singleton list of flow rule
      */
-    private Collection<FlowRule> processPwVersatile(ForwardingObjective forwardingObjective) {
+    private Collection<FlowRule> processInitPwVersatile(ForwardingObjective forwardingObjective) {
         // We retrieve the matching criteria for mpls l2 port.
         TunnelIdCriterion tunnelIdCriterion = (TunnelIdCriterion) forwardingObjective.selector()
                 .getCriterion(TUNNEL_ID);
@@ -315,7 +401,7 @@
             return Collections.emptySet();
         }
         // 0x0000XXXX is UNI interface.
-        if (portCriterion.port().toLong() > 0x0000FFFF) {
+        if (portCriterion.port().toLong() > MPLS_UNI_PORT_MAX) {
             log.error("Pw Versatile Forwarding Objective invalid logical port {}",
                       portCriterion.port().toLong());
             fail(forwardingObjective, ObjectiveError.BADPARAMS);
@@ -369,4 +455,94 @@
                 .forTable(MPLS_L2_PORT_FLOW_TABLE);
         return Collections.singletonList(ruleBuilder.build());
     }
+
+    /**
+     * Utility function to get the mod tunnel id instruction
+     * if present.
+     *
+     * @param treatment the treatment to analyze
+     * @return the mod tunnel id instruction if present,
+     * otherwise null
+     */
+    private ModTunnelIdInstruction getModTunnelIdInstruction(TrafficTreatment treatment) {
+        L2ModificationInstruction l2ModificationInstruction;
+        for (Instruction instruction : treatment.allInstructions()) {
+            if (instruction.type() == L2MODIFICATION) {
+                l2ModificationInstruction = (L2ModificationInstruction) instruction;
+                if (l2ModificationInstruction.subtype() == L2SubType.TUNNEL_ID) {
+                    return (ModTunnelIdInstruction) l2ModificationInstruction;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Utility function to get the output instruction
+     * if present.
+     *
+     * @param treatment the treatment to analyze
+     * @return the output instruction if present,
+     * otherwise null
+     */
+    private OutputInstruction getOutputInstruction(TrafficTreatment treatment) {
+        for (Instruction instruction : treatment.allInstructions()) {
+            if (instruction.type() == Instruction.Type.OUTPUT) {
+                return (OutputInstruction) instruction;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Helper method for dividing the tunnel instructions from the mpls
+     * instructions.
+     *
+     * @param treatment the treatment to analyze
+     * @param mplsTreatment the mpls treatment builder
+     */
+    private void createMplsTreatment(TrafficTreatment treatment,
+                                     TrafficTreatment.Builder mplsTreatment) {
+
+        for (Instruction ins : treatment.allInstructions()) {
+
+            if (ins.type() == Instruction.Type.L2MODIFICATION) {
+                L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
+                switch (l2ins.subtype()) {
+                    // These instructions have to go in the mpls
+                    // treatment.
+                    case TUNNEL_ID:
+                        break;
+                    case DEC_MPLS_TTL:
+                    case MPLS_POP:
+                        mplsTreatment.add(ins);
+                        break;
+                    default:
+                        log.warn("Driver does not handle this type of TrafficTreatment"
+                                         + " instruction in nextObjectives: {} - {}",
+                                 ins.type(), ins);
+                        break;
+                }
+            } else if (ins.type() == Instruction.Type.OUTPUT) {
+                break;
+            } else if (ins.type() == Instruction.Type.L3MODIFICATION) {
+                // We support partially the l3 instructions.
+                L3ModificationInstruction l3ins = (L3ModificationInstruction) ins;
+                switch (l3ins.subtype()) {
+                    case TTL_IN:
+                        mplsTreatment.add(ins);
+                        break;
+                    default:
+                        log.warn("Driver does not handle this type of TrafficTreatment"
+                                         + " instruction in nextObjectives: {} - {}",
+                                 ins.type(), ins);
+                }
+
+            } else {
+                log.warn("Driver does not handle this type of TrafficTreatment"
+                                 + " instruction in nextObjectives: {} - {}",
+                         ins.type(), ins);
+            }
+        }
+    }
 }