[ONOS-7948] Netconf driver for Lumentum ROADM-20.
Added support for flowrules coming from the compilation of an OpticalConnectivityIntent.
FlowRules coming from ROADM app are still supported.
Minor updates to DeviceDiscovery and PowerConfig.

Patch 2: Driver and OpticalPathIntent support for partner-ports.
Now bidirectional intents can be configured also in presence of unidirectional links.
ROADM app visualizes devices of type TERMINAL_DEVICE and partner port details.
Checkstyle.

Patch 3: [ONOS-7974] [ONOS-79076] partner-port renamed to reverse-port. Bug fix.
Addressed comments by Ramon Casellas and Andrea Campanella. Checkstyles.

Patch 4: documentation.

Patch 5: addressed comments by Andrea Campanella.

Patch 6: checkstyle.

Change-Id: Ie5ca41eee6cb6f8f87f076f2af19c99cadd6d851
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalPathIntentCompiler.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalPathIntentCompiler.java
index d269843..f2a20fa 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalPathIntentCompiler.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalPathIntentCompiler.java
@@ -17,6 +17,12 @@
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.Port;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Deactivate;
@@ -24,11 +30,7 @@
 import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.Device;
 import org.onosproject.net.Device.Type;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.device.DeviceServiceAdapter;
 import org.onosproject.net.flow.DefaultFlowRule;
@@ -204,7 +206,7 @@
      */
     private List<FlowRule> createReverseRules(OpticalPathIntent intent) {
         TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
-        selectorBuilder.matchInPort(intent.dst().port());
+        selectorBuilder.matchInPort(reversePort(intent.dst().deviceId(), intent.dst().port()));
 
         List<FlowRule> rules = new LinkedList<>();
 
@@ -219,7 +221,7 @@
             if (!isTransparent(intent.src().deviceId())) {
                 treatmentBuilder.add(Instructions.modL0Lambda(intent.lambda()));
             }
-            treatmentBuilder.setOutput(intent.src().port());
+            treatmentBuilder.setOutput(reversePort(intent.src().deviceId(), intent.src().port()));
 
             FlowRule rule = DefaultFlowRule.builder()
                     .forDevice(intent.src().deviceId())
@@ -240,7 +242,7 @@
             if (!isTransparent(current.deviceId())) {
                 treatmentBuilder.add(Instructions.modL0Lambda(intent.lambda()));
             }
-            treatmentBuilder.setOutput(link.dst().port());
+            treatmentBuilder.setOutput(reversePort(link.dst().deviceId(), link.dst().port()));
 
             FlowRule rule = DefaultFlowRule.builder()
                     .forDevice(current.deviceId())
@@ -257,7 +259,7 @@
             }
 
             current = link.src();
-            selectorBuilder.matchInPort(link.src().port());
+            selectorBuilder.matchInPort(reversePort(link.src().deviceId(), link.src().port()));
             if (!isTransparent(current.deviceId())) {
                 selectorBuilder.add(Criteria.matchLambda(intent.lambda()));
                 selectorBuilder.add(Criteria.matchOchSignalType(intent.signalType()));
@@ -266,7 +268,7 @@
 
         // Build the egress ROADM rule
         TrafficTreatment.Builder treatmentLast = DefaultTrafficTreatment.builder();
-        treatmentLast.setOutput(intent.src().port());
+        treatmentLast.setOutput(reversePort(intent.src().deviceId(), intent.src().port()));
 
         FlowRule rule = new DefaultFlowRule.Builder()
                 .forDevice(intent.src().deviceId())
@@ -285,6 +287,25 @@
     }
 
     /**
+     * Returns the PortNum of reverse port if annotation is present, otherwise return PortNum of the port itself.
+     * In the OpenROADM YANG models it is used the term "partner-port.
+     *
+     * @param portNumber the port
+     * @return the PortNum of reverse port if annotation is present, otherwise PortNum of the port itself.
+     */
+    private PortNumber reversePort(DeviceId deviceId, PortNumber portNumber) {
+        Port port = deviceService.getPort(deviceId, portNumber);
+
+        String reversePort = port.annotations().value(OpticalPathIntent.REVERSE_PORT_ANNOTATION_KEY);
+        if (reversePort != null) {
+            PortNumber reversePortNumber = PortNumber.portNumber(reversePort);
+            return reversePortNumber;
+        } else {
+            return portNumber;
+        }
+    }
+
+    /**
      * Returns true if device does not accept flow rules, false otherwise.
      *
      * @param deviceId the device
diff --git a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmDeviceViewMessageHandler.java b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmDeviceViewMessageHandler.java
index 926b576..51c8946 100644
--- a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmDeviceViewMessageHandler.java
+++ b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmDeviceViewMessageHandler.java
@@ -92,7 +92,8 @@
         protected void populateTable(TableModel tm, ObjectNode payload) {
             for (Device device : deviceService.getDevices()) {
                 Type type = device.type();
-                if (type == Type.ROADM || type == Type.OPTICAL_AMPLIFIER || type == Type.FIBER_SWITCH) {
+                if (type == Type.ROADM || type == Type.TERMINAL_DEVICE
+                        || type == Type.OPTICAL_AMPLIFIER || type == Type.FIBER_SWITCH) {
                     populateRow(tm.addRow(), device);
                 }
             }
diff --git a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java
index 52a73d5..192eb2e 100644
--- a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java
+++ b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java
@@ -31,6 +31,7 @@
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.intent.OpticalPathIntent;
 import org.onosproject.net.optical.OpticalAnnotations;
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiConnection;
@@ -69,6 +70,7 @@
     private static final String ROADM_SET_OPS_MODE_RESP = "roadmSetOpsModeResponse";
 
     private static final String ID = "id";
+    private static final String REVERSE_PORT = "reversePort";
     private static final String NAME = "name";
     private static final String TYPE = "type";
     private static final String ENABLED = "enabled";
@@ -82,7 +84,7 @@
     private static final String SERVICE_STATE = "serviceState";
 
     private static final String[] COLUMN_IDS = {
-            ID, TYPE, NAME, ENABLED, MIN_FREQ, MAX_FREQ, GRID, POWER_RANGE,
+            ID, REVERSE_PORT, TYPE, NAME, ENABLED, MIN_FREQ, MAX_FREQ, GRID, POWER_RANGE,
             CURRENT_POWER, SERVICE_STATE, TARGET_POWER, HAS_TARGET_POWER
     };
 
@@ -139,6 +141,8 @@
             PortNumber portNum = port.number();
             getFrequencyLimit(deviceId, portNum);
             row.cell(ID, portNum.toLong())
+                    .cell(REVERSE_PORT, RoadmUtil.getAnnotation(port.annotations(),
+                            OpticalPathIntent.REVERSE_PORT_ANNOTATION_KEY))
                     .cell(TYPE, port.type())
                     .cell(ENABLED, port.isEnabled())
                     .cell(NAME, RoadmUtil.getAnnotation(port.annotations(), AnnotationKeys.PORT_NAME))
diff --git a/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.html b/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.html
index f896ebd..57cc441 100644
--- a/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.html
+++ b/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.html
@@ -31,6 +31,7 @@
             <table>
                 <tr>
                     <td colId="id" sortable>Port Number </td>
+                    <td colId="reversePort" sortable>Reverse </td>
                     <td colId="name" sortable>Name </td>
                     <td colId="type" sortable>Type </td>
                     <td colId="enabled" sortable>Enabled </td>
@@ -56,6 +57,7 @@
                 <tr ng-repeat="item in tableData track by $index"
                     ng-class="{selected: item.id === selId}">
                     <td>{{item.id}}</td>
+                    <td>{{item.reversePort}}</td>
                     <td>{{item.name}}</td>
                     <td>{{item.type}}</td>
                     <td>{{item.enabled}}</td>
diff --git a/core/api/src/main/java/org/onosproject/net/intent/OpticalPathIntent.java b/core/api/src/main/java/org/onosproject/net/intent/OpticalPathIntent.java
index 1369098..b2e7a3c 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/OpticalPathIntent.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/OpticalPathIntent.java
@@ -41,6 +41,8 @@
     private final OchSignalType signalType;
     private final boolean isBidirectional;
 
+    public static final String REVERSE_PORT_ANNOTATION_KEY = "reverse-port";
+
     private OpticalPathIntent(ApplicationId appId,
                               Key key,
                               ConnectPoint src,
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumConnection.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumConnection.java
index c63ac1e..58c5b78 100644
--- a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumConnection.java
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumConnection.java
@@ -16,7 +16,6 @@
 
 package org.onosproject.drivers.lumentum;
 
-import org.onosproject.driver.optical.flowrule.CrossConnectFlowRule;
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.PortNumber;
 import org.slf4j.Logger;
@@ -45,6 +44,8 @@
     protected double inputPower;
     protected double outputPower;
 
+    protected LumentumFlowRule rule;
+
     //TODO: compute target attenuation to obtain the desired targetPower
 
     /**
@@ -54,7 +55,7 @@
      * @param flowRuleHash the hash code associated to the Flow Rule
      * @param xc the cross connect flow rule
      */
-    public LumentumConnection(Integer id, Integer flowRuleHash, CrossConnectFlowRule xc) {
+    public LumentumConnection(Integer id, Integer flowRuleHash, LumentumFlowRule xc) {
 
         connectionId = id;
         hashCode = flowRuleHash;
@@ -64,6 +65,7 @@
         attenuation = 0.0; //dB
         inputPower = 0.0;  //dBm
         outputPower = 0.0; //dBm
+        rule = xc;
 
         if (isAddRule) {
             outPortNumber = LumentumNetconfRoadmFlowRuleProgrammable.LINE_PORT_NUMBER;
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumFlowRule.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumFlowRule.java
new file mode 100644
index 0000000..9489ff1
--- /dev/null
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumFlowRule.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2017-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.
+ */
+
+package org.onosproject.drivers.lumentum;
+
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.OchSignalCriterion;
+import org.onosproject.net.flow.criteria.OchSignalTypeCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class LumentumFlowRule extends DefaultFlowRule {
+    private static final Logger log = LoggerFactory.getLogger(LumentumFlowRule.class);
+
+    protected enum Type {
+        OPTICAL_CONNECTIVITY_INTENT_RULE,
+        ROADM_APP_RULE
+    }
+    public Type type;
+
+    private PortNumber addDrop;
+
+    private PortNumber inputPort;
+    private PortNumber outputPort;
+
+    private OchSignal ochSignal;
+    private boolean isAddRule;
+
+    //As generated by the OpticalConnectivityIntentCompiler
+    private static final int NUM_CRITERIA_INTENT = 3;
+    private static final int NUM_INSTRUCTIONS_INTENT = 2;
+
+    //As generated dy ROADM app
+    private static final int NUM_CRITERIA_ROADM = 3;
+    private static final int NUM_INSTRUCTIONS_ROADM = 1;
+
+    public LumentumFlowRule(FlowRule rule, List<PortNumber> linePorts) {
+        super(rule);
+
+        Set<Criterion> criteria = rule.selector().criteria();
+        List<Instruction> instructions = rule.treatment().immediate();
+
+        // Proper cross connect has criteria for input port, OChSignal and OCh signal type.
+        // Instruction is only output to port (rule generated by ROADM app).
+        // Instruction includes output port and OChSignal (rule generated by OpticalConnectivityIntent).
+        checkArgument((criteria.size() == NUM_CRITERIA_INTENT) ||
+                        (criteria.size() == NUM_CRITERIA_ROADM),
+                "Wrong size of flow rule criteria for cross connect.");
+        checkArgument((instructions.size() == NUM_INSTRUCTIONS_INTENT) ||
+                        (instructions.size() == NUM_INSTRUCTIONS_ROADM),
+                "Wrong size of flow rule instructions for cross connect.");
+
+        // FIXME: Ensure criteria has exactly one of each match type
+        criteria.forEach(
+                c -> checkArgument(c instanceof OchSignalCriterion ||
+                                c instanceof OchSignalTypeCriterion ||
+                                c instanceof PortCriterion,
+                        "Incompatible flow rule criteria for LumentumROADM: " + criteria
+                )
+        );
+
+        instructions.forEach(
+                c -> checkArgument(c instanceof Instructions.OutputInstruction ||
+                        c instanceof L0ModificationInstruction,
+                        "Incompatible flow rule instruction for LumentumROADM: " + instructions
+                )
+        );
+
+        if (criteria.size() == NUM_CRITERIA_INTENT && instructions.size() == NUM_INSTRUCTIONS_INTENT) {
+            log.info("Lumentum device, FlowRule coming from OpticalConnectivityIntentCompiler");
+
+            type = Type.OPTICAL_CONNECTIVITY_INTENT_RULE;
+        } else {
+            log.info("Lumentum device, FlowRule coming from ROADM app");
+
+            type = Type.ROADM_APP_RULE;
+        }
+
+        ochSignal = criteria.stream()
+                .filter(c -> c instanceof OchSignalCriterion)
+                .map(c -> ((OchSignalCriterion) c).lambda())
+                .findAny()
+                .orElse(null);
+
+        inputPort = criteria.stream()
+                .filter(c -> c instanceof PortCriterion)
+                .map(c -> ((PortCriterion) c).port())
+                .findAny()
+                .orElse(null);
+
+        outputPort = instructions.stream()
+                .filter(c -> c instanceof Instructions.OutputInstruction)
+                .map(c -> ((Instructions.OutputInstruction) c).port())
+                .findAny()
+                .orElse(null);
+
+        // Add or drop rule? Get the output instruction
+        Instructions.OutputInstruction outInstruction = (Instructions.OutputInstruction) instructions.stream()
+                .filter(c -> c instanceof Instructions.OutputInstruction)
+                .findAny()
+                .orElse(null);
+
+        if (linePorts.contains(outInstruction.port())) {
+            addDrop = criteria.stream()
+                    .filter(c -> c instanceof PortCriterion)
+                    .map(c -> ((PortCriterion) c).port())
+                    .findAny()
+                    .orElse(null);
+            isAddRule = true;
+        } else {
+            addDrop = outInstruction.port();
+            isAddRule = false;
+        }
+    }
+
+    public PortNumber addDrop() {
+        return addDrop;
+    }
+
+    public OchSignal ochSignal() {
+        return ochSignal;
+    }
+
+    public boolean isAddRule() {
+        return isAddRule;
+    }
+
+    public PortNumber getInputPort() {
+        return inputPort;
+    }
+
+    public PortNumber getOutputPort() {
+        return outputPort;
+    }
+}
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmDiscovery.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmDiscovery.java
index 2cdcc1a..eea8b59 100644
--- a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmDiscovery.java
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmDiscovery.java
@@ -23,14 +23,17 @@
 import org.apache.commons.configuration.XMLConfiguration;
 import org.apache.commons.lang3.StringUtils;
 
+import org.onlab.packet.ChassisId;
+import org.onlab.util.Frequency;
 import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Device;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
-import org.onosproject.net.DefaultAnnotations;
-import org.onosproject.net.SparseAnnotations;
 import org.onosproject.net.AnnotationKeys;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
 import org.onosproject.net.device.DefaultDeviceDescription;
 import org.onosproject.net.device.DefaultPortDescription;
 import org.onosproject.net.device.DeviceDescription;
@@ -38,6 +41,7 @@
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.device.PortDescription;
 import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.intent.OpticalPathIntent;
 import org.onosproject.netconf.NetconfController;
 import org.onosproject.netconf.NetconfException;
 import org.onosproject.netconf.NetconfSession;
@@ -49,6 +53,7 @@
 import java.util.List;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.optical.device.OmsPortHelper.omsPortDescription;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -72,27 +77,33 @@
     private static final String IN_SERVICE = "in-service";
     private static final String PORT_NAME = "entity-description";
 
+    public static final ChannelSpacing CHANNEL_SPACING_50 = ChannelSpacing.CHL_50GHZ;
+    public static final Frequency START_CENTER_FREQ_50 = Frequency.ofGHz(191_350);
+    public static final Frequency END_CENTER_FREQ_50 = Frequency.ofGHz(196_100);
+
+    private static final int MIN_MUX_PORT = 4101;
+    private static final int MAX_MUX_PORT = 4120;
+    private static final int MIN_DEM_PORT = 5201;
+    private static final int MAX_DEM_PORT = 5220;
+    private static final int DELTA_MUX_DEM_PORT = MIN_DEM_PORT - MIN_MUX_PORT;
+
     private final Logger log = getLogger(getClass());
 
     @Override
     public DeviceDescription discoverDeviceDetails() {
+        SparseAnnotations annotations = DefaultAnnotations.builder().build();
+
+        log.info("Lumentum NETCONF - starting discoverDeviceDetails");
 
         // Some defaults values
         String vendor       = "Lumentum";
         String hwVersion    = "not loaded";
         String swVersion    = "not loaded";
         String serialNumber = "not loaded";
-        String chassisId    = "not loaded";
+        String chassisData    = "ne=1;chassis=10";
 
+        ChassisId chassisId = null;
         DeviceId deviceId = handler().data().deviceId();
-        DeviceService deviceService = checkNotNull(handler().get(DeviceService.class));
-        Device device = deviceService.getDevice(deviceId);
-
-        //Get the configuration from the device
-        if (device == null) {
-            log.error("Lumentum NETCONF - device object not found for {}", deviceId);
-            return null;
-        }
 
         NetconfSession session = getNetconfSession();
 
@@ -131,7 +142,10 @@
 
             hwVersion    = xconf.getString("data.chassis-list.chassis.state.loteq:hardware-rev", hwVersion);
             serialNumber = xconf.getString("data.chassis-list.chassis.state.loteq:serial-no", serialNumber);
-            chassisId    = xconf.getString("data.chassis-list.chassis.dn", chassisId);
+            chassisData  = xconf.getString("data.chassis-list.chassis.dn", chassisData);
+
+            String[] parts = chassisData.split("chassis=");
+            chassisId = new ChassisId(Long.valueOf(parts[1], 10));
 
         } catch (NetconfException e) {
             log.error("Lumentum NETCONF error in session.get", e);
@@ -151,8 +165,7 @@
 
         //Return the Device Description
         return new DefaultDeviceDescription(deviceId.uri(), Device.Type.ROADM,
-                                            vendor, hwVersion, swVersion, serialNumber,
-                                            device.chassisId(), (SparseAnnotations) device.annotations());
+                vendor, hwVersion, swVersion, serialNumber, chassisId, annotations);
     }
 
     @Override
@@ -246,6 +259,27 @@
                 log.error("Port Type not correctly loaded");
             }
 
+            //Store reverse port index in the annotations
+            Long reversePortId;
+
+            /**
+             * Setting the reverse port value for the unidirectional ports.
+             *
+             * In this device each port includes an input fiber and an output fiber.
+             * The 20 input  fibers are numbered from MIN_MUX_PORT = 4101 to MAX_MUX_PORT = 4120.
+             * The 20 output fibers are numbered from MIN_DEM_PORT = 5201 to MAX_DEM_PORT = 5220.
+             *
+             * Where port 520x is always the reverse of 410x.
+             */
+            if ((portNum.toLong() >= MIN_MUX_PORT) && (portNum.toLong() <= MAX_MUX_PORT)) {
+                reversePortId = portNum.toLong() + DELTA_MUX_DEM_PORT;
+                annotations.set(OpticalPathIntent.REVERSE_PORT_ANNOTATION_KEY, reversePortId.toString());
+            }
+            if ((portNum.toLong() >= MIN_DEM_PORT) && (portNum.toLong() <= MAX_DEM_PORT)) {
+                reversePortId = portNum.toLong() - DELTA_MUX_DEM_PORT;
+                annotations.set(OpticalPathIntent.REVERSE_PORT_ANNOTATION_KEY, reversePortId.toString());
+            }
+
             //Load other information
             pcfg.getKeys().forEachRemaining(k -> {
                 if (!k.contains(DN) && !k.contains(PORT_SPEED) && !k.contains(PORT_EXTENSION)
@@ -271,14 +305,23 @@
             log.debug("Lumentum NETCONF - retrieved port {},{},{},{},{}",
                     portNum, isEnabled, type, speed, annotations.build());
 
-            DefaultPortDescription.Builder portDescriptionBuilder = DefaultPortDescription.builder();
-            portDescriptionBuilder.withPortNumber(portNum)
-                    .isEnabled(isEnabled)
-                    .type(type)
-                    .portSpeed(speed)
-                    .annotations(annotations.build());
+            if (type == Port.Type.FIBER) {
+                portDescriptions.add(omsPortDescription(portNum,
+                        isEnabled,
+                        START_CENTER_FREQ_50,
+                        END_CENTER_FREQ_50,
+                        CHANNEL_SPACING_50.frequency(),
+                        annotations.build()));
+            } else {
+                DefaultPortDescription.Builder portDescriptionBuilder = DefaultPortDescription.builder();
+                portDescriptionBuilder.withPortNumber(portNum)
+                        .isEnabled(isEnabled)
+                        .type(type)
+                        .portSpeed(speed)
+                        .annotations(annotations.build());
 
-            portDescriptions.add(portDescriptionBuilder.build());
+                portDescriptions.add(portDescriptionBuilder.build());
+            }
         });
 
         return portDescriptions;
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmFlowRuleProgrammable.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmFlowRuleProgrammable.java
index ccf2c5a..13f82eb 100644
--- a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmFlowRuleProgrammable.java
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmFlowRuleProgrammable.java
@@ -22,7 +22,6 @@
 import org.onlab.util.Frequency;
 import org.onlab.util.Spectrum;
 import org.onosproject.driver.optical.flowrule.CrossConnectCache;
-import org.onosproject.driver.optical.flowrule.CrossConnectFlowRule;
 import org.onosproject.drivers.utilities.XmlConfigParser;
 import org.onosproject.net.ChannelSpacing;
 import org.onosproject.net.GridType;
@@ -42,6 +41,7 @@
 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.Instructions;
 import org.onosproject.netconf.NetconfController;
 import org.onosproject.netconf.NetconfException;
 import org.onosproject.netconf.NetconfSession;
@@ -127,14 +127,14 @@
     public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
         // Apply the  rules on the device
         Collection<FlowRule> added = rules.stream()
-                .map(r -> new CrossConnectFlowRule(r, getLinePorts()))
+                .map(r -> new LumentumFlowRule(r, getLinePorts()))
                 .filter(xc -> rpcAddConnection(xc))
                 .collect(Collectors.toList());
 
         // Cache the cookie/priority
         CrossConnectCache cache = this.handler().get(CrossConnectCache.class);
         added.forEach(xc -> cache.set(
-                Objects.hash(data().deviceId(), xc.selector(), xc.treatment()),
+                Objects.hash(data().deviceId(), xc.selector()),
                 xc.id(),
                 xc.priority()));
 
@@ -148,14 +148,14 @@
     public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
         // Remove the valid rules from the device
         Collection<FlowRule> removed = rules.stream()
-                .map(r -> new CrossConnectFlowRule(r, getLinePorts()))
+                .map(r -> new LumentumFlowRule(r, getLinePorts()))
                 .filter(xc -> rpcDeleteConnection(xc))
                 .collect(Collectors.toList());
 
         // Remove flow rule from cache
         CrossConnectCache cache = this.handler().get(CrossConnectCache.class);
         removed.forEach(xc -> cache.remove(
-                Objects.hash(data().deviceId(), xc.selector(), xc.treatment())));
+                Objects.hash(data().deviceId(), xc.selector())));
 
         removed.forEach(xc -> log.debug("Lumentum NETCONF - removed cached FlowRule selector {} treatment {}",
                 xc.selector(), xc.treatment()));
@@ -261,14 +261,11 @@
                 .add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID))
                 .add(Criteria.matchLambda(toOchSignal(startFreq, endFreq)))
                 .build();
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setOutput(pair.getLeft() == 1 ? LINE_PORT_NUMBER : portNumber)
-                .build();
 
         log.debug("Lumentum NETCONF - retrieved FlowRule startFreq {} endFreq {}", startFreq, endFreq);
 
         // Lookup flow ID and priority
-        int hash = Objects.hash(data().deviceId(), selector, treatment);
+        int hash = Objects.hash(data().deviceId(), selector);
         CrossConnectCache cache = this.handler().get(CrossConnectCache.class);
         Pair<FlowId, Integer> lookup = cache.get(hash);
 
@@ -277,6 +274,18 @@
                 .findFirst()
                 .orElse(null);
 
+        TrafficTreatment treatment;
+        if (conn.rule.type == LumentumFlowRule.Type.OPTICAL_CONNECTIVITY_INTENT_RULE) {
+            treatment = DefaultTrafficTreatment.builder()
+                    .add(Instructions.modL0Lambda(toOchSignal(startFreq, endFreq)))
+                    .setOutput(pair.getLeft() == 1 ? LINE_PORT_NUMBER : portNumber)
+                    .build();
+        } else { // This is ROADM_APP_RULE
+            treatment = DefaultTrafficTreatment.builder()
+                    .setOutput(pair.getLeft() == 1 ? LINE_PORT_NUMBER : portNumber)
+                    .build();
+        }
+
         //If the flow entry is not in the cache: return null/publish the flow rule
         if ((lookup == null) || (conn == null)) {
            log.error("Lumentum NETCONF connection not in connectionSet {}", pair.getRight());
@@ -328,7 +337,7 @@
      * @param xc the cross connect flow rule
      * @return pair of module (1 for MUX/ADD, 2 for DEMUX/DROP) and connection number
      */
-    private Pair<Short, Short> setModuleConnection(CrossConnectFlowRule xc, Integer id) {
+    private Pair<Short, Short> setModuleConnection(LumentumFlowRule xc, Integer id) {
         if (xc.isAddRule()) {
             return Pair.of((short) 1, id.shortValue());
         } else {
@@ -341,12 +350,12 @@
      *
      * Connection number is incremental within the class and associated to the rule hash.
      *
-     * @param xc the cross connect flow rule
+     * @param xc the cross connect flow
      * @return pair of module (1 for MUX/ADD, 2 for DEMUX/DROP) and connection number
      */
-    private Pair<Short, Short> retrieveModuleConnection(CrossConnectFlowRule xc) {
+    private Pair<Short, Short> retrieveModuleConnection(LumentumFlowRule xc) {
 
-        int hash = Objects.hash(data().deviceId(), xc.selector(), xc.treatment());
+        int hash = Objects.hash(data().deviceId(), xc.selector());
 
         LumentumConnection retrievedConnection = CONNECTION_SET.stream()
                 .filter(conn -> conn.getHash() == hash)
@@ -372,7 +381,7 @@
     }
 
     //Following Lumentum documentation rpc operation to configure a new connection
-    private boolean rpcAddConnection(CrossConnectFlowRule xc) {
+    private boolean rpcAddConnection(LumentumFlowRule xc) {
 
         int currentConnectionId = generateConnectionId();
 
@@ -382,7 +391,7 @@
         }
 
         LumentumConnection connection = new LumentumConnection(currentConnectionId,
-                Objects.hash(data().deviceId(), xc.selector(), xc.treatment()), xc);
+                Objects.hash(data().deviceId(), xc.selector()), xc);
 
         CONNECTION_SET.add(connection);
 
@@ -464,7 +473,7 @@
     }
 
     //Following Lumentum documentation rpc operation to delete a new connection
-    private boolean rpcDeleteConnection(CrossConnectFlowRule xc) {
+    private boolean rpcDeleteConnection(LumentumFlowRule xc) {
         Pair<Short, Short> pair = retrieveModuleConnection(xc);
 
         if (pair == null) {
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumPowerConfig.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumPowerConfig.java
index e292947..16000bd 100644
--- a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumPowerConfig.java
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumPowerConfig.java
@@ -102,7 +102,7 @@
     //TODO implement actual get configuration from the device
     //This is used by ROADM application to retrieve attenuation parameter, with T instanceof OchSignal
     private Long acquireTargetPower(PortNumber port, T component) {
-        log.info("Lumentum get port {} target power...", port);
+        log.debug("Lumentum get port {} target power...", port);
 
         if (component instanceof OchSignal) {
             //FIXME include port in the filter
@@ -112,10 +112,10 @@
                     .orElse(null);
 
             if (conn == null) {
-                log.info("Lumentum NETCONF fail to retrieve attenuation signal {} port {}", component, port);
+                log.debug("Lumentum NETCONF fail to retrieve attenuation signal {} port {}", component, port);
                 return 0L;
             } else {
-                log.info("Lumentum NETCONF on port {} attenuation {}", port, conn.attenuation);
+                log.debug("Lumentum NETCONF on port {} attenuation {}", port, conn.attenuation);
                 return ((long) (conn.attenuation * 100));
             }
         }
@@ -126,7 +126,7 @@
     //TODO implement actual get configuration from the device
     //This is used by ROADM application to retrieve attenuation parameter, with T instanceof OchSignal
     private Long acquireCurrentPower(PortNumber port, T component) {
-        log.info("Lumentum get port {} current power...", port);
+        log.debug("Lumentum get port {} current power...", port);
 
         if (component instanceof OchSignal) {
             //FIXME include port in the filter
@@ -136,10 +136,10 @@
                     .orElse(null);
 
             if (conn == null) {
-                log.info("Lumentum NETCONF fail to retrieve power signal {} port {}", component, port);
+                log.debug("Lumentum NETCONF fail to retrieve power signal {} port {}", component, port);
                 return 0L;
             } else {
-                log.info("Lumentum NETCONF on port {} power {}", port, conn.inputPower);
+                log.debug("Lumentum NETCONF on port {} power {}", port, conn.inputPower);
                 return ((long) (conn.inputPower * 100));
             }
         }