Add Stratum driver

Change-Id: I4d9e774f1a3b20eb1f32f5cc25993ba19ca34584
diff --git a/drivers/stratum/BUILD b/drivers/stratum/BUILD
new file mode 100644
index 0000000..bce83cb
--- /dev/null
+++ b/drivers/stratum/BUILD
@@ -0,0 +1,27 @@
+COMPILE_DEPS = CORE_DEPS + KRYO + JACKSON + [
+    "@io_grpc_grpc_java//core",
+    "//drivers/p4runtime:onos-drivers-p4runtime",
+    "//drivers/gnmi:onos-drivers-gnmi",
+    "//pipelines/basic:onos-pipelines-basic",
+]
+
+osgi_jar(
+    resources = glob(["src/main/resources/**"]),
+    resources_root = "src/main/resources",
+    deps = COMPILE_DEPS,
+)
+
+onos_app(
+    app_name = "org.onosproject.drivers.stratum",
+    category = "Drivers",
+    description = "Adds support for Stratum-based devices",
+    required_apps = [
+        "org.onosproject.generaldeviceprovider",
+        "org.onosproject.protocols.grpc",
+        "org.onosproject.drivers.gnmi",
+        "org.onosproject.drivers.p4runtime",
+        "org.onosproject.pipelines.basic",
+    ],
+    title = "Stratum Drivers",
+    url = "http://onosproject.org",
+)
diff --git a/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/StratumDriversLoader.java b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/StratumDriversLoader.java
new file mode 100644
index 0000000..d022dd4
--- /dev/null
+++ b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/StratumDriversLoader.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018-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.stratum;
+
+import org.apache.felix.scr.annotations.Component;
+import org.onosproject.net.driver.AbstractDriverLoader;
+
+/**
+ * Loader for Stratum Switch device drivers.
+ */
+@Component(immediate = true)
+public class StratumDriversLoader extends AbstractDriverLoader {
+
+    public StratumDriversLoader() {
+        super("/stratum-drivers.xml");
+    }
+}
diff --git a/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/StratumHandshaker.java b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/StratumHandshaker.java
new file mode 100644
index 0000000..1d414cc
--- /dev/null
+++ b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/StratumHandshaker.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2018-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.stratum;
+
+import io.grpc.StatusRuntimeException;
+import org.onosproject.drivers.gnmi.GnmiHandshaker;
+import org.onosproject.drivers.p4runtime.P4RuntimeHandshaker;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.device.DeviceAgentListener;
+import org.onosproject.net.device.DeviceHandshaker;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.driver.DriverData;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.provider.ProviderId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Implementation of DeviceHandshaker for Stratum device.
+ */
+public class StratumHandshaker extends AbstractHandlerBehaviour implements DeviceHandshaker {
+    private static final Logger log = LoggerFactory.getLogger(StratumHandshaker.class);
+    private static final int DEFAULT_DEVICE_REQ_TIMEOUT = 60;
+    private P4RuntimeHandshaker p4RuntimeHandshaker;
+    private GnmiHandshaker gnmiHandshaker;
+    private DeviceId deviceId;
+
+    public StratumHandshaker() {
+        p4RuntimeHandshaker = new P4RuntimeHandshaker();
+        gnmiHandshaker = new GnmiHandshaker();
+    }
+
+    @Override
+    public void setHandler(DriverHandler handler) {
+        super.setHandler(handler);
+        p4RuntimeHandshaker.setHandler(handler);
+        gnmiHandshaker.setHandler(handler);
+    }
+
+    @Override
+    public void setData(DriverData data) {
+        super.setData(data);
+        p4RuntimeHandshaker.setData(data);
+        gnmiHandshaker.setData(data);
+        deviceId = data.deviceId();
+    }
+
+    @Override
+    public CompletableFuture<Boolean> isReachable() {
+        return p4RuntimeHandshaker.isReachable()
+                .thenCombine(gnmiHandshaker.isReachable(), Boolean::logicalAnd);
+    }
+
+    @Override
+    public void roleChanged(MastershipRole newRole) {
+        p4RuntimeHandshaker.roleChanged(newRole);
+        // gNMI doesn't support mastership handling now.
+    }
+
+    @Override
+    public MastershipRole getRole() {
+        return p4RuntimeHandshaker.getRole();
+        // gNMI doesn't support mastership handling now.
+    }
+
+    @Override
+    public void addDeviceAgentListener(ProviderId providerId, DeviceAgentListener listener) {
+        p4RuntimeHandshaker.addDeviceAgentListener(providerId, listener);
+        // TODO: Support gNMI device events
+    }
+
+    @Override
+    public void removeDeviceAgentListener(ProviderId providerId) {
+        p4RuntimeHandshaker.removeDeviceAgentListener(providerId);
+        // TODO: Support gNMI device events
+    }
+
+    @Override
+    public CompletableFuture<Boolean> connect() {
+        return p4RuntimeHandshaker.connect()
+                .thenCombine(gnmiHandshaker.connect(), Boolean::logicalAnd);
+    }
+
+    @Override
+    public boolean isConnected() {
+        CompletableFuture<Boolean> p4runtimeConnected =
+                CompletableFuture.supplyAsync(p4RuntimeHandshaker::isConnected);
+        CompletableFuture<Boolean> gnmiConnected =
+                CompletableFuture.supplyAsync(gnmiHandshaker::isConnected);
+
+        try {
+            return p4runtimeConnected
+                    .thenCombine(gnmiConnected, Boolean::logicalAnd)
+                    .get(DEFAULT_DEVICE_REQ_TIMEOUT, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            log.error("Exception while checking connectivity on {}", deviceId);
+        } catch (ExecutionException e) {
+            final Throwable cause = e.getCause();
+            if (cause instanceof StatusRuntimeException) {
+                final StatusRuntimeException grpcError = (StatusRuntimeException) cause;
+                log.warn("Error while checking connectivity on {}: {}", deviceId, grpcError.getMessage());
+            } else {
+                log.error("Exception while checking connectivity on {}", deviceId, e.getCause());
+            }
+        } catch (TimeoutException e) {
+            log.error("Operation TIMEOUT while checking connectivity on {}", deviceId);
+        }
+        return false;
+    }
+
+    @Override
+    public CompletableFuture<Boolean> disconnect() {
+        return p4RuntimeHandshaker.disconnect()
+                .thenCombine(gnmiHandshaker.disconnect(), Boolean::logicalAnd);
+    }
+}
diff --git a/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/dummy/StratumDummyPipelineProgrammable.java b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/dummy/StratumDummyPipelineProgrammable.java
new file mode 100644
index 0000000..e07e3b3
--- /dev/null
+++ b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/dummy/StratumDummyPipelineProgrammable.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018-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.stratum.dummy;
+
+import org.onosproject.drivers.p4runtime.AbstractP4RuntimePipelineProgrammable;
+import org.onosproject.net.behaviour.PiPipelineProgrammable;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.pipelines.basic.PipeconfLoader;
+
+import java.nio.ByteBuffer;
+import java.util.Optional;
+
+/**
+ * Implementation of the PiPipelineProgrammable behavior for Dummy Stratum Switch.
+ */
+public class StratumDummyPipelineProgrammable
+        extends AbstractP4RuntimePipelineProgrammable
+        implements PiPipelineProgrammable {
+
+    @Override
+    public ByteBuffer createDeviceDataBuffer(PiPipeconf pipeconf) {
+        // No pipeline binary needed
+        return ByteBuffer.allocate(1);
+    }
+
+    @Override
+    public Optional<PiPipeconf> getDefaultPipeconf() {
+        return Optional.of(PipeconfLoader.BASIC_PIPECONF);
+    }
+}
diff --git a/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/dummy/package-info.java b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/dummy/package-info.java
new file mode 100644
index 0000000..7633706
--- /dev/null
+++ b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/dummy/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2018-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.
+ */
+
+/**
+ * Behaviours for Dummy Stratum Device.
+ */
+
+package org.onosproject.drivers.stratum.dummy;
diff --git a/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/package-info.java b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/package-info.java
new file mode 100644
index 0000000..145022a
--- /dev/null
+++ b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-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.
+ */
+
+/**
+ * Driver for Stratum devices.
+ */
+package org.onosproject.drivers.stratum;
diff --git a/drivers/stratum/src/main/resources/stratum-drivers.xml b/drivers/stratum/src/main/resources/stratum-drivers.xml
new file mode 100644
index 0000000..2ba79a3
--- /dev/null
+++ b/drivers/stratum/src/main/resources/stratum-drivers.xml
@@ -0,0 +1,30 @@
+<?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="stratum" manufacturer="Open Networking Foundation" hwVersion="master" swVersion="Stratum" extends="p4runtime,gnmi">
+        <behaviour api="org.onosproject.net.device.DeviceHandshaker"
+                   impl="org.onosproject.drivers.stratum.StratumHandshaker"/>
+    </driver>
+    <driver name="stratum-dummy" manufacturer="Open Networking Foundation" hwVersion="Dummy" swVersion="Stratum" extends="stratum">
+        <behaviour api="org.onosproject.net.behaviour.PiPipelineProgrammable"
+                   impl="org.onosproject.drivers.stratum.dummy.StratumDummyPipelineProgrammable"/>
+
+        <property name="tableReadFromMirror">true</property>
+        <property name="actionGroupReadFromMirror">true</property>
+    </driver>
+</drivers>
+
diff --git a/modules.bzl b/modules.bzl
index c1b22eb..837ecd0 100644
--- a/modules.bzl
+++ b/modules.bzl
@@ -113,6 +113,7 @@
     "//drivers/polatis/netconf:onos-drivers-polatis-netconf-oar",
     "//drivers/polatis/openflow:onos-drivers-polatis-openflow-oar",
     "//drivers/odtn-driver:onos-drivers-odtn-driver-oar",
+    "//drivers/stratum:onos-drivers-stratum-oar",
 ]
 
 ONOS_PROVIDERS = [
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 e5c321e..3782871 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
@@ -499,13 +499,16 @@
         // Get one from driver or forge.
         final DeviceDescriptionDiscovery deviceDiscovery = getBehaviour(
                 deviceId, DeviceDescriptionDiscovery.class);
-        if (deviceDiscovery != null) {
-            // Enforce defaultAvailable flag over the one obtained from driver.
-            final DeviceDescription d = deviceDiscovery.discoverDeviceDetails();
-            return new DefaultDeviceDescription(d, defaultAvailable, d.annotations());
-        } else {
+        if (deviceDiscovery == null) {
             return forgeDeviceDescription(deviceId, defaultAvailable);
         }
+
+        final DeviceDescription d = deviceDiscovery.discoverDeviceDetails();
+        if (d == null) {
+            return forgeDeviceDescription(deviceId, defaultAvailable);
+        }
+        // Enforce defaultAvailable flag over the one obtained from driver.
+        return new DefaultDeviceDescription(d, defaultAvailable, d.annotations());
     }
 
     private List<PortDescription> getPortDetails(DeviceId deviceId) {