Allow sharing the same gRPC channel between clients

This change introduces a refactoring of the gRPC protocol subsystem that
allows the creation of a gRPC chanel independently of the client, while
allowing multiple clients to share the same channel (e.g. as in Stratum
where we use 3 clients).

Moreover, we refactor the P4RuntimeClient API to support multiple
P4Runtime-internal device ID using the same client. While before the
client was associated to one of such ID.

Finally, we provide an abstract implementation for gRPC-based driver
behaviors, reducing code duplication in P4Runtime, gNMI and gNOI drivers.

Change-Id: I1a46352bbbef1e0d24042f169ae8ba580202944f
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeActionGroupProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeActionGroupProgrammable.java
index d14b917..612394f 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeActionGroupProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeActionGroupProgrammable.java
@@ -131,7 +131,8 @@
         }
 
         // Dump groups and members from device for all action profiles.
-        final P4RuntimeReadClient.ReadRequest request = client.read(pipeconf);
+        final P4RuntimeReadClient.ReadRequest request = client.read(
+                p4DeviceId, pipeconf);
         pipeconf.pipelineModel().actionProfiles()
                 .stream().map(PiActionProfileModel::id)
                 .forEach(id -> request.actionProfileGroups(id)
@@ -184,7 +185,7 @@
             log.warn("Cleaning up {} action profile groups and " +
                              "{} members on {}...",
                      groupHandlesToRemove.size(), memberHandlesToRemove.size(), deviceId);
-            client.write(pipeconf)
+            client.write(p4DeviceId, pipeconf)
                     .delete(groupHandlesToRemove)
                     .delete(memberHandlesToRemove)
                     .submit().whenComplete((r, ex) -> {
@@ -244,7 +245,8 @@
         // found on the device.
         if (!validateGroupMembers(piGroupFromStore, membersOnDevice)) {
             log.warn("Group on device {} refers to members that are different " +
-                             "than those found in translation store: {}", handle);
+                             "than those found in translation store: {}",
+                     deviceId, handle);
             return null;
         }
         if (mirrorEntry == null) {
@@ -308,7 +310,7 @@
         if (members == null) {
             return;
         }
-        final WriteRequest request = client.write(pipeconf);
+        final WriteRequest request = client.write(p4DeviceId, pipeconf);
         WRITE_LOCKS.get(deviceId).lock();
         try {
             if (operation == Operation.APPLY) {