ONOS-7975 ODTN OpenROADM v2.2 NetConf drivers.

Change-Id: I7b4115146bfda7b0061e095ca189ea4d2e9d993d
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmAddConnection.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmAddConnection.java
new file mode 100644
index 0000000..bb39189
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmAddConnection.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+
+package org.onosproject.drivers.odtn.openroadm;
+
+import org.onosproject.net.device.DeviceService;
+
+/**
+ * Class that models an ADD connection.
+ *
+ */
+public class OpenRoadmAddConnection extends OpenRoadmConnection {
+
+    /**
+     * Constructor for an Add Connection (from a client to line ports).
+     *
+     * @param openRoadmName name of the Connection.
+     * @param xc the associated OpenRoadmFlowRule.
+     * @param deviceService ONOS device service.
+     */
+    public OpenRoadmAddConnection(String openRoadmName, OpenRoadmFlowRule xc,
+                                  DeviceService deviceService) {
+        super(openRoadmName, xc, deviceService);
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmCBandLambdaQuery.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmCBandLambdaQuery.java
new file mode 100644
index 0000000..89ee6d8
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmCBandLambdaQuery.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import java.util.stream.IntStream;
+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;
+
+/**
+ * Abstract C-band DWDM plan lambda query.
+ *
+ */
+public abstract class OpenRoadmCBandLambdaQuery
+  extends AbstractHandlerBehaviour implements LambdaQuery {
+
+    protected ChannelSpacing channelSpacing;
+    protected int lambdaCount;
+    protected int slotGranularity;
+
+    @Override
+    public Set<OchSignal> queryLambdas(PortNumber port) {
+        return IntStream.range(0, lambdaCount)
+          .mapToObj(
+            x
+            -> new OchSignal(GridType.DWDM, channelSpacing, x - (lambdaCount / 2), slotGranularity))
+          .collect(ImmutableSet.toImmutableSet());
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmConnection.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmConnection.java
new file mode 100644
index 0000000..5afaf2f
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmConnection.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2018-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.
+ *
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+
+package org.onosproject.drivers.odtn.openroadm;
+
+import org.onlab.util.Frequency;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+class OpenRoadmConnectionBase {
+
+    protected static final Logger log = LoggerFactory.getLogger(OpenRoadmConnection.class);
+
+    // Parameters of the FlowRule traslated into the OpenRoadm connection
+    protected DeviceId deviceId;
+    protected FlowId id;
+    protected int priority;
+
+    protected OpenRoadmFlowRule.Type type; // enum (EXPRESS_LINK, ADD_LINK, DROP_LINK)
+    protected OchSignal ochSignal;
+    protected PortNumber inPortNumber;
+    protected PortNumber outPortNumber;
+
+    protected Port srcPort; // used to retrieve info in the annotations
+    protected Port dstPort; // used to retrieve info in the annotations
+
+    // Parameters of <roadm-connections>
+    protected String connectionName;
+    protected String opticalControlMode;
+    protected double targetOutputPower = 0.0;
+    protected String srcConnInterface; // this is an NMC interface
+    protected String dstConnInterface; // this is an NMC interface
+
+    // Parameters of associated NMC interfaces:
+    // <type>openROADM-if:networkMediaChannelConnectionTerminationPoint</type>
+    protected String srcNmcName;
+    protected String srcNmcDescription;
+    protected String srcNmcType;
+    protected String srcNmcAdministrativeState;
+    protected String srcNmcSupportingCircuitPack;
+    protected String srcNmcSupportingInterface; // this is an MC interface (express-link) or a
+                                                // physical-port (add-drop)
+    protected String srcNmcSupportingPort;
+    protected Frequency srcNmcFrequency; // expressed in Thz
+    protected Frequency srcNmcWidth;     // expressed in Ghz
+
+    protected String dstNmcName;
+    protected String dstNmcDescription;
+    protected String dstNmcType;
+    protected String dstNmcAdministrativeState;
+    protected String dstNmcSupportingCircuitPack;
+    protected String dstNmcSupportingInterface; // this is an MC interface (express-link) or a
+                                                // physical-port (add-drop)
+    protected String dstNmcSupportingPort;
+    protected Frequency dstNmcFrequency; // expressed in Thz
+    protected Frequency dstNmcWidth;     // expressed in Ghz
+
+    // Parameters of associated MC interfaces:
+    // <type>openROADM-if:mediaChannelTrailTerminationPoint</type>
+    protected String srcMcName;
+    protected String srcMcDescription;
+    protected String srcMcType;
+    protected String srcMcAdministrativeState;
+    protected String srcMcSupportingCircuitPack;
+    protected String srcMcSupportingInterface; // this is a physical-port
+    protected String srcMcSupportingPort;
+    protected Frequency srcMcMinFrequency; // expressed in Thz
+    protected Frequency srcMcMaxFrequency; // expressed in Thz
+
+    protected String dstMcName;
+    protected String dstMcDescription;
+    protected String dstMcType;
+    protected String dstMcAdministrativeState;
+    protected String dstMcSupportingCircuitPack;
+    protected String dstMcSupportingInterface; // this is a physical-port
+    protected String dstMcSupportingPort;
+    protected Frequency dstMcMinFrequency; // expressed in Thz
+    protected Frequency dstMcMaxFrequency; // expressed in Thz
+
+
+
+    public OpenRoadmFlowRule.Type getType() {
+        return type;
+    }
+}
+
+
+
+/**
+ * Class that models an OpenROADM connection object (Yang leaf).
+ *
+ */
+public class OpenRoadmConnection extends OpenRoadmConnectionBase {
+
+    public static final String OPENROADM_CIRCUIT_PACK_NAME = "openroadm-circuit-pack-name";
+
+    public static final String OPENROADM_PORT_NAME = "openroadm-port-name";
+
+    public static final String OPENROADM_LOGICAL_CONNECTION_POINT =
+      "openroadm-logical-connection-point";
+
+    /**
+     * Constructor.
+     *
+     * @param openRoadmName name of the Connection.
+     * @param xc the associated OpenRoadmFlowRule.
+     * @param deviceService ONOS device service.
+     */
+    public OpenRoadmConnection(String openRoadmName, OpenRoadmFlowRule xc,
+                               DeviceService deviceService) {
+      connectionName = openRoadmName;
+      deviceId = xc.deviceId();
+      id = xc.id();
+      priority = xc.priority();
+
+      inPortNumber = xc.inPort();
+      outPortNumber = xc.outPort();
+      ochSignal = xc.ochSignal();
+      type = xc.type();
+
+      srcPort = deviceService.getPort(deviceId, xc.inPort());
+      dstPort = deviceService.getPort(deviceId, xc.outPort());
+
+      // Conversion from ochSignal (center frequency + diameter) to OpenRoadm
+      // Media Channel (start - end)
+      Frequency freqRadius = Frequency.ofHz(
+          xc.ochSignal().channelSpacing().frequency().asHz() / 2);
+      Frequency centerFreq = xc.ochSignal().centralFrequency();
+
+      // e.g. DEG1-TTP-RX
+      String srcTag =
+          srcPort.annotations().value(OPENROADM_LOGICAL_CONNECTION_POINT) +
+          "-" + centerFreq.asTHz();
+
+      // e.g. DEG2-TTP-TX or SRG2-PP1-TX
+      String dstTag =
+          dstPort.annotations().value(OPENROADM_LOGICAL_CONNECTION_POINT) +
+          "-" + centerFreq.asTHz();
+
+      srcMcMinFrequency = centerFreq.subtract(freqRadius);
+      srcMcMaxFrequency = centerFreq.add(freqRadius);
+      dstMcMinFrequency = srcMcMinFrequency;
+      dstMcMaxFrequency = srcMcMaxFrequency;
+      srcNmcFrequency = centerFreq;
+      dstNmcFrequency = centerFreq;
+      srcNmcWidth = xc.ochSignal().channelSpacing().frequency();
+      dstNmcWidth = xc.ochSignal().channelSpacing().frequency();
+
+      srcMcSupportingInterface =
+          "OMS-" +
+          srcPort.annotations().value(OPENROADM_LOGICAL_CONNECTION_POINT);
+      dstMcSupportingInterface =
+          "OMS-" +
+          dstPort.annotations().value(OPENROADM_LOGICAL_CONNECTION_POINT);
+
+      // Media Channel Interfaces
+      srcMcName = "MC-TTP-" + srcTag;
+      srcMcSupportingCircuitPack =
+          srcPort.annotations().value(OPENROADM_CIRCUIT_PACK_NAME);
+      srcMcSupportingPort = srcPort.annotations().value(OPENROADM_PORT_NAME);
+
+      dstMcName = "MC-TTP-" + dstTag;
+      dstMcSupportingCircuitPack =
+          dstPort.annotations().value(OPENROADM_CIRCUIT_PACK_NAME);
+      dstMcSupportingPort = dstPort.annotations().value(OPENROADM_PORT_NAME);
+
+      // Network Media Channel Interfaces
+      srcNmcName = "NMC-CTP-" + srcTag;
+      srcConnInterface = srcNmcName;
+      srcNmcSupportingInterface = srcMcName;
+      srcNmcSupportingCircuitPack =
+          srcPort.annotations().value(OPENROADM_CIRCUIT_PACK_NAME);
+      srcNmcSupportingPort = srcPort.annotations().value(OPENROADM_PORT_NAME);
+
+      dstNmcName = "NMC-CTP-" + dstTag;
+      dstConnInterface = dstNmcName;
+      dstNmcSupportingInterface = dstMcName;
+      dstNmcSupportingCircuitPack =
+          dstPort.annotations().value(OPENROADM_CIRCUIT_PACK_NAME);
+      dstNmcSupportingPort = dstPort.annotations().value(OPENROADM_PORT_NAME);
+    }
+
+    protected String getConnectionName() {
+        return connectionName;
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmConnectionFactory.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmConnectionFactory.java
new file mode 100644
index 0000000..7dda1aa
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmConnectionFactory.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+import org.onosproject.net.device.DeviceService;
+
+/**
+ * Factory class for Connection objects based on the OpenRoadmFlowRule.
+ */
+public final class OpenRoadmConnectionFactory {
+
+    /**
+     * Static method to create an OpenROADM connection.
+     *
+     * @param openRoadmName name of the Connection.
+     * @param xc the associated OpenRoadmFlowRule.
+     * @param deviceService ONOS device service.
+     * @return the OpenRoadmConnectionObject
+     * @throws IllegalArgumentException
+     *
+     * Based on the cross-connection type, the method allocates and
+     * Add, Drop or Express connection object (or local, from client to client).
+     */
+    public static OpenRoadmConnection create(String openRoadmName,
+                                             OpenRoadmFlowRule xc,
+                                             DeviceService deviceService)
+      throws IllegalArgumentException {
+        switch (xc.type()) {
+            case EXPRESS_LINK:
+                return new OpenRoadmExpressConnection(openRoadmName, xc,
+                                                      deviceService);
+            case ADD_LINK:
+                return new OpenRoadmAddConnection(openRoadmName, xc,
+                                                  deviceService);
+            case DROP_LINK:
+                return new OpenRoadmDropConnection(openRoadmName, xc,
+                                                   deviceService);
+            case LOCAL:
+                return new OpenRoadmLocalConnection(openRoadmName, xc,
+                                                    deviceService);
+            default:
+                throw new IllegalArgumentException(
+                  "Unknown OpenRoadmFlowRule type");
+        }
+    }
+
+    private OpenRoadmConnectionFactory() {
+        // Utility classes should not have a public or default constructor.
+        // [HideUtilityClassConstructor]
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmDeviceDescription.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmDeviceDescription.java
new file mode 100644
index 0000000..7a19fa9
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmDeviceDescription.java
@@ -0,0 +1,575 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
+import org.onlab.packet.ChassisId;
+import org.onlab.util.Frequency;
+import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.OduSignalType;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceDescriptionDiscovery;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.intent.OpticalPathIntent;
+import org.onosproject.net.optical.device.OchPortHelper;
+import org.onosproject.net.optical.device.OmsPortHelper;
+import org.onosproject.netconf.NetconfDevice;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+
+import java.util.concurrent.ExecutionException;
+/**
+ * Driver Implementation of the DeviceDescrption discovery for OpenROADM.
+ */
+public class OpenRoadmDeviceDescription extends OpenRoadmNetconfHandlerBehaviour
+  implements DeviceDescriptionDiscovery {
+
+    // These annotations are added to the device and ports
+    public final class AnnotationKeys {
+        public static final String OPENROADM_NODEID = "openroadm-node-id";
+        public static final String OPENROADM_CIRCUIT_PACK_NAME =
+          "openroadm-circuit-pack-name";
+        public static final String OPENROADM_PORT_NAME = "openroadm-port-name";
+        public static final String OPENROADM_PARTNER_CIRCUIT_PACK_NAME =
+          "openroadm-partner-circuit-pack-name";
+        public static final String OPENROADM_PARTNER_PORT_NAME =
+          "openroadm-partner-port-name";
+        public static final String OPENROADM_LOGICAL_CONNECTION_POINT =
+          "openroadm-logical-connection-point";
+        private AnnotationKeys() {
+            // utility class
+        }
+    }
+
+    public static final ChannelSpacing CHANNEL_SPACING =
+      ChannelSpacing.CHL_50GHZ;
+
+
+    /*
+     * The following 2 values are not specified by the OpenROADM standard,
+     * but they are a reasonable default for a tunable C-band, defined from
+     * Channel C1 at 191.35 to C96 at 196.10 GHz (for a spacing at 50GHz)
+     */
+    public static final Frequency START_CENTER_FREQ = Frequency.ofGHz(191_350);
+    public static final Frequency STOP_CENTER_FREQ = Frequency.ofGHz(196_100);
+
+
+    public static final String OPENROADM_DEVICE_OPEN = //
+      "<org-openroadm-device xmlns=\"http://org/openroadm/device\">";
+    public static final String OPENROADM_DEVICE_CLOSE = //
+      "</org-openroadm-device>";
+
+
+    /**
+     * Builds a request to get OpenRoadm Device main node (within root).
+     *
+     *  @param nodeTag the tag with the name to get e.g. <info/>
+     *
+     * @return A string with the Netconf RPC for a get with subtree info
+     */
+    private String getDeviceXmlNodeBuilder(final String nodeTag) {
+        StringBuilder filter = new StringBuilder();
+        filter.append(OPENROADM_DEVICE_OPEN);
+        filter.append(nodeTag);
+        filter.append(OPENROADM_DEVICE_CLOSE);
+        return filteredGetBuilder(filter.toString());
+    }
+
+    /**
+     * Builds a request to get Device details (<info>).
+     *
+     * @return A string with the Netconf RPC for a get with subtree info
+     */
+    private String getDeviceDetailsBuilder() {
+        return getDeviceXmlNodeBuilder("<info/>");
+    }
+
+    /**
+     * Builds a request to get Ports data (<circuit-packs>).
+     *
+     * @return A string with the Netconf RPC
+     */
+    private String getDeviceCircuitPacksBuilder() {
+        return getDeviceXmlNodeBuilder("<circuit-packs/>");
+    }
+
+    /**
+     * Builds a request to get External Links data (<external-link>).
+     *
+     * @return A string with the Netconf RPC
+     */
+    private String getDeviceExternalLinksBuilder() {
+        return getDeviceXmlNodeBuilder("<external-link/>");
+    }
+
+    /**
+     * Builds a request to get Device Degrees, config and operational data.
+     *
+     * @return A string with the Netconf RPC for a get with subtree rpcing based
+     * on /components/component/state/type being oc-platform-types:PORT
+     */
+    private String getDeviceDegreesBuilder() {
+        return getDeviceXmlNodeBuilder("<degree/>");
+    }
+
+    /**
+     * Builds a request to get Device SharedRiskGroups, config and operational
+     * data.
+     *
+     * @return A string with the Netconf RPC for a get with subtree
+     */
+    private String getDeviceSharedRiskGroupsBuilder() {
+        return getDeviceXmlNodeBuilder("<shared-risk-group/>");
+    }
+
+    /**
+     * Builds a request to get Ports data.
+     * Changed to XPath and added one based on classic filters since some agents
+     * do not support xpath filtering.
+     *
+     * @return A string with the Netconf RPC
+     */
+    private String getDeviceExternalPortsBuilderXPath() {
+        StringBuilder filter = new StringBuilder();
+        filter.append(
+          "/org-openroadm-device/circuit-packs/ports[port-qual='roadm-external']");
+        return xpathFilteredGetBuilder(filter.toString());
+    }
+
+    /**
+     * Builds a request to get Ports data.
+     *
+     * @return A string with the Netconf RPC
+     */
+    private String getDeviceExternalPortsBuilder() {
+        StringBuilder filter = new StringBuilder();
+        filter.append(OPENROADM_DEVICE_OPEN);
+        filter.append("<circuit-packs>");
+        filter.append(" <ports>");
+        filter.append("  <port-qual>roadm-external</port-qual>");
+        filter.append(" </ports>");
+        filter.append("</circuit-packs>");
+        filter.append(OPENROADM_DEVICE_CLOSE);
+        return filteredGetBuilder(filter.toString());
+    }
+
+    /**
+     * Builds a request to get External Links data.
+     *
+     * @return A string with the Netconf RPC
+     */
+    private String getDeviceExternalLinksBuilderXpath() {
+        StringBuilder filter = new StringBuilder();
+        filter.append("/org-openroadm-device/external-link");
+        return xpathFilteredGetBuilder(filter.toString());
+    }
+
+    /**
+     * Builds a request to get External Links data.
+     *
+     * @param nodeId OpenROADM node identifier.
+     * @param circuitPackName name of the circuit part of the port.
+     * @param portName name of the port.
+     * @return A string with the Netconf RPC
+     */
+    private String getDeviceExternalLinkForPortBuilderXPath(
+      String nodeId, String circuitPackName, String portName) {
+        StringBuilder filter = new StringBuilder();
+        filter.append("/org-openroadm-device/external-link[");
+        filter.append("./source/node-id='");
+        filter.append(nodeId);
+        filter.append("' and ");
+        filter.append("./source/circuit-pack-name='");
+        filter.append(circuitPackName);
+        filter.append("' and ");
+        filter.append("./source/port-name='");
+        filter.append(portName);
+        filter.append("']");
+        return xpathFilteredGetBuilder(filter.toString());
+    }
+
+    private String getDeviceExternalLinkForPortBuilder(String nodeId,
+                                                       String circuitPackName,
+                                                       String portName) {
+        StringBuilder filter = new StringBuilder();
+        filter.append(OPENROADM_DEVICE_OPEN);
+        filter.append("<external-link>");
+        filter.append(" <source>");
+        filter.append("  <node-id>");
+        filter.append(nodeId);
+        filter.append("</node-id>");
+        filter.append("  <circuit-pack-name>");
+        filter.append(circuitPackName);
+        filter.append("</circuit-pack-name>");
+        filter.append("  <port-name>");
+        filter.append(portName);
+        filter.append("</port-name>");
+        filter.append(" </source>");
+        filter.append("</external-link>");
+        filter.append(OPENROADM_DEVICE_CLOSE);
+        return xpathFilteredGetBuilder(filter.toString());
+    }
+
+
+    /**
+     * Returns a DeviceDescription with Device info.
+     *
+     * @return DeviceDescription or null
+     */
+    @Override
+    public DeviceDescription discoverDeviceDetails() {
+        boolean defaultAvailable = true;
+        NetconfDevice ncDevice = getNetconfDevice();
+        if (ncDevice == null) {
+            log.error("ONOS Error: Device reachable, deviceID {} is not in Map", did());
+            return null;
+        }
+        DefaultAnnotations.Builder annotationsBuilder =
+          DefaultAnnotations.builder();
+
+        // Some defaults
+        String vendor = "UNKNOWN";
+        String hwVersion = "2.2.0";
+        String swVersion = "2.2.0";
+        String serialNumber = "0x0000";
+        String chassisId = "0";
+        String nodeType = "rdm";
+
+        // Get the session, if null, at least we can use the defaults.
+        NetconfSession session = getNetconfSession(did());
+        if (session != null) {
+            try {
+                String reply = session.rpc(getDeviceDetailsBuilder()).get();
+                XMLConfiguration xconf =
+                  (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+                String nodeId =
+                  xconf.getString("data.org-openroadm-device.info.node-id", "");
+                if (nodeId.equals("")) {
+                    log.error("[OPENROADM] {} org-openroadm-device node-id undefined, returning", did());
+                    return null;
+                }
+                annotationsBuilder.set(AnnotationKeys.OPENROADM_NODEID, nodeId);
+                nodeType = xconf.getString("data.org-openroadm-device.info.node-type", "");
+                if (nodeType.equals("")) {
+                    log.error("[OPENROADM] {} empty node-type", did());
+                    return null;
+                }
+                vendor = xconf.getString(
+                  "data.org-openroadm-device.info.vendor", vendor);
+                hwVersion = xconf.getString(
+                  "data.org-openroadm-device.info.model", hwVersion);
+                swVersion = xconf.getString(
+                  "data.org-openroadm-device.info.softwareVersion", swVersion);
+                serialNumber = xconf.getString(
+                  "data.org-openroadm-device.info.serial-id", serialNumber);
+                chassisId = xconf.getString(
+                  "data.org-openroadm-device.info.node-number", chassisId);
+
+                // GEOLOCATION
+                String longitudeStr = xconf.getString(
+                  "data.org-openroadm-device.info.geoLocation.longitude");
+                String latitudeStr = xconf.getString(
+                  "data.org-openroadm-device.info.geoLocation.latitude");
+                if (longitudeStr != null && latitudeStr != null) {
+                    annotationsBuilder
+                      .set(org.onosproject.net.AnnotationKeys.LONGITUDE,
+                           longitudeStr)
+                      .set(org.onosproject.net.AnnotationKeys.LATITUDE,
+                           latitudeStr);
+                }
+            } catch (NetconfException | InterruptedException | ExecutionException e) {
+                log.error("[OPENROADM] {} exception", did());
+                return null;
+            }
+        } else {
+            log.debug("[OPENROADM] - No  session {}", did());
+        }
+
+        log.debug("[OPENROADM] {} - VENDOR {} HWVERSION {} SWVERSION {} SERIAL {} CHASSIS {}",
+                did(), vendor, hwVersion, swVersion, serialNumber, chassisId);
+        ChassisId cid = new ChassisId(Long.valueOf(chassisId, 10));
+        /*
+         * OpenROADM defines multiple devices (node types). This driver has been tested with
+         * ROADMS, (node type, "rdm"). Other devices can also be discovered, and this code is here
+         * for future developments - untested - it is likely that the XML documents
+         * are model specific.
+         */
+        org.onosproject.net.Device.Type type;
+        if (nodeType.equals("rdm")) {
+            type = org.onosproject.net.Device.Type.ROADM;
+        } else if (nodeType.equals("ila")) {
+            type = org.onosproject.net.Device.Type.OPTICAL_AMPLIFIER;
+        } else if (nodeType.equals("xpdr")) {
+            type = org.onosproject.net.Device.Type.TERMINAL_DEVICE;
+        } else if (nodeType.equals("extplug")) {
+            type = org.onosproject.net.Device.Type.OTHER;
+        } else {
+            log.error("[OPENROADM] {} unsupported node-type", did());
+            return null;
+        }
+        DeviceDescription desc = new DefaultDeviceDescription(
+                did().uri(), type, vendor, hwVersion, swVersion, serialNumber, cid,
+                defaultAvailable, annotationsBuilder.build());
+        return desc;
+    }
+
+
+
+
+    /**
+     * Get the external links as a list of XML hieriarchical configs.
+     *  @param session the NETConf session to the OpenROADM device.
+     *  @return a list of hierarchical conf. each one external link.
+     */
+    List<HierarchicalConfiguration> getExternalLinks(NetconfSession session) {
+        try {
+            String reply = session.rpc(getDeviceExternalLinksBuilder()).get();
+            XMLConfiguration extLinksConf = //
+                (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+            extLinksConf.setExpressionEngine(new XPathExpressionEngine());
+            return extLinksConf.configurationsAt(
+                    "/data/org-openroadm-device/external-link");
+        } catch (NetconfException | InterruptedException | ExecutionException e) {
+            log.error("[OPENROADM] {} exception getting external links", did());
+            return ImmutableList.of();
+        }
+    }
+
+
+    /**
+     * Get the circuit packs from the device as a list of XML hierarchical configs.
+     *  @param session the NETConf session to the OpenROADM device.
+     *  @return a list of hierarchical conf. each one circuit pack.
+     */
+    List<HierarchicalConfiguration> getCircuitPacks(NetconfSession session) {
+        try {
+            String reply = session.rpc(getDeviceCircuitPacksBuilder()).get();
+            XMLConfiguration cpConf = //
+                (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+            cpConf.setExpressionEngine(new XPathExpressionEngine());
+            return cpConf.configurationsAt(
+                    "/data/org-openroadm-device/circuit-packs");
+        } catch (NetconfException | InterruptedException | ExecutionException e) {
+            log.error("[OPENROADM] {} exception getting circuit packs", did());
+            return ImmutableList.of();
+        }
+    }
+
+    /**
+     * Returns a list of PortDescriptions for the device.
+     *
+     * @return a list of descriptions.
+     */
+    @Override
+        public List<PortDescription> discoverPortDetails() {
+            NetconfSession session = getNetconfSession(did());
+            if (session == null) {
+                log.error("discoverPortDetails null session for {}", did());
+                return ImmutableList.of();
+            }
+            if (!getDevice().annotations().keys().contains("openroadm-node-id")) {
+                log.error("PortDiscovery before DeviceDiscovery, using netconf");
+            return ImmutableList.of();
+        }
+        String nodeId = getDevice().annotations().value("openroadm-node-id");
+        List<PortDescription> list = new ArrayList<PortDescription>();
+        List<HierarchicalConfiguration> circuitPacks = getCircuitPacks(session);
+        /*
+         * Iterate all the ports. We need to pass the whole circuitPacks list
+         * because some port data refers to ports in other circuit packs
+         * (reverse), in addition to pass the current circuit pack name, list of
+         * external ports etc
+         */
+        for (HierarchicalConfiguration c : circuitPacks) {
+            parsePorts(list, nodeId, // c contains the whole circuit pack
+                       c.getString("circuit-pack-name"), //
+                       c.configurationsAt(
+                         "ports[port-qual='roadm-external']"), // ext ports
+                       circuitPacks, getExternalLinks(session));
+        }
+        return list;
+    }
+
+    /**
+     * Parses port information.
+     *
+     *  @param list  List of port descriptions to append to.
+     *  @param nodeId OpenROADM node identifier.
+     *  @param circuitPackName Name of the circuit pack the ports belong to
+     *  @param ports hierarchical conf containing all the ports for the circuit
+     * pack
+     *  @param circuitPacks all the circuit packs (to correlate data).
+     *  @param extLinks Hierarchical configuration containing all the ext.
+     * links.
+     */
+    protected void parsePorts(List<PortDescription> list, String nodeId,
+                              String circuitPackName,
+                              List<HierarchicalConfiguration> ports,
+                              List<HierarchicalConfiguration> circuitPacks,
+                              List<HierarchicalConfiguration> extLinks) {
+        checkNotNull(nodeId);
+        checkNotNull(circuitPackName);
+        for (HierarchicalConfiguration port : ports) {
+            try {
+                String portName = checkNotNull(port.getString("port-name"));
+                long portNum = Long.parseLong(port.getString("label"));
+                PortNumber pNum = PortNumber.portNumber(portNum);
+                PortNumber reversepNum = findReversePort(port, circuitPacks);
+                // To see if we have an external port
+                HierarchicalConfiguration eLink = null;
+                for (HierarchicalConfiguration extLink : extLinks) {
+                    String eln =
+                        checkNotNull(extLink.getString("external-link-name"));
+                    String esnid =
+                        checkNotNull(extLink.getString("source/node-id"));
+                    String escpn =
+                        checkNotNull(extLink.getString("source/circuit-pack-name"));
+                    String espn =
+                        checkNotNull(extLink.getString("source/port-name"));
+                    if (nodeId.equals(esnid) && circuitPackName.equals(escpn) &&
+                            portName.equals(espn)) {
+                        eLink = extLink;
+                    }
+                }
+                PortDescription pd = parsePortComponent(
+                        nodeId, circuitPackName, pNum, reversepNum, port, eLink);
+                if (pd != null) {
+                    list.add(pd);
+                }
+            } catch (NetconfException e) {
+                log.error("[OPENROADM] {} NetConf exception", did());
+                return;
+            }
+        }
+    }
+
+    /**
+     * Given a device port (external), return its patner/reverse port.
+     *
+     * @param thisPort the port for which we are looking for the reverse port.
+     * @param circuitPacks all the circuit packs (to correlate data).
+     * @return the port number for the reverse port.
+     * @throws NetconfException .
+     */
+    protected PortNumber
+    findReversePort(HierarchicalConfiguration thisPort,
+                    List<HierarchicalConfiguration> circuitPacks)
+      throws NetconfException {
+        String partnerCircuitPackName =
+          checkNotNull(thisPort.getString("partner-port/circuit-pack-name"));
+        String partnerPortName =
+          checkNotNull(thisPort.getString("partner-port/port-name"));
+        for (HierarchicalConfiguration c : circuitPacks) {
+            if (!partnerCircuitPackName.equals(
+                  c.getString("circuit-pack-name"))) {
+                continue;
+            }
+            for (HierarchicalConfiguration thatPort :
+                 c.configurationsAt("ports[port-qual='roadm-external']")) {
+                String thatPortName = thatPort.getString("port-name");
+                if (partnerPortName.equals(thatPortName)) {
+                    long thatPortNum =
+                      Long.parseLong(thatPort.getString("label"));
+                    return PortNumber.portNumber(thatPortNum);
+                }
+            }
+        }
+        // We should not reach here
+        throw new NetconfException("missing partner/reverse port info");
+    }
+
+    /**
+     * Parses a component XML doc into a PortDescription.
+     * An OMS port description is constructed from XML parsed data.
+     *
+     * @param port the port to parse
+     * @return PortDescription or null
+     */
+    private PortDescription
+    parsePortComponent(String nodeId, String circuitPackName, PortNumber pNum,
+                       PortNumber reversepNum, HierarchicalConfiguration port,
+                       HierarchicalConfiguration extLink) {
+        Map<String, String> annotations = new HashMap<>();
+        annotations.put(AnnotationKeys.OPENROADM_NODEID, nodeId);
+        annotations.put(AnnotationKeys.OPENROADM_CIRCUIT_PACK_NAME,
+                        circuitPackName);
+        annotations.put(AnnotationKeys.OPENROADM_PORT_NAME,
+                        port.getString("port-name"));
+        annotations.put(AnnotationKeys.OPENROADM_PARTNER_CIRCUIT_PACK_NAME,
+                        port.getString("partner-port/circuit-pack-name", ""));
+        annotations.put(AnnotationKeys.OPENROADM_PARTNER_PORT_NAME,
+                        port.getString("partner-port/port-name", ""));
+        annotations.put(AnnotationKeys.OPENROADM_LOGICAL_CONNECTION_POINT,
+                        port.getString("logical-connection-point", ""));
+        // Annotate the reverse port, this is needed for bidir intents.
+        annotations.put(OpticalPathIntent.REVERSE_PORT_ANNOTATION_KEY,
+                        Long.toString(reversepNum.toLong()));
+
+        // for backwards compatibility
+        annotations.put("logical-connection-point",
+                        port.getString("logical-connection-point", ""));
+        // Annotate external link if we found one for this port
+        if (extLink != null) {
+            String ednid = extLink.getString("destination/node-id");
+            String edcpn = extLink.getString("destination/circuit-pack-name");
+            String edpn = extLink.getString("destination/port-name");
+            annotations.put("openroadm-external-node-id", ednid);
+            annotations.put("openroadm-external-circuit-pack-name", edcpn);
+            annotations.put("openroadm-external-port-name", edpn);
+        }
+
+        /*
+         * Declare the actual optical port:
+         * Assumptions: client ports are OCh, assumed to carry ODU4 (should be
+         * configurable)
+         */
+        if (port.getString("port-wavelength-type", "wavelength")
+              .equals("wavelength")) {
+            // OchSignal is needed for OchPortDescription constructor, but it's
+            // tunable
+            OchSignal signalId =
+              OchSignal.newDwdmSlot(ChannelSpacing.CHL_50GHZ, 3);
+            return OchPortHelper.ochPortDescription(
+              pNum, true /* enabled */, OduSignalType.ODU4, true /* tunable */,
+              signalId,
+              DefaultAnnotations.builder().putAll(annotations).build());
+        } else {
+            return OmsPortHelper.omsPortDescription(
+              pNum, true /* enabled */,
+              // Relationship : START and STOP Freq  not being used (See
+              // LambdaQuery)
+              START_CENTER_FREQ, STOP_CENTER_FREQ, CHANNEL_SPACING.frequency(),
+              DefaultAnnotations.builder().putAll(annotations).build());
+        }
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmDropConnection.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmDropConnection.java
new file mode 100644
index 0000000..ad93359
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmDropConnection.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+import org.onosproject.net.device.DeviceService;
+
+/**
+ * Class that models a DROP connection.
+ *
+ */
+public class OpenRoadmDropConnection extends OpenRoadmConnection {
+
+    /**
+     * Constructor for a Drop Connection (from a line to a client port).
+     *
+     *  @param openRoadmName name given to the connection.
+     *  @param xc cross-connection
+     *  @param deviceService ONOS device service.
+     */
+    public OpenRoadmDropConnection(String openRoadmName, OpenRoadmFlowRule xc,
+                                   DeviceService deviceService) {
+        super(openRoadmName, xc, deviceService);
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmExpressConnection.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmExpressConnection.java
new file mode 100644
index 0000000..8c1aad9
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmExpressConnection.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+import org.onosproject.net.device.DeviceService;
+
+/**
+ * Class that models an EXPRESS connection.
+ *
+ */
+public class OpenRoadmExpressConnection extends OpenRoadmConnection {
+
+    /**
+     * Constructor for an Express connection (from line port to line port).
+     *
+     * @param openRoadmName name of the Connection.
+     * @param xc the associated OpenRoadmFlowRule.
+     * @param deviceService ONOS device service.
+     */
+    public OpenRoadmExpressConnection(String openRoadmName, OpenRoadmFlowRule xc,
+                                      DeviceService deviceService) {
+        super(openRoadmName, xc, deviceService);
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmFlowRule.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmFlowRule.java
new file mode 100644
index 0000000..7138ec4
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmFlowRule.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
+import static org.onosproject.net.flow.criteria.Criterion.Type.OCH_SIGID;
+
+import com.google.common.base.MoreObjects;
+import java.util.List;
+import java.util.Objects;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.OchSignalType;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.OchSignalCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
+
+/**
+ * Class that models a FlowRule in an OpenROADM context.
+ *
+ */
+public class OpenRoadmFlowRule extends DefaultFlowRule {
+
+    /**
+     * Type of conenction.
+     */
+    public enum Type {
+        EXPRESS_LINK, // Degree to Degree
+        ADD_LINK,     // SRG to Degree
+        DROP_LINK,    // Degree to SRG
+        LOCAL         // SRG to SRG
+    }
+
+    private Type type;
+
+    private PortNumber inPortNumber;
+
+    private PortNumber outPortNumber;
+
+    private OchSignal ochSignal;
+
+    private OchSignalType ochSignalType;
+
+    /**
+     * Constructor. Build an OpenRoadm flow rule from the passed rule.
+     *
+     *  @param rule ONOS flow rule that we have to process
+     *  @param linePorts List of ports that are line ports (degrees) used
+     *   to know the Type of this connection.
+     *
+     * We store and construct attributes like interface names to support
+     * this OpenROADM connection.
+     */
+    public OpenRoadmFlowRule(FlowRule rule, List<PortNumber> linePorts) {
+        super(rule);
+
+        TrafficSelector trafficSelector = rule.selector();
+        PortCriterion pc = (PortCriterion) trafficSelector.getCriterion(IN_PORT);
+        checkArgument(pc != null, "Missing IN_PORT Criterion");
+        inPortNumber = pc.port();
+
+        // Generally, Sigtype and ochSignal could be null. This would mean e.g. a
+        // port switching connection.
+        OchSignalCriterion osc = (OchSignalCriterion) trafficSelector.getCriterion(OCH_SIGID);
+        // checkArgument(osc != null, "Missing OCH_SIGID Criterion");
+        if (osc != null) {
+            ochSignal = osc.lambda();
+        }
+
+        TrafficTreatment trafficTreatment = rule.treatment();
+        List<Instruction> instructions = trafficTreatment.immediate();
+
+        outPortNumber = instructions.stream()
+                          .filter(i -> i.type() == Instruction.Type.OUTPUT)
+                          .map(i -> ((OutputInstruction) i).port())
+                          .findFirst()
+                          .orElse(null);
+        checkArgument(outPortNumber != null, "Missing OUTPUT Instruction");
+
+        if (linePorts.contains(inPortNumber) && linePorts.contains(outPortNumber)) {
+            type = Type.EXPRESS_LINK;
+        }
+        if (!linePorts.contains(inPortNumber) && linePorts.contains(outPortNumber)) {
+            type = Type.ADD_LINK;
+        }
+        if (linePorts.contains(inPortNumber) && !linePorts.contains(outPortNumber)) {
+            type = Type.DROP_LINK;
+        }
+        if (!linePorts.contains(inPortNumber) && !linePorts.contains(outPortNumber)) {
+            type = Type.LOCAL;
+        }
+    }
+
+    /**
+     * Get the type of the connection.
+     *
+     * @return type (express, add, drop, local)
+     */
+    public Type type() {
+        return type;
+    }
+
+    /**
+     * Get the input port.
+     *
+     * @return the input port (connection source).
+     */
+    public PortNumber inPort() {
+        return inPortNumber;
+    }
+
+
+    /**
+     * Get the output port.
+     *
+     * @return the output port (connection destination).
+     */
+    public PortNumber outPort() {
+        return outPortNumber;
+    }
+
+
+    /**
+     * Get the OchSignal for this connection.
+     *
+     * @return the OchSignal.
+     */
+    public OchSignal ochSignal() {
+        return ochSignal;
+    }
+
+
+    /**
+     * Get the OchSignalType for this connection.
+     *
+     * @return the OchSignalType.
+     */
+    public OchSignalType ochSignalType() {
+        return ochSignalType;
+    }
+
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof OpenRoadmFlowRule)) {
+            return false;
+        }
+        OpenRoadmFlowRule that = (OpenRoadmFlowRule) o;
+        return Objects.equals(this.inPortNumber, that.inPortNumber) &&
+          Objects.equals(this.outPortNumber, that.outPortNumber) &&
+          Objects.equals(this.ochSignal, that.ochSignal) &&
+          Objects.equals(this.ochSignalType, that.ochSignalType) &&
+          Objects.equals(this.type, that.type);
+    }
+
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(inPortNumber, outPortNumber, ochSignal, ochSignalType, type);
+    }
+
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+          .add("type", type)
+          .add("inPortNumber", inPortNumber)
+          .add("outPortNumber", outPortNumber)
+          .add("ochSignal", ochSignal)
+          .add("ochSignalType", ochSignalType)
+          .toString();
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmFlowRuleProgrammable.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmFlowRuleProgrammable.java
new file mode 100644
index 0000000..54d5152
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmFlowRuleProgrammable.java
@@ -0,0 +1,746 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.ImmutableList;
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.onlab.util.Frequency;
+import org.onlab.util.Spectrum;
+import org.onosproject.drivers.odtn.impl.DeviceConnectionCache;
+import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.GridType;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.OchSignalType;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleProgrammable;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.netconf.DatastoreId;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of FlowRuleProgrammable interface for OpenROADM devices.
+ */
+public class OpenRoadmFlowRuleProgrammable
+  extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
+
+    private static final Logger log =
+      LoggerFactory.getLogger(OpenRoadmFlowRuleProgrammable.class);
+
+    private static final String RPC_TAG_NETCONF_BASE =
+      "<rpc xmlns='urn:ietf:params:xml:ns:netconf:base:1.0'>";
+
+    private static final String RPC_CLOSE_TAG = "</rpc>";
+
+    private static final String ORG_OPENROADM_DEVICE_OPEN_TAG =
+      "<org-openroadm-device xmlns='http://org/openroadm/device'  xmlns:nc='urn:ietf:params:xml:ns:netconf:base:1.0'>";
+    private static final String ORG_OPENROADM_DEVICE_CLOSE_TAG = "</org-openroadm-device>";
+
+    /**
+     * Helper method to get the DeviceId.
+     * <p>
+     */
+    private DeviceId did() {
+        return data().deviceId();
+    }
+
+    private DeviceConnectionCache getConnectionCache() {
+        return DeviceConnectionCache.init();
+    }
+
+    /**
+     * Helper method to log from this class adding DeviceId.
+     * <p>
+     */
+    private void openRoadmLog(String format, Object... arguments) {
+        log.debug("OPENROADM {}: " + format, did(), arguments);
+    }
+
+    /**
+     * Helper method to log from this class adding DeviceId.
+     * <p>
+     */
+    private void openRoadmInfo(String format, Object... arguments) {
+        log.info("OPENROADM {}: " + format, did(), arguments);
+    }
+
+
+    /**
+     * Get a list of Port numbers that are LINE ports (degree).
+     * <p>
+     *  @return list of port numbers
+     */
+    private List<PortNumber> getLinePorts() {
+        DeviceService deviceService = this.handler().get(DeviceService.class);
+        return deviceService.getPorts(did())
+          .stream()
+          .filter(
+            p -> p.annotations().value("openroadm-logical-connection-point").contains("DEG"))
+          .map(p -> p.number())
+          .collect(Collectors.toList());
+    }
+
+    /**
+     * Helper method to get the Netconf Session.
+     *  @return the netconf session, which may be null.
+     */
+    private NetconfSession getNetconfSession() {
+        NetconfController controller = handler().get(NetconfController.class);
+        NetconfSession session = controller.getNetconfDevice(did()).getSession();
+        return session;
+    }
+
+    /**
+     * Fetches list of connections from device.
+     *
+     * @return list of connections as XML hierarchy
+     */
+    private List<HierarchicalConfiguration> getDeviceConnections() {
+        NetconfSession session = getNetconfSession();
+        if (session == null) {
+            log.error("OPENROADM {}: session not found", did());
+            return ImmutableList.of();
+        }
+        try {
+            StringBuilder rb = new StringBuilder();
+            rb.append(ORG_OPENROADM_DEVICE_OPEN_TAG);
+            rb.append("  <roadm-connections/>");
+            rb.append(ORG_OPENROADM_DEVICE_CLOSE_TAG);
+            String reply = session.getConfig(DatastoreId.RUNNING, rb.toString());
+            log.debug("REPLY to getDeviceConnections {}", reply);
+            HierarchicalConfiguration cfg =
+              XmlConfigParser.loadXml(new ByteArrayInputStream(reply.getBytes()));
+            return cfg.configurationsAt("data.org-openroadm-device.roadm-connections");
+        } catch (NetconfException e) {
+            return ImmutableList.of();
+        }
+    }
+
+
+    /**
+     * Get the flow entries that are present on the device, called by
+     * FlowRuleDriverProvider. <p> 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() {
+        List<HierarchicalConfiguration> conf = getDeviceConnections();
+        List<FlowEntry> entries = new ArrayList<>();
+        for (HierarchicalConfiguration c : conf) {
+            openRoadmLog("Existing connection {}", c);
+            FlowRule r = buildFlowrule(c);
+            if (r != null) {
+                FlowEntry e = new DefaultFlowEntry(r, FlowEntry.FlowEntryState.ADDED, 0, 0, 0);
+                openRoadmLog("RULE RETRIEVED {}", r);
+                entries.add(e);
+            }
+        }
+        return entries;
+    }
+
+
+    /**
+     * Apply the flow entries specified in the collection rules.
+     *
+     * @param rules A collection of Flow Rules to be applied
+     * @return The collection of added Flow Entries
+     */
+    @Override
+    public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
+        List<FlowRule> added = new ArrayList<>();
+        for (FlowRule r : rules) {
+            openRoadmLog("TO APPLY RULE {}", r);
+            OpenRoadmFlowRule xc = new OpenRoadmFlowRule(r, getLinePorts());
+            openRoadmInfo("OpenRoadmRule {}", xc);
+            if (editConfigCreateConnection(xc)) {
+                added.add(xc);
+                openRoadmLog("RULE APPLIED {}", r);
+            }
+        }
+        openRoadmLog("applyFlowRules added {}", added.size());
+        return added;
+    }
+
+
+    /**
+     * Remove the specified flow rules.
+     *
+     * @param rules A collection of Flow Rules to be removed
+     * @return The collection of removed Flow Rules
+     */
+    @Override
+    public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
+        List<FlowRule> removed = new ArrayList<>();
+        for (FlowRule r : rules) {
+            OpenRoadmFlowRule xc = new OpenRoadmFlowRule(r, getLinePorts());
+            openRoadmLog("TO REMOVE RULE {}", xc);
+            if (editConfigDeleteConnection(xc)) {
+                removed.add(r);
+                openRoadmLog("RULE REMOVED {}", r);
+            }
+        }
+        openRoadmLog("removedFlowRules removed {}", removed.size());
+        return removed;
+    }
+
+    /**
+     * Construct a connection name for a connection between two ports.
+     *
+     * @param srcPort source port, can be a degree port or a SRG port
+     * @param dstPort destination port, can be a degree port or a SRG port
+     * @param ncFreq Nominal Center freq.
+     * @return a string with the connection name.
+     *
+     * OpenROADM Connections are of the form <nmc-interface>-to-<nmc-interface>.
+     * NMC interfaces are created before the connection, directly over the SRG
+     * port or a supporting MC interface.
+     *
+     */
+    private String openRoadmConnectionName(Port srcPort, Port dstPort, Frequency ncFreq) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("NMC-CTP-");
+        sb.append(srcPort.annotations().value("openroadm-logical-connection-point"));
+        sb.append("-");
+        sb.append(ncFreq.asTHz());
+        sb.append("-to-NMC-CTP-");
+        sb.append(dstPort.annotations().value("openroadm-logical-connection-point"));
+        sb.append("-");
+        sb.append(ncFreq.asTHz());
+        return sb.toString();
+    }
+
+    /**
+     * Construct a connection name given an OpenRoadmFlowRule.
+     *
+     * @param xc the flow rule or crossconnection.
+     *
+     */
+    private String openRoadmConnectionName(OpenRoadmFlowRule xc) {
+        DeviceService deviceService = this.handler().get(DeviceService.class);
+        Port srcPort = deviceService.getPort(did(), xc.inPort());
+        Port dstPort = deviceService.getPort(did(), xc.outPort());
+        Frequency centerFreq = xc.ochSignal().centralFrequency();
+        return openRoadmConnectionName(srcPort, dstPort, centerFreq);
+    }
+
+    /**
+     * Builds a flow rule from a connection object (as XML object).
+     *
+     * @param connection the connection hierarchy
+     * @return the flow rule
+     */
+    private FlowRule buildFlowrule(HierarchicalConfiguration connection) {
+        String name = connection.getString("connection-name");
+        if (name == null) {
+            log.error("OPENROADM {}: connection name not correctly retrieved", did());
+            return null;
+        }
+        // If the flow entry is not in the cache: return null
+        FlowRule flowRule = getConnectionCache().get(did(), name);
+        if (flowRule == null) {
+            log.error("OPENROADM {}: name {} not in cache. delete editConfig", did(), name);
+            editConfigDeleteConnection(name);
+            return null;
+        } else {
+            openRoadmLog("connection retrieved {}", name);
+        }
+        OpenRoadmFlowRule xc = new OpenRoadmFlowRule(flowRule, getLinePorts());
+        DeviceService deviceService = this.handler().get(DeviceService.class);
+        OpenRoadmConnection conn = OpenRoadmConnectionFactory.create(name, xc, deviceService);
+        OchSignal och = toOchSignalCenterWidth(conn.srcNmcFrequency, conn.srcNmcWidth);
+        // Build the rule selector and treatment
+        TrafficSelector selector =
+          DefaultTrafficSelector.builder()
+            .matchInPort(conn.inPortNumber)
+            .add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID))
+            .add(Criteria.matchLambda(och))
+            .build();
+        Instruction ochInstruction = Instructions.modL0Lambda(och);
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                                       .add(ochInstruction)
+                                       .setOutput(conn.outPortNumber)
+                                       .build();
+
+        return DefaultFlowRule.builder()
+          .forDevice(data().deviceId())
+          .makePermanent()
+          .withSelector(selector)
+          .withTreatment(treatment)
+          .withPriority(conn.priority)
+          .withCookie(conn.id.value())
+          .build();
+    }
+
+
+
+    /**
+     * Delete a ROADM Interface given its name.
+     *
+     * @param interfaceName name of the interface to be removed.
+     */
+    private void editConfigDeleteInterfaceEntry(String interfaceName) {
+        checkNotNull(interfaceName);
+        StringBuilder sb = new StringBuilder();
+        sb.append(ORG_OPENROADM_DEVICE_OPEN_TAG);
+        sb.append("  <interface nc:operation='delete'>");
+        sb.append("    <name>" + interfaceName + "</name>");
+        sb.append("  </interface>");
+        sb.append(ORG_OPENROADM_DEVICE_CLOSE_TAG);
+        if (!editConfig(sb.toString())) {
+            log.error("OPENROADM {}: failed to delete interface{}", did(), interfaceName);
+        }
+    }
+
+
+
+    /**
+     * Delete a ROADM Connection given its name.
+     *
+     * @param connectionName name of the connection to be removed.
+     */
+    private void editConfigDeleteConnectionEntry(String connectionName) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(ORG_OPENROADM_DEVICE_OPEN_TAG);
+        sb.append("   <roadm-connections nc:operation='delete'>");
+        sb.append("     <connection-name>" + connectionName + "</connection-name>");
+        sb.append("    </roadm-connections>");
+        sb.append(ORG_OPENROADM_DEVICE_CLOSE_TAG);
+        if (!editConfig(sb.toString())) {
+            log.error("OPENROADM {}: failed to delete Connection {}", did(), connectionName);
+        }
+    }
+
+    /**
+     * Entry point to remove a Crossconnect.
+     *
+     * @param connectionName name of the connection to be removed.
+     * @return true (an edit delete is assumed to not fail)
+     *
+     * NMC-CTP-<rx-logical-port>-<freq>-to-NMC-CTP-<tx-logical-port>-<freq>
+     * NMC-CTP-DEG1-TTP-RX-192.7-to-NMC-CTP-DEG3-TTP-TX-192.7
+     *
+     * Local:
+     * NMC-CTP-SRG1-PP1-RX-190.7-to-NMC-CTP-SRG1-PP2-TX-190.7
+     *
+     * Note this method is rarely used, only when it exists in the Datastore
+     * but not in the connection cache.
+     */
+    private boolean editConfigDeleteConnection(String connectionName) {
+        String[] nmcNames = connectionName.split("-to-");
+        // Connection src NMC interface (RX) is NMC-CTP-DEG1-TTP-RX-192.7
+        // Connection dst NMC interface (TX) is NMC-CTP-DEG3-TTP-TX-192.7
+        String nmcRxName = nmcNames[0];
+        String nmcTxName = nmcNames[1];
+        // Connection src MC interface (RX) is MC-TTP-DEG3-TTP-RX-192.7
+        // Connection dst MC interface (TX) is MC-TTP-DEG3-TTP-TX-192.7
+        String mcRxName = "MC-TTP" + nmcRxName.substring(7);
+        String mcTxName = "MC-TTP" + nmcTxName.substring(7);
+
+        // Delete Connection
+        editConfigDeleteConnectionEntry(connectionName);
+        // Delete interfaces
+        editConfigDeleteInterfaceEntry(nmcRxName);
+        editConfigDeleteInterfaceEntry(nmcTxName);
+        if (!nmcRxName.contains("SRG")) { // Source MC interfaces not in ADD
+            editConfigDeleteInterfaceEntry(mcRxName);
+        }
+        if (!nmcTxName.contains("SRG")) { // Dest MC interfaces not in ADD
+            editConfigDeleteInterfaceEntry(mcTxName);
+        }
+        return true;
+    }
+
+
+
+    /**
+     * Entry point to remove a Crossconnect.
+     *
+     * @param xc - OpenROADM flow rule (cross-connect data)
+     */
+    private boolean editConfigDeleteConnection(OpenRoadmFlowRule xc) {
+        String name = openRoadmConnectionName(xc);
+        FlowRule flowRule = getConnectionCache().get(did(), name);
+        if (flowRule == null) {
+            openRoadmLog("editConfigDeleteConnection,  {} not in cache", name);
+            // What to do ? it should be in the cache
+            return true;
+        }
+        // Delete Connection
+        editConfigDeleteConnectionEntry(name);
+        // Remove connection from cache
+        getConnectionCache().remove(did(), xc);
+
+        DeviceService deviceService = this.handler().get(DeviceService.class);
+        OpenRoadmConnection conn = OpenRoadmConnectionFactory.create(name, xc, deviceService);
+
+        // Delete interfaces. Note, deletion of interfaces may fail if
+        // they are used by other connections.
+        editConfigDeleteInterfaceEntry(conn.dstNmcName);
+        editConfigDeleteInterfaceEntry(conn.srcNmcName);
+        if ((conn.getType() != OpenRoadmFlowRule.Type.ADD_LINK) &&
+            (conn.getType() != OpenRoadmFlowRule.Type.LOCAL)) {
+            editConfigDeleteInterfaceEntry(conn.srcMcName);
+        }
+        if ((conn.getType() != OpenRoadmFlowRule.Type.DROP_LINK) &&
+            (conn.getType() != OpenRoadmFlowRule.Type.LOCAL)) {
+            editConfigDeleteInterfaceEntry(conn.dstMcName);
+        }
+
+
+        return true;
+    }
+
+    /**
+     * Create a ROADM NMC Interfaces.
+     *
+     * @param conn connection to create on the device.
+     * @param operation netconf operation (e.g. merge)
+     * @return true if Netconf operation was ok, false otherwise.
+     */
+    private boolean editConfigCreateMcInterfaces(OpenRoadmConnection conn, String operation) {
+
+        openRoadmLog("Checking MC interafaces for {}", conn);
+        // clang-format off
+        // Creation of MC in Input
+        if ((conn.getType() != OpenRoadmFlowRule.Type.ADD_LINK) &&
+            (conn.getType() != OpenRoadmFlowRule.Type.LOCAL)) {
+            StringBuilder sb = new StringBuilder();
+            openRoadmLog("Creating MC SRC interface {}", conn.srcMcName);
+            sb.append(ORG_OPENROADM_DEVICE_OPEN_TAG);
+            sb.append("<interface nc:operation='" + operation + "'>");
+            sb.append("  <name>" + conn.srcMcName + "</name>");
+            sb.append("  <description>Media-Channel</description>");
+            sb.append("  <type xmlns:openROADM-if='http://org/openroadm/interfaces'>" +
+                    "openROADM-if:mediaChannelTrailTerminationPoint</type>");
+            sb.append("  <administrative-state>inService</administrative-state>");
+            sb.append("  <supporting-circuit-pack-name>" +
+                    conn.srcMcSupportingCircuitPack +
+                    "</supporting-circuit-pack-name>");
+            sb.append("  <supporting-port>" + conn.srcMcSupportingPort + "</supporting-port>");
+            sb.append("  <supporting-interface>" + conn.srcMcSupportingInterface + "</supporting-interface>");
+            sb.append("  <mc-ttp xmlns='http://org/openroadm/media-channel-interfaces'>");
+            sb.append("    <min-freq>" + conn.srcMcMinFrequency.asTHz() + "</min-freq>");
+            sb.append("    <max-freq>" + conn.srcMcMaxFrequency.asTHz() + "</max-freq>");
+            sb.append("  </mc-ttp>");
+            sb.append("</interface>");
+            sb.append(ORG_OPENROADM_DEVICE_CLOSE_TAG);
+            if (!editConfig(sb.toString())) {
+                log.error("OPENROADM {}: failed to create interface\n {}", did(), sb.toString());
+                return false;
+            }
+        }
+        if ((conn.getType() != OpenRoadmFlowRule.Type.DROP_LINK) &&
+            (conn.getType() != OpenRoadmFlowRule.Type.LOCAL)) {
+            StringBuilder sb = new StringBuilder();
+            openRoadmLog("Creating MC DST interface {}", conn.dstMcName);
+            sb.append(ORG_OPENROADM_DEVICE_OPEN_TAG);
+            sb.append("<interface nc:operation='" + operation + "'>");
+            sb.append("  <name>" + conn.dstMcName + "</name>");
+            sb.append("  <description>Media-Channel</description>");
+            sb.append("  <type xmlns:openROADM-if='http://org/openroadm/interfaces'>" +
+                    "openROADM-if:mediaChannelTrailTerminationPoint</type>");
+            sb.append("  <administrative-state>inService</administrative-state>");
+            sb.append("  <supporting-circuit-pack-name>" +
+                conn.dstMcSupportingCircuitPack +
+                "</supporting-circuit-pack-name>");
+            sb.append("  <supporting-port>" + conn.dstMcSupportingPort + "</supporting-port>");
+            sb.append("  <supporting-interface>" + conn.dstMcSupportingInterface + "</supporting-interface>");
+            sb.append("  <mc-ttp xmlns='http://org/openroadm/media-channel-interfaces'>");
+            sb.append("    <min-freq>" + conn.dstMcMinFrequency.asTHz() + "</min-freq>");
+            sb.append("    <max-freq>" + conn.dstMcMaxFrequency.asTHz() + "</max-freq>");
+            sb.append("  </mc-ttp>");
+            sb.append("</interface>");
+            sb.append(ORG_OPENROADM_DEVICE_CLOSE_TAG);
+            if (!editConfig(sb.toString())) {
+                log.error("OPENROADM {}: failed to create interface\n {}", did(), sb.toString());
+                return false;
+            }
+        }
+        // clang-format on
+        return true;
+    }
+
+    /**
+     * Create a ROADM NMC Interfaces.
+     *
+     * @param conn connection to create on the device.
+     * @param operation netconf operation (e.g. merge)
+     * @return true if Netconf operation was ok, false otherwise.
+     */
+    private boolean editConfigCreateNmcInterfaces(OpenRoadmConnection conn, String operation) {
+        // clang-format off
+        openRoadmLog("Creating NMC interfaces SRC {}", conn.srcNmcName);
+        StringBuilder sb = new StringBuilder();
+        sb.append(ORG_OPENROADM_DEVICE_OPEN_TAG);
+        sb.append("<interface nc:operation='" + operation + "'>");
+        sb.append("  <name>" + conn.srcNmcName + "</name>");
+        sb.append("  <description>Network-Media-Channel</description>");
+        sb.append("  <type xmlns:openROADM-if='http://org/openroadm/interfaces'>" +
+                  "openROADM-if:networkMediaChannelConnectionTerminationPoint</type>");
+        sb.append("  <administrative-state>inService</administrative-state>");
+        sb.append("  <supporting-circuit-pack-name>" +
+                conn.srcNmcSupportingCircuitPack +
+                "</supporting-circuit-pack-name>");
+        sb.append("  <supporting-port>" + conn.srcNmcSupportingPort + "</supporting-port>");
+        if ((conn.getType() != OpenRoadmFlowRule.Type.ADD_LINK) &&
+            (conn.getType() != OpenRoadmFlowRule.Type.LOCAL)) {
+            sb.append("<supporting-interface>" + conn.srcNmcSupportingInterface + "</supporting-interface>");
+        }
+        sb.append("  <nmc-ctp xmlns='http://org/openroadm/network-media-channel-interfaces'>");
+        sb.append("    <frequency>" + conn.srcNmcFrequency.asTHz() + "</frequency>");
+        sb.append("    <width>" + conn.srcNmcWidth.asGHz() + "</width>");
+        sb.append("  </nmc-ctp>");
+        sb.append("</interface>");
+        sb.append(ORG_OPENROADM_DEVICE_CLOSE_TAG);
+        if (!editConfig(sb.toString())) {
+            log.error("OpenRoadm driver - failed to create interface");
+            return false;
+        }
+
+        openRoadmLog("Creating NMC interfaces DST {}", conn.dstNmcName);
+        sb = new StringBuilder();
+        sb.append(ORG_OPENROADM_DEVICE_OPEN_TAG);
+        sb.append("<interface nc:operation='" + operation + "'>");
+        sb.append("  <name>" + conn.dstNmcName + "</name>");
+        sb.append("  <description>Network-Media-Channel</description>");
+        sb.append("  <type xmlns:openROADM-if='http://org/openroadm/interfaces'>" +
+                "openROADM-if:networkMediaChannelConnectionTerminationPoint</type>");
+        sb.append("  <administrative-state>inService</administrative-state>");
+        sb.append("  <supporting-circuit-pack-name>" +
+                    conn.dstNmcSupportingCircuitPack +
+                    "</supporting-circuit-pack-name>");
+        sb.append("  <supporting-port>" + conn.dstNmcSupportingPort + "</supporting-port>");
+        if ((conn.getType() != OpenRoadmFlowRule.Type.DROP_LINK) &&
+            (conn.getType() != OpenRoadmFlowRule.Type.LOCAL)) {
+            sb.append("<supporting-interface>" + conn.dstNmcSupportingInterface + "</supporting-interface>");
+        }
+        sb.append("  <nmc-ctp xmlns='http://org/openroadm/network-media-channel-interfaces'>");
+        sb.append("    <frequency>" + conn.dstNmcFrequency.asTHz() + "</frequency>");
+        sb.append("    <width>" + conn.dstNmcWidth.asGHz() + "</width>");
+        sb.append("  </nmc-ctp>");
+        sb.append("</interface>");
+        sb.append(ORG_OPENROADM_DEVICE_CLOSE_TAG);
+        if (!editConfig(sb.toString())) {
+            log.error("OpenRoadm driver - failed to create interface");
+            return false;
+        }
+        return true;
+        // clang-format on
+    }
+
+    /**
+     * Create the MC and NMC interfaces supporting a connection.
+     *
+     * @param conn connection to create on the device.
+     * @return true if Netconf operation was ok, false otherwise.
+     */
+    private boolean editConfigCreateInterfaces(OpenRoadmConnection conn) {
+        if (!editConfigCreateMcInterfaces(conn, "merge")) {
+            return false;
+        }
+        if (!editConfigCreateNmcInterfaces(conn, "merge")) {
+            return false;
+        }
+        return true;
+    }
+
+
+    /**
+     * Create a ROADM Connection given its data.
+     *
+     * @param conn connection to create on the device.
+     * @return true if Netconf operation was ok, false otherwise.
+     */
+    private boolean editConfigCreateConnectionEntry(OpenRoadmConnection conn,
+                                                    String operation) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(ORG_OPENROADM_DEVICE_OPEN_TAG);
+        sb.append("  <roadm-connections nc:operation='" + operation + "'>");
+        sb.append("    <connection-name>" + conn.connectionName + "</connection-name>");
+        sb.append("    <opticalControlMode>off</opticalControlMode>");
+        sb.append("    <target-output-power>0</target-output-power>");
+        sb.append("    <source>");
+        sb.append("      <src-if>" + conn.srcConnInterface + "</src-if>");
+        sb.append("    </source>");
+        sb.append("    <destination>");
+        sb.append("      <dst-if>" + conn.dstConnInterface + "</dst-if>");
+        sb.append("    </destination>");
+        sb.append("  </roadm-connections>");
+        sb.append(ORG_OPENROADM_DEVICE_CLOSE_TAG);
+        if (!editConfig(sb.toString())) {
+            log.error("OPENROADM {}: failed to create Connection {}", did(),
+                      conn.connectionName);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Request the device to setup the Connection for the rule.
+
+     * @param xc - OpenRoadmFlowRule crossconnect
+     *
+     * @return true if operation was completed, false otherwise.
+     */
+    private boolean editConfigCreateConnection(OpenRoadmFlowRule xc) {
+        checkNotNull(xc);
+        String openRoadmConnectionName = openRoadmConnectionName(xc);
+        DeviceService deviceService = this.handler().get(DeviceService.class);
+        OpenRoadmConnection connection =
+          OpenRoadmConnectionFactory.create(openRoadmConnectionName, xc, deviceService);
+
+        if (!editConfigCreateInterfaces(connection)) {
+            return false;
+        }
+
+        if (!editConfigCreateConnectionEntry(connection, "merge")) {
+            return false;
+        }
+
+        // Add connection to local cache
+        getConnectionCache().add(did(), openRoadmConnectionName, xc);
+        openRoadmLog("Connection {} created", connection.connectionName);
+        return true;
+    }
+
+    /**
+     * Helper function to send an edit-config message.
+     * @param config XML string to send
+     * @return false on error, true otherwise
+     * <p>
+     * This method uses the running datastore.
+     */
+    private boolean editConfig(String config) {
+        NetconfSession session = getNetconfSession();
+        if (session == null) {
+            log.error("OPENROADM {}: session not found", did());
+            return false;
+        }
+        try {
+            return session.editConfig(DatastoreId.RUNNING, null, config);
+        } catch (NetconfException e) {
+            log.error("OPENROADM {}: failed to editConfig device {}", did(), e);
+            return false;
+        }
+    }
+
+    /**
+     * Convert start and end frequencies to OCh signal.
+     *
+     * FIXME: assumes slots of 12.5 GHz while devices allows granularity 6.25
+     * GHz and only supports channel spacing 50 and 100
+     *
+     * @param min starting frequency as double in THz
+     * @param max end frequency as double in THz
+     * @return OCh signal
+     */
+    public static OchSignal toOchSignalMinMax(Frequency min, Frequency max) {
+        double start = min.asGHz();
+        double end = max.asGHz();
+
+        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;
+    }
+
+    /**
+     * Helper method to create an OchSignal for a frequency slot.
+     *
+     *  @param center the center frequency as per the ITU-grid.
+     *  @param width slot width
+     * @return OCh signal
+     */
+    public static OchSignal toOchSignalCenterWidth(Frequency center, Frequency width) {
+
+        Frequency radius = width.floorDivision(2);
+
+        // Frequency slot start and end frequency.
+        double start = center.subtract(radius).asGHz();
+        double end = center.add(radius).asGHz();
+
+        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;
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmInterface.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmInterface.java
new file mode 100644
index 0000000..ba6f7e2
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmInterface.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+
+public class OpenRoadmInterface {
+
+    protected String name;
+    protected String description;
+    protected String type;
+    protected String administrativeState;
+    protected String supportingCircuitPack;
+    protected String supportingPort;
+    protected String supportingInterface;
+
+    public abstract static class Builder<T extends Builder<T>> {
+        protected String name;
+        protected String description;
+        protected String type;
+        protected String administrativeState;
+        protected String supportingCircuitPack;
+        protected String supportingPort;
+        protected String supportingInterface;
+
+        protected abstract T self();
+
+        public T name(String name) {
+            this.name = name;
+            return self();
+        }
+
+        public T description(String description) {
+            this.description = description;
+            return self();
+        }
+
+        public T administrativeState(String administrativeState) {
+            this.administrativeState = administrativeState;
+            return self();
+        }
+
+        public T supportingCircuitPack(String supportingCircuitPack) {
+            this.supportingCircuitPack = supportingCircuitPack;
+            return self();
+        }
+
+        public T supportingPort(String supportingPort) {
+            this.supportingPort = supportingPort;
+            return self();
+        }
+
+        public T supportingInterface(String supportingInterface) {
+            this.supportingInterface = supportingInterface;
+            return self();
+        }
+
+        public OpenRoadmInterface build() { //
+            return new OpenRoadmInterface(this);
+        }
+    }
+
+    private static class Builder2 extends Builder<Builder2> {
+        @Override
+        protected Builder2 self() {
+            return this;
+        }
+    }
+
+    public static Builder<?> builder() {
+        return new Builder2();
+    }
+
+    protected OpenRoadmInterface(Builder<?> builder) {
+        this.name = builder.name;
+        this.description = builder.description;
+        this.type = builder.type;
+        this.administrativeState = builder.administrativeState;
+        this.supportingCircuitPack = builder.supportingCircuitPack;
+        this.supportingPort = builder.supportingPort;
+        this.supportingInterface = builder.supportingInterface;
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmLambdaQuery.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmLambdaQuery.java
new file mode 100644
index 0000000..882c78a
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmLambdaQuery.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+import java.util.Set;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+
+/**
+ * Lambda Query for OpenROADM ports (see Optical model).
+ *
+ *  This needs to be addapted to a given use case.
+ */
+public class OpenRoadmLambdaQuery extends OpenRoadmCBandLambdaQuery {
+
+    @Override
+    public Set<OchSignal> queryLambdas(PortNumber port) {
+        channelSpacing = ChannelSpacing.CHL_50GHZ;
+        lambdaCount = 32;
+        slotGranularity = 4;
+        Set<OchSignal> set = super.queryLambdas(port);
+        return set;
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmLocalConnection.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmLocalConnection.java
new file mode 100644
index 0000000..e2fb974
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmLocalConnection.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+import org.onosproject.net.device.DeviceService;
+
+/**
+ * Class that models a LOCAL connection.
+ *
+ */
+public class OpenRoadmLocalConnection extends OpenRoadmConnection {
+
+    /**
+     * Constructor for a Local Connection (from client to client ports).
+     *
+     * @param openRoadmName name of the Connection.
+     * @param xc the associated OpenRoadmFlowRule.
+     * @param deviceService ONOS device service.
+     */
+    public OpenRoadmLocalConnection(String openRoadmName, OpenRoadmFlowRule xc,
+                                    DeviceService deviceService) {
+        super(openRoadmName, xc, deviceService);
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmMcInterface.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmMcInterface.java
new file mode 100644
index 0000000..bf78929
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmMcInterface.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+import org.onlab.util.Frequency;
+
+
+public class OpenRoadmMcInterface extends OpenRoadmInterface {
+
+    protected Frequency mcMinFrequency; // expressed in Thz
+    protected Frequency mcMaxFrequency; // expressed in Thz
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<interface>");
+        sb.append("<name>" + name + "</name>");
+        sb.append("<description>Media-Channel</description>");
+        sb.append("<type xmlns:openROADM-if=\"http://org/openroadm/interfaces\">"
+                  + "openROADM-if:mediaChannelTrailTerminationPoint</type>");
+        sb.append("<administrative-state>inService</administrative-state>");
+        sb.append("<supporting-circuit-pack-name>" + supportingCircuitPack +
+                  "</supporting-circuit-pack-name>");
+        sb.append("<supporting-port>" + supportingPort + "</supporting-port>");
+        if (!supportingInterface.isEmpty()) {
+            sb.append("<supporting-interface>" + supportingInterface +
+                      "</supporting-interface>");
+        }
+        sb.append("<mc-ttp xmlns=\"http://org/openroadm/media-channel-interfaces\">");
+        sb.append("<min-freq>" + mcMinFrequency.asTHz() + "</min-freq>");
+        sb.append("<max-freq>" + mcMaxFrequency.asTHz() + "</max-freq>");
+        sb.append("</mc-ttp>");
+        sb.append("</interface>");
+
+        return sb.toString();
+    }
+
+    public abstract static class Builder<T extends Builder<T>>
+      extends OpenRoadmInterface.Builder<T> {
+
+        protected Frequency mcMinFrequency; // expressed in Thz
+        protected Frequency mcMaxFrequency; // expressed in Thz
+
+        public T mcMinFrequency(Frequency mcMinFrequency) {
+            this.mcMinFrequency = mcMinFrequency;
+            return self();
+        }
+
+        public T mcMaxFrequency(Frequency mcMaxFrequency) {
+            this.mcMaxFrequency = mcMaxFrequency;
+            return self();
+        }
+    }
+
+    private static class Builder2 extends Builder<Builder2> {
+        @Override
+        protected Builder2 self() {
+            return this;
+        }
+    }
+
+    public static Builder<?> builder() {
+        return new Builder2();
+    }
+
+
+    protected OpenRoadmMcInterface(Builder<?> builder) {
+        super(builder);
+        this.mcMinFrequency = builder.mcMinFrequency;
+        this.mcMaxFrequency = builder.mcMaxFrequency;
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmNetconfHandlerBehaviour.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmNetconfHandlerBehaviour.java
new file mode 100644
index 0000000..643714f
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmNetconfHandlerBehaviour.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfDevice;
+import org.onosproject.netconf.NetconfSession;
+import org.slf4j.Logger;
+
+
+
+/**
+ * Driver Implementation of the DeviceDescrption discovery for OpenROADM.
+ */
+public class OpenRoadmNetconfHandlerBehaviour extends AbstractHandlerBehaviour {
+
+    protected static final String RPC_TAG_NETCONF_BASE =
+      "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
+
+    protected static final String RPC_CLOSE_TAG = "</rpc>";
+
+    protected static final Logger log = getLogger(OpenRoadmNetconfHandlerBehaviour.class);
+
+
+    /**
+     * Returns the NetconfSession with the device for which the method was called.
+     *
+     * @param deviceId device indetifier
+     *
+     * @return The netconf session or null
+     */
+    protected NetconfSession getNetconfSession(DeviceId deviceId) {
+        NetconfController controller = handler().get(NetconfController.class);
+        NetconfDevice ncdev = controller.getDevicesMap().get(deviceId);
+        if (ncdev == null) {
+            log.trace("No netconf device, returning null session");
+            return null;
+        }
+        return ncdev.getSession();
+    }
+
+
+    /**
+     * Get the deviceId for which the methods apply.
+     *
+     * @return The deviceId as contained in the handler data
+     */
+    protected DeviceId did() {
+        return handler().data().deviceId();
+    }
+
+
+    /**
+     * Get the device instance for which the methods apply.
+     *
+     * @return The device instance
+     */
+    protected Device getDevice() {
+        DeviceService deviceService = checkNotNull(handler().get(DeviceService.class));
+        Device device = deviceService.getDevice(did());
+        return device;
+    }
+
+
+    /**
+     * Get the device instance for which the methods apply.
+     *
+     * @return The device instance
+     */
+    protected NetconfDevice getNetconfDevice() {
+        NetconfController controller = checkNotNull(handler().get(NetconfController.class));
+        NetconfDevice ncDevice = controller.getDevicesMap().get(did());
+        return ncDevice;
+    }
+
+
+    /**
+     * Construct a String with a Netconf filtered get RPC Message.
+     *
+     * @param filter A valid XML tree with the filter to apply in the get
+     * @return a String containing the RPC XML Document
+     */
+    protected String filteredGetBuilder(String filter) {
+        StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
+        rpc.append("<get>");
+        rpc.append("<filter type='subtree'>");
+        rpc.append(filter);
+        rpc.append("</filter>");
+        rpc.append("</get>");
+        rpc.append(RPC_CLOSE_TAG);
+        return rpc.toString();
+    }
+
+    /**
+     * Construct a String with a Netconf filtered get RPC Message.
+     *
+     * @param filter A valid XPath Expression with the filter to apply in the get
+     * @return a String containing the RPC XML Document
+     *
+     * Note: server must support xpath capability.
+     */
+    protected String xpathFilteredGetBuilder(String filter) {
+        StringBuilder rpc = new StringBuilder(RPC_TAG_NETCONF_BASE);
+        rpc.append("<get>");
+        rpc.append("<filter type='xpath' select=\"");
+        rpc.append(filter);
+        rpc.append("\"/>");
+        rpc.append("</get>");
+        rpc.append(RPC_CLOSE_TAG);
+        return rpc.toString();
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmNmcInterface.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmNmcInterface.java
new file mode 100644
index 0000000..ca5e8a4
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmNmcInterface.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+import org.onlab.util.Frequency;
+
+public class OpenRoadmNmcInterface extends OpenRoadmInterface {
+
+    protected Frequency nmcFrequency; // expressed in Thz
+    protected Frequency nmcWidth;     // expressed in Ghz
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<interface>");
+        sb.append("<name>" + name + "</name>");
+        sb.append("<description>Network-Media-Channel</description>");
+        sb.append(
+            "<type xmlns:openROADM-if=\"http://org/openroadm/interfaces\">"
+            +
+            "openROADM-if:networkMediaChannelConnectionTerminationPoint</type>");
+        sb.append("<administrative-state>inService</administrative-state>");
+        sb.append("<supporting-circuit-pack-name>" + supportingCircuitPack +
+                  "</supporting-circuit-pack-name>");
+        sb.append("<supporting-port>" + supportingPort + "</supporting-port>");
+        if (!supportingInterface.isEmpty()) {
+            sb.append("<supporting-interface>" + supportingInterface + "</supporting-interface>");
+        }
+
+        sb.append("<nmc-ctp xmlns=\"http://org/openroadm/network-media-channel-interfaces\">");
+        sb.append("<frequency>" + nmcFrequency.asTHz() + "</frequency>");
+        sb.append("<width>" + nmcWidth.asGHz() + "</width>");
+        sb.append("</nmc-ctp>");
+        sb.append("</interface>");
+        return sb.toString();
+    }
+
+    public abstract static class Builder<T extends Builder<T>>
+        extends OpenRoadmInterface.Builder<T> {
+
+      protected Frequency nmcFrequency; // expressed in Thz
+      protected Frequency nmcWidth;     // expressed in Ghz
+
+      public T nmcFrequency(Frequency nmcFrequency) {
+        this.nmcFrequency = nmcFrequency;
+        return self();
+      }
+
+      public T nmcWidth(Frequency nmcWidth) {
+        this.nmcWidth = nmcWidth;
+        return self();
+      }
+    }
+
+    private static class Builder2 extends Builder<Builder2> {
+        @Override
+        protected Builder2 self() {
+            return this;
+        }
+    }
+
+    public static Builder<?> builder() {
+        return new Builder2();
+    }
+
+    protected OpenRoadmNmcInterface(Builder<?> builder) {
+        super(builder);
+        this.nmcFrequency = builder.nmcFrequency;
+        this.nmcWidth = builder.nmcWidth;
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmOmsInterface.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmOmsInterface.java
new file mode 100644
index 0000000..0516dfb
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmOmsInterface.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+public class OpenRoadmOmsInterface extends OpenRoadmInterface {
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<interface>");
+        sb.append("<name>" + name + "</name>");
+        sb.append("<description>" + description + "</description>");
+        sb.append(
+          "<type xmlns:openROADM-if=\"http://org/openroadm/interfaces\">openROADM-if:openROADMOpticalMultiplex</type>");
+        sb.append("<administrative-state>inService</administrative-state>");
+        sb.append("<supporting-circuit-pack-name>" + supportingCircuitPack +
+                  "</supporting-circuit-pack-name>");
+        sb.append("<supporting-port>" + supportingPort + "</supporting-port>");
+        if (!supportingInterface.isEmpty()) {
+            sb.append("<supporting-interface>" + supportingInterface + "</supporting-interface>");
+        }
+        return sb.toString();
+    }
+
+    public abstract static class Builder<T extends Builder<T>>
+        extends OpenRoadmInterface.Builder<T> {
+      //
+    }
+
+    private static class Builder2 extends Builder<Builder2> {
+        @Override
+        protected Builder2 self() {
+            return this;
+        }
+    }
+
+    public static Builder<?> builder() {
+        return new Builder2();
+    }
+
+    protected OpenRoadmOmsInterface(Builder<?> builder) {
+        super(builder);
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmOtsInterface.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmOtsInterface.java
new file mode 100644
index 0000000..0fc82ef
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/OpenRoadmOtsInterface.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+package org.onosproject.drivers.odtn.openroadm;
+
+public class OpenRoadmOtsInterface extends OpenRoadmInterface {
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<interface>");
+        sb.append("<name>" + name + "</name>");
+        sb.append("<description>" + description + "</name>");
+        sb.append(
+          "<type xmlns:openROADM-if=\"http://org/openroadm/interfaces\">openROADM-if:opicalTransport</type>");
+        sb.append("<administrative-state>inService</administrative-state>");
+        sb.append("<supporting-circuit-pack-name>" + supportingCircuitPack +
+                  "</supporting-circuit-pack-name>");
+        sb.append("<supporting-port>" + supportingPort + "</supporting-port>");
+        sb.append("</interface>");
+        return sb.toString();
+    }
+
+    public abstract static class Builder<T extends Builder<T>>
+        extends OpenRoadmInterface.Builder<T> {
+      //
+    }
+
+    private static class Builder2 extends Builder<Builder2> {
+        @Override
+        protected Builder2 self() {
+            return this;
+        }
+    }
+
+    public static Builder<?> builder() {
+        return new Builder2();
+    }
+
+    protected OpenRoadmOtsInterface(Builder<?> builder) {
+        super(builder);
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/package-info.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/package-info.java
new file mode 100644
index 0000000..03711c1
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openroadm/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2018-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.
+
+ * This work was partially supported by EC H2020 project METRO-HAUL (761727).
+ */
+/**
+ * Package for Metro-Haul OpenROADM device drivers.
+ */
+package org.onosproject.drivers.odtn.openroadm;
diff --git a/drivers/odtn-driver/src/main/resources/odtn-drivers.xml b/drivers/odtn-driver/src/main/resources/odtn-drivers.xml
index 8c7a799..ba102a7 100644
--- a/drivers/odtn-driver/src/main/resources/odtn-drivers.xml
+++ b/drivers/odtn-driver/src/main/resources/odtn-drivers.xml
@@ -43,6 +43,17 @@
                    impl="org.onosproject.drivers.odtn.openconfig.TerminalDeviceFlowRuleProgrammable"/>
     </driver>
 
+    <driver name="openroadm" extends="" manufacturer="CTTC/CNIT" hwVersion="0.0.1" swVersion="0.0.1">
+        <behaviour api ="org.onosproject.net.device.DeviceDescriptionDiscovery"
+                   impl="org.onosproject.drivers.odtn.openroadm.OpenRoadmDeviceDescription"/>
+        <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
+                   impl="org.onosproject.drivers.odtn.openroadm.OpenRoadmFlowRuleProgrammable"/>
+        <behaviour api ="org.onosproject.net.behaviour.LambdaQuery"
+                   impl="org.onosproject.drivers.odtn.openroadm.OpenRoadmLambdaQuery"/>
+        <behaviour api ="org.onosproject.net.optical.OpticalDevice"
+                   impl="org.onosproject.net.optical.DefaultOpticalDevice"/>
+   </driver>
+
     <driver name="infinera-xt3300" manufacturer="infinera" hwVersion="xt3300" swVersion="18.0">
         <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
                    impl="org.onosproject.drivers.odtn.InfineraOpenConfigDeviceDiscovery"/>