TL1 device provider with driver for Lumentum WaveReady.

ONOS-5800 & ONOS-5801

Change-Id: Icd820285eb8db2fd92c03ebf11ce022b6a82b48a
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);
+        }
+    }
+}