Lumentum ROADM-20 Whitebox, NETCONF driver supporting ROADM app main functionalities

Change-Id: Iee24a3db7ebd948a251f74c91e4b5de57b8f1b19
diff --git a/drivers/lumentum/BUCK b/drivers/lumentum/BUCK
index 468aad6..1699870 100644
--- a/drivers/lumentum/BUCK
+++ b/drivers/lumentum/BUCK
@@ -4,6 +4,7 @@
     '//drivers/utilities:onos-drivers-utilities',
     '//protocols/snmp/api:onos-protocols-snmp-api',
     '//protocols/tl1/api:onos-protocols-tl1-api',
+    '//protocols/netconf/api:onos-protocols-netconf-api',
     '//incubator/api:onos-incubator-api',
     '//apps/optical-model:onos-apps-optical-model',
     '//drivers/optical:onos-drivers-optical',
@@ -14,6 +15,11 @@
     '//core/api:onos-api-tests',
 ]
 
+BUNDLES = [
+    ':onos-drivers-lumentum',
+    '//drivers/utilities:onos-drivers-utilities',
+]
+
 osgi_jar_with_tests (
     deps = COMPILE_DEPS,
     test_deps = TEST_DEPS,
@@ -26,7 +32,10 @@
     title = 'Lumentum Drivers',
     category = 'Drivers',
     url = 'http://onosproject.org',
-    description = 'Adds support for Lumentum devices.',
-    required_apps = [ 'org.onosproject.snmp', 'org.onosproject.optical-model',
-     'org.onosproject.tl1', 'org.onosproject.drivers.optical'],
+
+    description = 'ONOS Lumentum Device Drivers application.',
+    included_bundles = BUNDLES,
+    required_apps = [ 'org.onosproject.snmp', 'org.onosproject.faultmanagement', 'org.onosproject.optical-model',
+     'org.onosproject.tl1', 'org.onosproject.netconf', 'org.onosproject.drivers.optical'],
+
 )
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
new file mode 100644
index 0000000..c63ac1e
--- /dev/null
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumConnection.java
@@ -0,0 +1,99 @@
+/*
+ * 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.driver.optical.flowrule.CrossConnectFlowRule;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Lumentum cross connection abstraction.
+ *
+ * Required to store all information needed by Lumentum device not included in CrossConnectFlowRule.
+ */
+public class LumentumConnection {
+
+    private static final Logger log = LoggerFactory.getLogger(LumentumConnection.class);
+
+    protected boolean isAddRule;
+    protected int connectionId;
+    protected int connectionModule;
+    protected int hashCode;
+    protected PortNumber inPortNumber;
+    protected PortNumber outPortNumber;
+    protected OchSignal ochSignal;
+
+    protected double attenuation;
+    protected double targetAttenuation;
+    protected double targetPower;
+    protected double inputPower;
+    protected double outputPower;
+
+    //TODO: compute target attenuation to obtain the desired targetPower
+
+    /**
+     * Builds a LumentumConnection.
+     *
+     * @param id the id retrieved from the device
+     * @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) {
+
+        connectionId = id;
+        hashCode = flowRuleHash;
+
+        isAddRule = xc.isAddRule();
+        ochSignal = xc.ochSignal();
+        attenuation = 0.0; //dB
+        inputPower = 0.0;  //dBm
+        outputPower = 0.0; //dBm
+
+        if (isAddRule) {
+            outPortNumber = LumentumNetconfRoadmFlowRuleProgrammable.LINE_PORT_NUMBER;
+            inPortNumber = xc.addDrop();
+        } else {
+            outPortNumber = xc.addDrop();
+            inPortNumber = LumentumNetconfRoadmFlowRuleProgrammable.LINE_PORT_NUMBER;
+        }
+
+        log.debug("Lumentum NETCONF inPort {} outPort {} ochSignal {}", inPortNumber, outPortNumber, xc.ochSignal());
+    }
+
+    protected Integer getConnectionId() {
+        return connectionId;
+    }
+
+    protected Integer getHash() {
+        return hashCode;
+    }
+
+    protected void setAttenuation(double att) {
+        attenuation = att;
+    }
+
+    protected void setInputPower(double power) {
+        inputPower = power;
+    }
+
+    protected void setOutputPower(double power) {
+        outputPower = power;
+    }
+
+}
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
new file mode 100644
index 0000000..791e0cd
--- /dev/null
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmDiscovery.java
@@ -0,0 +1,324 @@
+/*
+ * 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 com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.commons.lang3.StringUtils;
+
+import org.onosproject.drivers.utilities.XmlConfigParser;
+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;
+import org.onosproject.net.device.DeviceDescriptionDiscovery;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+import org.onosproject.netconf.NetconfDevice;
+
+import org.slf4j.Logger;
+
+import java.io.ByteArrayInputStream;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Device description behaviour for Lumentum ROADM-A Whitebox devices using NETCONF.
+ */
+public class LumentumNetconfRoadmDiscovery
+        extends AbstractHandlerBehaviour implements DeviceDescriptionDiscovery {
+
+    private static final String PHYSICAL_PORT = "data.physical-ports.physical-port";
+
+    private static final String DN = "dn";
+    private static final String DN_PORT = "port=";
+    private static final String PORT_EXTENSION = "port-extension";
+    protected static final String OPTICAL_INPUT = "port-optical-input";
+    protected static final String OPTICAL_OUTPUT = "port-optical-output";
+    private static final String PORT_PLUGGABLE = "port-pluggable";
+    private static final String PORT_ETHERNET = "port-ethernet";
+
+    private static final String MAINTENANCE_STATE = "config.maintenance-state";
+    private static final String PORT_SPEED = "config.loteeth:port-speed";
+    private static final String IN_SERVICE = "in-service";
+    private static final String PORT_NAME = "entity-description";
+
+    private final Logger log = getLogger(getClass());
+
+    @Override
+    public DeviceDescription discoverDeviceDetails() {
+
+        // Some defaults values
+        String vendor       = "Lumentum";
+        String hwVersion    = "not loaded";
+        String swVersion    = "not loaded";
+        String serialNumber = "not loaded";
+        String chassisId    = "not loaded";
+
+        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();
+
+        if (session == null) {
+            log.error("Lumentum NETCONF - session not found for {}", deviceId);
+            return null;
+        }
+
+        //Retrieve system information from ietf-system
+        StringBuilder systemRequestBuilder = new StringBuilder();
+        systemRequestBuilder.append("<system-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">");
+        systemRequestBuilder.append("</system-state>");
+
+        try {
+            String reply = session.get(systemRequestBuilder.toString(), null);
+            log.info("Lumentum NETCONF - session.get reply {}", reply);
+
+            XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+
+            vendor    = xconf.getString("data.system-state.platform.machine", vendor);
+            swVersion    = xconf.getString("data.system-state.platform.os-version", swVersion);
+        } catch (NetconfException e) {
+            log.error("Lumentum NETCONF error in session.get with filter <system-state>", e);
+        }
+
+        //Retrieve system information
+        StringBuilder chassisRequestBuilder = new StringBuilder();
+        chassisRequestBuilder.append("<chassis-list xmlns=\"http://www.lumentum.com/lumentum-ote-equipment\">");
+        chassisRequestBuilder.append("</chassis-list>");
+
+        try {
+            String reply = session.get(chassisRequestBuilder.toString(), null);
+            log.info("Lumentum NETCONF - session.get reply {}", reply);
+
+            XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+
+            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);
+
+        } catch (NetconfException e) {
+            log.error("Lumentum NETCONF error in session.get", e);
+        }
+
+        //Upon connection of a new devices all pre-configured connections are removed
+        //TODO: do not cancel and import already configured connections
+        rpcRemoveAllConnections("1");
+        rpcRemoveAllConnections("2");
+
+        log.info("TYPE      {}", Device.Type.ROADM);
+        log.info("VENDOR    {}", vendor);
+        log.info("HWVERSION {}", hwVersion);
+        log.info("SWVERSION {}", swVersion);
+        log.info("SERIAL    {}", serialNumber);
+        log.info("CHASSISID {}", chassisId);
+
+        //Return the Device Description
+        return new DefaultDeviceDescription(deviceId.uri(), Device.Type.ROADM,
+                                            vendor, hwVersion, swVersion, serialNumber,
+                                            device.chassisId(), (SparseAnnotations) device.annotations());
+    }
+
+    @Override
+    public List<PortDescription> discoverPortDetails() {
+        String reply;
+        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 ImmutableList.of();
+        }
+
+        NetconfSession session = getNetconfSession();
+
+        if (session == null) {
+            log.error("Lumentum NETCONF - session not found for {}", deviceId);
+            return ImmutableList.of();
+        }
+
+        StringBuilder requestBuilder = new StringBuilder();
+        requestBuilder.append("<physical-ports xmlns=\"http://www.lumentum.com/lumentum-ote-port\" ");
+        requestBuilder.append("xmlns:lotep=\"http://www.lumentum.com/lumentum-ote-port\" ");
+        requestBuilder.append("xmlns:lotepopt=\"http://www.lumentum.com/lumentum-ote-port-optical\" ");
+        requestBuilder.append("xmlns:loteeth=\"http://www.lumentum.com/lumentum-ote-port-ethernet\">");
+        requestBuilder.append("</physical-ports>");
+
+        try {
+            reply = session.get(requestBuilder.toString(), null);
+        } catch (NetconfException e) {
+            log.error("Lumentum NETCONF - " +
+                    "discoverPortDetails failed to retrieve port details {}", handler().data().deviceId(), e);
+            return ImmutableList.of();
+        }
+
+        List<PortDescription> descriptions = parseLumentumRoadmPorts(XmlConfigParser.
+                        loadXml(new ByteArrayInputStream(reply.getBytes())));
+
+        return ImmutableList.copyOf(descriptions);
+    }
+
+    /**
+     * Parses a configuration and returns a set of ports.
+     *
+     * @param cfg a hierarchical configuration
+     * @return a list of port descriptions
+     */
+    protected List<PortDescription> parseLumentumRoadmPorts(HierarchicalConfiguration cfg) {
+        List<PortDescription> portDescriptions = Lists.newArrayList();
+        List<HierarchicalConfiguration> ports = cfg.configurationsAt(PHYSICAL_PORT);
+
+        ports.stream().forEach(pcfg -> {
+
+            DefaultAnnotations.Builder annotations = DefaultAnnotations.builder();
+
+            //Load port number
+            PortNumber portNum = PortNumber.portNumber(
+                    pcfg.getString(DN).substring(pcfg.getString(DN).lastIndexOf(DN_PORT) + 5));
+
+            //Load port state
+            String maintenanceState = pcfg.getString(MAINTENANCE_STATE);
+            boolean isEnabled = ((maintenanceState != null) && (maintenanceState).equals(IN_SERVICE));
+
+            //Load port type (FIBER/COPPER)
+            Port.Type type = null;
+            for (Object o : pcfg.getList(PORT_EXTENSION)) {
+                String s = (String) o;
+                if (s.equals(OPTICAL_INPUT) || s.equals(OPTICAL_OUTPUT)) {
+                    type = Port.Type.FIBER;
+
+                } else if (s.equals(PORT_ETHERNET) || s.equals(PORT_PLUGGABLE)) {
+                    type = Port.Type.COPPER;
+                }
+            }
+
+            //Load port speed of Ethernet interface, expressed in Mb/s
+            Long speed = 0L; //should be the speed of optical port
+            if (type != null) {
+                if (type.equals(Port.Type.COPPER)) {
+                    String speedString = pcfg.getString(PORT_SPEED);
+                    if (speedString != null) {
+                        speed = Long.parseLong(speedString.substring(speedString.lastIndexOf("speed_") + 6,
+                                speedString.lastIndexOf("Mb")));
+                    } else {
+                        log.error("Lumentum NETCONF - Port speed of Ethernet port not correctly loaded");
+                    }
+                }
+            } else {
+                log.error("Port Type not correctly loaded");
+            }
+
+            //Load other information
+            pcfg.getKeys().forEachRemaining(k -> {
+                if (!k.contains(DN) && !k.contains(PORT_SPEED) && !k.contains(PORT_EXTENSION)
+                        && !k.contains(MAINTENANCE_STATE)) {
+                    String value = pcfg.getString(k);
+                    if (!value.isEmpty()) {
+                        k = StringUtils.replaceEach(k, new String[]{"loteeth:", "lotep:",
+                                                            "lotepopt:", "config.", "=", ":",
+                                                            "state."},
+                                                    new String[]{"", "", "", "", "", "", ""});
+
+                        annotations.set(k, value);
+
+                        //To visualize port name in the ROADM app GUI
+                        if (k.equals(PORT_NAME)) {
+                            annotations.set(AnnotationKeys.PORT_NAME, value);
+                        }
+
+                    }
+                }
+            });
+
+            log.debug("Lumentum NETCONF - retrieved port {},{},{},{},{}",
+                    portNum, isEnabled, type, speed, annotations.build());
+
+            portDescriptions.add(new DefaultPortDescription(portNum, isEnabled,
+                                                            type, speed,
+                                                            annotations.build()));
+        });
+
+        return portDescriptions;
+    }
+
+    //Following Lumentum documentation rpc operation to delete all connections
+    private boolean rpcRemoveAllConnections(String module) {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
+        stringBuilder.append(
+                "<remove-all-connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
+        stringBuilder.append("<dn>ne=1;chassis=1;card=1;module=" + module + "</dn>" + "\n");
+        stringBuilder.append("</remove-all-connections>" + "\n");
+        stringBuilder.append("</rpc>" + "\n");
+
+        return editCrossConnect(stringBuilder.toString());
+    }
+
+    private boolean editCrossConnect(String xcString) {
+        NetconfSession session = getNetconfSession();
+
+        if (session == null) {
+            log.error("Lumentum NETCONF - session not found for {}", handler().data().deviceId());
+            return false;
+        }
+
+        try {
+            return session.editConfig(xcString);
+        } catch (NetconfException e) {
+            log.error("Failed to edit the CrossConnect edid-cfg for device {}",
+                    handler().data().deviceId(), e);
+            log.debug("Failed configuration {}", xcString);
+            return false;
+        }
+    }
+
+    private NetconfSession getNetconfSession() {
+        NetconfController controller = checkNotNull(handler().get(NetconfController.class));
+        NetconfDevice ncDevice = controller.getNetconfDevice(handler().data().deviceId());
+
+        if (ncDevice == null) {
+            log.error("Lumentum NETCONF - device not found for {}", handler().data().deviceId());
+            return null;
+        }
+
+        return ncDevice.getSession();
+    }
+}
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
new file mode 100644
index 0000000..88dd9ee
--- /dev/null
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumNetconfRoadmFlowRuleProgrammable.java
@@ -0,0 +1,592 @@
+/*
+ * 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 com.google.common.collect.ImmutableList;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.lang3.tuple.Pair;
+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;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.OchSignalType;
+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.FlowId;
+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.netconf.NetconfController;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.util.stream.Collectors;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+import java.util.List;
+import java.util.HashSet;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Implementation of FlowRuleProgrammable interface for Lumentum ROADM-A Whitebox devices using NETCONF.
+ */
+public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
+
+    private static final Logger log =
+            LoggerFactory.getLogger(LumentumNetconfRoadmFlowRuleProgrammable.class);
+
+    private static final String DN = "dn";
+    private static final String DN_PORT = "port=";
+    private static final String DN_CARD1 = "ne=1;chassis=1;card=1;port=";
+    private static final String CONNECTION = "connection";
+    private static final String CONNECTIONS = "data.connections.connection";
+    private static final String CONFIG = "config";
+    private static final String STATE = "state";
+    private static final String START_FREQ = "start-freq";
+    private static final String END_FREQ = "end-freq";
+    private static final String MODULE = "module";
+    private static final String SEMI_COLON = ";";
+    private static final String EQUAL = "=";
+    private static final String INPUT_PORT_REFERENCE = "input-port-reference";
+    private static final String OUTPUT_PORT_REFERENCE = "output-port-reference";
+
+    private static final String CHANNEL_ATTENUATION = "attenuation";
+    private static final String CHANNEL_INPUT_POWER = "input-channel-attributes.power";
+    private static final String CHANNEL_OUTPUT_POWER = "output-channel-attributes.power";
+
+    protected static final long LINE_PORT = 3001;
+    protected static final PortNumber LINE_PORT_NUMBER = PortNumber.portNumber(LINE_PORT);
+    protected static final long MUX_OUT = 4201;
+    protected static final long DEMUX_IN = 5101;
+    protected static final long GHZ = 1_000_000_000L;
+    protected static final int MAX_CONNECTIONS = 100;
+
+    //List of LumentumConnections to associate ConnectionId and other info to the relative hash
+
+    //This is required because CrossConnect, CrossConnect Cache do not include all parameters required by Lumentum
+    //TODO: Use an external cache as CrossConnectCache to avoid problems in case of multiple devices using this driver
+
+    protected static final Set<LumentumConnection> CONNECTION_SET = new HashSet<>();
+
+    /**Get the flow entries that are present on the Lumentum device, called by FlowRuleDriverProvider.
+     *
+     * The flow entries must match exactly the FlowRule entries in the ONOS store. If they are not an
+     * exact match the device will be requested to remove those flows.
+     *
+     * @return A collection of Flow Entries
+     */
+    @Override
+    public Collection<FlowEntry> getFlowEntries() {
+        return ImmutableList.copyOf(
+                fetchConnectionsFromDevice().stream()
+                        .map(conn -> buildFlowrule(conn))
+                        .filter(Objects::nonNull)
+                        .map(fr -> new DefaultFlowEntry(
+                                fr, FlowEntry.FlowEntryState.ADDED, 0, 0, 0))
+                        .collect(Collectors.toList()));
+    }
+
+    /**Apply the flow entries specified in the collection rules.
+     *
+     * @param rules A collection of Flow Rules to be applied to the Lumentum device
+     * @return The collection of added Flow Entries
+     */
+    @Override
+    public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
+        // Apply the  rules on the device
+        Collection<FlowRule> added = rules.stream()
+                .map(r -> new CrossConnectFlowRule(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()),
+                xc.id(),
+                xc.priority()));
+
+        added.forEach(xc -> log.debug("Lumentum build cached FlowRule selector {} treatment {}",
+                xc.selector().toString(), xc.treatment().toString()));
+
+        return added;
+    }
+
+    @Override
+    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()))
+                .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())));
+
+        removed.forEach(xc -> log.debug("Lumentum NETCONF - removed cached FlowRule selector {} treatment {}",
+                xc.selector(), xc.treatment()));
+
+        return removed;
+    }
+
+    private List<PortNumber> getLinePorts() {
+        DeviceService deviceService = this.handler().get(DeviceService.class);
+        return deviceService.getPorts(data().deviceId()).stream()
+                .filter(p -> p.number().toLong() == LINE_PORT)
+                .map(p -> p.number())
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Fetches list of connections from device.
+     *
+     * @return list of connections as XML hierarchy
+     */
+    private List<HierarchicalConfiguration> fetchConnectionsFromDevice() {
+        String reply;
+
+        StringBuilder requestBuilder = new StringBuilder();
+        requestBuilder.append("<connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">");
+        requestBuilder.append("</connections>");
+
+        NetconfSession session = getNetconfSession();
+
+        if (session == null) {
+            log.error("Lumentum NETCONF - session not found for {}", handler().data().deviceId());
+            return ImmutableList.of();
+        }
+
+        try {
+            reply = session.get(requestBuilder.toString(), null);
+            log.info("Lumentum NETCONF - fetchConnectionsFromDevice reply {}", reply);
+        } catch (NetconfException e) {
+            log.error("Failed to retrieve configuration details for device {}",
+                      handler().data().deviceId(), e);
+            return ImmutableList.of();
+        }
+
+        HierarchicalConfiguration cfg =
+                XmlConfigParser.loadXml(new ByteArrayInputStream(reply.getBytes()));
+
+        return cfg.configurationsAt(CONNECTIONS);
+    }
+
+    // Example input dn: ne=1;chassis=1;card=1;module=2;connection=89
+    private Pair<Short, Short> parseDn(String dn) {
+        Short module = null;
+        Short connection = null;
+        for (String entry : dn.split(SEMI_COLON)) {
+            String[] keyVal = entry.split(EQUAL);
+            if (keyVal.length != 2) {
+                continue;
+            }
+            if (keyVal[0].equals(MODULE)) {
+                module = Short.valueOf(keyVal[1]);
+            }
+            if (keyVal[0].equals(CONNECTION)) {
+                connection = Short.valueOf(keyVal[1]);
+            }
+            if (module != null && connection != null) {
+                return Pair.of(module, connection);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Builds a flow rule from a connection hierarchy.
+     *
+     * @param connection the connection hierarchy
+     * @return the flow rule
+     */
+    private FlowRule buildFlowrule(HierarchicalConfiguration connection) {
+
+        String dn = connection.getString(DN);
+        Pair<Short, Short> pair = parseDn(dn);
+
+        HierarchicalConfiguration config = connection.configurationAt(CONFIG);
+        double startFreq = config.getDouble(START_FREQ);
+        double endFreq = config.getDouble(END_FREQ);
+        String inputPortReference = config.getString(INPUT_PORT_REFERENCE);
+        String outputPortReference = config.getString(OUTPUT_PORT_REFERENCE);
+
+        HierarchicalConfiguration state = connection.configurationAt(STATE);
+        double attenuation = state.getDouble(CHANNEL_ATTENUATION);
+        double inputPower = state.getDouble(CHANNEL_INPUT_POWER);
+        double outputPower = state.getDouble(CHANNEL_OUTPUT_POWER);
+
+        if (pair == null) {
+            return null;
+        }
+
+        PortNumber portNumber = getPortNumber(pair.getLeft(), inputPortReference, outputPortReference);
+
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPort(pair.getLeft() == 1 ? portNumber : LINE_PORT_NUMBER)
+                .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);
+        CrossConnectCache cache = this.handler().get(CrossConnectCache.class);
+        Pair<FlowId, Integer> lookup = cache.get(hash);
+
+        LumentumConnection conn = CONNECTION_SET.stream()
+                .filter(c -> hash == c.getHash())
+                .findFirst()
+                .orElse(null);
+
+        //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());
+           rpcDeleteUnwantedConnection(pair.getRight().toString());
+           return null;
+        } else {
+            log.debug("Lumentum NETCONF attenuation and parameters set {} for connection id {}",
+                    attenuation,
+                    conn.getConnectionId());
+
+            conn.setAttenuation(attenuation);
+            conn.setInputPower(inputPower);
+            conn.setOutputPower(outputPower);
+        }
+
+        return DefaultFlowRule.builder()
+                .forDevice(data().deviceId())
+                .makePermanent()
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(lookup.getRight())
+                .withCookie(lookup.getLeft().value())
+                .build();
+    }
+
+    /**
+     * Get the port number.
+     * If this is a MUX connection return input-port. Outport is always MUX_OUT = 4201.
+     * If this is a DEMUX connection return output-port. Inport is always DEMUX_IN= 5101.
+     *
+     * @param module the module (1 for MUX/ADD, 2 for DEMUX/DROP)
+     * @return the add/drop port number
+     */
+    private PortNumber getPortNumber(short module, String inputPort, String outputPort) {
+        checkArgument(module == 1 || module == 2, "Module must be 1 (MUX/ADD) or 2 (DEMUX/DROP)");
+
+        if (module == 1) {
+            return PortNumber.portNumber(inputPort.split(DN_PORT)[1]);
+        } else {
+            return PortNumber.portNumber(outputPort.split(DN_PORT)[1]);
+        }
+    }
+
+    /**
+     * Converts cross connect flow rule to module and connection.
+     *
+     * Connection number is incremental within the class and associated to the rule hash.
+     *
+     * @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) {
+        if (xc.isAddRule()) {
+            return Pair.of((short) 1, id.shortValue());
+        } else {
+            return Pair.of((short) 2, id.shortValue());
+        }
+    }
+
+    /**
+     * Retrieve module and connection from the cache.
+     *
+     * Connection number is incremental within the class and associated to the rule hash.
+     *
+     * @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> retrieveModuleConnection(CrossConnectFlowRule xc) {
+
+        int hash = Objects.hash(data().deviceId(), xc.selector(), xc.treatment());
+
+        LumentumConnection retrievedConnection = CONNECTION_SET.stream()
+                .filter(conn -> conn.getHash() == hash)
+                .findFirst()
+                .orElse(null);
+
+        if (retrievedConnection == null) {
+            log.error("Lumentum connection not found");
+            return null;
+        }
+
+        //Remove connection id from the local cache
+        CONNECTION_SET.remove(retrievedConnection);
+
+        log.debug("Lumentum NETCONF - retrieveModuleConnection {} retrievedConnectionId {} port {}",
+                xc.isAddRule(), retrievedConnection.getConnectionId(), xc.addDrop());
+
+        if (xc.isAddRule()) {
+            return Pair.of((short) 1, retrievedConnection.getConnectionId().shortValue());
+        } else {
+            return Pair.of((short) 2, retrievedConnection.getConnectionId().shortValue());
+        }
+    }
+
+    //Following Lumentum documentation rpc operation to configure a new connection
+    private boolean rpcAddConnection(CrossConnectFlowRule xc) {
+
+        int currentConnectionId = generateConnectionId();
+
+        if (currentConnectionId == 0) {
+            log.error("Lumentum driver - 100 connections are already configured on the device");
+            return false;
+        }
+
+        LumentumConnection connection = new LumentumConnection(currentConnectionId,
+                Objects.hash(data().deviceId(), xc.selector(), xc.treatment()), xc);
+
+        CONNECTION_SET.add(connection);
+
+        Pair<Short, Short> pair = setModuleConnection(xc, currentConnectionId);
+        String module = pair.getLeft().toString();
+        String connectionId = pair.getRight().toString();
+
+        //Conversion of ochSignal format (center frequency + diameter) to Lumentum frequency slot format (start - end)
+        Frequency freqRadius = Frequency.ofHz(xc.ochSignal().channelSpacing().frequency().asHz() / 2);
+        Frequency center = xc.ochSignal().centralFrequency();
+        String startFreq = String.valueOf(center.subtract(freqRadius).asHz() / GHZ);
+        String endFreq = String.valueOf(center.add(freqRadius).asHz() / GHZ);
+
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
+        stringBuilder.append("<add-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
+        stringBuilder.append(
+                "<dn>ne=1;chassis=1;card=1;module=" + module + ";connection=" + connectionId + "</dn>" + "\n");
+        stringBuilder.append("<start-freq>" + startFreq + "</start-freq>" + "\n");
+        stringBuilder.append("<end-freq>" + endFreq + "</end-freq>" + "\n");
+        stringBuilder.append("<attenuation>" + "0.0" + "</attenuation>" + "\n");
+        stringBuilder.append("<blocked>" + "false" + "</blocked>" + "\n");
+        stringBuilder.append("<maintenance-state>" + "in-service" + "</maintenance-state>" + "\n");
+
+        if (xc.isAddRule()) {
+            stringBuilder.append(
+                    "<input-port-reference>"  + DN_CARD1 + xc.addDrop().toString() + "</input-port-reference>" + "\n");
+            stringBuilder.append(
+                    "<output-port-reference>" + DN_CARD1 + MUX_OUT + "</output-port-reference>" + "\n");
+        } else  {
+            stringBuilder.append(
+                    "<input-port-reference>"  + DN_CARD1 + DEMUX_IN + "</input-port-reference>" + "\n");
+            stringBuilder.append(
+                    "<output-port-reference>" + DN_CARD1 + xc.addDrop().toString() + "</output-port-reference>" + "\n");
+        }
+        stringBuilder.append("<custom-name>" + "onos-connection" + "</custom-name>" + "\n");
+        stringBuilder.append("</add-connection>" + "\n");
+        stringBuilder.append("</rpc>" + "\n");
+
+        log.info("Lumentum NETCONF - RPC add-connection {}", stringBuilder);
+
+        return editCrossConnect(stringBuilder.toString());
+    }
+
+    //Following Lumentum documentation <edit-config> operation to edit connection parameter
+    //Currently only edit the "attenuation" parameter
+    private boolean editConnection(String moduleId, String connectionId, int attenuation) {
+
+        double attenuationDouble = ((double) attenuation) / 100;
+
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
+        stringBuilder.append("<edit-config>" + "\n");
+        stringBuilder.append("<target>" + "\n");
+        stringBuilder.append("<running/>" + "\n");
+        stringBuilder.append("</target>" + "\n");
+        stringBuilder.append("<config>" + "\n");
+        stringBuilder.append("<connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
+        stringBuilder.append("<connection>" + "\n");
+        stringBuilder.append("" +
+                "<dn>ne=1;chassis=1;card=1;module=" + moduleId + ";connection=" + connectionId + "</dn>" + "\n");
+        //Other configurable parameters
+        //stringBuilder.append("<custom-name/>" + "\n");
+        //stringBuilder.append("<maintenance-state>" + "in-service" + "</maintenance-state>" + "\n");
+        //stringBuilder.append("<start-freq>" + startFreq + "</start-freq>" + "\n");
+        //stringBuilder.append("<end-freq>" + endFreq + "</end-freq>" + "\n");
+        stringBuilder.append("<config>" + "\n");
+        stringBuilder.append("<attenuation>" + attenuationDouble + "</attenuation>" + "\n");
+        stringBuilder.append("</config>" + "\n");
+        stringBuilder.append("</connection>" + "\n");
+        stringBuilder.append("</connections>" + "\n");
+        stringBuilder.append("</config>" + "\n");
+        stringBuilder.append("</edit-config>" + "\n");
+        stringBuilder.append("</rpc>" + "\n");
+
+        log.info("Lumentum {} - edit-connection {}", data().deviceId(), stringBuilder);
+
+        return editCrossConnect(stringBuilder.toString());
+    }
+
+    //Following Lumentum documentation rpc operation to delete a new connection
+    private boolean rpcDeleteConnection(CrossConnectFlowRule xc) {
+        Pair<Short, Short> pair = retrieveModuleConnection(xc);
+
+        if (pair == null) {
+            log.error("Lumentum RPC delete-connection, connection not found on the local cache");
+        }
+
+        String module = pair.getLeft().toString();
+        String connection = pair.getRight().toString();
+
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
+        stringBuilder.append("<delete-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
+        stringBuilder.append(
+                "<dn>ne=1;chassis=1;card=1;module=" + module + ";connection=" + connection + "</dn>" + "\n");
+        stringBuilder.append("</delete-connection>" + "\n");
+        stringBuilder.append("</rpc>" + " \n");
+
+        log.info("Lumentum RPC delete-connection {}", stringBuilder);
+
+        return editCrossConnect(stringBuilder.toString());
+    }
+
+    //Following Lumentum documentation rpc operation to delete a new connection
+    //Executed if for some reason a connection not in the cache is detected
+    private boolean rpcDeleteUnwantedConnection(String connectionId) {
+
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
+        stringBuilder.append("<delete-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
+        stringBuilder.append("<dn>ne=1;chassis=1;card=1;module=1;connection=" + connectionId + "</dn>" + "\n");
+        stringBuilder.append("</delete-connection>" + "\n");
+        stringBuilder.append("</rpc>" + "\n");
+
+        log.info("Lumentum {} - RPC delete-connection unwanted {}", data().deviceId(), stringBuilder);
+
+        return editCrossConnect(stringBuilder.toString());
+    }
+
+
+    private boolean editCrossConnect(String xcString) {
+        NetconfSession session = getNetconfSession();
+
+        if (session == null) {
+            log.error("Lumentum NETCONF - session not found for device {}", handler().data().deviceId());
+            return false;
+        }
+
+        try {
+            return session.editConfig(xcString);
+        } catch (NetconfException e) {
+            log.error("Failed to edit the CrossConnect edid-cfg for device {}",
+                      handler().data().deviceId(), e);
+            log.debug("Failed configuration {}", xcString);
+            return false;
+        }
+    }
+
+    private NetconfSession getNetconfSession() {
+        NetconfController controller = checkNotNull(handler().get(NetconfController.class));
+
+        try {
+            NetconfSession session = checkNotNull(
+                    controller.getNetconfDevice(handler().data().deviceId()).getSession());
+            return session;
+        } catch (NullPointerException e) {
+            log.error("Lumentum NETCONF - session not found for {}", handler().data().deviceId());
+            return null;
+        }
+    }
+
+     /**
+     * Convert start and end frequencies to OCh signal.
+     *
+     * FIXME: assumes slots of 12.5 GHz while devices allows granularity 6.25 GHz
+     * FIXME: supports channel spacing 50 and 100
+     *
+     * @param start starting frequency as double in GHz
+     * @param end end frequency as double in GHz
+     * @return OCh signal
+     */
+    public static OchSignal toOchSignal(double start, double end) {
+        int slots = (int) ((end - start) / ChannelSpacing.CHL_12P5GHZ.frequency().asGHz());
+        int multiplier = 0;
+
+        //Conversion for 50 GHz slots
+        if (end - start == 50) {
+            multiplier = (int) (((end - start) / 2 + start - Spectrum.CENTER_FREQUENCY.asGHz())
+                    / ChannelSpacing.CHL_50GHZ.frequency().asGHz());
+
+            return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, multiplier, slots);
+        }
+
+        //Conversion for 100 GHz slots
+        if (end - start == 100) {
+            multiplier = (int) (((end - start) / 2 + start - Spectrum.CENTER_FREQUENCY.asGHz())
+                    / ChannelSpacing.CHL_100GHZ.frequency().asGHz());
+
+            return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_100GHZ, multiplier, slots);
+        }
+
+        return null;
+    }
+
+    /**
+     * Generate a valid connectionId, the connectionId is a field required by the device every time
+     * a connection is created/edited/removed.
+     *
+     *
+     * Device only supports connection id < 100
+     */
+    private static Integer generateConnectionId() {
+
+        //Device only supports connection id < 100
+        for (int i = 1; i < MAX_CONNECTIONS; i++) {
+            Set<Integer> connIds = CONNECTION_SET.stream()
+                    .map(conn -> conn.getConnectionId())
+                    .collect(Collectors.toSet());
+
+            if (!connIds.contains(i)) {
+                return i;
+            }
+        }
+        return 0;
+    }
+}
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
new file mode 100644
index 0000000..e292947
--- /dev/null
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumPowerConfig.java
@@ -0,0 +1,175 @@
+/*
+ * 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 com.google.common.collect.Range;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.behaviour.PowerConfig;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.IntStream;
+
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+public class LumentumPowerConfig<T> extends AbstractHandlerBehaviour
+        implements PowerConfig<T> {
+
+    // log
+    private final Logger log = getLogger(getClass());
+
+
+    @Override
+    public Optional<Long> getTargetPower(PortNumber port, T component) {
+        return Optional.ofNullable(acquireTargetPower(port, component));
+    }
+
+    @Override
+    public void setTargetPower(PortNumber port, T component, long power) {
+        if (component instanceof OchSignal) {
+            setConnectionTargetPower(port, (OchSignal) component, power);
+        } else {
+            setPortTargetPower(port, power);
+        }
+    }
+
+    @Override
+    public Optional<Long> currentPower(PortNumber port, T component) {
+        return Optional.ofNullable(acquireCurrentPower(port, component));
+    }
+
+    @Override
+    public Optional<Range<Long>> getTargetPowerRange(PortNumber portNumber, T component) {
+
+        log.debug("Lumentum getTargetPowerRange {}", portNumber);
+        //TODO automatically read if a port is input or output
+
+        Set<PortNumber> outputPorts = new HashSet<>();
+
+        //Output port on the optical-line
+        outputPorts.add(PortNumber.portNumber(3001));
+
+        //Output ports of the demux module (module=2)
+        IntStream.rangeClosed(5201, 5220)
+                .forEach(i -> outputPorts.add(PortNumber.portNumber(i)));
+
+        if (outputPorts.contains(portNumber)) {
+            return Optional.ofNullable(getTxPowerRange(portNumber, component));
+        }
+        return Optional.empty();
+    }
+
+    @Override
+    public Optional<Range<Long>> getInputPowerRange(PortNumber portNumber, T component) {
+
+        log.debug("Lumentum getInputPowerRange {}", portNumber);
+        //TODO automatically read if a port is input or output
+
+        Set<PortNumber> inputPorts = new HashSet<>();
+
+        //Input port on the optical-line
+        inputPorts.add(PortNumber.portNumber(3001));
+
+        //Input ports of the mux module (module=1)
+        IntStream.rangeClosed(4101, 4120)
+                .forEach(i -> inputPorts.add(PortNumber.portNumber(i)));
+
+        if (inputPorts.contains(portNumber)) {
+            return Optional.ofNullable(getRxPowerRange(portNumber, component));
+        }
+        return Optional.empty();
+    }
+
+    //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);
+
+        if (component instanceof OchSignal) {
+            //FIXME include port in the filter
+            LumentumConnection conn = LumentumNetconfRoadmFlowRuleProgrammable.CONNECTION_SET.stream()
+                    .filter(c -> c.ochSignal == component)
+                    .findFirst()
+                    .orElse(null);
+
+            if (conn == null) {
+                log.info("Lumentum NETCONF fail to retrieve attenuation signal {} port {}", component, port);
+                return 0L;
+            } else {
+                log.info("Lumentum NETCONF on port {} attenuation {}", port, conn.attenuation);
+                return ((long) (conn.attenuation * 100));
+            }
+        }
+
+        return 0L;
+    }
+
+    //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);
+
+        if (component instanceof OchSignal) {
+            //FIXME include port in the filter
+            LumentumConnection conn = LumentumNetconfRoadmFlowRuleProgrammable.CONNECTION_SET.stream()
+                    .filter(c -> c.ochSignal == component)
+                    .findFirst()
+                    .orElse(null);
+
+            if (conn == null) {
+                log.info("Lumentum NETCONF fail to retrieve power signal {} port {}", component, port);
+                return 0L;
+            } else {
+                log.info("Lumentum NETCONF on port {} power {}", port, conn.inputPower);
+                return ((long) (conn.inputPower * 100));
+            }
+        }
+
+        return 0L;
+    }
+
+    //TODO implement actual get configuration from the device
+    //Return PowerRange -60 dBm to 60 dBm
+    private Range<Long> getTxPowerRange(PortNumber port, T component) {
+        log.debug("Get port {} tx power range...", port);
+        return Range.closed(-60L, 60L);
+    }
+
+    //TODO implement actual get configuration from the device
+    //Return PowerRange -60dBm to 60 dBm
+    private Range<Long> getRxPowerRange(PortNumber port, T component) {
+        log.debug("Get port {} rx power range...", port);
+        return Range.closed(-60L, 60L);
+    }
+
+    //TODO implement configuration on the device
+    //Nothing to do
+    private void setPortTargetPower(PortNumber port, long power) {
+        log.debug("Set port {} target power {}", port, power);
+    }
+
+    //TODO implement configuration on the device
+    //Nothing to do
+    private void setConnectionTargetPower(PortNumber port, OchSignal signal, long power) {
+        log.debug("Set connection target power {} ochsignal {} port {}", power, signal, port);
+    }
+}
\ No newline at end of file
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumRoadmLambdaQuery.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumRoadmLambdaQuery.java
new file mode 100644
index 0000000..cfd54c8
--- /dev/null
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumRoadmLambdaQuery.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2016-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.onlab.util.Frequency;
+import org.onlab.util.Spectrum;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.GridType;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.LambdaQuery;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+
+import java.util.Set;
+import java.util.stream.IntStream;
+import java.util.stream.Collectors;
+import com.google.common.collect.Sets;
+
+/**
+ * Implementation of lambda query interface for Lumentum ROADMs.
+ *
+ * Tested on both Lumentum SDN ROADM and Lumentum ROADM-A Whitebox.
+ *
+ * 96 OchSignals of 50 GHz, center frequencies range 191.350 - 196.100 THz.
+ * 48 OchSignals of 100 GHz, center frequencies 191.375 - 196.075 THz
+ */
+public class LumentumRoadmLambdaQuery extends AbstractHandlerBehaviour implements LambdaQuery {
+
+
+    public static final GridType GRID_TYPE = GridType.DWDM;
+
+    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 LAMBDA_COUNT_50 = 96;
+
+    public static final ChannelSpacing CHANNEL_SPACING_100 = ChannelSpacing.CHL_100GHZ;
+    public static final Frequency START_CENTER_FREQ_100 = Frequency.ofGHz(191_375);
+    public static final Frequency END_CENTER_FREQ_100 = Frequency.ofGHz(196_075);
+    private static final int LAMBDA_COUNT_100 = 48;
+
+    @Override
+    public Set<OchSignal> queryLambdas(PortNumber port) {
+
+        //Complete set of 50GHz OchSignal
+        int startMultiplier50 = (int) (START_CENTER_FREQ_50.subtract(Spectrum.CENTER_FREQUENCY).asHz()
+                / Frequency.ofGHz(50).asHz());
+
+        Set<OchSignal> channels50 = IntStream.range(0, LAMBDA_COUNT_50)
+                .mapToObj(x -> new OchSignal(GRID_TYPE, CHANNEL_SPACING_50,
+                        startMultiplier50 + x,
+                        4))
+                .collect(Collectors.toSet());
+
+        //Complete set of 100GHz OchSignal
+        int startMultiplier100 = (int) (START_CENTER_FREQ_100.subtract(Spectrum.CENTER_FREQUENCY).asHz()
+                / Frequency.ofGHz(100).asHz());
+
+        Set<OchSignal> channels100 = IntStream.range(0, LAMBDA_COUNT_100)
+                .mapToObj(x -> new OchSignal(GRID_TYPE,
+                        CHANNEL_SPACING_100, startMultiplier100 + x, 8))
+                .collect(Collectors.toSet());
+
+        Set<OchSignal> channels = Sets.union(channels50, channels100);
+
+        return channels;
+    }
+}
+
+
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSnmpDevice.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSnmpDevice.java
index cd6ba1a..47b98e2 100644
--- a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSnmpDevice.java
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumSnmpDevice.java
@@ -85,7 +85,7 @@
         target.setCommunity(new OctetString("public"));
         target.setAddress(targetAddress);
         target.setRetries(3);
-        target.setTimeout(1000L * 3L);
+        target.setTimeout(1000 * 3);
         target.setVersion(SnmpConstants.version2c);
         target.setMaxSizeRequestPDU(MAX_SIZE_RESPONSE_PDU);
     }
diff --git a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumWaveReadyDiscovery.java b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumWaveReadyDiscovery.java
index 3ced845..5bd629f 100644
--- a/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumWaveReadyDiscovery.java
+++ b/drivers/lumentum/src/main/java/org/onosproject/drivers/lumentum/LumentumWaveReadyDiscovery.java
@@ -182,14 +182,14 @@
         Arrays.stream(s.split("\"\"")).forEach(p -> {
             if (p.contains(EIGHTFIFTY)) {
                 PortDescription cltPort = oduCltPortDescription(
-                        PortNumber.portNumber(ports.size() + 1L),
+                        PortNumber.portNumber(ports.size() + 1),
                         true,
                         CltSignalType.CLT_10GBE,
                         extractAnnotations(p));
                 ports.add(cltPort);
             } else {
                 PortDescription netPort = ochPortDescription(
-                        PortNumber.portNumber(ports.size() + 1L),
+                        PortNumber.portNumber(ports.size() + 1),
                         true,
                         OduSignalType.ODU2e,
                         true,
diff --git a/drivers/lumentum/src/main/resources/lumentum-drivers.xml b/drivers/lumentum/src/main/resources/lumentum-drivers.xml
index 01ccb3e..8bf9d17 100644
--- a/drivers/lumentum/src/main/resources/lumentum-drivers.xml
+++ b/drivers/lumentum/src/main/resources/lumentum-drivers.xml
@@ -19,7 +19,7 @@
         <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
                    impl="org.onosproject.drivers.lumentum.LumentumRoadmDiscovery"/>
         <behaviour api="org.onosproject.net.behaviour.LambdaQuery"
-                   impl="org.onosproject.drivers.lumentum.LumentumSdnRoadmLambdaQuery"/>
+                   impl="org.onosproject.drivers.lumentum.LumentumRoadmLambdaQuery"/>
         <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
                    impl="org.onosproject.drivers.lumentum.LumentumSdnRoadmFlowRuleProgrammable"/>
         <behaviour api="org.onosproject.incubator.net.faultmanagement.alarm.AlarmConsumer"
@@ -37,5 +37,18 @@
         <behaviour api="org.onosproject.net.behaviour.LambdaQuery"
                    impl="org.onosproject.driver.optical.query.CBand50LambdaQuery"/>
     </driver>
+
+    <driver name="lumentum-roadm20whitebox-netconf" manufacturer="Lumentum" hwVersion="SDN-ROADM-NETCONF" swVersion="R2.1.4">
+        <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
+                   impl="org.onosproject.drivers.lumentum.LumentumNetconfRoadmDiscovery"/>
+        <behaviour api="org.onosproject.net.behaviour.LambdaQuery"
+                   impl="org.onosproject.drivers.lumentum.LumentumRoadmLambdaQuery"/>
+        <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
+                   impl="org.onosproject.drivers.lumentum.LumentumNetconfRoadmFlowRuleProgrammable"/>
+        <behaviour api="org.onosproject.net.optical.OpticalDevice"
+                   impl="org.onosproject.net.optical.DefaultOpticalDevice"/>
+        <behaviour api="org.onosproject.net.behaviour.PowerConfig"
+                   impl="org.onosproject.drivers.lumentum.LumentumPowerConfig"/>
+    </driver>
 </drivers>