Add support for enabling/disabling ports for gNMI devices

This change also includes:
- Refactoring of gNMI protocol+driver to take advantage of the recent
changes to the gRPC protocol subsystem (e.g. no more locking, start RPC
with timeouts, etc.).
- Fixed Stratum driver to work after GeneralDeviceProvider refactoring
- Updated bmv2.py to generate ChassisConfig for stratum_bmv2
- Fixed portstate command to use the same port name as in the store

Change-Id: I0dad3bc73e4b6d907b5cf6b7b9a2852943226be7
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
new file mode 100644
index 0000000..4f050b0
--- /dev/null
+++ b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiPortAdminBehaviour.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * 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.gnmi;
+
+import gnmi.Gnmi;
+import org.onosproject.gnmi.api.GnmiClient;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.PortAdmin;
+
+import java.util.concurrent.CompletableFuture;
+
+import static java.util.concurrent.CompletableFuture.completedFuture;
+
+/**
+ * Implementation of PortAdmin for gNMI devices with OpenConfig support.
+ */
+public class OpenConfigGnmiPortAdminBehaviour
+        extends AbstractGnmiHandlerBehaviour
+        implements PortAdmin {
+
+    @Override
+    public CompletableFuture<Boolean> enable(PortNumber number) {
+        doEnable(number, true);
+        // Always returning true is OK assuming this is used only by the
+        // GeneralDeviceProvider, which ignores the return value and instead
+        // waits for a gNMI Update over the Subscribe RPC.
+        return completedFuture(true);
+    }
+
+    @Override
+    public CompletableFuture<Boolean> disable(PortNumber number) {
+        doEnable(number, false);
+        return completedFuture(true);
+    }
+
+    @Override
+    public CompletableFuture<Boolean> isEnabled(PortNumber number) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    private void doEnable(PortNumber portNumber, boolean enabled) {
+        if (portNumber.isLogical()) {
+            log.warn("Cannot update port status for logical port {} on {}",
+                     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")
+                                 .putKey("name", portNumber.name()).build())
+                .addElem(Gnmi.PathElem.newBuilder().setName("config").build())
+                .addElem(Gnmi.PathElem.newBuilder().setName("enabled").build())
+                .build();
+        final Gnmi.TypedValue value = Gnmi.TypedValue.newBuilder()
+                .setBoolVal(enabled)
+                .build();
+        final Gnmi.SetRequest request = Gnmi.SetRequest.newBuilder()
+                .addUpdate(Gnmi.Update.newBuilder()
+                                   .setPath(path)
+                                   .setVal(value)
+                                   .build())
+                .build();
+
+        // Async submit request and forget about it. In case of errors, the
+        // client will log them. In case of success, we should receive a gNMI
+        // Update over the Subscribe RPC with the new oper status.
+        client.set(request);
+    }
+}