[ODTN] support to FLEX_GRID intents: REST inetrface, intent compilation, openconfig and openroadm drivers.
patch 2: checkstyle
patch 3: davide's comments

Change-Id: I7574e8e6ee3ac3c18dc1533cad1a8f25142c26bc
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 682ea1f..68ccd45 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
@@ -83,7 +83,9 @@
 
     private 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;
+    private static final int DEFAULT_SLOT_GRANULARITY = 4;
+    private static final GridType DEFAULT_GRID_TYPE = GridType.DWDM;
+    private static final ChannelSpacing DEFAULT_CHANNEL_SPACING = ChannelSpacing.CHL_50GHZ;
     private static final ProviderId PROVIDER_ID = new ProviderId("opticalConnectivityIntent",
             "org.onosproject.net.optical.intent");
 
@@ -110,9 +112,20 @@
         intentManager.unregisterCompiler(OpticalConnectivityIntent.class);
     }
 
+    /**
+     * Path computation and spectrum assignment.
+     * Supports fixed and flex grids.
+     *
+     * Path and signal can be provided within the intent.
+     * If not provided they are computed in this function.
+     * If signal is not provided a default channel is allocated with width 50 GHz, on the 50 GHz spacing.
+     *
+     * @param intent this intent (used for resource tracking)
+     * @param installable intents
+     * @return list of optical path intents (one per direction)
+     */
     @Override
-    public List<Intent> compile(OpticalConnectivityIntent intent,
-                                List<Intent> installable) {
+    public List<Intent> compile(OpticalConnectivityIntent intent, List<Intent> installable) {
         // Check if source and destination are optical OCh ports
         ConnectPoint src = intent.getSrc();
         ConnectPoint dst = intent.getDst();
@@ -127,7 +140,7 @@
         // TODO: try to release intent resources in IntentManager.
         resourceService.release(intent.key());
 
-        // Check OCh port availability
+        // Check OCH src and dst 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();
@@ -139,7 +152,8 @@
         resources.add(srcPortResource);
         resources.add(dstPortResource);
 
-        // If there is a suggestedPath, use this path without further checking, otherwise trigger path computation
+        // If there is a valid suggestedPath, use this path without further checking
+        // Otherwise trigger path computation
         Stream<Path> paths;
         if (intent.suggestedPath().isPresent()) {
             paths = Stream.of(intent.suggestedPath().get());
@@ -157,11 +171,19 @@
 
         // Allocate resources and create optical path intent
         if (found.isPresent()) {
-            log.debug("Suitable lightpath FOUND for intent {}", intent);
+            log.debug("Suitable path and lambdas FOUND for intent {}", intent);
             resources.addAll(convertToResources(found.get().getKey(), found.get().getValue()));
             allocateResources(intent, resources);
-            OchSignal ochSignal = OchSignal.toFixedGrid(found.get().getValue(), ChannelSpacing.CHL_50GHZ);
+
+            if (intent.ochSignal().isPresent()) {
+                if (intent.ochSignal().get().gridType() == GridType.FLEX) {
+                    return ImmutableList.of(createIntent(intent, found.get().getKey(), intent.ochSignal().get()));
+                }
+            }
+
+            OchSignal ochSignal = OchSignal.toFixedGrid(found.get().getValue(), DEFAULT_CHANNEL_SPACING);
             return ImmutableList.of(createIntent(intent, found.get().getKey(), ochSignal));
+
         } else {
             log.error("Unable to find suitable lightpath for intent {}", intent);
             throw new OpticalIntentCompilationException("Unable to find suitable lightpath for intent " + intent);
@@ -170,7 +192,7 @@
 
     /**
      * Create installable optical path intent.
-     * Only supports fixed grid for now.
+     * Supports fixed and flex grids.
      *
      * @param parentIntent this intent (used for resource tracking)
      * @param path         the path to use
@@ -178,7 +200,12 @@
      * @return optical path intent
      */
     private Intent createIntent(OpticalConnectivityIntent parentIntent, Path path, OchSignal lambda) {
-        OchSignalType signalType = OchSignalType.FIXED_GRID;
+        OchSignalType signalType;
+        if (lambda.gridType().equals(GridType.FLEX)) {
+            signalType = OchSignalType.FLEX_GRID;
+        } else {
+            signalType = OchSignalType.FIXED_GRID;
+        }
 
         return OpticalPathIntent.builder()
                 .appId(parentIntent.appId())
@@ -243,18 +270,23 @@
             //create lambdas w.r.t. slotGanularity/slotWidth
             OchSignal ochSignal = intent.ochSignal().get();
             if (ochSignal.gridType() == GridType.FLEX) {
-                // multiplier sits in the middle of slots
-                int startMultiplier = ochSignal.spacingMultiplier() - (ochSignal.slotGranularity() / 2);
-                return IntStream.range(0, ochSignal.slotGranularity())
+                int startMultiplier = (int) (1 - ochSignal.slotGranularity() + ochSignal.spacingMultiplier());
+
+                List<OchSignal> channels = IntStream.range(0, ochSignal.slotGranularity())
                         .mapToObj(x -> OchSignal.newFlexGridSlot(startMultiplier + (2 * x)))
                         .collect(Collectors.toList());
+
+                return channels;
             } else if (ochSignal.gridType() == GridType.DWDM) {
                 int startMultiplier = (int) (1 - ochSignal.slotGranularity() +
                         ochSignal.spacingMultiplier() * ochSignal.channelSpacing().frequency().asHz() /
                                 ChannelSpacing.CHL_6P25GHZ.frequency().asHz());
-                return IntStream.range(0, ochSignal.slotGranularity())
+
+                List<OchSignal> channels = IntStream.range(0, ochSignal.slotGranularity())
                         .mapToObj(x -> OchSignal.newFlexGridSlot(startMultiplier + (2 * x)))
                         .collect(Collectors.toList());
+
+                return channels;
             }
             //TODO: add support for other gridTypes
             log.error("Grid type: {} not supported for user defined signal intents", ochSignal.gridType());
@@ -266,19 +298,7 @@
             return Collections.emptyList();
         }
 
-        return findFirstLambda(lambdas, slotCount());
-    }
-
-    /**
-     * Get the number of 12.5 GHz slots required for the path.
-     * <p>
-     * 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;
+        return findFirstLambda(lambdas, DEFAULT_SLOT_GRANULARITY);
     }
 
     /**
@@ -458,7 +478,6 @@
                         return path;
                     });
         }
-
         return paths;
     }
 }
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/util/OpticalIntentUtility.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/util/OpticalIntentUtility.java
index 7956f57..b351210 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/util/OpticalIntentUtility.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/util/OpticalIntentUtility.java
@@ -163,7 +163,7 @@
 
         if (ingress == null || egress == null) {
             log.error("Invalid endpoint(s); could not create optical intent");
-            return intent;
+            return null;
         }
 
         DeviceService ds = opticalView(deviceService);
@@ -178,7 +178,7 @@
             // continue only if both OduClt port's Devices are of the same type
             if (!(srcDevice.type().equals(dstDevice.type()))) {
                 log.debug("Devices without same deviceType: SRC={} and DST={}", srcDevice.type(), dstDevice.type());
-                return intent;
+                return null;
             }
 
             CltSignalType signalType = ((OduCltPort) srcPort).signalType();
diff --git a/apps/optical-rest/src/main/java/org/onosproject/net/optical/rest/OpticalIntentsWebResource.java b/apps/optical-rest/src/main/java/org/onosproject/net/optical/rest/OpticalIntentsWebResource.java
index 1570a2e..c0dedf5 100644
--- a/apps/optical-rest/src/main/java/org/onosproject/net/optical/rest/OpticalIntentsWebResource.java
+++ b/apps/optical-rest/src/main/java/org/onosproject/net/optical/rest/OpticalIntentsWebResource.java
@@ -25,8 +25,10 @@
 import org.onlab.graph.ScalarWeight;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.net.ChannelSpacing;
 import org.onosproject.net.Device;
 import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.GridType;
 import org.onosproject.net.Link;
 import org.onosproject.net.DefaultPath;
 import org.onosproject.net.ConnectPoint;
@@ -279,6 +281,49 @@
                 throw new IllegalArgumentException(JSON_INVALID);
             } else {
                 signal = OchSignalCodec.decode((ObjectNode) signalJson);
+
+                if (signal.gridType() == GridType.FLEX) {
+                    if (signal.channelSpacing() != ChannelSpacing.CHL_6P25GHZ) {
+                        throw new IllegalArgumentException("FLEX grid requires CHL_6P25GHZ spacing");
+                    }
+                    if (isOdd(signal.slotGranularity()) && !isOdd(signal.spacingMultiplier())) {
+                        throw new IllegalArgumentException("FLEX grid using odd Granularity requires odd Multiplier");
+                    }
+                    if (!isOdd(signal.slotGranularity()) && isOdd(signal.spacingMultiplier())) {
+                        throw new IllegalArgumentException("FLEX grid using even Granularity requires even Multiplier");
+                    }
+                }
+
+                if (signal.gridType() == GridType.DWDM) {
+                    if (signal.channelSpacing() == ChannelSpacing.CHL_6P25GHZ) {
+                        throw new IllegalArgumentException("DWDM grid requires spacing CHL_12P5GHZ, " +
+                                "CHL_25GHZ, CHL_50GHZ or CHL_100GHZ");
+                    }
+
+                    if (signal.channelSpacing() == ChannelSpacing.CHL_12P5GHZ) {
+                        if (signal.slotGranularity() != 1) {
+                            throw new IllegalArgumentException("Spacing CHL_12P5GHZ requires granularity 1");
+                        }
+                    }
+
+                    if (signal.channelSpacing() == ChannelSpacing.CHL_25GHZ) {
+                        if (signal.slotGranularity() != 2) {
+                            throw new IllegalArgumentException("Spacing CHL_25GHZ requires granularity 2");
+                        }
+                    }
+
+                    if (signal.channelSpacing() == ChannelSpacing.CHL_50GHZ) {
+                        if (signal.slotGranularity() != 4) {
+                            throw new IllegalArgumentException("Spacing CHL_50GHZ requires granularity 4");
+                        }
+                    }
+
+                    if (signal.channelSpacing() == ChannelSpacing.CHL_100GHZ) {
+                        if (signal.slotGranularity() != 8) {
+                            throw new IllegalArgumentException("Spacing CHL_100GHZ requires granularity 8");
+                        }
+                    }
+                }
             }
         }
 
@@ -361,4 +406,11 @@
         }
         return true;
     }
+
+    private boolean isOdd(int n) {
+        if (n % 2 != 0) {
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/ClientLineTerminalDeviceFlowRuleProgrammable.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/ClientLineTerminalDeviceFlowRuleProgrammable.java
index 8896b98..ac9fd48 100644
--- a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/ClientLineTerminalDeviceFlowRuleProgrammable.java
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/ClientLineTerminalDeviceFlowRuleProgrammable.java
@@ -604,16 +604,10 @@
         //Retrieved rules and cached rules are considered equal if both selector and treatment are equal
         cacheAddRule = null;
         cacheDropRule = null;
-        if (getConnectionCache().size(did()) != 0) {
-            cacheDropRule = getConnectionCache().get(did()).stream()
-                    .filter(r -> (r.selector().equals(selectorDrop) && r.treatment().equals(treatmentDrop)))
-                    .findFirst()
-                    .orElse(null);
 
-            cacheAddRule = getConnectionCache().get(did()).stream()
-                    .filter(r -> (r.selector().equals(selectorAdd) && r.treatment().equals(treatmentAdd)))
-                    .findFirst()
-                    .orElse(null);
+        if (getConnectionCache().size(did()) != 0) {
+            cacheDropRule = findDropRule(inputPortNumber, outputPortNumber, centralFreq);
+            cacheAddRule = findAddRule(inputPortNumber, outputPortNumber, centralFreq);
         }
 
         //Include the DROP rule to the retrieved rules if found in cache
@@ -937,6 +931,33 @@
                 .collect(Collectors.toList());
 
         return linePorts;
+    }
 
+    private FlowRule findDropRule(PortNumber inPort, PortNumber outPort, Frequency freq) {
+
+        FlowRule rule = getConnectionCache().get(did()).stream()
+                .filter(r -> r instanceof TerminalDeviceFlowRule)
+                .filter(r -> ((TerminalDeviceFlowRule) r).type.equals(TerminalDeviceFlowRule.Type.LINE_EGRESS))
+                .filter(r -> ((TerminalDeviceFlowRule) r).ochSignal().centralFrequency().equals(freq))
+                .filter(r -> ((TerminalDeviceFlowRule) r).inPort().equals(inPort))
+                .filter(r -> ((TerminalDeviceFlowRule) r).outPort().equals(outPort))
+                .findFirst()
+                .orElse(null);
+
+        return rule;
+    }
+
+    private FlowRule findAddRule(PortNumber inPort, PortNumber outPort, Frequency freq) {
+
+        FlowRule rule = getConnectionCache().get(did()).stream()
+                .filter(r -> r instanceof TerminalDeviceFlowRule)
+                .filter(r -> ((TerminalDeviceFlowRule) r).type.equals(TerminalDeviceFlowRule.Type.LINE_INGRESS))
+                .filter(r -> ((TerminalDeviceFlowRule) r).ochSignal().centralFrequency().equals(freq))
+                .filter(r -> ((TerminalDeviceFlowRule) r).inPort().equals(inPort))
+                .filter(r -> ((TerminalDeviceFlowRule) r).outPort().equals(outPort))
+                .findFirst()
+                .orElse(null);
+
+        return rule;
     }
 }
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmConnection.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmConnection.java
index 5afaf2f..63a1be9 100644
--- a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmConnection.java
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmConnection.java
@@ -28,7 +28,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-
 class OpenRoadmConnectionBase {
 
     protected static final Logger log = LoggerFactory.getLogger(OpenRoadmConnection.class);
@@ -146,7 +145,7 @@
       // Conversion from ochSignal (center frequency + diameter) to OpenRoadm
       // Media Channel (start - end)
       Frequency freqRadius = Frequency.ofHz(
-          xc.ochSignal().channelSpacing().frequency().asHz() / 2);
+          xc.ochSignal().slotWidth().asHz() / 2);
       Frequency centerFreq = xc.ochSignal().centralFrequency();
 
       // e.g. DEG1-TTP-RX
@@ -161,12 +160,15 @@
 
       srcMcMinFrequency = centerFreq.subtract(freqRadius);
       srcMcMaxFrequency = centerFreq.add(freqRadius);
+
+      log.error("OPENROADM CONNECTION GENERATED min {} max {}", srcMcMinFrequency, srcMcMaxFrequency);
+
       dstMcMinFrequency = srcMcMinFrequency;
       dstMcMaxFrequency = srcMcMaxFrequency;
       srcNmcFrequency = centerFreq;
       dstNmcFrequency = centerFreq;
-      srcNmcWidth = xc.ochSignal().channelSpacing().frequency();
-      dstNmcWidth = xc.ochSignal().channelSpacing().frequency();
+      srcNmcWidth = xc.ochSignal().slotWidth();
+      dstNmcWidth = xc.ochSignal().slotWidth();
 
       srcMcSupportingInterface =
           "OMS-" +
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmFlowRuleProgrammable.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmFlowRuleProgrammable.java
index 54d5152..e29bd60 100644
--- a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmFlowRuleProgrammable.java
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmFlowRuleProgrammable.java
@@ -34,23 +34,14 @@
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.GridType;
 import org.onosproject.net.OchSignal;
-import org.onosproject.net.OchSignalType;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.AbstractHandlerBehaviour;
 import org.onosproject.net.flow.DefaultFlowEntry;
-import org.onosproject.net.flow.DefaultFlowRule;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.FlowRuleProgrammable;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.Criteria;
-import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions;
 import org.onosproject.netconf.DatastoreId;
 import org.onosproject.netconf.NetconfController;
 import org.onosproject.netconf.NetconfException;
@@ -104,7 +95,6 @@
         log.info("OPENROADM {}: " + format, did(), arguments);
     }
 
-
     /**
      * Get a list of Port numbers that are LINE ports (degree).
      * <p>
@@ -156,7 +146,6 @@
         }
     }
 
-
     /**
      * Get the flow entries that are present on the device, called by
      * FlowRuleDriverProvider. <p> The flow entries must match exactly the
@@ -181,7 +170,6 @@
         return entries;
     }
 
-
     /**
      * Apply the flow entries specified in the collection rules.
      *
@@ -204,7 +192,6 @@
         return added;
     }
 
-
     /**
      * Remove the specified flow rules.
      *
@@ -287,35 +274,9 @@
         } else {
             openRoadmLog("connection retrieved {}", name);
         }
-        OpenRoadmFlowRule xc = new OpenRoadmFlowRule(flowRule, getLinePorts());
-        DeviceService deviceService = this.handler().get(DeviceService.class);
-        OpenRoadmConnection conn = OpenRoadmConnectionFactory.create(name, xc, deviceService);
-        OchSignal och = toOchSignalCenterWidth(conn.srcNmcFrequency, conn.srcNmcWidth);
-        // Build the rule selector and treatment
-        TrafficSelector selector =
-          DefaultTrafficSelector.builder()
-            .matchInPort(conn.inPortNumber)
-            .add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID))
-            .add(Criteria.matchLambda(och))
-            .build();
-        Instruction ochInstruction = Instructions.modL0Lambda(och);
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                                       .add(ochInstruction)
-                                       .setOutput(conn.outPortNumber)
-                                       .build();
-
-        return DefaultFlowRule.builder()
-          .forDevice(data().deviceId())
-          .makePermanent()
-          .withSelector(selector)
-          .withTreatment(treatment)
-          .withPriority(conn.priority)
-          .withCookie(conn.id.value())
-          .build();
+        return flowRule;
     }
 
-
-
     /**
      * Delete a ROADM Interface given its name.
      *
@@ -334,8 +295,6 @@
         }
     }
 
-
-
     /**
      * Delete a ROADM Connection given its name.
      *
@@ -393,8 +352,6 @@
         return true;
     }
 
-
-
     /**
      * Entry point to remove a Crossconnect.
      *
@@ -429,7 +386,6 @@
             editConfigDeleteInterfaceEntry(conn.dstMcName);
         }
 
-
         return true;
     }
 
@@ -587,7 +543,6 @@
         return true;
     }
 
-
     /**
      * Create a ROADM Connection given its data.
      *
@@ -740,7 +695,6 @@
 
             return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_100GHZ, multiplier, slots);
         }
-
         return null;
     }
 }