Simplify optical compiler by always searching for common lambda (ONOS-5765)

Change-Id: I9128f59cc9c0bf10b88341cac289fb606906d98b
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
index 5593233..2609cf4 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
@@ -24,7 +24,6 @@
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 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;
@@ -42,8 +41,8 @@
 import org.onosproject.net.intent.OpticalConnectivityIntent;
 import org.onosproject.net.intent.OpticalPathIntent;
 import org.onosproject.net.optical.OchPort;
-import org.onosproject.net.resource.ResourceAllocation;
 import org.onosproject.net.resource.Resource;
+import org.onosproject.net.resource.ResourceAllocation;
 import org.onosproject.net.resource.ResourceService;
 import org.onosproject.net.resource.Resources;
 import org.onosproject.net.topology.AdapterLinkWeigher;
@@ -66,7 +65,6 @@
 import java.util.stream.Stream;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static org.onosproject.net.OchSignal.toFlexGrid;
 import static org.onosproject.net.optical.device.OpticalDeviceServiceView.opticalView;
 
 /**
@@ -108,10 +106,9 @@
         // Check if source and destination are optical OCh ports
         ConnectPoint src = intent.getSrc();
         ConnectPoint dst = intent.getDst();
-        Port srcPort = deviceService.getPort(src.deviceId(), src.port());
-        Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
-        checkArgument(srcPort instanceof OchPort);
-        checkArgument(dstPort instanceof OchPort);
+        checkArgument(deviceService.getPort(src.deviceId(), src.port()) instanceof OchPort);
+        checkArgument(deviceService.getPort(dst.deviceId(), dst.port()) instanceof OchPort);
+        List<Resource> resources = new LinkedList<>();
 
         log.debug("Compiling optical connectivity intent between {} and {}", src, dst);
 
@@ -121,101 +118,30 @@
         resourceService.release(intent.key());
 
         // Check OCh port availability
+        // If ports are not available, compilation fails
+        // Else add port to resource reservation list
         Resource srcPortResource = Resources.discrete(src.deviceId(), src.port()).resource();
         Resource dstPortResource = Resources.discrete(dst.deviceId(), dst.port()).resource();
-        // If ports are not available, compilation fails
         if (!Stream.of(srcPortResource, dstPortResource).allMatch(resourceService::isAvailable)) {
             log.error("Ports for the intent are not available. Intent: {}", intent);
             throw new OpticalIntentCompilationException("Ports for the intent are not available. Intent: " + intent);
         }
-
-        List<Resource> resources = new ArrayList<>();
         resources.add(srcPortResource);
         resources.add(dstPortResource);
 
-        // Calculate available light paths
+        // Find first path that has the required resources
         Stream<Path> paths = getOpticalPaths(intent);
-
-        // Static or dynamic lambda allocation
-        String staticLambda = srcPort.annotations().value(AnnotationKeys.STATIC_LAMBDA);
-        OchPort srcOchPort = (OchPort) srcPort;
-        OchPort dstOchPort = (OchPort) dstPort;
-
-        // FIXME: need to actually reserve the lambda for static lambda's
-        // static lambda case: early return
-        if (staticLambda != null) {
-
-            OchSignal lambda = new OchSignal(Frequency.ofHz(Long.parseLong(staticLambda)),
-                                             srcOchPort.lambda().channelSpacing(),
-                                             srcOchPort.lambda().slotGranularity());
-            log.debug("Using statically assigned lambda : {}", lambda);
-
-            List<Resource> res = new ArrayList<>();
-
-            Path satPath = paths.filter(path -> {
-                // FIXME trimming both ends (staticLambda assigned ports)
-                // Proper fix is to let device advertise static lambda as resource.
-                LinkedList<Link> links = new LinkedList<>(path.links());
-                links.pollFirst();
-                links.pollLast();
-
-                List<Resource> r = convertToResources(links, toFlexGrid(lambda));
-                if (r.stream().allMatch(resourceService::isAvailable)) {
-                    // FIXME bad practice to have side-effect during stream
-                    res.addAll(r);
-                    return true;
-                }
-                return false;
-            }).findFirst().orElse(null);
-
-            if (satPath == null) {
-                // FIXME doing something very wrong to preserve old behavior
-                // as a fallback
-                log.warn("No feasible path between {}-{} using {}",
-                         src, dst, lambda);
-
-                satPath = getOpticalPaths(intent).iterator().next();
-                if (satPath == null) {
-                    throw new OpticalIntentCompilationException(
-                        "Unable to find suitable lightpath for intent " + intent);
-                }
-                return ImmutableList.of(createIntent(intent, satPath, lambda));
-            }
-
-            resources.addAll(res);
-            allocateResources(intent, resources);
-
-            return ImmutableList.of(createIntent(intent, satPath, lambda));
-        }
-
-        // FIXME: also check destination OCh port
-        // non-tunable case: early return
-        if (!srcOchPort.isTunable() || !dstOchPort.isTunable()) {
-            Path firstPath = paths.findAny().orElse(null);
-            if (firstPath == null) {
-                log.error("Unable to find suitable lightpath for intent {}", intent);
-                throw new OpticalIntentCompilationException("Unable to find suitable lightpath for intent " + intent);
-            }
-            OchSignal lambda = srcOchPort.lambda();
-            // TODO apply the same as static lambda case above
-            // - pick feasible path
-            // - allocate resources
-            //resources.addAll(convertToResources(firstPath.links(), toFlexGrid(lambda)));
-            allocateResources(intent, resources);
-            return ImmutableList.of(createIntent(intent, firstPath, lambda));
-        }
-
-        // remaining cases
-        // Use first path that the required resources are available
         Optional<Map.Entry<Path, List<OchSignal>>> found = paths
-                .map(path -> Maps.immutableEntry(path, findFirstAvailableOch(path)))
+                .map(path ->
+                        Maps.immutableEntry(path, findFirstAvailableLambda(path)))
                 .filter(entry -> !entry.getValue().isEmpty())
-                .filter(entry -> convertToResources(entry.getKey().links(),
+                .filter(entry -> convertToResources(entry.getKey(),
                         entry.getValue()).stream().allMatch(resourceService::isAvailable))
                 .findFirst();
 
+        // Allocate resources and create optical path intent
         if (found.isPresent()) {
-            resources.addAll(convertToResources(found.get().getKey().links(), found.get().getValue()));
+            resources.addAll(convertToResources(found.get().getKey(), found.get().getValue()));
 
             allocateResources(intent, resources);
 
@@ -227,9 +153,16 @@
         }
     }
 
+    /**
+     * Create installable optical path intent.
+     * Only supports fixed grid for now.
+     *
+     * @param parentIntent this intent (used for resource tracking)
+     * @param path the path to use
+     * @param lambda the lambda to use
+     * @return optical path intent
+     */
     private Intent createIntent(OpticalConnectivityIntent parentIntent, Path path, OchSignal lambda) {
-        // Create installable optical path intent
-        // Only support fixed grid for now
         OchSignalType signalType = OchSignalType.FIXED_GRID;
 
         return OpticalPathIntent.builder()
@@ -245,8 +178,30 @@
                 .build();
     }
 
+    /**
+     * Convert given lambda as discrete resource of all path ports.
+     *
+     * @param path the path
+     * @param lambda the lambda
+     * @return list of discrete resources
+     */
+    private List<Resource> convertToResources(Path path, Collection<OchSignal> lambda) {
+        return path.links().stream()
+                .flatMap(x -> Stream.of(
+                        Resources.discrete(x.src().deviceId(), x.src().port()).resource(),
+                        Resources.discrete(x.dst().deviceId(), x.dst().port()).resource()
+                ))
+                .flatMap(x -> lambda.stream().map(x::child))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Reserve all required resources for this intent.
+     *
+     * @param intent the intent
+     * @param resources list of resources to reserve
+     */
     private void allocateResources(Intent intent, List<Resource> resources) {
-        // reserve all of required resources
         List<ResourceAllocation> allocations = resourceService.allocate(intent.key(), resources);
         if (allocations.isEmpty()) {
             log.error("Resource allocation for {} failed (resource request: {})", intent.key(), resources);
@@ -259,8 +214,14 @@
         }
     }
 
-    private List<OchSignal> findFirstAvailableOch(Path path) {
-        Set<OchSignal> lambdas = findCommonLambdasOverLinks(path.links());
+    /**
+     * Find the first available lambda on the given path by checking all the port resources.
+     *
+     * @param path the path
+     * @return list of consecutive and available OChSignals
+     */
+    private List<OchSignal> findFirstAvailableLambda(Path path) {
+        Set<OchSignal> lambdas = findCommonLambdas(path);
         if (lambdas.isEmpty()) {
             return Collections.emptyList();
         }
@@ -268,16 +229,6 @@
         return findFirstLambda(lambdas, slotCount());
     }
 
-    private List<Resource> convertToResources(List<Link> links, Collection<OchSignal> lambda) {
-        return links.stream()
-                .flatMap(x -> Stream.of(
-                        Resources.discrete(x.src().deviceId(), x.src().port()).resource(),
-                        Resources.discrete(x.dst().deviceId(), x.dst().port()).resource()
-                ))
-                .flatMap(x -> lambda.stream().map(x::child))
-                .collect(Collectors.toList());
-    }
-
     /**
      * Get the number of 12.5 GHz slots required for the path.
      *
@@ -290,8 +241,14 @@
         return SLOT_COUNT;
     }
 
-    private Set<OchSignal> findCommonLambdasOverLinks(List<Link> links) {
-        return links.stream()
+    /**
+     * Find common lambdas on all ports that compose the path.
+     *
+     * @param path the path
+     * @return set of common lambdas
+     */
+    private Set<OchSignal> findCommonLambdas(Path path) {
+        return path.links().stream()
                 .flatMap(x -> Stream.of(
                         Resources.discrete(x.src().deviceId(), x.src().port()).id(),
                         Resources.discrete(x.dst().deviceId(), x.dst().port()).id()