ONOS-7251 ONOS-7264 Support for clone to CPU action in fabric.p4

Clone to CPU is available only for packets processed via multicast
groups. Can be changed in the future when implementation for clone
session APIs is available in PI and P4 targets.

Also:
- compile "fabric-full" profile and generate constants from it
- use interpreter to map logical ports to data plane port IDs

Change-Id: I7db30c08dcf69ed9c870748cce8a797bbd5d6f78
diff --git a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java
index a28c5f1..4414931 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java
@@ -17,6 +17,7 @@
 package org.onosproject.net.pi.model;
 
 import com.google.common.annotations.Beta;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.driver.HandlerBehaviour;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criterion;
@@ -35,8 +36,9 @@
 public interface PiPipelineInterpreter extends HandlerBehaviour {
 
     /**
-     * Returns a PI match field ID that is equivalent to the given criterion type, if present. If not present, it means
-     * that the given criterion type is not supported by this interpreter.
+     * Returns a PI match field ID that is equivalent to the given criterion
+     * type, if present. If not present, it means that the given criterion type
+     * is not supported by this interpreter.
      *
      * @param type criterion type
      * @return optional match field ID
@@ -44,8 +46,9 @@
     Optional<PiMatchFieldId> mapCriterionType(Criterion.Type type);
 
     /**
-     * Returns the criterion type that is equivalent to the given PI match field ID, if present. If not present, it
-     * means that the given match field is not supported by this interpreter.
+     * Returns the criterion type that is equivalent to the given PI match field
+     * ID, if present. If not present, it means that the given match field is
+     * not supported by this interpreter.
      *
      * @param fieldId match field ID
      * @return optional criterion type
@@ -53,9 +56,10 @@
     Optional<Criterion.Type> mapPiMatchFieldId(PiMatchFieldId fieldId);
 
     /**
-     * Returns a PI table ID equivalent to the given numeric table ID (as in {@link
-     * org.onosproject.net.flow.FlowRule#tableId()}). If not present, it means that the given integer table ID cannot be
-     * mapped to any table of the pipeline model.
+     * Returns a PI table ID equivalent to the given numeric table ID (as in
+     * {@link org.onosproject.net.flow.FlowRule#tableId()}). If not present, it
+     * means that the given integer table ID cannot be mapped to any table of
+     * the pipeline model.
      *
      * @param flowRuleTableId a numeric table ID
      * @return PI table ID
@@ -63,9 +67,10 @@
     Optional<PiTableId> mapFlowRuleTableId(int flowRuleTableId);
 
     /**
-     * Returns an integer table ID equivalent to the given PI table ID. If not present, it means that the given PI table
-     * ID cannot be mapped to any integer table ID, because such mapping would be meaningless or because such PI table
-     * ID is not defined by the pipeline model.
+     * Returns an integer table ID equivalent to the given PI table ID. If not
+     * present, it means that the given PI table ID cannot be mapped to any
+     * integer table ID, because such mapping would be meaningless or because
+     * such PI table ID is not defined by the pipeline model.
      *
      * @param piTableId PI table ID
      * @return numeric table ID
@@ -73,23 +78,26 @@
     Optional<Integer> mapPiTableId(PiTableId piTableId);
 
     /**
-     * Returns an action of a PI pipeline that is functionally equivalent to the given traffic treatment for the given
-     * table.
+     * Returns an action of a PI pipeline that is functionally equivalent to the
+     * given traffic treatment for the given table.
      *
      * @param treatment traffic treatment
      * @param piTableId PI table ID
      * @return action object
-     * @throws PiInterpreterException if the treatment cannot be mapped to any PI action
+     * @throws PiInterpreterException if the treatment cannot be mapped to any
+     *                                PI action
      */
     PiAction mapTreatment(TrafficTreatment treatment, PiTableId piTableId)
             throws PiInterpreterException;
 
     /**
-     * Returns a collection of PI packet operations equivalent to the given outbound packet instance.
+     * Returns a collection of PI packet operations equivalent to the given
+     * outbound packet instance.
      *
      * @param packet outbound packet
      * @return collection of PI packet operations
-     * @throws PiInterpreterException if the packet treatments cannot be executed by this pipeline
+     * @throws PiInterpreterException if the packet treatments cannot be
+     *                                executed by this pipeline
      */
     Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet)
             throws PiInterpreterException;
@@ -99,12 +107,25 @@
      *
      * @param packetOperation packet operation
      * @return inbound packet
-     * @throws PiInterpreterException if the packet operation cannot be mapped to an inbound packet
+     * @throws PiInterpreterException if the packet operation cannot be mapped
+     *                                to an inbound packet
      */
     InboundPacket mapInboundPacket(PiPacketOperation packetOperation)
             throws PiInterpreterException;
 
     /**
+     * Maps the given logical port number to the data plane port ID (integer)
+     * identifying the same port for this pipeconf, if such mapping is
+     * possible.
+     *
+     * @param port port number
+     * @return optional integer
+     */
+    default Optional<Integer> mapLogicalPortNumber(PortNumber port) {
+        return Optional.empty();
+    }
+
+    /**
      * Signals that an error was encountered while executing the interpreter.
      */
     @Beta
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiMulticastGroupTranslatorImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiMulticastGroupTranslatorImpl.java
index 3d524c7..7d48209 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiMulticastGroupTranslatorImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiMulticastGroupTranslatorImpl.java
@@ -18,11 +18,14 @@
 
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import org.onosproject.net.Device;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
 import org.onosproject.net.group.Group;
 import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiPipelineInterpreter;
 import org.onosproject.net.pi.runtime.PiMulticastGroupEntry;
 import org.onosproject.net.pi.runtime.PiPreReplica;
 import org.onosproject.net.pi.service.PiTranslationException;
@@ -30,6 +33,7 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -52,10 +56,12 @@
      * The passed group is expected to have type {@link GroupDescription.Type#ALL}.
      *
      * @param group    group
+     * @param pipeconf pipeconf
+     * @param device   device
      * @return PI PRE entry
      * @throws PiTranslationException if the group cannot be translated
      */
-    static PiMulticastGroupEntry translate(Group group)
+    static PiMulticastGroupEntry translate(Group group, PiPipeconf pipeconf, Device device)
             throws PiTranslationException {
 
         checkNotNull(group);
@@ -84,19 +90,24 @@
 
         return PiMulticastGroupEntry.builder()
                 .withGroupId(group.id().id())
-                .addReplicas(getReplicas(outInstructions))
+                .addReplicas(getReplicas(outInstructions, device))
                 .build();
     }
 
-    private static Set<PiPreReplica> getReplicas(Collection<OutputInstruction> instructions) {
+    private static Set<PiPreReplica> getReplicas(
+            Collection<OutputInstruction> instructions, Device device)
+            throws PiTranslationException {
         // Account for multiple replicas for the same port.
         final Map<PortNumber, Set<PiPreReplica>> replicaMap = Maps.newHashMap();
         final List<PortNumber> ports = instructions.stream()
                 .map(OutputInstruction::port)
                 .collect(Collectors.toList());
         for (PortNumber port : ports) {
-            replicaMap.putIfAbsent(port, Sets.newHashSet());
+            if (port.isLogical()) {
+                port = logicalToPipelineSpecific(port, device);
+            }
             // Use incremental instance IDs for replicas of the same port.
+            replicaMap.putIfAbsent(port, Sets.newHashSet());
             replicaMap.get(port).add(
                     new PiPreReplica(port, replicaMap.get(port).size() + 1));
         }
@@ -104,4 +115,20 @@
                 .flatMap(Collection::stream)
                 .collect(Collectors.toSet());
     }
+
+    private static PortNumber logicalToPipelineSpecific(
+            PortNumber logicalPort, Device device)
+            throws PiTranslationException {
+        if (!device.is(PiPipelineInterpreter.class)) {
+            throw new PiTranslationException(
+                    "missing interpreter, cannot map logical port " + logicalPort.toString());
+        }
+        final PiPipelineInterpreter interpreter = device.as(PiPipelineInterpreter.class);
+        Optional<Integer> mappedPort = interpreter.mapLogicalPortNumber(logicalPort);
+        if (!mappedPort.isPresent()) {
+            throw new PiTranslationException(
+                    "interpreter cannot map logical port " + logicalPort.toString());
+        }
+        return PortNumber.portNumber(mappedPort.get());
+    }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
index 1ee53cf..a28ab0a 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
@@ -165,7 +165,8 @@
         @Override
         public PiMulticastGroupEntry translate(Group original, PiPipeconf pipeconf)
                 throws PiTranslationException {
-            return PiMulticastGroupTranslatorImpl.translate(original);
+            return PiMulticastGroupTranslatorImpl.translate(
+                    original, pipeconf, getDevice(original.deviceId()));
         }
     }
 
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/PiMulticastGroupTranslatorImplTest.java b/core/net/src/test/java/org/onosproject/net/pi/impl/PiMulticastGroupTranslatorImplTest.java
index 475b7d8..e68c7d3 100644
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/PiMulticastGroupTranslatorImplTest.java
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/PiMulticastGroupTranslatorImplTest.java
@@ -111,9 +111,9 @@
     public void testTranslatePreGroups() throws Exception {
 
         PiMulticastGroupEntry multicastGroup = PiMulticastGroupTranslatorImpl
-                .translate(ALL_GROUP);
+                .translate(ALL_GROUP, null, null);
         PiMulticastGroupEntry multicastGroup2 = PiMulticastGroupTranslatorImpl
-                .translate(ALL_GROUP_2);
+                .translate(ALL_GROUP_2, null, null);
 
         new EqualsTester()
                 .addEqualityGroup(multicastGroup, PI_MULTICAST_GROUP)