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/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiPortAdminBehaviour.java b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiPortAdminBehaviour.java
index 4f050b0..666d5b1 100644
--- a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiPortAdminBehaviour.java
+++ b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiPortAdminBehaviour.java
@@ -18,6 +18,8 @@
 
 import gnmi.Gnmi;
 import org.onosproject.gnmi.api.GnmiClient;
+import org.onosproject.gnmi.api.GnmiController;
+import org.onosproject.grpc.utils.AbstractGrpcHandlerBehaviour;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.behaviour.PortAdmin;
 
@@ -29,11 +31,18 @@
  * Implementation of PortAdmin for gNMI devices with OpenConfig support.
  */
 public class OpenConfigGnmiPortAdminBehaviour
-        extends AbstractGnmiHandlerBehaviour
+        extends AbstractGrpcHandlerBehaviour<GnmiClient, GnmiController>
         implements PortAdmin {
 
+    public OpenConfigGnmiPortAdminBehaviour() {
+        super(GnmiController.class);
+    }
+
     @Override
     public CompletableFuture<Boolean> enable(PortNumber number) {
+        if (!setupBehaviour("enable()")) {
+            return completedFuture(false);
+        }
         doEnable(number, true);
         // Always returning true is OK assuming this is used only by the
         // GeneralDeviceProvider, which ignores the return value and instead
@@ -43,6 +52,9 @@
 
     @Override
     public CompletableFuture<Boolean> disable(PortNumber number) {
+        if (!setupBehaviour("disable()")) {
+            return completedFuture(false);
+        }
         doEnable(number, false);
         return completedFuture(true);
     }
@@ -58,11 +70,6 @@
                      portNumber, deviceId);
             return;
         }
-        final GnmiClient client = getClientByKey();
-        if (client == null) {
-            log.warn("Cannot update ports on {}, missing gNMI client", deviceId);
-            return;
-        }
         final Gnmi.Path path = Gnmi.Path.newBuilder()
                 .addElem(Gnmi.PathElem.newBuilder().setName("interfaces").build())
                 .addElem(Gnmi.PathElem.newBuilder().setName("interface")