Refactor channel and mastership handling in P4Runtime

This (big) change aims at solving the issue observed with mastership flapping
and device connection/disconnection with P4Runtime.

Channel handling is now based on the underlying gRPC channel state. Before,
channel events (open/close/error) were generated as a consequence of P4Runtime
StreamChannel events, making device availability dependent on mastership. Now
Stream Channel events only affect mastership (MASTER/STANDBY or NONE when the
SteamChannel RPC is not active).

Mastership handling has been refactored to generate P4Runtime election IDs that
are compatible with the mastership preference decided by the MastershipService.

GeneralDeviceProvider has been re-implemented to support in-order
device event processing and to reduce implementation complexity. Stats polling
has been moved to a separate component, and netcfg handling updated to only
depend on BasicDeviceConfig, augmented with a pipeconf field, and re-using the
managementAddress field to set the gRPC server endpoints (e.g.
grpc://myswitch.local:50051). Before it was depending on 3 different config
classes, making hard to detect changes.

Finally, this change affects some core interfaces:
- Adds a method to DeviceProvider and DeviceHandshaker to check for device
availability, making the meaning of availability device-specific. This is needed
in cases where the device manager needs to change the availability state of a
device (as in change #20842)
- Support device providers not capable of reconciling mastership role responses
with requests (like P4Runtime).
- Clarify the meaning of "connection" in the DeviceConnect behavior.
- Allows driver-based providers to check devices for reachability and
availability without probing the device via the network.

Change-Id: I7ff30d29f5d02ad938e3171536e54ae2916629a2
diff --git a/protocols/grpc/api/src/main/java/org/onosproject/grpc/api/GrpcClientController.java b/protocols/grpc/api/src/main/java/org/onosproject/grpc/api/GrpcClientController.java
index 3cdbcd1..b47d499 100644
--- a/protocols/grpc/api/src/main/java/org/onosproject/grpc/api/GrpcClientController.java
+++ b/protocols/grpc/api/src/main/java/org/onosproject/grpc/api/GrpcClientController.java
@@ -18,10 +18,11 @@
 
 import com.google.common.annotations.Beta;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceAgentListener;
+import org.onosproject.net.provider.ProviderId;
 
 /**
- * Abstraction of a gRPC controller which controls specific gRPC client {@link
- * C} with specific client key {@link K}.
+ * Abstraction of controller that manages gRPC clients.
  *
  * @param <K> the gRPC client key
  * @param <C> the gRPC client type
@@ -34,20 +35,27 @@
      * given information. As a result of this method, a client can be later
      * obtained by invoking {@link #getClient(DeviceId)}.
      * <p>
+     * Upon creation, a connection to the server is automatically started, which
+     * blocks execution. If the connection is successful, the client is created
+     * and this method returns true, otherwise (e.g., socket error) any state
+     * associated with this client is destroyed and returns false.
+     * <p>
      * Only one client can exist for the same device ID. Calls to this method
      * are idempotent fot the same client key, i.e. returns true if such client
-     * already exists but a new one is not created. If there exists a client
-     * with same device ID but different address and port, removes old one and
-     * recreate new one.
+     * already exists. Otherwise, if a client for the same device ID but
+     * different client key already exists, throws an exception.
      *
      * @param clientKey the client key
      * @return true if the client was created and the channel to the server is
      * open; false otherwise
+     * @throws IllegalArgumentException if a client for the same device ID but
+     *                                  different client key already exists.
      */
     boolean createClient(K clientKey);
 
     /**
-     * Retrieves the gRPC client to operate on the given device.
+     * Returns the gRPC client previously created for the given device, or null
+     * if such client does not exist.
      *
      * @param deviceId the device identifier
      * @return the gRPC client of the device if exists; null otherwise
@@ -55,24 +63,47 @@
     C getClient(DeviceId deviceId);
 
     /**
-     * Removes the gRPC client for the given device. If no client exists for the
-     * given device, the result is a no-op.
+     * Returns the gRPC client previously created for the given client key, or
+     * null if such client does not exist.
+     *
+     * @param clientKey client key
+     * @return the gRPC client of the device if exists; null otherwise
+     */
+    C getClient(K clientKey);
+
+    /**
+     * Removes the gRPC client for the given device and any gRPC channel state
+     * associated to it. If no client exists for the given device, the result is
+     * a no-op.
      *
      * @param deviceId the device identifier
      */
     void removeClient(DeviceId deviceId);
 
     /**
-     * Check reachability of the gRPC server running on the given device.
-     * Reachability can be tested only if a client is previously created using
-     * {@link #createClient(GrpcClientKey)}. Note that this only checks the
-     * reachability instead of checking service availability, different
-     * service-specific gRPC clients might check service availability in a
-     * different way.
+     * Similar to {@link #removeClient(DeviceId)} but uses the client key to
+     * identify the client to remove.
      *
-     * @param deviceId the device identifier
-     * @return true if client was created and is able to contact the gNMI
-     * server; false otherwise
+     * @param clientKey the client key
      */
-    boolean isReachable(DeviceId deviceId);
+    void removeClient(K clientKey);
+
+    /**
+     * Adds a listener for device agent events for the given provider.
+     *
+     * @param deviceId device identifier
+     * @param providerId provider ID
+     * @param listener the device agent listener
+     */
+    void addDeviceAgentListener(DeviceId deviceId, ProviderId providerId,
+                                DeviceAgentListener listener);
+
+    /**
+     * Removes the listener for device agent events that was previously
+     * registered for the given provider.
+     *
+     * @param deviceId   device identifier
+     * @param providerId the provider ID
+     */
+    void removeDeviceAgentListener(DeviceId deviceId, ProviderId providerId);
 }