Added support for clone session on fabric.p4

Now on ACL table in fabric.p4 you can clone a packet to the CPU using the clone session.

Change-Id: Ic21f948cffe553e32e7b2fe1f7af49b6a387fffb
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricConstants.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricConstants.java
index eb927ad..f1bb185 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricConstants.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricConstants.java
@@ -223,6 +223,8 @@
             PiActionId.of("FabricEgress.process_int_main.process_int_transit.init_metadata");
     public static final PiActionId FABRIC_INGRESS_ACL_DROP =
             PiActionId.of("FabricIngress.acl.drop");
+    public static final PiActionId FABRIC_INGRESS_ACL_SET_CLONE_SESSION_ID =
+            PiActionId.of("FabricIngress.acl.set_clone_session_id");
     public static final PiActionId FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_PUNT_TO_CPU =
             PiActionId.of("FabricIngress.bng_ingress.upstream.punt_to_cpu");
     public static final PiActionId FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_SET_LINE =
@@ -276,8 +278,6 @@
     public static final PiActionId NOP = PiActionId.of("nop");
     public static final PiActionId FABRIC_INGRESS_NEXT_OUTPUT_SIMPLE =
             PiActionId.of("FabricIngress.next.output_simple");
-    public static final PiActionId FABRIC_INGRESS_ACL_CLONE_TO_CPU =
-            PiActionId.of("FabricIngress.acl.clone_to_cpu");
     public static final PiActionId FABRIC_INGRESS_FILTERING_DENY =
             PiActionId.of("FabricIngress.filtering.deny");
     public static final PiActionId FABRIC_INGRESS_NEXT_SET_MCAST_GROUP_ID =
@@ -298,6 +298,8 @@
     public static final PiActionParamId S1U_SGW_ADDR =
             PiActionParamId.of("s1u_sgw_addr");
     public static final PiActionParamId SMAC = PiActionParamId.of("smac");
+    public static final PiActionParamId CLONE_ID =
+            PiActionParamId.of("clone_id");
     public static final PiActionParamId VLAN_ID = PiActionParamId.of("vlan_id");
     public static final PiActionParamId LABEL = PiActionParamId.of("label");
     public static final PiActionParamId SRC_IP = PiActionParamId.of("src_ip");
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java
index bca8424..b7ee4c5 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java
@@ -42,7 +42,6 @@
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_POP;
 import static org.onosproject.pipelines.fabric.FabricUtils.instruction;
 import static org.onosproject.pipelines.fabric.FabricUtils.l2Instruction;
-import static org.onosproject.pipelines.fabric.FabricUtils.outputPort;
 
 /**
  * Treatment translation logic.
@@ -193,23 +192,12 @@
         if (isNoAction(treatment)) {
             return nop(tableId);
         }
+        treatmentException(
+                tableId, treatment,
+                "unsupported treatment");
 
-        final PortNumber outPort = outputPort(treatment);
-        if (outPort == null
-                || !outPort.equals(PortNumber.CONTROLLER)
-                || treatment.allInstructions().size() > 1) {
-            treatmentException(
-                    tableId, treatment,
-                    "supports only punt/clone to CPU actions");
-        }
-
-        final PiActionId actionId = treatment.clearedDeferred()
-                ? FabricConstants.FABRIC_INGRESS_ACL_PUNT_TO_CPU
-                : FabricConstants.FABRIC_INGRESS_ACL_CLONE_TO_CPU;
-
-        return PiAction.builder()
-                .withId(actionId)
-                .build();
+        // This function will never return null
+        return null;
     }
 
 
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingObjectiveTranslator.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingObjectiveTranslator.java
index 5647d26..066af7d 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingObjectiveTranslator.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingObjectiveTranslator.java
@@ -18,11 +18,14 @@
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowRule;
@@ -34,7 +37,14 @@
 import org.onosproject.net.flow.criteria.MplsCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.Objective;
 import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupKey;
 import org.onosproject.net.pi.model.PiActionId;
 import org.onosproject.net.pi.model.PiTableId;
 import org.onosproject.net.pi.runtime.PiAction;
@@ -49,6 +59,9 @@
 
 import static java.lang.String.format;
 import static org.onosproject.pipelines.fabric.FabricUtils.criterionNotNull;
+import static org.onosproject.pipelines.fabric.FabricUtils.outputPort;
+import static org.onosproject.net.group.DefaultGroupBucket.createCloneGroupBucket;
+
 
 /**
  * ObjectiveTranslator implementation ForwardingObjective.
@@ -56,6 +69,8 @@
 class ForwardingObjectiveTranslator
         extends AbstractObjectiveTranslator<ForwardingObjective> {
 
+    //FIXME: Max number supported by PI
+    static final int CLONE_TO_CPU_ID = 511;
     private static final List<String> DEFAULT_ROUTE_PREFIXES = Lists.newArrayList(
             "0.0.0.0/1", "128.0.0.0/1");
 
@@ -245,11 +260,64 @@
     private void aclRule(ForwardingObjective obj,
                          ObjectiveTranslation.Builder resultBuilder)
             throws FabricPipelinerException {
+        if (obj.nextId() == null && obj.treatment() != null) {
+            final TrafficTreatment treatment = obj.treatment();
+            final PortNumber outPort = outputPort(treatment);
+            if (outPort != null
+                    && outPort.equals(PortNumber.CONTROLLER)
+                    && treatment.allInstructions().size() == 1) {
 
+                final PiAction aclAction;
+                if (treatment.clearedDeferred()) {
+                    aclAction = PiAction.builder()
+                            .withId(FabricConstants.FABRIC_INGRESS_ACL_PUNT_TO_CPU)
+                            .build();
+                } else {
+                    // Action is SET_CLONE_SESSION_ID
+                    if (obj.op() == Objective.Operation.ADD) {
+                        // Action is ADD, create clone group
+                        final DefaultGroupDescription cloneGroup =
+                                createCloneGroup(obj.appId(),
+                                                 CLONE_TO_CPU_ID,
+                                                 outPort);
+                        resultBuilder.addGroup(cloneGroup);
+                    }
+                    aclAction = PiAction.builder()
+                            .withId(FabricConstants.FABRIC_INGRESS_ACL_SET_CLONE_SESSION_ID)
+                            .withParameter(new PiActionParam(
+                                    FabricConstants.CLONE_ID, CLONE_TO_CPU_ID))
+                            .build();
+                }
+                final TrafficTreatment piTreatment = DefaultTrafficTreatment.builder()
+                        .piTableAction(aclAction)
+                        .build();
+                resultBuilder.addFlowRule(flowRule(
+                        obj, FabricConstants.FABRIC_INGRESS_ACL_ACL, obj.selector(), piTreatment));
+                return;
+            }
+        }
         resultBuilder.addFlowRule(flowRule(
                 obj, FabricConstants.FABRIC_INGRESS_ACL_ACL, obj.selector()));
     }
 
+    private DefaultGroupDescription createCloneGroup(
+            ApplicationId appId,
+            int cloneSessionId,
+            PortNumber outPort) {
+        final GroupKey groupKey = new DefaultGroupKey(
+                FabricPipeliner.KRYO.serialize(cloneSessionId));
+
+        final List<GroupBucket> bucketList = ImmutableList.of(
+                createCloneGroupBucket(DefaultTrafficTreatment.builder()
+                                               .setOutput(outPort)
+                                               .build()));
+        final DefaultGroupDescription cloneGroup = new DefaultGroupDescription(
+                deviceId, GroupDescription.Type.CLONE,
+                new GroupBuckets(bucketList),
+                groupKey, cloneSessionId, appId);
+        return cloneGroup;
+    }
+
     private FlowRule flowRule(
             ForwardingObjective obj, PiTableId tableId, TrafficSelector selector)
             throws FabricPipelinerException {
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/NextObjectiveTranslator.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/NextObjectiveTranslator.java
index 2c2fd77..ec1c64b 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/NextObjectiveTranslator.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/NextObjectiveTranslator.java
@@ -436,15 +436,6 @@
                 .map(p -> DefaultTrafficTreatment.builder().setOutput(p).build())
                 .map(DefaultGroupBucket::createAllGroupBucket)
                 .collect(Collectors.toList());
-        // FIXME: remove once support for clone sessions is available
-        // Right now we add a CPU port to all multicast groups. The egress
-        // pipeline is expected to drop replicated packets to the CPU if a clone
-        // was  not requested in the ingress pipeline.
-        bucketList.add(
-                DefaultGroupBucket.createAllGroupBucket(
-                        DefaultTrafficTreatment.builder()
-                                .setOutput(PortNumber.CONTROLLER)
-                                .build()));
 
         final int groupId = obj.id();
         // Use DefaultGroupKey instead of PiGroupKey as we don't have any