Implementation of lambda query behaviour for LINC and Calient drivers.

ONOS-3431

Change-Id: I03b7e7c9d2b0ba7e55d09745cfc6ceb57cc6eb5e
diff --git a/core/api/src/main/java/org/onosproject/net/DefaultOchSignalComparator.java b/core/api/src/main/java/org/onosproject/net/DefaultOchSignalComparator.java
new file mode 100644
index 0000000..e605dcf
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/DefaultOchSignalComparator.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 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.net;
+
+import java.util.Comparator;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Comparator implementation for OchSignal. Assumes identical grid type and channel spacing.
+ */
+public class DefaultOchSignalComparator implements Comparator<OchSignal> {
+    @Override
+    public int compare(OchSignal o1, OchSignal o2) {
+        checkNotNull(o1.gridType());
+        checkNotNull(o1.channelSpacing());
+
+        checkArgument(o1.gridType().equals(o2.gridType()));
+        checkArgument(o1.channelSpacing().equals(o2.channelSpacing()));
+
+        return o1.spacingMultiplier() * o1.slotGranularity() - o2.spacingMultiplier() * o2.slotGranularity();
+    }
+}
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 5a5af34..3adc908 100644
--- a/core/api/src/main/java/org/onosproject/net/OchSignal.java
+++ b/core/api/src/main/java/org/onosproject/net/OchSignal.java
@@ -17,6 +17,7 @@
 
 import com.google.common.base.MoreObjects;
 import org.onlab.util.Frequency;
+import org.onlab.util.Spectrum;
 
 import java.util.Objects;
 
@@ -32,7 +33,6 @@
  */
 public class OchSignal implements Lambda {
 
-    public static final Frequency CENTER_FREQUENCY = Frequency.ofTHz(193.1);
     public static final Frequency FLEX_GRID_SLOT = Frequency.ofGHz(12.5);
     private static final GridType DEFAULT_OCH_GRIDTYPE = GridType.DWDM;
     private static final ChannelSpacing DEFAULT_CHANNEL_SPACING = ChannelSpacing.CHL_50GHZ;
@@ -78,7 +78,7 @@
 
         this.gridType = DEFAULT_OCH_GRIDTYPE;
         this.channelSpacing = DEFAULT_CHANNEL_SPACING;
-        this.spacingMultiplier = (int) (centerFrequency.subtract(OchSignal.CENTER_FREQUENCY).asHz() / grid.asHz());
+        this.spacingMultiplier = (int) (centerFrequency.subtract(Spectrum.CENTER_FREQUENCY).asHz() / grid.asHz());
         this.slotGranularity = (int) Math.round((double) grid.asHz() / ChannelSpacing.CHL_12P5GHZ.frequency().asHz());
     }
 
@@ -86,7 +86,7 @@
         this.gridType = DEFAULT_OCH_GRIDTYPE;
         this.channelSpacing = channelSpacing;
         this.spacingMultiplier = (int) Math.round((double) centerFrequency.
-                subtract(OchSignal.CENTER_FREQUENCY).asHz() / channelSpacing().frequency().asHz());
+                subtract(Spectrum.CENTER_FREQUENCY).asHz() / channelSpacing().frequency().asHz());
         this.slotGranularity = slotGranularity;
     }
 
@@ -132,7 +132,7 @@
      * @return frequency in MHz
      */
     public Frequency centralFrequency() {
-        return CENTER_FREQUENCY.add(channelSpacing().frequency().multiply(spacingMultiplier));
+        return Spectrum.CENTER_FREQUENCY.add(channelSpacing().frequency().multiply(spacingMultiplier));
     }
 
     /**
diff --git a/drivers/src/main/java/org/onosproject/driver/handshaker/CalientFiberSwitchHandshaker.java b/drivers/src/main/java/org/onosproject/driver/handshaker/CalientFiberSwitchHandshaker.java
index df4dfa5..270008f 100644
--- a/drivers/src/main/java/org/onosproject/driver/handshaker/CalientFiberSwitchHandshaker.java
+++ b/drivers/src/main/java/org/onosproject/driver/handshaker/CalientFiberSwitchHandshaker.java
@@ -17,7 +17,14 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import org.onlab.util.Spectrum;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.DefaultOchSignalComparator;
 import org.onosproject.net.Device;
+import org.onosproject.net.GridType;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.LambdaQuery;
 import org.onosproject.openflow.controller.OpenFlowOpticalSwitch;
 import org.onosproject.openflow.controller.PortDescPropertyType;
 import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
@@ -41,9 +48,24 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
-public class CalientFiberSwitchHandshaker extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch {
+/**
+ * Driver for Calient S160 Optical Circuit Switch. Untested on Calient S320 but probably works ok.
+ *
+ * Driver implements custom handshaker, and rewrites flow stats as expected by the device. Port stats are currently
+ * not supported.
+ *
+ * The device consists of OMS ports only, and each port exposes lambda resources covering the whole
+ * usable optical spectrum (U to O band, see {@link Spectrum} for spectrum definitions).
+ */
+public class CalientFiberSwitchHandshaker
+        extends AbstractOpenFlowSwitch
+        implements OpenFlowOpticalSwitch, LambdaQuery {
 
     private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false);
     private List<OFCalientPortDescStatsEntry> fiberPorts = new ArrayList<>();
@@ -178,4 +200,22 @@
 
         super.sendMsg(newMsg);
     }
+
+    @Override
+    public SortedSet<OchSignal> queryLambdas(PortNumber port) {
+        // S160 data sheet
+        // Wavelength range: 1260 - 1630 nm
+        long startSpacingMultiplier = Spectrum.U_BAND_MIN.subtract(Spectrum.CENTER_FREQUENCY).asHz() /
+                ChannelSpacing.CHL_12P5GHZ.frequency().asHz();
+        long stopSpacingMultiplier = Spectrum.O_BAND_MAX.subtract(Spectrum.CENTER_FREQUENCY).asHz() /
+                ChannelSpacing.CHL_12P5GHZ.frequency().asHz();
+        List<OchSignal> lambdas = IntStream.rangeClosed((int) startSpacingMultiplier, (int) stopSpacingMultiplier)
+                .mapToObj(x -> new OchSignal(GridType.FLEX, ChannelSpacing.CHL_12P5GHZ, x, 1))
+                .collect(Collectors.toList());
+
+        SortedSet<OchSignal> result = new TreeSet<>(new DefaultOchSignalComparator());
+        result.addAll(lambdas);
+
+        return result;
+    }
 }
diff --git a/drivers/src/main/java/org/onosproject/driver/handshaker/OfOpticalSwitchImplLinc13.java b/drivers/src/main/java/org/onosproject/driver/handshaker/OfOpticalSwitchImplLinc13.java
index f91e2a7..23378e9 100644
--- a/drivers/src/main/java/org/onosproject/driver/handshaker/OfOpticalSwitchImplLinc13.java
+++ b/drivers/src/main/java/org/onosproject/driver/handshaker/OfOpticalSwitchImplLinc13.java
@@ -16,7 +16,13 @@
 package org.onosproject.driver.handshaker;
 
 import com.google.common.collect.ImmutableSet;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.DefaultOchSignalComparator;
 import org.onosproject.net.Device;
+import org.onosproject.net.GridType;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.LambdaQuery;
 import org.onosproject.openflow.controller.OpenFlowOpticalSwitch;
 import org.onosproject.openflow.controller.PortDescPropertyType;
 import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
@@ -42,7 +48,6 @@
 import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxmExpOchSigId;
 import org.projectfloodlight.openflow.types.CircuitSignalID;
-import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.U8;
 
 import java.io.IOException;
@@ -51,7 +56,11 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 /**
  * LINC-OE Optical Emulator switch class.
@@ -65,10 +74,13 @@
  * As LINC implements custom OF optical extensions (in contrast to the final standard as specified in
  * ONF TS-022 (March 15, 2015), we need to rewrite flow stat requests and flow mods in {@link #sendMsg(OFMessage)}.
  *
+ * LINC exposes OchSignal resources: 80 lambdas of 50 GHz around ITU-T G.694.1 center frequency 193.1 GHz.
+ *
  */
 public class OfOpticalSwitchImplLinc13
- extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch {
+ extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch, LambdaQuery {
 
+    private static final int LAMBDA_COUNT = 80;
     private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false);
     private long barrierXidToWaitFor = -1;
 
@@ -267,13 +279,13 @@
     /**
      * Checks if given port is also part of the regular port desc stats, i.e., is the port a tap port.
      *
-     * @param port given OF port
+     * @param port given port number
      * @return true if the port is a tap (OCh), false otherwise (OMS port)
      */
-    private boolean hasPort(OFPort port) {
+    private boolean isOChPort(long port) {
         for (OFPortDescStatsReply reply : this.ports) {
             for (OFPortDesc p : reply.getEntries()) {
-                if (p.getPortNo().equals(port)) {
+                if (p.getPortNo().getPortNumber() == port) {
                     return true;
                 }
             }
@@ -328,7 +340,7 @@
             short signalType;
 
             // FIXME: use constants once loxi has full optical extensions
-            if (hasPort(p.getPortNo())) {
+            if (isOChPort(p.getPortNo().getPortNumber())) {
                 signalType = 5;      // OCH port
             } else {
                 signalType = 2;      // OMS port
@@ -351,4 +363,23 @@
     public Set<PortDescPropertyType> getPortTypes() {
         return ImmutableSet.of(PortDescPropertyType.OPTICAL_TRANSPORT);
     }
+
+    @Override
+    public SortedSet<OchSignal> queryLambdas(PortNumber port) {
+        // OCh ports don't have lambdas
+        if (isOChPort(port.toLong())) {
+            return Collections.emptySortedSet();
+        }
+
+        // OMS ports expose 80 lambdas of 50GHz width, centered around the ITU-T center frequency.
+        // We report these with a spacing of 12.5 GHz.
+        List<OchSignal> lambdas = IntStream.range(0, LAMBDA_COUNT)
+                .mapToObj(x -> new OchSignal(GridType.FLEX, ChannelSpacing.CHL_12P5GHZ, x - (LAMBDA_COUNT / 2), 1))
+                .collect(Collectors.toList());
+
+        SortedSet<OchSignal> result = new TreeSet<>(new DefaultOchSignalComparator());
+        result.addAll(lambdas);
+
+        return result;
+    }
 }
diff --git a/drivers/src/main/resources/onos-drivers.xml b/drivers/src/main/resources/onos-drivers.xml
index 6989683..2494e12 100644
--- a/drivers/src/main/resources/onos-drivers.xml
+++ b/drivers/src/main/resources/onos-drivers.xml
@@ -74,6 +74,8 @@
             swVersion="LINC-OE OpenFlow Software Switch 1.1">
         <behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver"
                    impl="org.onosproject.driver.handshaker.OfOpticalSwitchImplLinc13"/>
+        <behaviour api="org.onosproject.net.behaviour.LambdaQuery"
+                   impl="org.onosproject.driver.handshaker.OfOpticalSwitchImplLinc13"/>
     </driver>
     <driver name="corsa"
             manufacturer="Corsa" hwVersion="Corsa Element" swVersion="2.3.1">
@@ -135,6 +137,8 @@
             swVersion="ocs switch">
         <behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver"
                    impl="org.onosproject.driver.handshaker.CalientFiberSwitchHandshaker"/>
+        <behaviour api="org.onosproject.net.behaviour.LambdaQuery"
+                   impl="org.onosproject.driver.handshaker.CalientFiberSwitchHandshaker"/>
     </driver>
     <driver name="onosfw" extends="ovs"
             manufacturer="" hwVersion="" swVersion="">
diff --git a/utils/misc/src/main/java/org/onlab/util/Spectrum.java b/utils/misc/src/main/java/org/onlab/util/Spectrum.java
index 42d7609..ca01c6e 100644
--- a/utils/misc/src/main/java/org/onlab/util/Spectrum.java
+++ b/utils/misc/src/main/java/org/onlab/util/Spectrum.java
@@ -19,11 +19,12 @@
  * Telecom optical wavelength bands: O, E, S, C, L and U bands.
  *
  * See ITU-T G-Series Recommendations, Supplement 39
+ * See ITU-T G.694.1 for center frequency definition.
  */
 public final class Spectrum {
 
-    private Spectrum() {
-    }
+    // Center frequency
+    public static final Frequency CENTER_FREQUENCY = Frequency.ofTHz(193.1);
 
     // O band (original): 1260 to 1360 nm
     public static final Frequency O_BAND_MIN = Frequency.ofTHz(220.436);
@@ -48,4 +49,7 @@
     // U band (ultra-long wavelength): 1625 to 1675 nm
     public static final Frequency U_BAND_MIN = Frequency.ofTHz(178.981);
     public static final Frequency U_BAND_MAX = Frequency.ofTHz(184.488);
+
+    private Spectrum() {
+    }
 }