[ONOS-7137] Interfaces Discovery through gNMI

Also, bumped version of gNMI proto as supported by PI

Change-Id: If2903b2dc483f545893daae77f993cc7dadee468
diff --git a/drivers/bmv2/BUCK b/drivers/bmv2/BUCK
index 63feb68..728f92b 100644
--- a/drivers/bmv2/BUCK
+++ b/drivers/bmv2/BUCK
@@ -28,6 +28,7 @@
     included_bundles = BUNDLES,
     required_apps = [
         'org.onosproject.drivers.p4runtime',
+        'org.onosproject.drivers.gnmi',
         'org.onosproject.pipelines.basic',
     ],
 )
diff --git a/drivers/bmv2/src/main/resources/bmv2-drivers.xml b/drivers/bmv2/src/main/resources/bmv2-drivers.xml
index 22bf067..0d6e0daa 100644
--- a/drivers/bmv2/src/main/resources/bmv2-drivers.xml
+++ b/drivers/bmv2/src/main/resources/bmv2-drivers.xml
@@ -15,7 +15,7 @@
   ~ limitations under the License.
   -->
 <drivers>
-    <driver name="bmv2" manufacturer="p4.org" hwVersion="master" swVersion="master" extends="p4runtime">
+    <driver name="bmv2" manufacturer="p4.org" hwVersion="master" swVersion="master" extends="p4runtime, gnmi">
         <behaviour api="org.onosproject.net.behaviour.PiPipelineProgrammable"
                    impl="org.onosproject.drivers.bmv2.Bmv2PipelineProgrammable"/>
     </driver>
diff --git a/drivers/gnmi/BUCK b/drivers/gnmi/BUCK
new file mode 100644
index 0000000..0fe379e
--- /dev/null
+++ b/drivers/gnmi/BUCK
@@ -0,0 +1,38 @@
+PROTOBUF_VER = '3.2.0'
+GRPC_VER = '1.3.1'
+
+COMPILE_DEPS = [
+    '//lib:CORE_DEPS',
+    '//lib:KRYO',
+    '//protocols/grpc/api:onos-protocols-grpc-api',
+    '//protocols/grpc/proto:onos-protocols-grpc-proto',
+    '//incubator/grpc-dependencies:grpc-core-repkg-' + GRPC_VER,
+    '//lib:grpc-netty-' + GRPC_VER,
+    '//lib:protobuf-java-' + PROTOBUF_VER,
+    '//lib:grpc-stub-' + GRPC_VER,
+    '//core/store/serializers:onos-core-serializers',
+    '//protocols/gnmi/stub:onos-protocols-gnmi-stub',
+]
+
+BUNDLES = [
+    ':onos-drivers-gnmi',
+]
+
+osgi_jar (
+    deps = COMPILE_DEPS,
+)
+
+onos_app (
+    app_name = 'org.onosproject.drivers.gnmi',
+    title = 'gNMI Drivers',
+    category = 'Drivers',
+    url = 'http://onosproject.org',
+    description = 'Adds support for devices using gNMI protocol based on ' +
+    ' openconfig models: http://openconfig.net/ .',
+    included_bundles = BUNDLES,
+    required_apps = [
+        'org.onosproject.generaldeviceprovider',
+        'org.onosproject.protocols.grpc',
+        'org.onosproject.protocols.gnmi'
+    ],
+)
\ No newline at end of file
diff --git a/drivers/gnmi/features.xml b/drivers/gnmi/features.xml
new file mode 100644
index 0000000..448f07a
--- /dev/null
+++ b/drivers/gnmi/features.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  ~ Copyright 2017-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.
+  -->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
+    <feature name="${project.artifactId}" version="${project.version}"
+             description="${project.description}">
+        <feature>onos-api</feature>
+        <bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
+
+    </feature>
+</features>
diff --git a/drivers/gnmi/pom.xml b/drivers/gnmi/pom.xml
new file mode 100644
index 0000000..ae046d6
--- /dev/null
+++ b/drivers/gnmi/pom.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2017-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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>onos-drivers-general</artifactId>
+        <groupId>org.onosproject</groupId>
+        <version>1.11.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>onos-drivers-gnmi</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>gNMI device drivers</description>
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-grpc-protocol-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-netty</artifactId>
+            <version>1.3.1</version>
+        </dependency>
+
+        <!-- protocols/gnmi/api missing -->
+
+    </dependencies>
+
+    <properties>
+        <onos.app.name>org.onosproject.drivers.gnmi</onos.app.name>
+        <onos.app.origin>ON.Lab</onos.app.origin>
+        <onos.app.title>gNMI Device Drivers</onos.app.title>
+        <onos.app.category>Drivers</onos.app.category>
+        <onos.app.url>http://onosproject.org</onos.app.url>
+        <onos.app.requires>
+            org.onosproject.generaldeviceprovider
+        </onos.app.requires>
+    </properties>
+
+</project>
\ No newline at end of file
diff --git a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDeviceDescriptionDiscovery.java b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDeviceDescriptionDiscovery.java
new file mode 100644
index 0000000..04c9f01
--- /dev/null
+++ b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDeviceDescriptionDiscovery.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright 2017-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 com.google.common.collect.ImmutableList;
+import gnmi.gNMIGrpc;
+import io.grpc.ManagedChannel;
+import io.grpc.ManagedChannelBuilder;
+import io.grpc.Status;
+import io.grpc.StatusRuntimeException;
+import io.grpc.internal.DnsNameResolverProvider;
+import io.grpc.netty.NettyChannelBuilder;
+import io.grpc.stub.StreamObserver;
+import org.onosproject.grpc.api.GrpcChannelId;
+import org.onosproject.grpc.api.GrpcController;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DefaultPortDescription;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceDescriptionDiscovery;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static gnmi.Gnmi.Path;
+import static gnmi.Gnmi.PathElem;
+import static gnmi.Gnmi.SubscribeRequest;
+import static gnmi.Gnmi.SubscribeResponse;
+import static gnmi.Gnmi.Subscription;
+import static gnmi.Gnmi.SubscriptionList;
+import static gnmi.Gnmi.Update;
+
+/**
+ * Class that discovers the device description and ports of a device that
+ * supports the gNMI protocol and Openconfig models.
+ */
+public class GnmiDeviceDescriptionDiscovery
+        extends AbstractHandlerBehaviour
+        implements DeviceDescriptionDiscovery {
+
+    private static final int REQUEST_TIMEOUT_SECONDS = 5;
+
+    private static final Logger log = LoggerFactory
+            .getLogger(GnmiDeviceDescriptionDiscovery.class);
+
+    private static final String GNMI_SERVER_ADDR_KEY = "gnmi_ip";
+    private static final String GNMI_SERVER_PORT_KEY = "gnmi_port";
+
+    @Override
+    public DeviceDescription discoverDeviceDetails() {
+        return null;
+    }
+
+    @Override
+    public List<PortDescription> discoverPortDetails() {
+        log.info("Discovering port details on device {}", handler().data().deviceId());
+
+        // Get the channel
+        ManagedChannel channel = getChannel();
+
+        if (channel == null) {
+            return ImmutableList.of();
+        }
+
+        // Build the subscribe request
+        SubscribeRequest request = subscribeRequest();
+
+        // New stub
+        gNMIGrpc.gNMIStub gnmiStub = gNMIGrpc.newStub(channel);
+
+        final CompletableFuture<List<PortDescription>>
+                reply = new CompletableFuture<>();
+
+        // Subscribe to the replies
+        StreamObserver<SubscribeRequest> subscribeRequest = gnmiStub
+                .subscribe(new SubscribeResponseObserver(reply));
+        log.debug("Interfaces request {}", request);
+
+        List<PortDescription> ports;
+        try {
+            // Issue the request
+            subscribeRequest.onNext(request);
+            ports = reply.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        } catch (InterruptedException | ExecutionException | TimeoutException
+                | StatusRuntimeException e) {
+            log.warn("Unable to discover ports from {}: {}",
+                     data().deviceId(), e.getMessage());
+            log.debug("{}", e);
+            return ImmutableList.of();
+        } finally {
+            subscribeRequest.onCompleted();
+        }
+
+        return ports;
+    }
+
+    /**
+     * Obtains the ManagedChannel to be used for the communication.
+     *
+     * @return the managed channel
+     */
+    private ManagedChannel getChannel() {
+
+        DeviceId deviceId = handler().data().deviceId();
+        String serverAddr = this.data().value(GNMI_SERVER_ADDR_KEY);
+        String serverPortString = this.data().value(GNMI_SERVER_PORT_KEY);
+
+        GrpcController controller = handler().get(GrpcController.class);
+        ManagedChannel channel = null;
+
+        //FIXME can be optimized
+        //getting a channel if exists.
+        ManagedChannel managedChannel = controller
+                .getChannels(handler().data().deviceId()).stream().filter(c -> {
+                    String[] authority = c.authority().split(":");
+                    String host = authority[0];
+                    String port = authority[1];
+                    return host.equals(serverAddr) && port.equals(serverPortString);
+                }).findAny().orElse(null);
+
+        if (managedChannel != null) {
+            log.debug("Reusing Channel");
+            channel = managedChannel;
+        } else {
+            log.debug("Creating Channel");
+            GrpcChannelId newChannelId = GrpcChannelId.of(deviceId, "gnmi");
+
+            ManagedChannelBuilder channelBuilder = NettyChannelBuilder
+                    .forAddress(serverAddr, Integer.valueOf(serverPortString))
+                    .usePlaintext(true)
+                    .nameResolverFactory(new DnsNameResolverProvider());
+
+            try {
+                channel = controller.connectChannel(newChannelId, channelBuilder);
+            } catch (IOException e) {
+                log.warn("Unable to connect to gRPC server of {}: {}",
+                         deviceId, e.getMessage());
+            }
+        }
+        return channel;
+    }
+
+    /**
+     * Creates the subscribe request for the interfaces.
+     *
+     * @return subscribe request
+     */
+    private SubscribeRequest subscribeRequest() {
+        Path path = Path.newBuilder()
+                .addElem(PathElem.newBuilder().setName("interfaces").build())
+                .addElem(PathElem.newBuilder().setName("interface").build())
+                .addElem(PathElem.newBuilder().setName("...").build())
+                .build();
+        Subscription subscription = Subscription.newBuilder().setPath(path).build();
+        SubscriptionList list = SubscriptionList.newBuilder().setMode(SubscriptionList.Mode.ONCE)
+                .addSubscription(subscription).build();
+        return SubscribeRequest.newBuilder().setSubscribe(list).build();
+    }
+
+    /**
+     * Handles messages received from the device on the stream channel.
+     */
+    private final class SubscribeResponseObserver
+            implements StreamObserver<SubscribeResponse> {
+
+        private final CompletableFuture<List<PortDescription>> reply;
+
+        private SubscribeResponseObserver(CompletableFuture<List<PortDescription>> reply) {
+            this.reply = reply;
+        }
+
+        @Override
+        public void onNext(SubscribeResponse message) {
+            Map<String, DefaultPortDescription.Builder> ports = new HashMap<>();
+            Map<String, DefaultAnnotations.Builder> portsAnnotations = new HashMap<>();
+            log.debug("Response {} ", message.getUpdate().toString());
+            message.getUpdate().getUpdateList().forEach(update -> {
+                parseUpdate(ports, portsAnnotations, update);
+            });
+
+            List<PortDescription> portDescriptionList = new ArrayList<>();
+            ports.forEach((k, v) -> {
+//                v.portSpeed(1000L);
+                v.type(Port.Type.COPPER);
+                v.annotations(portsAnnotations.get(k).set("name", k).build());
+                portDescriptionList.add(v.build());
+            });
+
+            reply.complete(portDescriptionList);
+        }
+
+
+        @Override
+        public void onError(Throwable throwable) {
+            log.warn("Error on stream channel for {}: {}",
+                     data().deviceId(), Status.fromThrowable(throwable));
+            log.debug("{}", throwable);
+        }
+
+        @Override
+        public void onCompleted() {
+            log.debug("SubscribeResponseObserver completed");
+        }
+    }
+
+    /**
+     * Parses the update received from the device.
+     *
+     * @param ports            the ports description to build
+     * @param portsAnnotations the ports annotations list to populate
+     * @param update           the update received
+     */
+    private void parseUpdate(Map<String, DefaultPortDescription.Builder> ports,
+                             Map<String, DefaultAnnotations.Builder> portsAnnotations,
+                             Update update) {
+
+        //FIXME crude parsing, can be done via object (de)serialization
+        if (update.getPath().getElemList().size() > 3) {
+            String name = update.getPath().getElem(3).getName();
+            String portId = update.getPath().getElem(1).getKeyMap().get("name");
+            if (!ports.containsKey(portId)) {
+                int number = Character.getNumericValue(portId.charAt(portId.length() - 1));
+                PortNumber portNumber = PortNumber.portNumber(number, portId);
+                ports.put(portId, DefaultPortDescription.builder()
+                        .withPortNumer(portNumber));
+            }
+            if (name.equals("enabled")) {
+                DefaultPortDescription.Builder builder = ports.get(portId);
+                builder = builder.isEnabled(update.getVal().getBoolVal());
+                ports.put(portId, builder);
+            } else if (name.equals("state")) {
+                String speedName = update.getPath().getElem(4).getName();
+                if (speedName.equals("negotiated-port-speed")) {
+                    DefaultPortDescription.Builder builder = ports.get(portId);
+                    long speed = parsePortSpeed(update.getVal().getStringVal());
+                    builder = builder.portSpeed(speed);
+                    ports.put(portId, builder);
+                }
+            } else if (!name.equals("ifindex")) {
+                if (!portsAnnotations.containsKey(portId)) {
+                    portsAnnotations.put(portId, DefaultAnnotations.builder()
+                            .set(name, update.getVal().toByteString()
+                                    .toString(Charset.defaultCharset()).trim()));
+                } else {
+                    DefaultAnnotations.Builder builder = portsAnnotations.get(portId);
+                    builder = builder.set(name, update.getVal().toByteString().
+                            toString(Charset.defaultCharset()).trim());
+                    portsAnnotations.put(portId, builder);
+                }
+            }
+        }
+    }
+
+    private long parsePortSpeed(String speed) {
+        log.debug("Speed from config {}", speed);
+        switch (speed) {
+            case "SPEED_10MB":
+                return 10;
+            case "SPEED_100MB":
+                return 10;
+            case "SPEED_1GB":
+                return 1000;
+            case "SPEED_10GB":
+                return 10000;
+            case "SPEED_25GB":
+                return 25000;
+            case "SPEED_40GB":
+                return 40000;
+            case "SPEED_50GB":
+                return 50000;
+            case "SPEED_100GB":
+                return 100000;
+            default:
+                return 1000;
+        }
+    }
+}
diff --git a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDriversLoader.java b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDriversLoader.java
new file mode 100644
index 0000000..689a53b
--- /dev/null
+++ b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDriversLoader.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017-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 org.apache.felix.scr.annotations.Component;
+import org.onosproject.net.driver.AbstractDriverLoader;
+
+/**
+ * Loader for gNMI device drivers.
+ */
+@Component(immediate = true)
+public class GnmiDriversLoader extends AbstractDriverLoader {
+
+    public GnmiDriversLoader() {
+        super("/gnmi-drivers.xml");
+    }
+
+    @Override
+    public void activate() {
+        super.activate();
+    }
+}
diff --git a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/package-info.java b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/package-info.java
new file mode 100644
index 0000000..832ad5a
--- /dev/null
+++ b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-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 for gnmi device drivers.
+ */
+package org.onosproject.drivers.gnmi;
\ No newline at end of file
diff --git a/drivers/gnmi/src/main/resources/gnmi-drivers.xml b/drivers/gnmi/src/main/resources/gnmi-drivers.xml
new file mode 100644
index 0000000..3744781
--- /dev/null
+++ b/drivers/gnmi/src/main/resources/gnmi-drivers.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2017-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.
+  -->
+<drivers>
+    <driver name="gnmi" manufacturer="gnmi" hwVersion="master" swVersion="master">
+        <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
+                   impl="org.onosproject.drivers.gnmi.GnmiDeviceDescriptionDiscovery"/>
+    </driver>
+</drivers>
+
diff --git a/modules.defs b/modules.defs
index 2ae8cfd..b090666 100644
--- a/modules.defs
+++ b/modules.defs
@@ -108,6 +108,7 @@
     '//drivers/barefoot:onos-drivers-barefoot-oar',
     '//drivers/hp:onos-drivers-hp-oar',
     '//drivers/p4runtime:onos-drivers-p4runtime-oar',
+    '//drivers/gnmi:onos-drivers-gnmi-oar',
     '//drivers/polatis/netconf:onos-drivers-polatis-netconf-oar',
 ]
 
diff --git a/protocols/gnmi/stub/src/main/proto/COMMIT_ID b/protocols/gnmi/stub/src/main/proto/COMMIT_ID
index 0ac43f2..acf773d 100644
--- a/protocols/gnmi/stub/src/main/proto/COMMIT_ID
+++ b/protocols/gnmi/stub/src/main/proto/COMMIT_ID
@@ -1,2 +1,2 @@
 https://github.com/openconfig/gnmi/blob/master/proto/gnmi/gnmi.proto
-8a14ac0e9ed67e08988f9913243d89f398454824
+9c8d9e965b3e854107ea02c12ab11b70717456f2
diff --git a/protocols/gnmi/stub/src/main/proto/gnmi.proto b/protocols/gnmi/stub/src/main/proto/gnmi.proto
index d497dc0..1f3bb7c 100644
--- a/protocols/gnmi/stub/src/main/proto/gnmi.proto
+++ b/protocols/gnmi/stub/src/main/proto/gnmi.proto
@@ -25,7 +25,7 @@
 // tree supported by a device ("target").
 //
 // This document references the gNMI Specification which can be found at
-// http://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi.md
+// http://github.com/openconfig/reference/blob/master/rpc/gnmi
 package gnmi;
 
 // Define a protobuf FileOption that defines the gNMI service version.
@@ -36,7 +36,7 @@
 
 // gNMI_service is the current version of the gNMI service, returned through
 // the Capabilities RPC.
-option (gnmi_service) = "0.4.0";
+option (gnmi_service) = "0.5.0";
 
 service gNMI {
     // Capabilities allows the client to retrieve the set of capabilities that
@@ -90,6 +90,7 @@
     Path path = 1;                      // The path (key) for the update.
     Value value = 2 [deprecated=true];  // The value (value) for the update.
     TypedValue val = 3;                 // The explicitly typed update value.
+    uint32 duplicates = 4;              // Number of coalesced duplicates.
 }
 
 // TypedValue is used to encode a value being sent between the client and
@@ -126,6 +127,8 @@
     repeated string element = 1 [deprecated=true];
     string origin = 2;                              // Label to disambiguate path.
     repeated PathElem elem = 3;                     // Elements of the path.
+    string target = 4;                              // The name of the target
+    // (Sec. 2.2.2.1)
 }
 
 // PathElem encodes an element of a gNMI path, along ith any attributes (keys)
@@ -173,7 +176,7 @@
 // is expressed as a set of digits with the precision specifying the
 // number of digits following the decimal point in the digit set.
 message Decimal64 {
-    uint64 digits = 1;        // Set of digits.
+    int64 digits = 1;         // Set of digits.
     uint32 precision = 2;     // Number of digits following the decimal point.
 }
 
@@ -250,6 +253,12 @@
     // The encoding that the target should use within the Notifications generated
     // corresponding to the SubscriptionList.
     Encoding encoding = 8;
+    // An optional field to specify that only updates to current state should be
+    // sent to a client. If set, the initial state is not sent to the client but
+    // rather only the sync message followed by any subsequent updates to the
+    // current state. For ONCE and POLL modes, this causes the server to send only
+    // the sync message (Sec. 3.5.2.3).
+    bool updates_only = 9;
 }
 
 // Subscription is a single request within a SubscriptionList. The path
diff --git a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
index f309497..c439094 100644
--- a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
+++ b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
@@ -441,15 +441,17 @@
                     //Empty list of ports
                     List<PortDescription> ports = new ArrayList<>();
 
-                    if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
-                        DeviceDescriptionDiscovery deviceDiscovery = driver
-                                .createBehaviour(driverData, DeviceDescriptionDiscovery.class);
-
+                    DeviceDescriptionDiscovery deviceDiscovery = getBehaviour(driver,
+                            DeviceDescriptionDiscovery.class, driverData);
+                    if (deviceDiscovery != null) {
                         DeviceDescription newdescription = deviceDiscovery.discoverDeviceDetails();
                         if (newdescription != null) {
                             description = newdescription;
                         }
                         ports = deviceDiscovery.discoverPortDetails();
+                    } else {
+                        log.info("No Device Description Discovery for device {}, no update for " +
+                                "description or ports.", deviceId);
                     }
 
                     if (!handlePipeconf(deviceId, driver, driverData, true)) {
diff --git a/tools/dev/mininet/bmv2.py b/tools/dev/mininet/bmv2.py
index cdc26e1..87ed1ba 100644
--- a/tools/dev/mininet/bmv2.py
+++ b/tools/dev/mininet/bmv2.py
@@ -83,7 +83,7 @@
     def __init__(self, name, json=None, debugger=False, loglevel="warn",
                  elogger=False, grpcPort=None, cpuPort=255,
                  thriftPort=None, netcfg=True, dryrun=False, pipeconfId="",
-                 pktdump=False, valgrind=False, **kwargs):
+                 pktdump=False, valgrind=False, injectPorts=False, **kwargs):
         Switch.__init__(self, name, **kwargs)
         self.grpcPort = pickUnusedPort() if not grpcPort else grpcPort
         self.thriftPort = pickUnusedPort() if not thriftPort else thriftPort
@@ -107,6 +107,7 @@
         self.valgrind = parseBoolean(valgrind)
         self.netcfgfile = '/tmp/bmv2-%d-netcfg.json' % self.deviceId
         self.pipeconfId = pipeconfId
+        self.injectPorts = parseBoolean(injectPorts)
         self.longitude = kwargs['longitude'] if 'longitude' in kwargs else None
         self.latitude = kwargs['latitude'] if 'latitude' in kwargs else None
         self.onosDeviceId = "device:bmv2:%d" % self.deviceId
@@ -130,20 +131,6 @@
         return r.group(1) if r else None
 
     def getDeviceConfig(self, srcIP):
-        portData = {}
-        portId = 1
-        for intfName in self.intfNames():
-            if intfName == 'lo':
-                continue
-            portData[str(portId)] = {
-                "number": portId,
-                "name": intfName,
-                "enabled": True,
-                "removed": False,
-                "type": "copper",
-                "speed": 10000
-            }
-            portId += 1
 
         basicCfg = {
             "driver": "bmv2"
@@ -160,15 +147,36 @@
                     "port": self.grpcPort,
                     "deviceId": self.deviceId,
                     "deviceKeyId": "p4runtime:%s" % self.onosDeviceId
+                },
+                "gnmi": {
+                    "ip": srcIP,
+                    "port": self.grpcPort
                 }
             },
             "piPipeconf": {
                 "piPipeconfId": self.pipeconfId
             },
-            "basic": basicCfg,
-            "ports": portData
+            "basic": basicCfg
         }
 
+        if(self.injectPorts):
+            portData = {}
+            portId = 1
+            for intfName in self.intfNames():
+                if intfName == 'lo':
+                    continue
+                portData[str(portId)] = {
+                    "number": portId,
+                    "name": intfName,
+                    "enabled": True,
+                    "removed": False,
+                    "type": "copper",
+                    "speed": 10000
+                }
+                portId += 1
+
+            cfgData['ports'] = portData
+
         return cfgData
 
     def doOnosNetcfg(self, controllerIP):