TIM WorkFlow

Change-Id: I63a1c5b1ce287186479e22979c37baa1fb7145d5
diff --git a/impl/src/main/java/org/opencord/olt/driver/OltPipeline.java b/impl/src/main/java/org/opencord/olt/driver/OltPipeline.java
index 29d234b..bb52bce 100644
--- a/impl/src/main/java/org/opencord/olt/driver/OltPipeline.java
+++ b/impl/src/main/java/org/opencord/olt/driver/OltPipeline.java
@@ -585,14 +585,13 @@
         }
 
         TrafficSelector selector = fwd.selector();
-
-        Criterion outerVlan = selector.getCriterion(Criterion.Type.VLAN_VID);
+        Criterion outerVlanCriterion = selector.getCriterion(Criterion.Type.VLAN_VID);
         Criterion outerPbit = selector.getCriterion(Criterion.Type.VLAN_PCP);
         Criterion innerVlanCriterion = selector.getCriterion(Criterion.Type.INNER_VLAN_VID);
         Criterion inport = selector.getCriterion(Criterion.Type.IN_PORT);
         Criterion dstMac = selector.getCriterion(Criterion.Type.ETH_DST);
-
-        if (outerVlan == null || innerVlanCriterion == null || inport == null) {
+        //TODO better check for innerVlan
+        if (outerVlanCriterion == null || inport == null) {
             // Avoid logging a non-error from lldp, bbdp and eapol core flows.
             if (!fwd.appId().name().equals(CORE_APP_NAME)) {
                 log.error("Forwarding objective is underspecified: {}", fwd);
@@ -603,31 +602,113 @@
             return;
         }
 
+        VlanId outerVlan = ((VlanIdCriterion) outerVlanCriterion).vlanId();
+        //Verify if this is needed.
+        Criterion outerVid = Criteria.matchVlanId(outerVlan);
+
         VlanId innerVlan = ((VlanIdCriterion) innerVlanCriterion).vlanId();
         Criterion innerVid = Criteria.matchVlanId(innerVlan);
 
         // In the case where the C-tag is the same for all the subscribers,
         // we add a metadata with the outport in the selector to make the flow unique
         Criterion innerSelectorMeta = Criteria.matchMetadata(output.port().toLong());
+        if (outerVlan.toShort() == VlanId.ANY_VALUE) {
+            Criterion metadata = Criteria.matchMetadata(innerVlan.toShort());
+            TrafficSelector outerSelector = buildSelector(inport, metadata, outerVlanCriterion, outerPbit, dstMac);
+            installDownstreamRulesForOuterAnyVlan(fwd, output, outerSelector, buildSelector(inport, innerVid,
+                    innerSelectorMeta));
 
-        if (innerVlan.toShort() == VlanId.ANY_VALUE) {
-            TrafficSelector outerSelector = buildSelector(inport, outerVlan, outerPbit, dstMac);
-            installDownstreamRulesForAnyVlan(fwd, output, outerSelector,
-                                             buildSelector(inport,
-                                                           Criteria.matchVlanId(VlanId.ANY),
-                                                           innerSelectorMeta));
+        } else if (innerVlan.toShort() == VlanId.ANY_VALUE) {
+            TrafficSelector outerSelector = buildSelector(inport, outerVlanCriterion, outerPbit, dstMac);
+
+            Criterion matchedVlanId = Criteria.matchVlanId(VlanId.ANY);
+            installDownstreamRulesForInnerAnyVlan(fwd, output, outerSelector,
+                                                  buildSelector(inport,
+                                                                matchedVlanId,
+                                                                innerSelectorMeta));
         } else {
             // Required to differentiate the same match flows
             // Please note that S tag and S p bit values will be same for the same service - so conflict flows!
             // Metadata match criteria solves the conflict issue - but not used by the voltha
             // Maybe - find a better way to solve the above problem
             Criterion metadata = Criteria.matchMetadata(innerVlan.toShort());
-            TrafficSelector outerSelector = buildSelector(inport, metadata, outerVlan, outerPbit, dstMac);
+            TrafficSelector outerSelector = buildSelector(inport, metadata, outerVlanCriterion, outerPbit, dstMac);
             installDownstreamRulesForVlans(fwd, output, outerSelector, buildSelector(inport, innerVid,
-                                                                                     innerSelectorMeta));
+                    innerSelectorMeta));
         }
     }
 
+    private void installDownstreamRulesForOuterAnyVlan(ForwardingObjective fwd, Instruction output,
+                                                TrafficSelector outerSelector, TrafficSelector innerSelector) {
+
+        Instruction onuDsMeter = fetchMeterById(fwd, fwd.annotations().value(DOWNSTREAM_ONU));
+        Instruction oltDsMeter = fetchMeterById(fwd, fwd.annotations().value(DOWNSTREAM_OLT));
+
+        List<Pair<Instruction, Instruction>> vlanOps =
+                vlanOps(fwd,
+                        L2ModificationInstruction.L2SubType.VLAN_POP);
+
+        if (vlanOps == null || vlanOps.isEmpty()) {
+            return;
+        }
+
+        Pair<Instruction, Instruction> popAndRewrite = vlanOps.remove(0);
+
+        TrafficTreatment innerTreatment;
+        VlanId setVlanId = ((L2ModificationInstruction.ModVlanIdInstruction) popAndRewrite.getRight()).vlanId();
+        if (VlanId.NONE.equals(setVlanId)) {
+            innerTreatment = (buildTreatment(popAndRewrite.getLeft(), onuDsMeter,
+                    writeMetadataIncludingOnlyTp(fwd), output));
+        } else {
+            innerTreatment = (buildTreatment(popAndRewrite.getRight(),
+                    onuDsMeter, writeMetadataIncludingOnlyTp(fwd), output));
+        }
+
+        List<Instruction> setVlanPcps = findL2Instructions(L2ModificationInstruction.L2SubType.VLAN_PCP,
+                fwd.treatment().allInstructions());
+
+        Instruction innerPbitSet = null;
+
+        if (setVlanPcps != null && !setVlanPcps.isEmpty()) {
+            innerPbitSet = setVlanPcps.get(0);
+        }
+
+        VlanId remarkInnerVlan = null;
+        Optional<Criterion> vlanIdCriterion = readFromSelector(innerSelector, Criterion.Type.VLAN_VID);
+        if (vlanIdCriterion.isPresent()) {
+            remarkInnerVlan = ((VlanIdCriterion) vlanIdCriterion.get()).vlanId();
+        }
+
+        Instruction modVlanId = null;
+        if (innerPbitSet != null) {
+            modVlanId = Instructions.modVlanId(remarkInnerVlan);
+        }
+
+        //match: in port (nni), s-tag
+        //action: pop vlan (s-tag), write metadata, go to table 1, meter
+        FlowRule.Builder outer = DefaultFlowRule.builder()
+                .fromApp(fwd.appId())
+                .forDevice(deviceId)
+                .makePermanent()
+                .withPriority(fwd.priority())
+                .withSelector(outerSelector)
+                .withTreatment(buildTreatment(oltDsMeter,
+                        fetchWriteMetadata(fwd),
+                        Instructions.transition(QQ_TABLE)));
+
+        //match: in port (nni), c-tag
+        //action: immediate: write metadata and pop, meter, output
+        FlowRule.Builder inner = DefaultFlowRule.builder()
+                .fromApp(fwd.appId())
+                .forDevice(deviceId)
+                .forTable(QQ_TABLE)
+                .makePermanent()
+                .withPriority(fwd.priority())
+                .withSelector(innerSelector)
+                .withTreatment(innerTreatment);
+        applyRules(fwd, inner, outer);
+    }
+
     private void installDownstreamRulesForVlans(ForwardingObjective fwd, Instruction output,
                                                 TrafficSelector outerSelector, TrafficSelector innerSelector) {
 
@@ -700,8 +781,8 @@
         applyRules(fwd, inner, outer);
     }
 
-    private void installDownstreamRulesForAnyVlan(ForwardingObjective fwd, Instruction output,
-                                                  TrafficSelector outerSelector, TrafficSelector innerSelector) {
+    private void installDownstreamRulesForInnerAnyVlan(ForwardingObjective fwd, Instruction output,
+                                                       TrafficSelector outerSelector, TrafficSelector innerSelector) {
 
         Instruction onuDsMeter = fetchMeterById(fwd, fwd.annotations().value(DOWNSTREAM_ONU));
         Instruction oltDsMeter = fetchMeterById(fwd, fwd.annotations().value(DOWNSTREAM_OLT));
@@ -735,7 +816,6 @@
         List<Pair<Instruction, Instruction>> vlanOps =
                 vlanOps(fwd,
                         L2ModificationInstruction.L2SubType.VLAN_PUSH);
-
         if (vlanOps == null || vlanOps.isEmpty()) {
             return;
         }
@@ -749,10 +829,15 @@
         Pair<Instruction, Instruction> outerPair = vlanOps.remove(0);
 
         boolean noneValueVlanStatus = checkNoneVlanCriteria(fwd);
+        //check if treatment is PUSH or POP
+        boolean popAndPush = checkIfIsPopAndPush(fwd);
         boolean anyValueVlanStatus = checkAnyVlanMatchCriteria(fwd);
-
         if (anyValueVlanStatus) {
             installUpstreamRulesForAnyVlan(fwd, output, outerPair);
+        } else if (popAndPush) {
+            Pair<Instruction, Instruction> innerPair = outerPair;
+            outerPair = vlanOps.remove(0);
+            installUpstreamRulesForAnyOuterVlan(fwd, output, innerPair, outerPair, noneValueVlanStatus);
         } else {
             Pair<Instruction, Instruction> innerPair = outerPair;
             outerPair = vlanOps.remove(0);
@@ -886,6 +971,22 @@
         return true;
     }
 
+    private boolean checkIfIsPopAndPush(ForwardingObjective fwd) {
+        TrafficTreatment treatment = fwd.treatment();
+        List<Instruction> instructions = treatment.allInstructions();
+        Optional<Instruction> vlanInstructionPush = instructions.stream()
+                .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
+                .filter(i -> ((L2ModificationInstruction) i).subtype() ==
+                        L2ModificationInstruction.L2SubType.VLAN_PUSH)
+                .findAny();
+        Optional<Instruction> vlanInstructionPop = instructions.stream()
+                .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
+                .filter(i -> ((L2ModificationInstruction) i).subtype() ==
+                                L2ModificationInstruction.L2SubType.VLAN_POP)
+                .findAny();
+        return vlanInstructionPush.isPresent() && vlanInstructionPop.isPresent();
+    }
+
     private Instruction fetchOutput(ForwardingObjective fwd, String direction) {
         Instruction output = fwd.treatment().allInstructions().stream()
                 .filter(i -> i.type() == Instruction.Type.OUTPUT)
@@ -1310,4 +1411,72 @@
             }
         }
     }
+
+    private void installUpstreamRulesForAnyOuterVlan(ForwardingObjective fwd, Instruction output,
+                                              Pair<Instruction, Instruction> innerPair,
+                                              Pair<Instruction, Instruction> outerPair, Boolean noneValueVlanStatus) {
+
+        Instruction onuUsMeter = fetchMeterById(fwd, fwd.annotations().value(UPSTREAM_ONU));
+        Instruction oltUsMeter = fetchMeterById(fwd, fwd.annotations().value(UPSTREAM_OLT));
+
+        List<Instruction> setVlanPcps = findL2Instructions(L2ModificationInstruction.L2SubType.VLAN_PCP,
+                fwd.treatment().allInstructions());
+
+        Instruction innerPbitSet = null;
+        Instruction outerPbitSet = null;
+
+        if (setVlanPcps != null && !setVlanPcps.isEmpty()) {
+            innerPbitSet = setVlanPcps.get(0);
+            outerPbitSet = setVlanPcps.get(1);
+        }
+
+        TrafficTreatment innerTreatment;
+        if (noneValueVlanStatus) {
+            innerTreatment = buildTreatment(innerPair.getLeft(), innerPair.getRight(), onuUsMeter,
+                    fetchWriteMetadata(fwd), innerPbitSet,
+                    Instructions.transition(QQ_TABLE));
+        } else {
+            innerTreatment = buildTreatment(innerPair.getRight(), onuUsMeter, fetchWriteMetadata(fwd),
+                    innerPbitSet, Instructions.transition(QQ_TABLE));
+        }
+
+        //match: in port, vlanId (0 or None)
+        //action:
+        //if vlanId None, push & set c-tag go to table 1
+        //if vlanId 0 or any specific vlan, set c-tag, write metadata, meter and go to table 1
+        FlowRule.Builder inner = DefaultFlowRule.builder()
+                .fromApp(fwd.appId())
+                .forDevice(deviceId)
+                .makePermanent()
+                .withPriority(fwd.priority())
+                .withSelector(fwd.selector())
+                .withTreatment(innerTreatment);
+
+        PortCriterion inPort = (PortCriterion)
+                fwd.selector().getCriterion(Criterion.Type.IN_PORT);
+
+        VlanId cVlanId = ((L2ModificationInstruction.ModVlanIdInstruction)
+                innerPair.getRight()).vlanId();
+
+        //match: in port, c-tag
+        //action: immediate: push s-tag, write metadata, meter and output
+        FlowRule.Builder outer = DefaultFlowRule.builder()
+                .fromApp(fwd.appId())
+                .forDevice(deviceId)
+                .forTable(QQ_TABLE)
+                .makePermanent()
+                .withPriority(fwd.priority())
+                .withTreatment(buildTreatment(oltUsMeter, writeMetadataIncludingOnlyTp(fwd),
+                        outerPbitSet, output));
+
+        if (innerPbitSet != null) {
+            byte innerPbit = ((L2ModificationInstruction.ModVlanPcpInstruction)
+                    innerPbitSet).vlanPcp();
+            outer.withSelector(buildSelector(inPort, Criteria.matchVlanId(cVlanId), Criteria.matchVlanPcp(innerPbit)));
+        } else {
+            outer.withSelector(buildSelector(inPort, Criteria.matchVlanId(cVlanId)));
+        }
+
+        applyRules(fwd, inner, outer);
+    }
 }
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/impl/OltFlowService.java b/impl/src/main/java/org/opencord/olt/impl/OltFlowService.java
index cbc538a..f4e859f 100644
--- a/impl/src/main/java/org/opencord/olt/impl/OltFlowService.java
+++ b/impl/src/main/java/org/opencord/olt/impl/OltFlowService.java
@@ -1227,9 +1227,13 @@
             treatmentBuilder.pushVlan()
                     .setVlanId(uti.getPonCTag());
         }
+        if (uti.getPonSTag().toShort() == VlanId.ANY_VALUE) {
+            treatmentBuilder.popVlan();
+        }
 
         if (uti.getUsPonCTagPriority() != -1) {
             treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
+
         }
 
         treatmentBuilder.pushVlan()