A new driver behavior call TapiDeviceLambdaQuery.
This new driver behavior queries the registered Open Line System (OLS) TAPI device to discover the map of available lambdas of the queried device's port.
The method queryLambdas returns a Set<OchSignal> which includes an entry for each available spectrum slot.
Currently only Dwdm 50Ghz grid spectrum type is implemented.
Additionally, the toString method of the OchSignal class has been modified to return the spectrum slot asociated to the OchSignal with it's actual ChannelSpacing value.

Change-Id: I1dfbc7fd7801f92588a5cf9b6316fa8dea01a85d
diff --git a/core/api/src/main/java/org/onosproject/net/OchSignal.java b/core/api/src/main/java/org/onosproject/net/OchSignal.java
index 7f67df3..2234aaf 100644
--- a/core/api/src/main/java/org/onosproject/net/OchSignal.java
+++ b/core/api/src/main/java/org/onosproject/net/OchSignal.java
@@ -238,7 +238,7 @@
                 this.getClass().getSimpleName(),
                 spacingMultiplier,
                 (double) channelSpacing.frequency().asHz() / Frequency.ofGHz(1).asHz(),
-                (double) slotGranularity * ChannelSpacing.CHL_12P5GHZ.frequency().asHz()
+                (double) slotGranularity * channelSpacing.frequency().asHz()
                         / Frequency.ofGHz(1).asHz() / 2.0);
     }
 }
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceDescriptionDiscovery.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceDescriptionDiscovery.java
index 59a5ef8..4bc7e69 100644
--- a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceDescriptionDiscovery.java
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceDescriptionDiscovery.java
@@ -46,6 +46,7 @@
 import java.util.List;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.*;
 import static org.onosproject.net.optical.device.OchPortHelper.ochPortDescription;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -60,24 +61,8 @@
 
     private static final Logger log = getLogger(TapiDeviceDescriptionDiscovery.class);
     private static final String SIP_REQUEST_DATA_API = "/data/context/";
-    public static final String SERVICE_INTERFACE_POINT = "service-interface-point";
-    public static final String UUID = "uuid";
-    public static final String MEDIA_CHANNEL_SERVICE_INTERFACE_POINT_SPEC =
-            "media-channel-service-interface-point-spec";
-    public static final String MC_POOL = "mc-pool";
-    public static final String LAYER_PROTOCOL_NAME = "layer-protocol-name";
-    public static final String PHOTONIC_MEDIA = "PHOTONIC_MEDIA";
-    public static final String SUPPORTED_LAYER_PROTOCOL_QUALIFIER = "supported-layer-protocol-qualifier";
-    public static final String PHOTONIC_LAYER_QUALIFIER_NMC = "PHOTONIC_LAYER_QUALIFIER_NMC";
-    public static final String FREQUENCY_CONSTRAINT = "frequency-constraint";
-    public static final String GRID_TYPE = "grid-type";
-    public static final String ADJUSTMENT_GRANULARITY = "adjustment-granularity";
-    public static final String UPPER_FREQUENCY = "upper-frequency";
-    public static final String LOWER_FREQUENCY = "lower-frequency";
-    public static final String AVAILABLE_SPECTRUM = "available-spectrum";
     private static PortNumber nPort = PortNumber.portNumber(1);
     private static final Object N_PORT_LOCK = new Object();
-    private static final long BASE_FREQUENCY = 193100000;   //Working in Mhz
 
     /**
      * Get the deviceId for which the methods apply.
@@ -149,6 +134,11 @@
                 String uuid = sipAttributes.get(UUID).textValue();
                 JsonNode mcPool = sipAttributes.get(MEDIA_CHANNEL_SERVICE_INTERFACE_POINT_SPEC).get(MC_POOL);
 
+                /*We create a sample OChSignal in the first position of the 50GHz DWDM Grid,
+                this should be replace for a OChSignal construct which has undefined spectrum.
+                OchSignal ochSignal = new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ,
+                        0, 1);*/
+
                 OchSignal ochSignal = getOchSignal(mcPool);
                 synchronized (N_PORT_LOCK) {
                     //annotations(portNumber-uuid)
@@ -156,7 +146,7 @@
 
                     //add och port
                     ports.add(ochPortDescription(nPort, true, OduSignalType.ODU4,
-                              false, ochSignal, annotations.build()));
+                            false, ochSignal, annotations.build()));
 
                     nPort = PortNumber.portNumber(counter++);
                 }
@@ -187,8 +177,10 @@
      * and spectrum-occupied tapi objects.
      **/
     private OchSignal getOchSignal(JsonNode mcPool) {
-        long availableUpperFrec = 0, availableLowerFrec = 0;
-        String availableAdjustmentGranularity = "", availableGridType = "";
+        long availableUpperFrec = 0;
+        long availableLowerFrec = 0;
+        String availableAdjustmentGranularity = "";
+        String availableGridType = "";
         JsonNode availableSpectrum = mcPool.get(AVAILABLE_SPECTRUM);
 
         /**At this time only the latest availableSpectrum is used**/
@@ -202,12 +194,13 @@
             availableGridType = availableSpec.get(FREQUENCY_CONSTRAINT).get(GRID_TYPE).textValue();
         }
 
-        int spacingMult = 0, slotGranularity = 1;
+        int spacingMult = 0;
+        int slotGranularity = 1;
         ChannelSpacing chSpacing = getChannelSpacing(availableAdjustmentGranularity);
         long spacingFrequency = chSpacing.frequency().asHz();
         long centralFrequency = (availableUpperFrec - (availableUpperFrec - availableLowerFrec) / 2);
 
-        GridType gridType = getGridType(availableGridType);
+        GridType gridType = GridType.valueOf(availableGridType);
         if (gridType == GridType.DWDM) {
             spacingMult = (int) ((centralFrequency - BASE_FREQUENCY) / toMbpsFromHz(spacingFrequency));
         } else if (gridType == GridType.CWDM) {
@@ -221,52 +214,4 @@
         return new OchSignal(gridType, chSpacing, spacingMult, slotGranularity);
     }
 
-    private int getSlotGranularity(ChannelSpacing chSpacing) {
-        if (chSpacing.equals(ChannelSpacing.CHL_100GHZ)) {
-            return 8;
-        } else if (chSpacing.equals(ChannelSpacing.CHL_50GHZ)) {
-            return 4;
-        } else if (chSpacing.equals(ChannelSpacing.CHL_25GHZ)) {
-            return 2;
-        } else if (chSpacing.equals(ChannelSpacing.CHL_12P5GHZ)) {
-            return 1;
-        } else {
-            return 0;
-        }
-    }
-
-    private GridType getGridType(String gridType) {
-        switch (gridType) {
-            case "DWDM":
-                return GridType.DWDM;
-            case "CWDM":
-                return GridType.CWDM;
-            case "FLEX":
-                return GridType.FLEX;
-            default:
-                return GridType.UNKNOWN;
-        }
-    }
-
-    private ChannelSpacing getChannelSpacing(String adjustmentGranularity) {
-        switch (adjustmentGranularity) {
-            case "G_100GHZ ":
-                return ChannelSpacing.CHL_100GHZ;
-            case "G_50GHZ":
-                return ChannelSpacing.CHL_50GHZ;
-            case "G_25GHZ":
-                return ChannelSpacing.CHL_25GHZ;
-            case "G_12_5GHZ ":
-                return ChannelSpacing.CHL_12P5GHZ;
-            case "G_6_25GHZ ":
-                return ChannelSpacing.CHL_6P25GHZ;
-            default:
-                return ChannelSpacing.CHL_0GHZ;
-        }
-    }
-
-    private static long toMbpsFromHz(long speed) {
-        return speed / 1000000;
-    }
-
 }
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceHelper.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceHelper.java
new file mode 100644
index 0000000..11bf38c
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceHelper.java
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ *
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.tapi;
+
+import org.onosproject.net.ChannelSpacing;
+
+/**
+ * Tapi 2.1 OLS device related helpers.
+ */
+public final class TapiDeviceHelper {
+
+    public static final String SERVICE_INTERFACE_POINT = "service-interface-point";
+    public static final String UUID = "uuid";
+    public static final String MEDIA_CHANNEL_SERVICE_INTERFACE_POINT_SPEC =
+            "media-channel-service-interface-point-spec";
+    public static final String MC_POOL = "mc-pool";
+    public static final String LAYER_PROTOCOL_NAME = "layer-protocol-name";
+    public static final String PHOTONIC_MEDIA = "PHOTONIC_MEDIA";
+    public static final String SUPPORTED_LAYER_PROTOCOL_QUALIFIER = "supported-layer-protocol-qualifier";
+    public static final String PHOTONIC_LAYER_QUALIFIER_NMC = "PHOTONIC_LAYER_QUALIFIER_NMC";
+    public static final String FREQUENCY_CONSTRAINT = "frequency-constraint";
+    public static final String GRID_TYPE = "grid-type";
+    public static final String ADJUSTMENT_GRANULARITY = "adjustment-granularity";
+    public static final String UPPER_FREQUENCY = "upper-frequency";
+    public static final String LOWER_FREQUENCY = "lower-frequency";
+    public static final String AVAILABLE_SPECTRUM = "available-spectrum";
+    public static final long BASE_FREQUENCY = 193100000;   //Working in Mhz
+
+    private TapiDeviceHelper(){}
+
+    /**
+     * Returns the slot granularity corresponding to a channelSpacing.
+     *
+     * @param chSpacing      OchSingal channel spacing {@link ChannelSpacing}
+     * @return OchSignal slot width granularity
+     */
+    public static int getSlotGranularity(ChannelSpacing chSpacing) {
+        if (chSpacing.equals(ChannelSpacing.CHL_100GHZ)) {
+            return 8;
+        } else if (chSpacing.equals(ChannelSpacing.CHL_50GHZ)) {
+            return 4;
+        } else if (chSpacing.equals(ChannelSpacing.CHL_25GHZ)) {
+            return 2;
+        } else if (chSpacing.equals(ChannelSpacing.CHL_12P5GHZ)) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Returns the ChannelSpacing corresponding to an adjustmentGranularity.
+     *
+     * @param adjustmentGranularity {@link String}
+     * @return OchSingnal ChannelSpacing {@link ChannelSpacing}
+     */
+    public static ChannelSpacing getChannelSpacing(String adjustmentGranularity) {
+        switch (adjustmentGranularity) {
+            case "G_100GHZ ":
+                return ChannelSpacing.CHL_100GHZ;
+            case "G_50GHZ":
+                return ChannelSpacing.CHL_50GHZ;
+            case "G_25GHZ":
+                return ChannelSpacing.CHL_25GHZ;
+            case "G_12_5GHZ ":
+                return ChannelSpacing.CHL_12P5GHZ;
+            case "G_6_25GHZ ":
+                return ChannelSpacing.CHL_6P25GHZ;
+            default:
+                return ChannelSpacing.CHL_0GHZ;
+        }
+    }
+
+    /**
+     *  To Mbps conversion from Hz.
+     *
+     *  @param speed the speed in Hz
+     *  @return the speed in Mbps
+     */
+    public static long toMbpsFromHz(long speed) {
+        return speed / 1000000;
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceLambdaQuery.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceLambdaQuery.java
new file mode 100644
index 0000000..f06c0b4
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/tapi/TapiDeviceLambdaQuery.java
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ *
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.tapi;
+
+import com.google.common.collect.ImmutableSet;
+import org.onosproject.net.Port;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.GridType;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.LambdaQuery;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.protocol.rest.RestSBController;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.MEDIA_CHANNEL_SERVICE_INTERFACE_POINT_SPEC;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.AVAILABLE_SPECTRUM;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.ADJUSTMENT_GRANULARITY;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.BASE_FREQUENCY;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.FREQUENCY_CONSTRAINT;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.GRID_TYPE;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.LOWER_FREQUENCY;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.MC_POOL;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.UPPER_FREQUENCY;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.toMbpsFromHz;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.getChannelSpacing;
+import static org.onosproject.drivers.odtn.tapi.TapiDeviceHelper.getSlotGranularity;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import static com.google.common.base.Preconditions.checkNotNull;
+import org.onosproject.net.device.DeviceService;
+import javax.ws.rs.core.MediaType;
+
+
+
+/**
+ * Driver behavior of TAPI devices to discover the map of available lambdas of the ports.
+ */
+public class TapiDeviceLambdaQuery extends AbstractHandlerBehaviour
+        implements LambdaQuery {
+
+    private static final Logger log = getLogger(TapiDeviceLambdaQuery.class);
+    private static final String SIP_REQUEST_DATA_API = "/data/context/service-interface-point=";
+
+    /**
+     * Get the deviceId for which the methods apply.
+     *
+     * @return The deviceId as contained in the handler data
+     */
+    private DeviceId did() {
+        return handler().data().deviceId();
+    }
+
+    @Override
+    public Set<OchSignal> queryLambdas(PortNumber port) {
+        RestSBController controller = checkNotNull(handler().get(RestSBController.class));
+        DeviceService deviceService = checkNotNull(handler().get(DeviceService.class));
+        DeviceId deviceId = did();
+        Device dev = deviceService.getDevice(deviceId);
+        if (dev == null) {
+            log.error("Device {} does not exist", deviceId);
+        }
+        Port p = deviceService.getPort(dev.id(), port);
+        if (p == null) {
+            log.error("Port {} does not exist", port);
+        }
+        String uuid = p.annotations().value(port.toString());
+
+        try {
+            InputStream inputStream = controller.get(deviceId, SIP_REQUEST_DATA_API + uuid,
+                    MediaType.APPLICATION_JSON_TYPE);
+            log.debug("Service interface point UUID: {}", uuid);
+            JsonNode sipAttributes = new ObjectMapper().readTree(inputStream);
+            JsonNode mcPool = sipAttributes.get(MEDIA_CHANNEL_SERVICE_INTERFACE_POINT_SPEC).get(MC_POOL);
+
+            //This creates a hashset of OChSignals representing the spectrum availability at the target port.
+            Set<OchSignal> lambdas = getOchSignal(mcPool);
+            log.debug("Lambdas: {}", lambdas.toString());
+            return lambdas;
+
+        } catch (IOException e) {
+            log.error("Exception discoverPortDetails() {}", did(), e);
+            return ImmutableSet.of();
+        }
+    }
+
+    /**
+     * If SIP info match our criteria, SIP component shall includes mc-pool information which must be obtained in order
+     * to complete the OchSignal info. with the TAPI SIP information included in spectrum-supported, spectrum-available
+     * and spectrum-occupied tapi objects.
+     **/
+    private Set<OchSignal> getOchSignal(JsonNode mcPool) {
+
+        Set<OchSignal> lambdas = new LinkedHashSet<>();
+        long availableUpperFrec = 0;
+        long availableLowerFrec = 0;
+        String availableAdjustmentGranularity = "";
+        String availableGridType = "";
+        JsonNode availableSpectrum = mcPool.get(AVAILABLE_SPECTRUM);
+
+        /**At this time only the latest availableSpectrum is used**/
+        Iterator<JsonNode> iterAvailable = availableSpectrum.iterator();
+        while (iterAvailable.hasNext()) {
+            JsonNode availableSpec = iterAvailable.next();
+            availableUpperFrec = availableSpec.get(UPPER_FREQUENCY).asLong();
+            availableLowerFrec = availableSpec.get(LOWER_FREQUENCY).asLong();
+            availableAdjustmentGranularity = availableSpec.get(FREQUENCY_CONSTRAINT)
+                    .get(ADJUSTMENT_GRANULARITY).textValue();
+            availableGridType = availableSpec.get(FREQUENCY_CONSTRAINT).get(GRID_TYPE).textValue();
+
+            int spacingMult = 0;
+            int slotGranularity = 1;
+            ChannelSpacing chSpacing = getChannelSpacing(availableAdjustmentGranularity);
+            long spacingFrequency = chSpacing.frequency().asHz();
+            long centralFrequency = (availableUpperFrec - (availableUpperFrec - availableLowerFrec) / 2);
+
+            GridType gridType = GridType.valueOf(availableGridType);
+            if (gridType == GridType.DWDM) {
+                spacingMult = (int) ((centralFrequency - BASE_FREQUENCY) / toMbpsFromHz(spacingFrequency));
+                OchSignal ochSignal = new OchSignal(gridType, chSpacing, spacingMult, slotGranularity);
+                lambdas.add(ochSignal);
+            } else if (gridType == GridType.CWDM) {
+                log.warn("GridType CWDM. Not implemented");
+            } else if (gridType == GridType.FLEX) {
+                log.warn("GridType FLEX. Not implemented");
+                slotGranularity = getSlotGranularity(chSpacing);
+            } else {
+                log.warn("Unknown GridType");
+            }
+        }
+        return lambdas;
+    }
+
+}
\ No newline at end of file
diff --git a/drivers/odtn-driver/src/main/resources/odtn-drivers.xml b/drivers/odtn-driver/src/main/resources/odtn-drivers.xml
index bd3dff4..60fe17f 100644
--- a/drivers/odtn-driver/src/main/resources/odtn-drivers.xml
+++ b/drivers/odtn-driver/src/main/resources/odtn-drivers.xml
@@ -18,6 +18,8 @@
     <driver name="ols" manufacturer="tapi-swagger" hwVersion="0" swVersion="2.1">
         <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
                    impl="org.onosproject.drivers.odtn.tapi.TapiDeviceDescriptionDiscovery"/>
+        <behaviour api ="org.onosproject.net.behaviour.LambdaQuery"
+                   impl="org.onosproject.drivers.odtn.tapi.TapiDeviceLambdaQuery"/>
         <!--<behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
                    impl="org.onosproject.drivers.odtn.TapiFlowRuleProgrammable-->
     </driver>