Added basic pseudowire support for Trellis.

- Configurable pseudowires supporting untagged-untagged, single-single, double-double tagged traffic.
- Commands for listing, adding and removing pseudowires.
- Initial support for link failures.
- Pseudowires also configurable by network configuration.
- Tested with ofdpa_3.0.5.

Known limitations that I plan to fix soon :
	- Adding pseudowires from configuration is inconvenient because we need to ammend new pws to the
	  existing configuration. We should create a REST API for adding/removing/listing pws and abandond the
	  network configuration.
	- Spine fabric switches have rules matching a special mpls tag used for pw traffic for a specific leaf.
	  However, this rules redirect to an "indirect" group for forwarding traffic. If the resulting port there
	  is no mechanism as of now to handle this. We should use the MPLS ECMP Groups of ofdpa, however they are
	  not functional. Thus, we need to inject logic into the application to handle this case.

Change-Id: Ia85cf4514ebab627fc6ed5a19ad9f6cdc67dc24c
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2GroupHandler.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2GroupHandler.java
index 24ababc..8641076 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2GroupHandler.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2GroupHandler.java
@@ -1468,14 +1468,16 @@
      *             this next objective
      */
     protected void removeGroup(NextObjective nextObjective, NextGroup next) {
+
         List<Deque<GroupKey>> allActiveKeys = appKryo.deserialize(next.data());
 
         List<GroupKey> groupKeys = allActiveKeys.stream()
                 .map(Deque::getFirst).collect(Collectors.toList());
         addPendingRemoveNextObjective(nextObjective, groupKeys);
 
-        allActiveKeys.forEach(groupChain -> groupChain.forEach(groupKey ->
-                groupService.removeGroup(deviceId, groupKey, nextObjective.appId())));
+        allActiveKeys
+                .forEach(groupChain -> groupChain.forEach(groupKey -> groupService
+                        .removeGroup(deviceId, groupKey, nextObjective.appId())));
         flowObjectiveStore.removeNextGroup(nextObjective.id());
     }
 
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa3Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa3Pipeline.java
index edf8c38..cbd6fb7 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa3Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa3Pipeline.java
@@ -17,6 +17,7 @@
 package org.onosproject.driver.pipeline.ofdpa;
 
 import com.google.common.collect.ImmutableList;
+import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.driver.extensions.Ofdpa3MatchMplsL2Port;
 import org.onosproject.driver.extensions.Ofdpa3MatchOvid;
@@ -91,19 +92,22 @@
     protected void processFilter(FilteringObjective filteringObjective,
                                  boolean install,
                                  ApplicationId applicationId) {
-        // We are looking for inner vlan id criterion. We use this
-        // to identify the pseudo wire flows. In future we can enforce
-        // using also the tunnel id in the meta.
-        VlanIdCriterion innerVlanIdCriterion = null;
-        for (Criterion criterion : filteringObjective.conditions()) {
-            if (criterion.type() == INNER_VLAN_VID) {
-                innerVlanIdCriterion = (VlanIdCriterion) criterion;
-                break;
-            }
-        }
-        if (innerVlanIdCriterion != null) {
+
+        // Check if filter is intended for pseudowire
+        boolean isPw = isPseudowire(filteringObjective);
+
+        if (isPw) {
             FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
             PortCriterion portCriterion;
+
+            VlanIdCriterion innerVlanIdCriterion = null;
+            for (Criterion criterion : filteringObjective.conditions()) {
+                if (criterion.type() == INNER_VLAN_VID) {
+                    innerVlanIdCriterion = (VlanIdCriterion) criterion;
+                    break;
+                }
+            }
+
             VlanIdCriterion outerVlanIdCriterion = null;
             // We extract the expected port criterion in the key.
             portCriterion = (PortCriterion) filteringObjective.key();
@@ -114,19 +118,25 @@
                     break;
                 }
             }
+
             // We extract the tunnel id.
             long tunnelId;
+            VlanId egressVlan;
+
             if (filteringObjective.meta() != null &&
-                    filteringObjective.meta().allInstructions().size() != 1) {
+                    filteringObjective.meta().allInstructions().size() != 2) {
                 log.warn("Bad filtering objective from app: {}. Not"
                                  + "processing filtering objective", applicationId);
                 fail(filteringObjective, ObjectiveError.BADPARAMS);
                 return;
             } else if (filteringObjective.meta() != null &&
-                    filteringObjective.meta().allInstructions().size() == 1 &&
-                    filteringObjective.meta().allInstructions().get(0).type() == L2MODIFICATION) {
+                    filteringObjective.meta().allInstructions().size() == 2 &&
+                    filteringObjective.meta().allInstructions().get(0).type() == L2MODIFICATION &&
+                    filteringObjective.meta().allInstructions().get(1).type() == L2MODIFICATION) {
+
                 L2ModificationInstruction l2instruction = (L2ModificationInstruction)
                         filteringObjective.meta().allInstructions().get(0);
+
                 if (l2instruction.subtype() != L2SubType.TUNNEL_ID) {
                     log.warn("Bad filtering objective from app: {}. Not"
                                      + "processing filtering objective", applicationId);
@@ -135,12 +145,25 @@
                 } else {
                     tunnelId = ((ModTunnelIdInstruction) l2instruction).tunnelId();
                 }
+
+                L2ModificationInstruction vlanInstruction = (L2ModificationInstruction)
+                        filteringObjective.meta().allInstructions().get(1);
+
+                if (vlanInstruction.subtype() != L2SubType.VLAN_ID) {
+                    log.warn("Bad filtering objective from app: {}. Not"
+                                     + "processing filtering objective", applicationId);
+                    fail(filteringObjective, ObjectiveError.BADPARAMS);
+                    return;
+                } else {
+                    egressVlan = ((L2ModificationInstruction.ModVlanIdInstruction) vlanInstruction).vlanId();
+                }
             } else {
                 log.warn("Bad filtering objective from app: {}. Not"
                                  + "processing filtering objective", applicationId);
                 fail(filteringObjective, ObjectiveError.BADPARAMS);
                 return;
             }
+
             // Mpls tunnel ids according to the OFDPA manual have to be
             // in the range [2^17-1, 2^16].
             tunnelId = MPLS_TUNNEL_ID_BASE | tunnelId;
@@ -160,12 +183,14 @@
                 fail(filteringObjective, ObjectiveError.BADPARAMS);
                 return;
             }
+
             // We create the flows.
             List<FlowRule> pwRules = processPwFilter(portCriterion,
                                                      innerVlanIdCriterion,
                                                      outerVlanIdCriterion,
                                                      tunnelId,
-                                                     applicationId
+                                                     applicationId,
+                                                     egressVlan
             );
             // We tag the flow for adding or for removing.
             for (FlowRule pwRule : pwRules) {
@@ -197,6 +222,32 @@
     }
 
     /**
+     * Determines if the filtering objective will be used for a pseudowire.
+     *
+     * @param filteringObjective
+     * @return True if objective was created for a pseudowire, false otherwise.
+     */
+    private boolean isPseudowire(FilteringObjective filteringObjective) {
+
+
+        if (filteringObjective.meta() != null) {
+
+            TrafficTreatment treatment = filteringObjective.meta();
+            for (Instruction instr : treatment.immediate()) {
+                if (instr.type().equals(Instruction.Type.L2MODIFICATION)) {
+
+                    L2ModificationInstruction l2Instr = (L2ModificationInstruction) instr;
+                    if (l2Instr.subtype().equals(L2SubType.TUNNEL_ID)) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
      * Method to process the pw related filtering objectives.
      *
      * @param portCriterion the in port match
@@ -204,61 +255,128 @@
      * @param outerVlanIdCriterion the outer vlan match
      * @param tunnelId the tunnel id
      * @param applicationId the application id
+     * @param egressVlan the vlan to modify, was passed in metadata
      * @return a list of flow rules to install
      */
     private List<FlowRule> processPwFilter(PortCriterion portCriterion,
                                            VlanIdCriterion innerVlanIdCriterion,
                                            VlanIdCriterion outerVlanIdCriterion,
                                            long tunnelId,
-                                           ApplicationId applicationId) {
-        // As first we create the flow rule for the vlan 1 table.
+                                           ApplicationId applicationId,
+                                           VlanId egressVlan) {
+
         FlowRule vlan1FlowRule;
         int mplsLogicalPort = ((int) portCriterion.port().toLong());
         // We have to match on the inner vlan and outer vlan at the same time.
         // Ofdpa supports this through the OVID meta-data type.
-        TrafficSelector.Builder vlan1Selector = DefaultTrafficSelector.builder()
-                .matchInPort(portCriterion.port())
-                .matchVlanId(innerVlanIdCriterion.vlanId())
-                .extension(new Ofdpa3MatchOvid(outerVlanIdCriterion.vlanId()), deviceId);
-        // TODO understand for the future how to manage the vlan rewrite.
-        TrafficTreatment.Builder vlan1Treatment = DefaultTrafficTreatment.builder()
-                .pushVlan()
-                .setVlanId(outerVlanIdCriterion.vlanId())
-                .extension(new Ofdpa3SetMplsType(VPWS), deviceId)
-                .extension(new Ofdpa3SetMplsL2Port(mplsLogicalPort), deviceId)
-                .setTunnelId(tunnelId)
-                .transition(MPLS_L2_PORT_FLOW_TABLE);
-        vlan1FlowRule = DefaultFlowRule.builder()
-                .forDevice(deviceId)
-                .withSelector(vlan1Selector.build())
-                .withTreatment(vlan1Treatment.build())
-                .withPriority(DEFAULT_PRIORITY)
-                .fromApp(applicationId)
-                .makePermanent()
-                .forTable(VLAN_1_TABLE)
-                .build();
-        // Finally we create the flow rule for the vlan table.
-        FlowRule vlanFlowRule;
-        // We have to match on the outer vlan.
-        TrafficSelector.Builder vlanSelector = DefaultTrafficSelector.builder()
-                .matchInPort(portCriterion.port())
-                .matchVlanId(outerVlanIdCriterion.vlanId());
-        // TODO understand for the future how to manage the vlan rewrite.
-        TrafficTreatment.Builder vlanTreatment = DefaultTrafficTreatment.builder()
-                .popVlan()
-                .extension(new Ofdpa3SetOvid(outerVlanIdCriterion.vlanId()), deviceId)
-                .transition(VLAN_1_TABLE);
-        vlanFlowRule = DefaultFlowRule.builder()
-                .forDevice(deviceId)
-                .withSelector(vlanSelector.build())
-                .withTreatment(vlanTreatment.build())
-                .withPriority(DEFAULT_PRIORITY)
-                .fromApp(applicationId)
-                .makePermanent()
-                .forTable(VLAN_TABLE)
-                .build();
 
-        return ImmutableList.of(vlan1FlowRule, vlanFlowRule);
+        ImmutableList<FlowRule> toReturn;
+
+        // pseudowire configured with double tagged vlans
+        if (!(innerVlanIdCriterion.vlanId().equals(VlanId.NONE))
+                && !(outerVlanIdCriterion.vlanId().equals(VlanId.NONE))) {
+
+            log.info("Installing filter objective for double tagged CE for tunnel {}", tunnelId);
+
+            TrafficSelector.Builder vlan1Selector = DefaultTrafficSelector.builder()
+                    .matchInPort(portCriterion.port())
+                    .matchVlanId(innerVlanIdCriterion.vlanId())
+                    .extension(new Ofdpa3MatchOvid(outerVlanIdCriterion.vlanId()), deviceId);
+            // TODO understand for the future how to manage the vlan rewrite.
+            TrafficTreatment.Builder vlan1Treatment = DefaultTrafficTreatment.builder()
+                    .pushVlan()
+                    .setVlanId(egressVlan)
+                    .extension(new Ofdpa3SetMplsType(VPWS), deviceId)
+                    .extension(new Ofdpa3SetMplsL2Port(mplsLogicalPort), deviceId)
+                    .setTunnelId(tunnelId)
+                    .transition(MPLS_L2_PORT_FLOW_TABLE);
+            vlan1FlowRule = DefaultFlowRule.builder()
+                    .forDevice(deviceId)
+                    .withSelector(vlan1Selector.build())
+                    .withTreatment(vlan1Treatment.build())
+                    .withPriority(DEFAULT_PRIORITY)
+                    .fromApp(applicationId)
+                    .makePermanent()
+                    .forTable(VLAN_1_TABLE)
+                    .build();
+            // Finally we create the flow rule for the vlan table.
+            FlowRule vlanFlowRule;
+            // We have to match on the outer vlan.
+            TrafficSelector.Builder vlanSelector = DefaultTrafficSelector.builder()
+                    .matchInPort(portCriterion.port())
+                    .matchVlanId(outerVlanIdCriterion.vlanId());
+            // TODO understand for the future how to manage the vlan rewrite.
+            TrafficTreatment.Builder vlanTreatment = DefaultTrafficTreatment.builder()
+                    .popVlan()
+                    .extension(new Ofdpa3SetOvid(outerVlanIdCriterion.vlanId()), deviceId)
+                    .transition(VLAN_1_TABLE);
+            vlanFlowRule = DefaultFlowRule.builder()
+                    .forDevice(deviceId)
+                    .withSelector(vlanSelector.build())
+                    .withTreatment(vlanTreatment.build())
+                    .withPriority(DEFAULT_PRIORITY)
+                    .fromApp(applicationId)
+                    .makePermanent()
+                    .forTable(VLAN_TABLE)
+                    .build();
+
+            return ImmutableList.of(vlan1FlowRule, vlanFlowRule);
+        } else  if (!(innerVlanIdCriterion.vlanId().equals(VlanId.NONE))
+                && (outerVlanIdCriterion.vlanId().equals(VlanId.NONE))) {
+
+            log.info("Installing filter objective for single tagged CE for tunnel {}", tunnelId);
+
+            TrafficSelector.Builder singleVlanSelector = DefaultTrafficSelector.builder()
+                    .matchInPort(portCriterion.port())
+                    .matchVlanId(innerVlanIdCriterion.vlanId());
+
+            TrafficTreatment.Builder singleVlanTreatment = DefaultTrafficTreatment.builder()
+                    // .pushVlan()
+                    .setVlanId(egressVlan)
+                    .extension(new Ofdpa3SetMplsType(VPWS), deviceId)
+                    .extension(new Ofdpa3SetMplsL2Port(mplsLogicalPort), deviceId)
+                    .setTunnelId(tunnelId)
+                    .transition(MPLS_L2_PORT_FLOW_TABLE);
+
+            vlan1FlowRule = DefaultFlowRule.builder()
+                    .forDevice(deviceId)
+                    .withSelector(singleVlanSelector.build())
+                    .withTreatment(singleVlanTreatment.build())
+                    .withPriority(DEFAULT_PRIORITY)
+                    .fromApp(applicationId)
+                    .makePermanent()
+                    .forTable(VLAN_TABLE)
+                    .build();
+
+            return ImmutableList.of(vlan1FlowRule);
+        } else if ((innerVlanIdCriterion.vlanId().equals(VlanId.NONE))
+                && (outerVlanIdCriterion.vlanId().equals(VlanId.NONE))) {
+
+            TrafficSelector.Builder singleVlanSelector = DefaultTrafficSelector.builder()
+                    .matchInPort(portCriterion.port())
+                    .matchVlanId(innerVlanIdCriterion.vlanId());
+
+            TrafficTreatment.Builder singleVlanTreatment = DefaultTrafficTreatment.builder()
+                    .extension(new Ofdpa3SetMplsType(VPWS), deviceId)
+                    .extension(new Ofdpa3SetMplsL2Port(mplsLogicalPort), deviceId)
+                    .setTunnelId(tunnelId)
+                    .transition(MPLS_L2_PORT_FLOW_TABLE);
+
+            vlan1FlowRule = DefaultFlowRule.builder()
+                    .forDevice(deviceId)
+                    .withSelector(singleVlanSelector.build())
+                    .withTreatment(singleVlanTreatment.build())
+                    .withPriority(DEFAULT_PRIORITY)
+                    .fromApp(applicationId)
+                    .makePermanent()
+                    .forTable(VLAN_TABLE)
+                    .build();
+
+            return ImmutableList.of(vlan1FlowRule);
+        } else {
+            // failure...
+            return Collections.emptyList();
+        }
     }
 
     @Override