Deprecate CpqD pipeliners

In addition,
  - Update processVersatile to handle more selectors in ovs-ofdpa
    This also fixes the issue of XConnect ACL flow not being programmed correctly
  - Refactor the code a bit to reduce duplication

Change-Id: I190aad904d3e6625ff9f089c74e3b98077bbe4a3
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/OvsOfdpaGroupHandler.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/OvsOfdpaGroupHandler.java
new file mode 100644
index 0000000..711ba3a
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/OvsOfdpaGroupHandler.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.driver.pipeline.ofdpa;
+
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupKey;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.List;
+
+import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Group handler for Open vSwitch OFDPA pipeline.
+ */
+public class OvsOfdpaGroupHandler extends Ofdpa2GroupHandler {
+    private final Logger log = getLogger(getClass());
+
+    @Override
+    protected boolean supportCopyTtl() {
+        return false;
+    }
+
+    @Override
+    protected boolean supportSetMplsBos() {
+        return false;
+    }
+
+    @Override
+    protected boolean requireVlanPopBeforeMplsPush() {
+        return true;
+    }
+
+    @Override
+    protected GroupInfo createL2L3Chain(TrafficTreatment treatment, int nextId,
+                                        ApplicationId appId, boolean mpls,
+                                        TrafficSelector meta) {
+        if (createUnfiltered(treatment, meta)) {
+            return createUnfilteredL2L3Chain(treatment, nextId, appId);
+        }
+        // for the l2interface group, get vlan and port info
+        // for the outer group, get the src/dst mac, and vlan info
+        TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
+        TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
+        VlanId vlanid = null;
+        long portNum = 0;
+        boolean setVlan = false, popVlan = false;
+        MacAddress srcMac = MacAddress.ZERO;
+        MacAddress dstMac = MacAddress.ZERO;
+        for (Instruction ins : treatment.allInstructions()) {
+            if (ins.type() == Instruction.Type.L2MODIFICATION) {
+                L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
+                switch (l2ins.subtype()) {
+                    case ETH_DST:
+                        dstMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
+                        outerTtb.setEthDst(dstMac);
+                        break;
+                    case ETH_SRC:
+                        srcMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
+                        outerTtb.setEthSrc(srcMac);
+                        break;
+                    case VLAN_ID:
+                        vlanid = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
+                        outerTtb.setVlanId(vlanid);
+                        setVlan = true;
+                        break;
+                    case VLAN_POP:
+                        innerTtb.popVlan();
+                        popVlan = true;
+                        break;
+                    case DEC_MPLS_TTL:
+                    case MPLS_LABEL:
+                    case MPLS_POP:
+                    case MPLS_PUSH:
+                    case VLAN_PCP:
+                    case VLAN_PUSH:
+                    default:
+                        break;
+                }
+            } else if (ins.type() == Instruction.Type.OUTPUT) {
+                portNum = ((Instructions.OutputInstruction) ins).port().toLong();
+                innerTtb.add(ins);
+            } else {
+                log.debug("Driver does not handle this type of TrafficTreatment"
+                        + " instruction in l2l3chain:  {} - {}", ins.type(),
+                         ins);
+            }
+        }
+
+        if (vlanid == null && meta != null) {
+            // use metadata if available
+            Criterion vidCriterion = meta.getCriterion(Criterion.Type.VLAN_VID);
+            if (vidCriterion != null) {
+                vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
+            }
+            // if vlan is not set, use the vlan in metadata for outerTtb
+            if (vlanid != null && !setVlan) {
+                outerTtb.setVlanId(vlanid);
+            }
+        }
+
+        if (vlanid == null) {
+            log.error("Driver cannot process an L2/L3 group chain without "
+                              + "egress vlan information for dev: {} port:{}",
+                      deviceId, portNum);
+            return null;
+        }
+
+        if (!setVlan && !popVlan) {
+            // untagged outgoing port
+            TrafficTreatment.Builder temp = DefaultTrafficTreatment.builder();
+            temp.popVlan();
+            innerTtb.build().allInstructions().forEach(i -> temp.add(i));
+            innerTtb = temp;
+        }
+
+        // assemble information for ofdpa l2interface group
+        int l2groupId = L2_INTERFACE_TYPE | (vlanid.toShort() << 16) | (int) portNum;
+        // a globally unique groupkey that is different for ports in the same device,
+        // but different for the same portnumber on different devices. Also different
+        // for the various group-types created out of the same next objective.
+        int l2gk = l2InterfaceGroupKey(deviceId, vlanid, portNum);
+        final GroupKey l2groupkey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2gk));
+
+        // assemble information for outer group
+        GroupDescription outerGrpDesc = null;
+        if (mpls) {
+            // outer group is MPLSInteface
+            int mplsInterfaceIndex = getNextAvailableIndex();
+            int mplsgroupId = MPLS_INTERFACE_TYPE | (SUBTYPE_MASK & mplsInterfaceIndex);
+            final GroupKey mplsgroupkey = new DefaultGroupKey(
+                    Ofdpa2Pipeline.appKryo.serialize(mplsInterfaceIndex));
+            outerTtb.group(new GroupId(l2groupId));
+            // create the mpls-interface group description to wait for the
+            // l2 interface group to be processed
+            GroupBucket mplsinterfaceGroupBucket =
+                    DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
+            outerGrpDesc = new DefaultGroupDescription(
+                    deviceId,
+                    GroupDescription.Type.INDIRECT,
+                    new GroupBuckets(Collections.singletonList(
+                            mplsinterfaceGroupBucket)),
+                    mplsgroupkey,
+                    mplsgroupId,
+                    appId);
+            log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} nextid:{}",
+                      deviceId, Integer.toHexString(mplsgroupId),
+                      mplsgroupkey, nextId);
+        } else {
+            // outer group is L3Unicast
+            int l3unicastIndex = getNextAvailableIndex();
+            int l3groupId = L3_UNICAST_TYPE | (TYPE_MASK & l3unicastIndex);
+            final GroupKey l3groupkey = new DefaultGroupKey(
+                    Ofdpa2Pipeline.appKryo.serialize(l3unicastIndex));
+            outerTtb.group(new GroupId(l2groupId));
+            // create the l3unicast group description to wait for the
+            // l2 interface group to be processed
+            GroupBucket l3unicastGroupBucket =
+                    DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
+            outerGrpDesc = new DefaultGroupDescription(
+                    deviceId,
+                    GroupDescription.Type.INDIRECT,
+                    new GroupBuckets(Collections.singletonList(
+                            l3unicastGroupBucket)),
+                    l3groupkey,
+                    l3groupId,
+                    appId);
+            log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}",
+                      deviceId, Integer.toHexString(l3groupId),
+                      l3groupkey, nextId);
+        }
+
+        // store l2groupkey with the groupChainElem for the outer-group that depends on it
+        GroupChainElem gce = new GroupChainElem(outerGrpDesc,
+                                                1,
+                                                false,
+                                                deviceId);
+        updatePendingGroups(l2groupkey, gce);
+
+        // create group description for the inner l2interfacegroup
+        GroupBucket l2InterfaceGroupBucket =
+                DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build());
+        GroupDescription l2groupDescription =
+                new DefaultGroupDescription(
+                        deviceId,
+                        GroupDescription.Type.INDIRECT,
+                        new GroupBuckets(Collections.singletonList(
+                                l2InterfaceGroupBucket)),
+                        l2groupkey,
+                        l2groupId,
+                        appId);
+        log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
+                  deviceId, Integer.toHexString(l2groupId),
+                  l2groupkey, nextId);
+        return new GroupInfo(l2groupDescription, outerGrpDesc);
+    }
+
+    /**
+     * In OFDPA2 we do not support the MPLS-ECMP, while we do in
+     * Open vSwitch implementation.
+     *
+     * @param nextObjective the hashed next objective to support.
+     */
+    @Override
+    protected void processHashedNextObjective(NextObjective nextObjective) {
+        // The case for MPLS-ECMP. For now, we try to create a MPLS-ECMP for
+        // the transport of a VPWS. The necessary info are contained in the
+        // meta selector. In particular we are looking for the case of BoS==False;
+        TrafficSelector metaSelector = nextObjective.meta();
+        if (metaSelector != null && Ofdpa2Pipeline.isNotMplsBos(metaSelector)) {
+            // storage for all group keys in the chain of groups created
+            List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
+            List<GroupInfo> unsentGroups = new ArrayList<>();
+            createHashBucketChains(nextObjective, allGroupKeys, unsentGroups);
+            // now we can create the outermost MPLS ECMP group
+            List<GroupBucket> mplsEcmpGroupBuckets = new ArrayList<>();
+            for (GroupInfo gi : unsentGroups) {
+                // create ECMP bucket to point to the outer group
+                TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
+                ttb.group(new GroupId(gi.nextGroupDesc().givenGroupId()));
+                GroupBucket sbucket = DefaultGroupBucket
+                        .createSelectGroupBucket(ttb.build());
+                mplsEcmpGroupBuckets.add(sbucket);
+            }
+            int mplsEcmpIndex = getNextAvailableIndex();
+            int mplsEcmpGroupId = makeMplsForwardingGroupId(OfdpaMplsGroupSubType.MPLS_ECMP, mplsEcmpIndex);
+            GroupKey mplsEmpGroupKey = new DefaultGroupKey(
+                    Ofdpa2Pipeline.appKryo.serialize(mplsEcmpIndex)
+            );
+            GroupDescription mplsEcmpGroupDesc = new DefaultGroupDescription(
+                    deviceId,
+                    GroupDescription.Type.SELECT,
+                    new GroupBuckets(mplsEcmpGroupBuckets),
+                    mplsEmpGroupKey,
+                    mplsEcmpGroupId,
+                    nextObjective.appId()
+            );
+            GroupChainElem mplsEcmpGce = new GroupChainElem(mplsEcmpGroupDesc,
+                                                            mplsEcmpGroupBuckets.size(),
+                                                            false,
+                                                            deviceId);
+
+            // create objects for local and distributed storage
+            allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(mplsEmpGroupKey));
+            OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObjective);
+
+            // store mplsEcmpGroupKey with the ofdpaGroupChain for the nextObjective
+            // that depends on it
+            updatePendingNextObjective(mplsEmpGroupKey, ofdpaGrp);
+
+            log.debug("Trying MPLS-ECMP: device:{} gid:{} gkey:{} nextId:{}",
+                      deviceId, Integer.toHexString(mplsEcmpGroupId),
+                      mplsEmpGroupKey, nextObjective.id());
+
+            // finally we are ready to send the innermost groups
+            for (GroupInfo gi : unsentGroups) {
+                log.debug("Sending innermost group {} in group chain on device {} ",
+                          Integer.toHexString(gi.innerMostGroupDesc().givenGroupId()), deviceId);
+                updatePendingGroups(gi.nextGroupDesc().appCookie(), mplsEcmpGce);
+                groupService.addGroup(gi.innerMostGroupDesc());
+            }
+            return;
+        }
+        super.processHashedNextObjective(nextObjective);
+    }
+
+    /**
+     * Internal implementation of createL2L3Chain to handle double-tagged vlan.
+     * L3UG Group carries dummyVlanId and output port information in its groupId,
+     * and does not set vlan.
+     * L2UG Group only has OUTPUT instruction.
+     *
+     * @param treatment that needs to be broken up to create the group chain
+     * @param nextId of the next objective that needs this group chain
+     * @param appId of the application that sent this next objective
+     * @return GroupInfo containing the GroupDescription of the
+     *         L2 Unfiltered Interface group(inner) and the GroupDescription of the (outer)
+     *         L3Unicast group. May return null if there is an error in processing the chain.
+     */
+    private GroupInfo createUnfilteredL2L3Chain(TrafficTreatment treatment, int nextId,
+                                                ApplicationId appId) {
+        // for the l2 unfiltered interface group, get port info
+        // for the l3 unicast group, get the src/dst mac, and vlan info
+        TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
+        TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
+        VlanId vlanId = VlanId.NONE;
+        long portNum = 0;
+        MacAddress srcMac;
+        MacAddress dstMac;
+        for (Instruction ins : treatment.allInstructions()) {
+            if (ins.type() == Instruction.Type.L2MODIFICATION) {
+                L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
+                switch (l2ins.subtype()) {
+                    case ETH_DST:
+                        dstMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
+                        outerTtb.setEthDst(dstMac);
+                        break;
+                    case ETH_SRC:
+                        srcMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
+                        outerTtb.setEthSrc(srcMac);
+                        break;
+                    case VLAN_ID:
+                        vlanId = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
+                        break;
+                    default:
+                        break;
+                }
+            } else if (ins.type() == Instruction.Type.OUTPUT) {
+                portNum = ((Instructions.OutputInstruction) ins).port().toLong();
+                innerTtb.add(ins);
+            } else {
+                log.debug("Driver does not handle this type of TrafficTreatment"
+                                  + " instruction in l2l3chain:  {} - {}", ins.type(),
+                          ins);
+            }
+        }
+
+        // assemble information for ofdpa l2 unfiltered interface group
+        int l2groupId = l2UnfilteredGroupId(portNum);
+        // a globally unique groupkey that is different for ports in the same device,
+        // but different for the same portnumber on different devices. Also different
+        // for the various group-types created out of the same next objective.
+        int l2gk = l2UnfilteredGroupKey(deviceId, portNum);
+        final GroupKey l2groupkey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2gk));
+
+        // assemble information for outer group (L3Unicast)
+        GroupDescription outerGrpDesc;
+        int l3groupId = doubleVlanL3UnicastGroupId(vlanId, portNum);
+        final GroupKey l3groupkey = new DefaultGroupKey(
+                Ofdpa3Pipeline.appKryo.serialize(doubleVlanL3UnicastGroupKey(deviceId, vlanId, portNum)));
+        outerTtb.group(new GroupId(l2groupId));
+        // create the l3unicast group description to wait for the
+        // l2 unfiltered interface group to be processed
+        GroupBucket l3unicastGroupBucket =
+                DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
+        outerGrpDesc = new DefaultGroupDescription(
+                deviceId,
+                GroupDescription.Type.INDIRECT,
+                new GroupBuckets(Collections.singletonList(l3unicastGroupBucket)),
+                l3groupkey,
+                l3groupId,
+                appId);
+        log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}",
+                  deviceId, Integer.toHexString(l3groupId),
+                  l3groupkey, nextId);
+
+        // store l2groupkey with the groupChainElem for the outer-group that depends on it
+        OfdpaGroupHandlerUtility.GroupChainElem gce = new OfdpaGroupHandlerUtility.GroupChainElem(
+                outerGrpDesc, 1, false, deviceId);
+        updatePendingGroups(l2groupkey, gce);
+
+        // create group description for the inner l2 unfiltered interface group
+        GroupBucket l2InterfaceGroupBucket =
+                DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build());
+        GroupDescription l2groupDescription =
+                new DefaultGroupDescription(deviceId,
+                                            GroupDescription.Type.INDIRECT,
+                                            new GroupBuckets(Collections.singletonList(l2InterfaceGroupBucket)),
+                                            l2groupkey,
+                                            l2groupId,
+                                            appId);
+        log.debug("Trying L2Unfiltered: device:{} gid:{} gkey:{} nextId:{}",
+                  deviceId, Integer.toHexString(l2groupId), l2groupkey, nextId);
+        return new OfdpaGroupHandlerUtility.GroupInfo(l2groupDescription, outerGrpDesc);
+    }
+
+}