Pi classes to support P4Runtime action profiles

+ modified default.p4 with ECMP capabilities (via action profiles)
+ sketched translation logic of ONOS groups (in Bmv2GroupProgrammable)
+ replaced existing instances of default.json/p4info with symlinks to
	p4src build directory (to avoid inconsistencies)

Change-Id: If82f0b8ce296c9b616415d99864d216b77645a87
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2GroupProgrammable.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2GroupProgrammable.java
new file mode 100644
index 0000000..0bca13b
--- /dev/null
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2GroupProgrammable.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.drivers.bmv2;
+
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupOperation;
+import org.onosproject.net.group.GroupOperations;
+import org.onosproject.net.group.GroupProgrammable;
+import org.onosproject.net.pi.model.PiPipelineInterpreter;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionGroup;
+import org.onosproject.net.pi.runtime.PiActionGroupId;
+import org.onosproject.net.pi.runtime.PiActionGroupMember;
+import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
+import org.onosproject.net.pi.runtime.PiTableId;
+
+import java.nio.ByteBuffer;
+
+public class Bmv2GroupProgrammable extends AbstractHandlerBehaviour implements GroupProgrammable {
+
+    /*
+    Work in progress.
+     */
+
+    private Device device;
+
+    /*
+    About action groups in P4runtime:
+    The type field is a place holder in p4runtime.proto right now, and we haven't defined it yet. You can assume all
+    the groups are "select" as per the OF spec. As a remainder, in the P4 terminology a member corresponds to an OF
+    bucket. Each member can also be used directly in the match table (kind of like an OF indirect group).
+     */
+
+    @Override
+    public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
+
+        for (GroupOperation groupOp : groupOps.operations()) {
+            switch (groupOp.opType()) {
+                case ADD:
+                    addGroup(deviceId, groupOp);
+                    break;
+                default:
+                    throw new UnsupportedOperationException();
+            }
+        }
+    }
+
+    private void addGroup(DeviceId deviceId, GroupOperation groupOp) {
+
+        // Most of this logic can go in a core service, e.g. PiGroupTranslationService
+
+        // From a P4Runtime perspective, we need first to insert members, then the group.
+
+        PiActionGroupId piActionGroupId = PiActionGroupId.of(groupOp.groupId().id());
+
+        PiActionGroup.Builder piActionGroupBuilder = PiActionGroup.builder()
+                .withId(piActionGroupId)
+                .withType(PiActionGroup.Type.SELECT);
+
+        if (groupOp.groupType() != GroupDescription.Type.SELECT) {
+            // log error
+        }
+
+        int bucketIdx = 0;
+        for (GroupBucket bucket : groupOp.buckets().buckets()) {
+            /*
+            Problem:
+            In P4Runtime action group members, i.e. action buckets, are associated to a numeric ID chosen
+            at member insertion time. This ID must be unique for the whole action profile (i.e. the group table in
+            OpenFlow). In ONOS, GroupBucket doesn't specify any ID.
+
+            Solutions:
+            - Change GroupBucket API to force application wanting to perform group operations to specify a member id.
+            - Maintain state to dynamically allocate/deallocate member IDs, e.g. in a dedicated service, or in a
+            P4Runtime Group Provider.
+
+            Hack:
+            Statically derive member ID by combining groupId and position of the bucket in the list.
+             */
+            int memberId = ByteBuffer.allocate(4)
+                    .putShort((short) (piActionGroupId.id() % 2 ^ 16))
+                    .putShort((short) (bucketIdx % 2 ^ 16))
+                    .getInt();
+
+            // Need an interpreter to map the bucket treatment to a PI action
+
+            if (!device.is(PiPipelineInterpreter.class)) {
+                // log error
+            }
+
+            PiPipelineInterpreter interpreter = device.as(PiPipelineInterpreter.class);
+
+            /*
+            Problem:
+            In P4Runtime, action profiles (i.e. group tables) are specific to one or more tables.
+            Mapping of treatments depends on the target table. How do we derive the target table from here?
+
+            Solution:
+            - Change GroupDescription to allow applications to specify a table where this group will be called from.
+
+            Hack:
+            Assume we support pipelines with only one action profile associated to only one table, i.e. derive the
+            table ID by looking at the P4Info.
+             */
+
+            PiTableId piTableId = PiTableId.of("derive from P4Info");
+
+
+            PiAction action = null;
+            try {
+                action = interpreter.mapTreatment(bucket.treatment(), piTableId);
+            } catch (PiPipelineInterpreter.PiInterpreterException e) {
+                // log error
+            }
+
+            PiActionGroupMember member = PiActionGroupMember.builder()
+                    .withId(PiActionGroupMemberId.of(memberId))
+                    .withAction(action)
+                    .withWeight(bucket.weight())
+                    .build();
+
+            piActionGroupBuilder.addMember(member);
+
+            // Use P4RuntimeClient to install member;
+            // TODO: implement P4RuntimeClient method.
+        }
+
+        PiActionGroup piActionGroup = piActionGroupBuilder.build();
+
+        // Use P4RuntimeClient to insert group.
+        // TODO: implement P4RuntimeClient method.
+    }
+}