TL1 device provider with driver for Lumentum WaveReady.

ONOS-5800 & ONOS-5801

Change-Id: Icd820285eb8db2fd92c03ebf11ce022b6a82b48a
diff --git a/drivers/lumentum/BUCK b/drivers/lumentum/BUCK
index 0feb6ee..02ea7ea 100644
--- a/drivers/lumentum/BUCK
+++ b/drivers/lumentum/BUCK
@@ -3,6 +3,8 @@
     '//lib:org.apache.servicemix.bundles.snmp4j',
     '//drivers/utilities:onos-drivers-utilities',
     '//protocols/snmp/api:onos-protocols-snmp-api',
+    '//protocols/tl1/api:onos-protocols-tl1-api',
+    '//protocols/tl1/ctl:onos-protocols-tl1-ctl',
     '//incubator/api:onos-incubator-api',
     '//apps/optical-model:onos-apps-optical-model',
 ]
@@ -25,5 +27,6 @@
     category = 'Drivers',
     url = 'http://onosproject.org',
     description = 'ONOS Lumentum Device Drivers application.',
-    required_apps = [ 'org.onosproject.snmp', 'org.onosproject.faultmanagement', 'org.onosproject.optical-model' ],
+    required_apps = [ 'org.onosproject.snmp', 'org.onosproject.faultmanagement', 'org.onosproject.optical-model',
+     'org.onosproject.tl1'],
 )
diff --git a/drivers/lumentum/pom.xml b/drivers/lumentum/pom.xml
index fdfd5a3..6dce6cd 100644
--- a/drivers/lumentum/pom.xml
+++ b/drivers/lumentum/pom.xml
@@ -38,7 +38,8 @@
         <onos.app.requires>
             org.onosproject.snmp,
             org.onosproject.faultmanagement,
-            org.onosproject.optical-model
+            org.onosproject.optical-model,
+            org.onosproject.tl1
         </onos.app.requires>
     </properties>
 
@@ -59,5 +60,15 @@
             <artifactId>onos-snmp-api</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-tl1-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-tl1-ctl</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
 </project>
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumRoadmDeviceDescription.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumRoadmDiscovery.java
similarity index 97%
rename from drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumRoadmDeviceDescription.java
rename to drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumRoadmDiscovery.java
index 46e0887..c49d6ed 100644
--- a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumRoadmDeviceDescription.java
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumRoadmDiscovery.java
@@ -46,7 +46,7 @@
 /**
  * Device description behaviour for Lumentum Snmp devices.
  */
-public class LumentumRoadmDeviceDescription extends AbstractHandlerBehaviour implements DeviceDescriptionDiscovery {
+public class LumentumRoadmDiscovery extends AbstractHandlerBehaviour implements DeviceDescriptionDiscovery {
 
     private final Logger log = getLogger(getClass());
 
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumFlowRuleProgrammable.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSdnRoadmFlowRuleProgrammable.java
similarity index 98%
rename from drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumFlowRuleProgrammable.java
rename to drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSdnRoadmFlowRuleProgrammable.java
index 21f9b24..7dde491 100644
--- a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumFlowRuleProgrammable.java
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSdnRoadmFlowRuleProgrammable.java
@@ -58,10 +58,10 @@
 import static com.google.common.base.Preconditions.checkArgument;
 
 // TODO: need to convert between OChSignal and XC channel number
-public class LumentumFlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
+public class LumentumSdnRoadmFlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
 
     private static final Logger log =
-            LoggerFactory.getLogger(LumentumFlowRuleProgrammable.class);
+            LoggerFactory.getLogger(LumentumSdnRoadmFlowRuleProgrammable.class);
 
     // Default values
     private static final int DEFAULT_TARGET_GAIN_PREAMP = 150;
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LambdaQueryLumentumRoadm.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSdnRoadmLambdaQuery.java
similarity index 94%
rename from drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LambdaQueryLumentumRoadm.java
rename to drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSdnRoadmLambdaQuery.java
index e61dc24..55f2aa7 100644
--- a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LambdaQueryLumentumRoadm.java
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSdnRoadmLambdaQuery.java
@@ -31,7 +31,7 @@
  *
  * Device supports 96 wavelengths of 50 GHz, between center frequencies 191.350 THz and 196.075 THz.
  */
-public class LambdaQueryLumentumRoadm extends AbstractHandlerBehaviour implements LambdaQuery {
+public class LumentumSdnRoadmLambdaQuery extends AbstractHandlerBehaviour implements LambdaQuery {
     private static final int LAMBDA_COUNT = 96;
 
     @Override
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumWaveReadyDiscovery.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumWaveReadyDiscovery.java
new file mode 100644
index 0000000..cf9ad21
--- /dev/null
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumWaveReadyDiscovery.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.lumentum;
+
+import org.onlab.packet.ChassisId;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.CltSignalType;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.GridType;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.OduSignalType;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.device.DefaultDeviceDescription;
+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.onosproject.tl1.Tl1Command;
+import org.onosproject.tl1.Tl1Controller;
+import org.onosproject.tl1.Tl1Device;
+import org.onosproject.tl1.impl.DefaultTl1Command;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.optical.device.OchPortHelper.ochPortDescription;
+import static org.onosproject.net.optical.device.OduCltPortHelper.oduCltPortDescription;
+
+/**
+ * Device description behaviour for Lumentum WaveReady devices.
+ *
+ * Tested with Lumentum WaveReady 3100 transponder.
+ */
+public class LumentumWaveReadyDiscovery extends AbstractHandlerBehaviour implements DeviceDescriptionDiscovery {
+    private final Logger log = LoggerFactory.getLogger(LumentumWaveReadyDiscovery.class);
+
+    // Time to wait for device response in milliseconds
+    private static final int TIMEOUT = 10000;
+
+    private static final String LUMENTUM = "Lumentum";
+    private static final String WAVEREADY = "WaveReady";
+    private static final String SWVERSION = "1.0";
+    private static final String SERIAL = "3100";
+
+    // Some TL1 string constants
+    private static final String ACT = "ACT";
+    private static final String USER = "USER";
+    private static final String RTRV = "RTRV";
+    private static final String NETYPE = "NETYPE";
+    private static final String PLUGGABLE_INV = "PLUGGABLE-INV";
+    private static final String CANC = "CANC";
+    private static final String EIGHTFIFTY = "850";
+
+
+    @Override
+    public DeviceDescription discoverDeviceDetails() {
+        DeviceId deviceId = handler().data().deviceId();
+        Tl1Controller ctrl = checkNotNull(handler().get(Tl1Controller.class));
+        // Something reasonable, unavailable by default
+        DeviceDescription defaultDescription = new DefaultDeviceDescription(deviceId.uri(), Device.Type.OTN,
+                LUMENTUM, WAVEREADY, SWVERSION, SERIAL,
+                new ChassisId(), false, DefaultAnnotations.EMPTY);
+
+        Optional<Tl1Device> device = ctrl.getDevice(deviceId);
+        if (!device.isPresent()) {
+            return defaultDescription;
+        }
+
+        // Login
+        Tl1Command loginCmd = DefaultTl1Command.builder()
+                .withVerb(ACT)
+                .withModifier(USER)
+                .withAid(device.get().username())
+                .withCtag(100)
+                .withParameters(device.get().password())
+                .build();
+        Future<String> login = ctrl.sendMsg(deviceId, loginCmd);
+
+        try {
+            String loginResponse = login.get(TIMEOUT, TimeUnit.MILLISECONDS);
+            if (loginResponse.contains("Access denied")) {
+                log.error("Access denied: {}", loginResponse);
+                return defaultDescription;
+            }
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            log.error("Login failed", e);
+            return defaultDescription;
+        }
+
+        // Fetch device description
+        Tl1Command ddCmd = DefaultTl1Command.builder()
+                .withVerb(RTRV)
+                .withModifier(NETYPE)
+                .withCtag(101)
+                .build();
+        Future<String> dd = ctrl.sendMsg(deviceId, ddCmd);
+
+        try {
+            String ddResponse = dd.get(TIMEOUT, TimeUnit.MILLISECONDS);
+
+            return new DefaultDeviceDescription(defaultDescription, true, extractAnnotations(ddResponse));
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            log.error("Device description not found", e);
+            return defaultDescription;
+        }
+    }
+
+    @Override
+    public List<PortDescription> discoverPortDetails() {
+        DeviceId deviceId = handler().data().deviceId();
+        Tl1Controller ctrl = checkNotNull(handler().get(Tl1Controller.class));
+
+        // Assume we're successfully logged in
+        // Fetch port descriptions
+        Tl1Command pdCmd = DefaultTl1Command.builder()
+                .withVerb(RTRV)
+                .withModifier(PLUGGABLE_INV)
+                .withCtag(102)
+                .build();
+        Future<String> pd = ctrl.sendMsg(deviceId, pdCmd);
+
+        try {
+            String pdResponse = pd.get(TIMEOUT, TimeUnit.MILLISECONDS);
+
+            return extractPorts(pdResponse);
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            log.error("Port description not found", e);
+            return Collections.EMPTY_LIST;
+        }
+    }
+
+    private SparseAnnotations extractAnnotations(String s) {
+        DefaultAnnotations.Builder annot = DefaultAnnotations.builder();
+
+        Arrays.stream(s.split(",")).forEach(w -> {
+            String[] pair = w.replaceAll("\\\\\"", "").split("=");
+            if (pair.length == 2) {
+                annot.set(pair[0], pair[1]);
+            } else {
+                annot.set(pair[0], "");
+            }
+        });
+
+        return annot.build();
+    }
+
+    // Extract ports from response on pluggable inventory retrieval.
+    // Client ports are identified by 850nm, everything else is a network port.
+    private List<PortDescription> extractPorts(String s) {
+        List<PortDescription> ports = new ArrayList<>();
+
+        if (s.length() == 0) {
+            return ports;
+        }
+
+        Arrays.stream(s.split("\"\"")).forEach(p -> {
+            if (p.contains(EIGHTFIFTY)) {
+                PortDescription cltPort = oduCltPortDescription(
+                        PortNumber.portNumber(ports.size() + 1),
+                        true,
+                        CltSignalType.CLT_10GBE,
+                        extractAnnotations(p));
+                ports.add(cltPort);
+            } else {
+                PortDescription netPort = ochPortDescription(
+                        PortNumber.portNumber(ports.size() + 1),
+                        true,
+                        OduSignalType.ODU2e,
+                        true,
+                        new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, 0, 4),
+                        extractAnnotations(p));
+                ports.add(netPort);
+            }
+        });
+
+        return ports;
+    }
+
+    // Unused but provided here for convenience.
+    private void logout() {
+        DeviceId deviceId = handler().data().deviceId();
+        Tl1Controller ctrl = checkNotNull(handler().get(Tl1Controller.class));
+
+        Optional<Tl1Device> device = ctrl.getDevice(deviceId);
+        if (!device.isPresent()) {
+            return;
+        }
+
+        // Logout command
+        Tl1Command logoutCmd = DefaultTl1Command.builder()
+                .withVerb(CANC)
+                .withModifier(USER)
+                .withAid(device.get().username())
+                .withCtag(103)
+                .build();
+        Future<String> logout = ctrl.sendMsg(deviceId, logoutCmd);
+
+        try {
+            String logoutResponse = logout.get(TIMEOUT, TimeUnit.MILLISECONDS);
+
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            log.error("Lougout failed", e);
+        }
+    }
+}
diff --git a/drivers/lumentum/src/main/resources/lumentum-drivers.xml b/drivers/lumentum/src/main/resources/lumentum-drivers.xml
index bb4b87b..230cc34 100644
--- a/drivers/lumentum/src/main/resources/lumentum-drivers.xml
+++ b/drivers/lumentum/src/main/resources/lumentum-drivers.xml
@@ -15,17 +15,23 @@
   ~ limitations under the License.
   -->
 <drivers>
-    <driver name="lumentum" manufacturer="Lumentum" hwVersion="SDN ROADM" swVersion="1.0">
+    <driver name="lumentum-sdn" manufacturer="Lumentum" hwVersion="SDN ROADM" swVersion="1.0">
         <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
-                   impl="org.onosproject.drivers.lumentum.LumentumRoadmDeviceDescription"/>
+                   impl="org.onosproject.drivers.lumentum.LumentumRoadmDiscovery"/>
         <behaviour api="org.onosproject.net.behaviour.LambdaQuery"
-                   impl="org.onosproject.drivers.lumentum.LambdaQueryLumentumRoadm"/>
+                   impl="org.onosproject.drivers.lumentum.LumentumSdnRoadmLambdaQuery"/>
         <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
-                   impl="org.onosproject.drivers.lumentum.LumentumFlowRuleProgrammable"/>
+                   impl="org.onosproject.drivers.lumentum.LumentumSdnRoadmFlowRuleProgrammable"/>
         <behaviour api="org.onosproject.incubator.net.faultmanagement.alarm.AlarmConsumer"
                    impl="org.onosproject.drivers.lumentum.LumentumAlarmConsumer"/>
         <behaviour api="org.onosproject.net.optical.OpticalDevice"
                    impl="org.onosproject.net.optical.DefaultOpticalDevice"/>
     </driver>
+    <driver name="lumentum-waveready" manufacturer="Lumentum" hwVersion="WR*">
+        <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
+                   impl="org.onosproject.drivers.lumentum.LumentumWaveReadyDiscovery"/>
+        <behaviour api="org.onosproject.net.optical.OpticalDevice"
+                   impl="org.onosproject.net.optical.DefaultOpticalDevice"/>
+    </driver>
 </drivers>
 
diff --git a/drivers/lumentum/src/test/java/org/onosproject/drivers/lumentum/LumentumDriversLoaderTest.java b/drivers/lumentum/src/test/java/org/onosproject/drivers/lumentum/LumentumDriversLoaderTest.java
deleted file mode 100644
index 1723337..0000000
--- a/drivers/lumentum/src/test/java/org/onosproject/drivers/lumentum/LumentumDriversLoaderTest.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.onosproject.drivers.lumentum;
-
-import org.junit.Before;
-import org.onosproject.net.driver.AbstractDriverLoaderTest;
-
-/**
- * Lumentum drivers loader test.
- */
-public class LumentumDriversLoaderTest extends AbstractDriverLoaderTest {
-
-    @Before
-    public void setUp() {
-        loader = new LumentumDriversLoader();
-    }
-}