Deprecate IndexedLambda and remove from optical intent compiler.
Allow drivers to report any spectral grid. Bugfixes.

ONOS-3495

Change-Id: Ied946660d48e482c1746d1e87735498b1637bb4b
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
index e017ac5..2d4545c 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
@@ -26,14 +26,14 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.util.Frequency;
 import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.ChannelSpacing;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultOchSignalComparator;
 import org.onosproject.net.DeviceId;
-import org.onosproject.net.IndexedLambda;
 import org.onosproject.net.Link;
 import org.onosproject.net.OchPort;
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.OchSignalType;
-import org.onosproject.net.OmsPort;
 import org.onosproject.net.Path;
 import org.onosproject.net.Port;
 import org.onosproject.net.device.DeviceService;
@@ -53,6 +53,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
@@ -64,11 +65,12 @@
 /**
  * An intent compiler for {@link org.onosproject.net.intent.OpticalConnectivityIntent}.
  */
-// For now, remove component designation until dependency on the new resource manager is available.
 @Component(immediate = true)
 public class OpticalConnectivityIntentCompiler implements IntentCompiler<OpticalConnectivityIntent> {
 
     protected static final Logger log = LoggerFactory.getLogger(OpticalConnectivityIntentCompiler.class);
+    // By default, allocate 50 GHz lambdas (4 slots of 12.5 GHz) for each intent.
+    private static final int SLOT_COUNT = 4;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected IntentExtensionService intentManager;
@@ -133,16 +135,15 @@
                         srcOchPort.lambda().channelSpacing(),
                         srcOchPort.lambda().slotGranularity());
             } else if (!srcOchPort.isTunable() || !dstOchPort.isTunable()) {
-                // FIXME: also check OCh port
+                // FIXME: also check destination OCh port
                 ochSignal = srcOchPort.lambda();
             } else {
                 // Request and reserve lambda on path
-                IndexedLambda lambda = assignWavelength(intent, path);
-                if (lambda == null) {
+                List<OchSignal> lambdas = assignWavelength(intent, path);
+                if (lambdas.isEmpty()) {
                     continue;
                 }
-                OmsPort omsPort = (OmsPort) deviceService.getPort(path.src().deviceId(), path.src().port());
-                ochSignal = new OchSignal((int) lambda.index(), omsPort.maxFrequency(), omsPort.grid());
+                ochSignal = OchSignal.toFixedGrid(lambdas, ChannelSpacing.CHL_50GHZ);
             }
 
             // Create installable optical path intent
@@ -174,48 +175,77 @@
      * @param path path in WDM topology
      * @return first available lambda allocated
      */
-    private IndexedLambda assignWavelength(Intent intent, Path path) {
-        Set<IndexedLambda> lambdas = findCommonLambdasOverLinks(path.links());
+    private List<OchSignal> assignWavelength(Intent intent, Path path) {
+        Set<OchSignal> lambdas = findCommonLambdasOverLinks(path.links());
         if (lambdas.isEmpty()) {
-            return null;
+            return Collections.emptyList();
         }
 
-        IndexedLambda minLambda = findFirstLambda(lambdas);
+        List<OchSignal> minLambda = findFirstLambda(lambdas, slotCount());
         List<ResourcePath> lambdaResources = path.links().stream()
                 .flatMap(x -> Stream.of(
                         ResourcePath.discrete(x.src().deviceId(), x.src().port()),
                         ResourcePath.discrete(x.dst().deviceId(), x.dst().port())
                 ))
-                .map(x -> x.child(minLambda))
+                .flatMap(x -> minLambda.stream().map(l -> x.child(l)))
                 .collect(Collectors.toList());
 
         List<ResourceAllocation> allocations = resourceService.allocate(intent.id(), lambdaResources);
         if (allocations.isEmpty()) {
             log.info("Resource allocation for {} failed (resource request: {})", intent, lambdaResources);
-            return null;
+            return Collections.emptyList();
         }
 
         return minLambda;
     }
 
-    private Set<IndexedLambda> findCommonLambdasOverLinks(List<Link> links) {
+    /**
+     * Get the number of 12.5 GHz slots required for the path.
+     *
+     * For now this returns a constant value of 4 (i.e., fixed grid 50 GHz slot),
+     * but in the future can depend on optical reach, line rate, transponder port capabilities, etc.
+     *
+     * @return number of slots
+     */
+    private int slotCount() {
+        return SLOT_COUNT;
+    }
+
+    private Set<OchSignal> findCommonLambdasOverLinks(List<Link> links) {
         return links.stream()
                 .flatMap(x -> Stream.of(
                         ResourcePath.discrete(x.src().deviceId(), x.src().port()),
                         ResourcePath.discrete(x.dst().deviceId(), x.dst().port())
                 ))
                 .map(resourceService::getAvailableResources)
-                .map(x -> Iterables.filter(x, r -> r.last() instanceof IndexedLambda))
-                .map(x -> Iterables.transform(x, r -> (IndexedLambda) r.last()))
-                .map(x -> (Set<IndexedLambda>) ImmutableSet.copyOf(x))
+                .map(x -> Iterables.filter(x, r -> r.last() instanceof OchSignal))
+                .map(x -> Iterables.transform(x, r -> (OchSignal) r.last()))
+                .map(x -> (Set<OchSignal>) ImmutableSet.copyOf(x))
                 .reduce(Sets::intersection)
                 .orElse(Collections.emptySet());
     }
 
-    private IndexedLambda findFirstLambda(Set<IndexedLambda> lambdas) {
-        return lambdas.stream()
-                .findFirst()
-                .get();
+    /**
+     * Returns list of consecutive resources in given set of lambdas.
+     *
+     * @param lambdas list of lambdas
+     * @param count number of consecutive lambdas to return
+     * @return list of consecutive lambdas
+     */
+    private List<OchSignal> findFirstLambda(Set<OchSignal> lambdas, int count) {
+        // Sort available lambdas
+        List<OchSignal> lambdaList = new ArrayList<>(lambdas);
+        lambdaList.sort(new DefaultOchSignalComparator());
+
+        // Look ahead by count and ensure spacing multiplier is as expected (i.e., no gaps)
+        for (int i = 0; i < lambdaList.size() - count; i++) {
+            if (lambdaList.get(i).spacingMultiplier() + 2 * count ==
+                    lambdaList.get(i + count).spacingMultiplier()) {
+                return lambdaList.subList(i, i + count);
+            }
+        }
+
+        return Collections.emptyList();
     }
 
     private ConnectPoint staticPort(ConnectPoint connectPoint) {