[ONOS-7893] TerminalDevice driver for OpenConfig transponders.
Includes FlowRuleProgrammable and DeviceDiscovery behaviours.
The patch enables the configuration of client and line sides of the transponder.
FlowRules coming from the compilation of OpticalConnectivity and OpticalCircuit intents are translated in proper netconf/xml messages.
The patch also fixes an issue in the OpticalCircuiIntentCompiler to avoid overwriting among generated flowrules in case of bidirectional intents.
The patch also extend optical-rest app to enable deletion of OpticalCircuit intents.
An example of OpenConfig TerminalDevice xml datastore is included.

patch 2: Addressed comments by Ramon Casellas. Checkstyle.

patch 3: Fixed typo in OpticalConnectivityIntentCompiler.

patch 4: Checkstyle.

patch 5: Addressed comments by Andrea Campanella.

patch 6: Introduced Jira tag

patch 7: Update commit message.

patch 8: Ranaming files.

patch 9: Checkstyle.

Change-Id: Ia50bc0877ecd314aaf48cdb8465451a68313ce3a
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalCircuitIntentCompiler.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalCircuitIntentCompiler.java
index a484d66..7e873d1 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalCircuitIntentCompiler.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalCircuitIntentCompiler.java
@@ -127,6 +127,11 @@
 
     private ApplicationId appId;
 
+    //Priority value of created OpticalConnectivity intent rules.
+    //The OpticalCircuitIntent is created with DEFAULT_INTENT_PRIORITY = 100 (see Intent.java)
+    //The two values have to be different to avoid rules overwriting.
+    private static final int OPTICAL_CONNECTIVITY_INTENT_PRIORITY = 200;
+
     @Modified
     public void modified(ComponentContext context) {
         if (context == null) {
@@ -253,6 +258,7 @@
                     // but `key` field cannot be used for the purpose.
                     .src(srcCP)
                     .dst(dstCP)
+                    .priority(OPTICAL_CONNECTIVITY_INTENT_PRIORITY)
                     .signalType(ochPorts.getLeft().signalType())
                     .bidirectional(intent.isBidirectional())
                     .resourceGroup(intent.resourceGroup())
@@ -320,12 +326,14 @@
         // Create optical circuit intent
         List<FlowRule> rules = new LinkedList<>();
         // at the source: ODUCLT port mapping to OCH port
+        log.debug("OpticalCircuitIntent creating FlowRules");
         rules.add(connectPorts(higherIntent.getSrc(), lowerIntent.getSrc(), higherIntent.priority(), slots));
         // at the destination: OCH port mapping to ODUCLT port
         rules.add(connectPorts(lowerIntent.getDst(), higherIntent.getDst(), higherIntent.priority(), slots));
 
         // Create flow rules for reverse path
         if (higherIntent.isBidirectional()) {
+            log.debug("OpticalCircuitIntent creating FlowRules for reverse path");
            // at the destination: OCH port mapping to ODUCLT port
             rules.add(connectPorts(lowerIntent.getSrc(), higherIntent.getSrc(), higherIntent.priority(), slots));
             // at the source: ODUCLT port mapping to OCH port
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
index dcd97bf..acedda2e 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
@@ -182,6 +182,7 @@
         return OpticalPathIntent.builder()
                 .appId(parentIntent.appId())
                 .key(parentIntent.key())
+                .priority(parentIntent.priority())
                 .src(parentIntent.getSrc())
                 .dst(parentIntent.getDst())
                 .path(path)
diff --git a/apps/optical-rest/src/main/java/org/onosproject/net/optical/rest/OpticalIntentsWebResource.java b/apps/optical-rest/src/main/java/org/onosproject/net/optical/rest/OpticalIntentsWebResource.java
index 4643d7b..cff8d93 100644
--- a/apps/optical-rest/src/main/java/org/onosproject/net/optical/rest/OpticalIntentsWebResource.java
+++ b/apps/optical-rest/src/main/java/org/onosproject/net/optical/rest/OpticalIntentsWebResource.java
@@ -41,6 +41,7 @@
 import org.onosproject.net.intent.IntentState;
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.OpticalConnectivityIntent;
+import org.onosproject.net.intent.OpticalCircuitIntent;
 import org.onosproject.net.link.LinkService;
 import org.onosproject.net.optical.json.OchSignalCodec;
 import org.onosproject.net.provider.ProviderId;
@@ -90,6 +91,7 @@
     private static final String MISSING_MEMBER_MESSAGE = " member is required";
     private static final String E_APP_ID_NOT_FOUND = "Application ID is not found";
     private static final ProviderId PROVIDER_ID = new ProviderId("netconf", "optical-rest");
+    private static final int NUM_CRITERIA_OPTICAL_CONNECTIVIY_RULE = 3;
 
     @Context
     private UriInfo uriInfo;
@@ -157,7 +159,8 @@
 
                 objectNode.put("intent id", opticalConnectivityIntent.id().toString());
                 objectNode.put("app id", opticalConnectivityIntent.appId().name());
-                objectNode.put("state", intentService.getIntentState(opticalConnectivityIntent.key()).toString());
+                objectNode.put("state",
+                        intentService.getIntentState(opticalConnectivityIntent.key()).toString());
                 objectNode.put("src", opticalConnectivityIntent.getSrc().toString());
                 objectNode.put("dst", opticalConnectivityIntent.getDst().toString());
                 objectNode.put("srcName", srcDeviceName);
@@ -174,10 +177,12 @@
                             .findFirst()
                             .orElse(null);
 
-                    //Retrieve used ochSignal from the Selector of one of the installed FlowRule
+                    //FlowRules computed by the OpticalConnectivityIntentCompiler includes 3 criteria, one of those
+                    //is the OchSignal, thus retrieve used ochSignal from the selector of one of the installed rules
                     //TODO store utilized ochSignal in the intent resources
                     if (installableIntent != null) {
                         OchSignal signal = installableIntent.flowRules().stream()
+                                .filter(r -> r.selector().criteria().size() == NUM_CRITERIA_OPTICAL_CONNECTIVIY_RULE)
                                 .map(r -> ((OchSignalCriterion)
                                         r.selector().getCriterion(Criterion.Type.OCH_SIGID)).lambda())
                                 .findFirst()
@@ -239,7 +244,7 @@
         }
         nullIsNotFound(intent, "Intent Id is not found");
 
-        if (intent instanceof OpticalConnectivityIntent) {
+        if ((intent instanceof OpticalConnectivityIntent) || (intent instanceof OpticalCircuitIntent)) {
             intentService.withdraw(intent);
         } else {
             throw new IllegalArgumentException("Specified intent is not of type OpticalConnectivityIntent");
@@ -306,8 +311,6 @@
 
                     Link link = linkService.getLink(srcConnectPoint, dstConnectPoint);
                     if (link == null) {
-                        log.warn("Not existing link in the suggested path src {} dst {}",
-                                 srcConnectPoint, dstConnectPoint);
                         throw new IllegalArgumentException("Not existing link in the suggested path");
                     }
 
diff --git a/apps/optical-rest/src/main/resources/definitions/CreateIntent.json b/apps/optical-rest/src/main/resources/definitions/CreateIntent.json
index da4f215..3106421 100644
--- a/apps/optical-rest/src/main/resources/definitions/CreateIntent.json
+++ b/apps/optical-rest/src/main/resources/definitions/CreateIntent.json
@@ -20,11 +20,11 @@
       "properties": {
         "device": {
           "type": "string",
-          "example": "netconf:10.255.255.14:2022"
+          "example": "netconf:10.10.255.14:2022"
         },
         "port": {
           "type": "string",
-          "example": "101"
+          "example": "10102"
         }
       }
     },
@@ -38,15 +38,15 @@
       "properties": {
         "device": {
           "type": "string",
-          "example": "netconf:10.255.255.17:2022"
+          "example": "netconf:10.10.255.15:2022"
         },
         "port": {
           "type": "string",
-          "example": "201"
+          "example": "10102"
         }
       }
     },
-    "bidirection": {
+    "bidirectional": {
       "type": "boolean",
       "example": false
     },
@@ -115,11 +115,11 @@
             "properties" : {
               "src": {
                 "type": "string",
-                "example": "netconf:10.255.255.14:2022/101"
+                "example": "netconf:10.10.255.14:2022/10102"
               },
               "dst": {
                 "type": "string",
-                "example": "netconf:10.255.255.9:2022/201"
+                "example": "netconf:10.10.255.15:2022/10102"
               }
             }
           }
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/ClientLineTerminalDeviceDiscovery.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/ClientLineTerminalDeviceDiscovery.java
new file mode 100644
index 0000000..4762bf1
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/ClientLineTerminalDeviceDiscovery.java
@@ -0,0 +1,598 @@
+/*
+ * 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.openconfig;
+
+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.DefaultAnnotations;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.CltSignalType;
+import org.onosproject.net.OduSignalType;
+import org.onosproject.net.optical.device.OduCltPortHelper;
+import org.slf4j.Logger;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.concurrent.CompletableFuture;
+
+import org.onlab.packet.ChassisId;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
+
+import org.onosproject.drivers.utilities.XmlConfigParser;
+
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceDescriptionDiscovery;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.PortDescription;
+
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+
+import org.onosproject.net.optical.device.OchPortHelper;
+
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfDevice;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+
+import com.google.common.collect.ImmutableList;
+
+import org.onosproject.odtn.behaviour.OdtnDeviceDescriptionDiscovery;
+
+
+/**
+ * Driver Implementation of the DeviceDescription discovery for OpenConfig terminal devices.
+ *
+ * As defined in OpenConfig each PORT component includes a subcomponent:
+ * --- client ports have a subcomponent of type oc-platform-types:TRANSCEIVER
+ * --- line   ports have a subcomponent of type oc-opt-types:OPTICAL_CHANNEL
+ *
+ * Tested with a model in which each port includes the following two properties:
+ * --- odtn-port-type: can assume values "client" and "line"
+ * --- onos-index: integer value
+ *
+ * Other assumptions:
+ * --- The port name is in the format "port-xxx"
+ * --- The subcomponent of type TRANSCEIVER has a name in the format "transceiver-xxx"
+ * --- The subcomponent of type OPTICAL_CHANNEL has a name in the format "channel-xxx"
+ * --- In the section <terminal-device><logical-channels> the channel with index xxx is associated to transceiver-xxx
+ *
+ * Where xxx is the value of the onos-index property (e.g., port-11801, transceiver-11801, channel-11801)
+ *
+ * See simplified example of a port component:
+ *
+ * //CHECKSTYLE:OFF
+ * <component>
+ *     <name>port-11801</name>
+ *     <state>
+ *         <name>port-11801</name>
+ *         <type>oc-platform-types:PORT</type>
+ *     </state>
+ *     <properties>
+ *         <property>
+ *             <name>odtn-port-type</name>
+ *             <state>
+ *                 <name>odtn-port-type</name>
+ *                 <value>client</value>
+ *             </state>
+ *         </property>
+ *         <property>
+ *             <name>onos-index</name>
+ *             <state>
+ *                 <name>onos-index</name>
+ *                 <value>11801</value>
+ *             </state>
+ *             </property>
+ *     </properties>
+ *     <subcomponents>
+ *         <subcomponent>
+ *             <name>transceiver-11801</name>
+ *             <state>
+ *                 <name>transceiver-11801</name>
+ *             </state>
+ *         </subcomponent>
+ *     </subcomponents>
+ * </component>
+ * <terminal-device>
+ *     <logical-channels>
+ *         <channel>
+ *             <index>11801</index>
+ *             <state>
+ *                 <index>11801</index>
+ *                 <description>Logical channel 11801</description>
+ *                 <admin-state>DISABLED</admin-state>
+ *                 <rate-class>oc-opt-types:TRIB_RATE_10G</rate-class>
+ *                 <trib-protocol>oc-opt-types:PROT_10GE_LAN</trib-protocol>
+ *                 <logical-channel-type>oc-opt-types:PROT_ETHERNET</logical-channel-type>
+ *                 <loopback-mode>NONE</loopback-mode>
+ *                 <test-signal>false</test-signal>
+ *                 <link-state>UP</link-state>
+ *             </state>
+ *             <ingress>
+ *                 <state>
+ *                     <transceiver>transceiver-11801</transceiver>
+ *                 </state>
+ *             </ingress>
+ *         </channel>
+ *     <logical-channels>
+ * <terminal-device>
+ * //CHECKSTYLE:ON
+ */
+public class ClientLineTerminalDeviceDiscovery
+    extends AbstractHandlerBehaviour
+    implements OdtnDeviceDescriptionDiscovery, DeviceDescriptionDiscovery {
+
+    private static final String RPC_TAG_NETCONF_BASE =
+        "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
+
+    private static final String OC_PLATFORM_TYPES_OPERATING_SYSTEM =
+            "oc-platform-types:OPERATING_SYSTEM";
+
+    private static final String RPC_CLOSE_TAG = "</rpc>";
+
+    private static final String OC_PLATFORM_TYPES_TRANSCEIVER =
+        "oc-platform-types:TRANSCEIVER";
+
+    private static final String OC_PLATFORM_TYPES_PORT =
+        "oc-platform-types:PORT";
+
+    private static final String OC_TRANSPORT_TYPES_OPTICAL_CHANNEL =
+        "oc-opt-types:OPTICAL_CHANNEL";
+
+    private static final Logger log = getLogger(ClientLineTerminalDeviceDiscovery.class);
+
+
+    /**
+     * Returns the NetconfSession with the device for which the method was called.
+     *
+     * @param deviceId device indetifier
+     *
+     * @return The netconf session or null
+     */
+    private 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
+     */
+    private DeviceId did() {
+        return handler().data().deviceId();
+    }
+
+
+    /**
+     * Get the device instance for which the methods apply.
+     *
+     * @return The device instance
+     */
+    private Device getDevice() {
+        DeviceService deviceService = checkNotNull(handler().get(DeviceService.class));
+        Device device = deviceService.getDevice(did());
+        return device;
+    }
+
+
+    /**
+     * 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
+     */
+    private 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.
+
+     * <select=" /components/component[name='PORT-A-In-1']/properties/...
+     * ...property[name='onos-index']/config/value" type="xpath"/>
+     */
+    private 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();
+    }
+
+
+    /**
+     * Builds a request to get Device details, 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:OPERATING_SYSTEM
+     */
+    private String getDeviceDetailsBuilder() {
+        StringBuilder filter = new StringBuilder();
+        filter.append("<components xmlns='http://openconfig.net/yang/platform'>");
+        filter.append(" <component>");
+        filter.append("  <state>");
+        filter.append("   <type xmlns:oc-platform-types='http://openconfig.net/yang/platform-types'>");
+        filter.append(OC_PLATFORM_TYPES_OPERATING_SYSTEM);
+        filter.append("   </type>");
+        filter.append("  </state>");
+        filter.append(" </component>");
+        filter.append("</components>");
+        return filteredGetBuilder(filter.toString());
+    }
+
+
+    /**
+     * Builds a request to get Device Components, config and operational data.
+     *
+     * @return A string with the Netconf RPC for a get with subtree rpcing based on
+     *    /components/
+     */
+    private String getDeviceComponentsBuilder() {
+        return filteredGetBuilder(
+            "<components xmlns='http://openconfig.net/yang/platform'/>");
+    }
+
+
+    /**
+     * Builds a request to get Device Ports, 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 getDevicePortsBuilder() {
+        StringBuilder rpc = new StringBuilder();
+        rpc.append("<components xmlns='http://openconfig.net/yang/platform'>");
+        rpc.append(" <component><state>");
+        rpc.append("   <type xmlns:oc-platform-types='http://openconfig.net/");
+        rpc.append("yang/platform-types'>oc-platform-types:PORT</type>");
+        rpc.append(" </state></component>");
+        rpc.append("</components>");
+        return filteredGetBuilder(rpc.toString());
+    }
+
+
+    /**
+     * Returns a DeviceDescription with Device info.
+     *
+     * @return DeviceDescription or null
+     *
+     * //CHECKSTYLE:OFF
+     * <pre>{@code
+     * <data>
+     * <components xmlns="http://openconfig.net/yang/platform">
+     *  <component>
+     *   <state>
+     *     <name>FIRMWARE</name>
+     *     <type>oc-platform-types:OPERATING_SYSTEM</type>
+     *     <description>CTTC METRO-HAUL Emulated OpenConfig TerminalDevice</description>
+     *     <version>0.0.1</version>
+     *   </state>
+     *  </component>
+     * </components>
+     * </data>
+     *}</pre>
+     * //CHECKSTYLE:ON
+     */
+    @Override
+    public DeviceDescription discoverDeviceDetails() {
+        boolean defaultAvailable = true;
+        SparseAnnotations annotations = DefaultAnnotations.builder().build();
+
+        log.debug("ClientLineTerminalDeviceDiscovery::discoverDeviceDetails device {}", did());
+
+        // Other option "OTN" or "OTHER", we use TERMINAL_DEVICE
+        org.onosproject.net.Device.Type type =
+            Device.Type.TERMINAL_DEVICE;
+
+        // Some defaults
+        String vendor       = "NOVENDOR";
+        String serialNumber = "0xCAFEBEEF";
+        String hwVersion    = "0.2.1";
+        String swVersion    = "0.2.1";
+        String chassisId    = "128";
+
+        // Get the session,
+        NetconfSession session = getNetconfSession(did());
+        try {
+            String reply = session.get(getDeviceDetailsBuilder());
+            log.debug("REPLY to DeviceDescription {}", reply);
+
+            // <rpc-reply> as root node, software hardare version requires openconfig >= 2018
+            XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
+            vendor       = xconf.getString("data.components.component.state.mfg-name", vendor);
+            serialNumber = xconf.getString("data.components.component.state.serial-no", serialNumber);
+            swVersion    = xconf.getString("data.components.component.state.software-version", swVersion);
+            hwVersion    = xconf.getString("data.components.component.state.hardware-version", hwVersion);
+        } catch (Exception e) {
+                log.error("ClientLineTerminalDeviceDiscovery::discoverDeviceDetails - Failed to retrieve session {}",
+                        did());
+                throw new IllegalStateException(new NetconfException("Failed to retrieve version info.", e));
+        }
+
+        ChassisId cid = new ChassisId(Long.valueOf(chassisId, 10));
+
+        log.info("Device retrieved details");
+        log.info("VENDOR    {}", vendor);
+        log.info("HWVERSION {}", hwVersion);
+        log.info("SWVERSION {}", swVersion);
+        log.info("SERIAL    {}", serialNumber);
+        log.info("CHASSISID {}", chassisId);
+
+        return new DefaultDeviceDescription(did().uri(),
+                    type, vendor, hwVersion, swVersion, serialNumber,
+                    cid, defaultAvailable, annotations);
+    }
+
+
+
+    /**
+     * Returns a list of PortDescriptions for the device.
+     *
+     * @return a list of descriptions.
+     *
+     * The RPC reply follows the following pattern:
+     * //CHECKSTYLE:OFF
+     * <pre>{@code
+     * <?xml version="1.0" encoding="UTF-8"?>
+     * <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="7">
+     * <data>
+     *   <components xmlns="http://openconfig.net/yang/platform">
+     *     <component>....
+     *     </component>
+     *     <component>....
+     *     </component>
+     *   </components>
+     * </data>
+     * </rpc-reply>
+     * }</pre>
+     * //CHECKSTYLE:ON
+     */
+    @Override
+    public List<PortDescription> discoverPortDetails() {
+        try {
+            XPathExpressionEngine xpe = new XPathExpressionEngine();
+            NetconfSession session = getNetconfSession(did());
+            if (session == null) {
+                log.error("discoverPortDetails called with null session for {}", did());
+                return ImmutableList.of();
+            }
+
+            CompletableFuture<String> fut = session.rpc(getDeviceComponentsBuilder());
+            String rpcReply = fut.get();
+
+            XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(rpcReply);
+            xconf.setExpressionEngine(xpe);
+
+            log.debug("REPLY {}", rpcReply);
+            HierarchicalConfiguration components = xconf.configurationAt("data/components");
+            return parsePorts(components);
+        } catch (Exception e) {
+            log.error("Exception discoverPortDetails() {}", did(), e);
+            return ImmutableList.of();
+        }
+    }
+
+    /**
+     * Parses port information from OpenConfig XML configuration.
+     *
+     * @param components the XML document with components root.
+     * @return List of ports
+     *
+     * //CHECKSTYLE:OFF
+     * <pre>{@code
+     *   <components xmlns="http://openconfig.net/yang/platform">
+     *     <component>....
+     *     </component>
+     *     <component>....
+     *     </component>
+     *   </components>
+     * }</pre>
+     * //CHECKSTYLE:ON
+     */
+    protected List<PortDescription> parsePorts(HierarchicalConfiguration components) {
+        return components.configurationsAt("component").stream()
+            .filter(component -> {
+                    return !component.getString("name", "unknown").equals("unknown") &&
+                    component.getString("state/type", "unknown").equals(OC_PLATFORM_TYPES_PORT);
+                    })
+            .map(component -> {
+                try {
+                    // Pass the root document for cross-reference
+                    return parsePortComponent(component, components);
+                } catch (Exception e) {
+                    return null;
+                }
+            }
+            )
+            .filter(Objects::nonNull)
+            .collect(Collectors.toList());
+    }
+
+
+    /**
+     * Checks if a given component has a subcomponent of a given type.
+     *
+     * @param component subtree to parse looking for subcomponents.
+     * @param components the full components tree, to cross-ref in
+     *  case we need to check (sub)components' types.
+     *
+     * @return true or false
+     */
+    private boolean hasSubComponentOfType(
+            HierarchicalConfiguration component,
+            HierarchicalConfiguration components,
+            String type) {
+        long count = component.configurationsAt("subcomponents/subcomponent")
+            .stream()
+            .filter(subcomponent -> {
+                        String scName = subcomponent.getString("name");
+                        StringBuilder sb = new StringBuilder("component[name='");
+                        sb.append(scName);
+                        sb.append("']/state/type");
+                        String scType = components.getString(sb.toString(), "unknown");
+                        return scType.equals(type);
+                    })
+            .count();
+        return (count > 0);
+    }
+
+
+    /**
+     * Checks if a given component has a subcomponent of type OPTICAL_CHANNEL.
+     *
+     * @param component subtree to parse
+     * @param components the full components tree, to cross-ref in
+     *  case we need to check transceivers or optical channels.
+     *
+     * @return true or false
+     */
+    private boolean hasOpticalChannelSubComponent(
+            HierarchicalConfiguration component,
+            HierarchicalConfiguration components) {
+        return hasSubComponentOfType(component, components, OC_TRANSPORT_TYPES_OPTICAL_CHANNEL);
+    }
+
+
+    /**
+     *  Checks if a given component has a subcomponent of type TRANSCEIVER.
+     *
+     * @param component subtree to parse
+     * @param components the full components tree, to cross-ref in
+     *  case we need to check transceivers or optical channels.
+     *
+     * @return true or false
+     */
+    private boolean hasTransceiverSubComponent(
+            HierarchicalConfiguration component,
+            HierarchicalConfiguration components) {
+        return hasSubComponentOfType(component, components, OC_PLATFORM_TYPES_TRANSCEIVER);
+    }
+
+
+    /**
+     * Parses a component XML doc into a PortDescription.
+     *
+     * @param component subtree to parse. It must be a component ot type PORT.
+     * @param components the full components tree, to cross-ref in
+     *  case we need to check transceivers or optical channels.
+     *
+     * @return PortDescription or null if component does not have onos-index
+     */
+    private PortDescription parsePortComponent(
+            HierarchicalConfiguration component,
+            HierarchicalConfiguration components) {
+        Map<String, String> annotations = new HashMap<>();
+        String name = component.getString("name");
+        String type = component.getString("state/type");
+
+        log.info("Parsing Component {} type {}", name, type);
+
+        annotations.put(OdtnDeviceDescriptionDiscovery.OC_NAME, name);
+        annotations.put(OdtnDeviceDescriptionDiscovery.OC_TYPE, type);
+
+        // Store all properties as port properties
+        component.configurationsAt("properties/property")
+            .forEach(property -> {
+                    String pn = property.getString("name");
+                    String pv = property.getString("state/value");
+                    annotations.put(pn, pv);
+                    });
+
+        // Assing an ONOS port number
+        PortNumber portNum;
+        if (annotations.containsKey(ONOS_PORT_INDEX)) {
+            portNum = PortNumber.portNumber(Long.parseLong(annotations.get(ONOS_PORT_INDEX)));
+        } else {
+            log.warn("PORT {} does not include onos-index, hashing...", name);
+            portNum = PortNumber.portNumber(name.hashCode());
+        }
+        log.debug("PORT {} number {}", name, portNum);
+
+        // The heuristic to know if it is client or line side
+        if (!annotations.containsKey(PORT_TYPE)) {
+            if (hasTransceiverSubComponent(component, components)) {
+                annotations.put(PORT_TYPE, OdtnPortType.CLIENT.value());
+            } else if (hasOpticalChannelSubComponent(component, components)) {
+                annotations.put(PORT_TYPE, OdtnPortType.LINE.value());
+            }
+        }
+
+        // Build the port
+        // NOTE: using portNumber(id, name) breaks things. Intent parsing, port resorce management, etc. There seems
+        // to be an issue with resource mapping
+        if (annotations.get(PORT_TYPE).equals(OdtnPortType.CLIENT.value())) {
+            log.debug("PORT {} number {} added as CLIENT port", name, portNum);
+
+            return OduCltPortHelper.oduCltPortDescription(portNum,
+                    true,
+                    CltSignalType.CLT_10GBE,
+                    DefaultAnnotations.builder().putAll(annotations).build());
+        }
+        if (annotations.get(PORT_TYPE).equals(OdtnPortType.LINE.value())) {
+            log.debug("PORT {} number {} added as LINE port", name, portNum);
+
+            // TODO: To be configured
+            OchSignal signalId = OchSignal.newDwdmSlot(ChannelSpacing.CHL_50GHZ, 1);
+
+            return OchPortHelper.ochPortDescription(
+                    portNum, true,
+                    OduSignalType.ODU4, // TODO Client signal to be discovered
+                    true,
+                    signalId,
+                    DefaultAnnotations.builder().putAll(annotations).build());
+        }
+        log.error("PORT {} number {} is of UNKNOWN type", name, portNum);
+        return null;
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/ClientLineTerminalDeviceFlowRuleProgrammable.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/ClientLineTerminalDeviceFlowRuleProgrammable.java
new file mode 100644
index 0000000..f316fc0
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/ClientLineTerminalDeviceFlowRuleProgrammable.java
@@ -0,0 +1,942 @@
+/*
+ * 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.openconfig;
+
+import com.google.common.collect.ImmutableList;
+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.odtn.impl.FlowRuleParser;
+import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.OchSignalType;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.GridType;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.FlowRuleProgrammable;
+import org.onosproject.net.flow.criteria.Criteria;
+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.onosproject.odtn.behaviour.OdtnDeviceDescriptionDiscovery;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathFactory;
+import java.io.ByteArrayInputStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Implementation of FlowRuleProgrammable interface for
+ * OpenConfig terminal devices.
+ */
+public class ClientLineTerminalDeviceFlowRuleProgrammable
+        extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
+
+    private static final Logger log =
+            LoggerFactory.getLogger(ClientLineTerminalDeviceFlowRuleProgrammable.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 PREFIX_PORT = "port-";
+    private static final String PREFIX_CHANNEL = "channel-";
+    private static final String DEFAULT_OPERATIONAL_MODE = "0";
+    private static final String DEFAULT_TARGET_POWER = "0";
+    private static final String DEFAULT_ASSIGNMENT_INDEX = "1";
+    private static final String DEFAULT_ALLOCATION_INDEX = "10";
+    private static final int DEFAULT_RULE_PRIORITY = 10;
+    private static final long DEFAULT_RULE_COOKIE = 1234L;
+    private static final String OPERATION_DISABLE = "DISABLED";
+    private static final String OPERATION_ENABLE = "ENABLED";
+    private static final String OC_TYPE_PROT_OTN = "oc-opt-types:PROT_OTN";
+    private static final String OC_TYPE_PROT_ETH = "oc-opt-types:PROT_ETHERNET";
+
+
+    /**
+     * 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) {
+        NetconfSession session = getNetconfSession();
+        if (session == null) {
+            openConfigError("null session");
+            return ImmutableList.of();
+        }
+
+        // Apply the  rules on the device
+        Collection<FlowRule> added = rules.stream()
+                .map(r -> new TerminalDeviceFlowRule(r, getLinePorts()))
+                .filter(xc -> applyFlowRule(session, xc))
+                .collect(Collectors.toList());
+
+        for (FlowRule flowRule : added) {
+            log.info("OpenConfig added flowrule {}", flowRule);
+            getConnectionCache().add(did(), ((TerminalDeviceFlowRule) flowRule).connectionName(), flowRule);
+        }
+
+        //Print out number of rules sent to the device (without receiving errors)
+        openConfigLog("applyFlowRules added {}", added.size());
+
+        return added;
+    }
+
+    /**
+     * Get the flow entries that are present on the device.
+     *
+     * @return A collection of Flow Entries
+     */
+    @Override
+    public Collection<FlowEntry> getFlowEntries() {
+        Collection<FlowEntry> fetched = fetchConnectionsFromDevice().stream()
+                .map(fr -> new DefaultFlowEntry(fr, FlowEntry.FlowEntryState.ADDED, 0, 0, 0))
+                .collect(Collectors.toList());
+
+        //Print out number of rules actually found on the device that are also included in the cache
+        openConfigLog("getFlowEntries fetched connections {}", fetched.size());
+
+        return fetched;
+    }
+
+    /**
+     * Remove the specified flow rules.
+     *
+     * @param rules A collection of Flow Rules to be removed
+     * @return The collection of removed Flow Entries
+     */
+    @Override
+    public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
+        NetconfSession session = getNetconfSession();
+        if (session == null) {
+            openConfigError("null session");
+            return ImmutableList.of();
+        }
+        List<FlowRule> removed = new ArrayList<>();
+        for (FlowRule r : rules) {
+            try {
+                TerminalDeviceFlowRule termFlowRule = new TerminalDeviceFlowRule(r, getLinePorts());
+                removeFlowRule(session, termFlowRule);
+                getConnectionCache().remove(did(), r);
+                removed.add(r);
+            } catch (Exception e) {
+                openConfigError("Error {}", e);
+                continue;
+            }
+        }
+
+        //Print out number of removed rules from the device (without receiving errors)
+        openConfigLog("removeFlowRules removed {}", removed.size());
+
+        return removed;
+    }
+
+    private DeviceConnectionCache getConnectionCache() {
+        return DeviceConnectionCache.init();
+    }
+
+    // Context so XPath expressions are aware of XML namespaces
+    private static final NamespaceContext NS_CONTEXT = new NamespaceContext() {
+        @Override
+        public String getNamespaceURI(String prefix) {
+            if (prefix.equals("oc-platform-types")) {
+                return "http://openconfig.net/yang/platform-types";
+            }
+            if (prefix.equals("oc-opt-term")) {
+                return "http://openconfig.net/yang/terminal-device";
+            }
+            return null;
+        }
+
+        @Override
+        public Iterator getPrefixes(String val) {
+            return null;
+        }
+
+        @Override
+        public String getPrefix(String uri) {
+            return null;
+        }
+    };
+
+
+    /**
+     * Helper method to get the device id.
+     */
+    private DeviceId did() {
+        return data().deviceId();
+    }
+
+    /**
+     * Helper method to log from this class adding DeviceId.
+     */
+    private void openConfigLog(String format, Object... arguments) {
+        log.info("OPENCONFIG {}: " + format, did(), arguments);
+    }
+
+    /**
+     * Helper method to log an error from this class adding DeviceId.
+     */
+    private void openConfigError(String format, Object... arguments) {
+        log.error("OPENCONFIG {}: " + format, did(), arguments);
+    }
+
+
+    /**
+     * Helper method to get the Netconf Session.
+     */
+    private NetconfSession getNetconfSession() {
+        NetconfController controller =
+                checkNotNull(handler().get(NetconfController.class));
+        return controller.getNetconfDevice(did()).getSession();
+    }
+
+
+    /**
+     * 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
+     */
+    private 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 get request to retrieve Components and their
+     * properties (for the ONOS port, index).
+     *
+     * @return The filt content to send to the device.
+     */
+    private String getComponents() {
+        StringBuilder filt = new StringBuilder();
+        filt.append("<components xmlns='http://openconfig.net/yang/platform'>");
+        filt.append(" <component>");
+        filt.append("  <name/>");
+        filt.append("  <properties/>");
+        filt.append(" </component>");
+        filt.append("</components>");
+        return filteredGetBuilder(filt.toString());
+    }
+
+    /**
+     * Construct a get request to retrieve Optical Channels and
+     * the line port they are using.
+     * <p>
+     * This method is used to query the device so we can find the
+     * OpticalChannel component name that used a given line port.
+     *
+     * @return The filt content to send to the device.
+     */
+    private String getOpticalChannels() {
+        StringBuilder filt = new StringBuilder();
+        filt.append("<components xmlns='http://openconfig.net/yang/platform'>");
+        filt.append(" <component>");
+        filt.append("  <name/>");
+        filt.append("  <state/>");
+        filt.append("  <oc-opt-term:optical-channel xmlns:oc-opt-term"
+                + " = 'http://openconfig.net/yang/terminal-device'>");
+        filt.append("    <oc-opt-term:config>");
+        filt.append("     <oc-opt-term:line-port/>");
+        filt.append("    </oc-opt-term:config>");
+        filt.append("  </oc-opt-term:optical-channel>");
+        filt.append(" </component>");
+        filt.append("</components>");
+        return filteredGetBuilder(filt.toString());
+    }
+
+    /**
+     * Get the OpenConfig component name for the OpticalChannel component
+     * associated to the passed port number (typically a line side port, already
+     * mapped to ONOS port).
+     *
+     * @param session    The netconf session to the device.
+     * @param portNumber ONOS port number of the Line port ().
+     * @return the channel component name or null
+     */
+    private String getOpticalChannel(NetconfSession session,
+                                     PortNumber portNumber) {
+        try {
+            checkNotNull(session);
+            checkNotNull(portNumber);
+            XPath xp = XPathFactory.newInstance().newXPath();
+            xp.setNamespaceContext(NS_CONTEXT);
+
+            // Get the port name for a given port number
+            // We could iterate the port annotations too, no need to
+            // interact with device.
+            String xpGetPortName =
+                    "/rpc-reply/data/components/"
+                            +
+                            "component[./properties/property[name='onos-index']/config/value ='" +
+                            portNumber.toLong() + "']/"
+                            + "name/text()";
+
+            // Get all the components and their properties
+            String compReply = session.rpc(getComponents()).get();
+            DocumentBuilderFactory builderFactory =
+                    DocumentBuilderFactory.newInstance();
+            DocumentBuilder builder = builderFactory.newDocumentBuilder();
+            Document document =
+                    builder.parse(new InputSource(new StringReader(compReply)));
+            String portName = xp.evaluate(xpGetPortName, document);
+            String xpGetOptChannelName =
+                    "/rpc-reply/data/components/"
+                            + "component[./optical-channel/config/line-port='" + portName +
+                            "']/name/text()";
+
+            String optChannelReply = session.rpc(getOpticalChannels()).get();
+            document =
+                    builder.parse(new InputSource(new StringReader(optChannelReply)));
+            return xp.evaluate(xpGetOptChannelName, document);
+        } catch (Exception e) {
+            openConfigError("Exception {}", e);
+            return null;
+        }
+    }
+
+    private void setLogicalChannel(NetconfSession session, String operation, String logChannel)
+            throws NetconfException {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("<terminal-device xmlns='http://openconfig.net/yang/terminal-device'>");
+        sb.append("<logical-channels>");
+        sb.append("<channel>");
+        sb.append("<index>" + logChannel + "</index>");
+        sb.append("<config>");
+        sb.append("<admin-state>" + operation + "</admin-state>");
+        sb.append("</config>");
+        sb.append("</channel>");
+        sb.append("</logical-channels>");
+        sb.append("</terminal-device>");
+
+        boolean ok =
+                session.editConfig(DatastoreId.RUNNING, null, sb.toString());
+        if (!ok) {
+            throw new NetconfException("error writing the logical channel");
+        }
+    }
+
+    private void setOpticalChannelFrequency(NetconfSession session, String optChannel, Frequency freq)
+            throws NetconfException {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("<components xmlns='http://openconfig.net/yang/platform'>");
+        sb.append("<component>");
+        sb.append("<name>" + PREFIX_CHANNEL + optChannel + "</name>");
+        sb.append("<oc-opt-term:optical-channel xmlns:oc-opt-term='http://openconfig.net/yang/terminal-device'>");
+        sb.append("<oc-opt-term:config>");
+        sb.append("<oc-opt-term:frequency>" + (long) freq.asMHz() + "</oc-opt-term:frequency>");
+        sb.append("<oc-opt-term:target-output-power>" + DEFAULT_TARGET_POWER + "</oc-opt-term:target-output-power>");
+        sb.append("<oc-opt-term:operational-mode>" + DEFAULT_OPERATIONAL_MODE + "</oc-opt-term:operational-mode>");
+        sb.append("<oc-opt-term:line-port>" + PREFIX_PORT + optChannel + "</oc-opt-term:line-port>");
+        sb.append("</oc-opt-term:config>");
+        sb.append("</oc-opt-term:optical-channel>");
+        sb.append("</component>");
+        sb.append("</components>");
+
+        boolean ok =
+                session.editConfig(DatastoreId.RUNNING, null, sb.toString());
+        if (!ok) {
+            throw new NetconfException("error writing channel frequency");
+        }
+    }
+
+    private void setLogicalChannelAssignment(NetconfSession session, String operation, String client, String line,
+                                             String assignmentIndex, String allocationIndex)
+            throws NetconfException {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("<terminal-device xmlns='http://openconfig.net/yang/terminal-device'>");
+        sb.append("<logical-channels>");
+        sb.append("<channel>");
+        sb.append("<index>" + client + "</index>");
+        sb.append("<config>");
+        sb.append("<admin-state>" + operation + "</admin-state>");
+        sb.append("</config>");
+        sb.append("<logical-channel-assignments>");
+        sb.append("<assignment>");
+        sb.append("<index>" + assignmentIndex + "</index>");
+        sb.append("<config>");
+        sb.append("<logical-channel>" + line + "</logical-channel>");
+        sb.append("<allocation>" + allocationIndex + "</allocation>");
+        sb.append("</config>");
+        sb.append("</assignment>");
+        sb.append("</logical-channel-assignments>");
+        sb.append("</channel>");
+        sb.append("</logical-channels>");
+        sb.append("</terminal-device>");
+
+        boolean ok =
+                session.editConfig(DatastoreId.RUNNING, null, sb.toString());
+        if (!ok) {
+            throw new NetconfException("error writing logical channel assignment");
+        }
+    }
+
+    /**
+     * Apply a single flowrule to the device.
+     *
+     * --- Directionality details:
+     * Driver supports ADD (INGRESS) and DROP (EGRESS) rules generated by OpticalCircuit/OpticalConnectivity intents
+     * the format of the rules are checked in class TerminalDeviceFlowRule
+     *
+     * However, the physical transponder is always bidirectional as specified in OpenConfig YANG models
+     * therefore ADD and DROP rules are mapped in the same xml that ENABLE (and tune) a transponder port.
+     *
+     * If the intent is generated as bidirectional both ADD and DROP flowrules are generated for each device, thus
+     * the same xml is sent twice to the device.
+     *
+     * @param session   The Netconf session.
+     * @param rule      Flow Rules to be applied.
+     * @return true if no Netconf errors are received from the device when xml is sent
+     * @throws NetconfException if exchange goes wrong
+     */
+    protected boolean applyFlowRule(NetconfSession session, TerminalDeviceFlowRule rule) {
+
+        //Configuration of LINE side, used for OpticalConnectivity intents
+        //--- configure central frequency
+        //--- enable the line port
+        if (rule.type == TerminalDeviceFlowRule.Type.LINE_INGRESS ||
+                rule.type == TerminalDeviceFlowRule.Type.LINE_EGRESS) {
+
+            FlowRuleParser frp = new FlowRuleParser(rule);
+            String componentName = frp.getPortNumber().toString();
+            Frequency centralFrequency = frp.getCentralFrequency();
+
+            StringBuilder componentConf = new StringBuilder();
+
+            log.info("Sending LINE FlowRule to device {} LINE port {}, frequency {}",
+                    did(), componentName, centralFrequency);
+
+            try {
+                setOpticalChannelFrequency(session, componentName, centralFrequency);
+            } catch (NetconfException e) {
+                log.error("Error writing central frequency in the component");
+                return false;
+            }
+
+            try {
+                setLogicalChannel(session, OPERATION_ENABLE, componentName);
+            } catch (NetconfException e) {
+                log.error("Error enabling the logical channel");
+                return false;
+            }
+        }
+
+        //Configuration of CLIENT side, used for OpticalCircuit intents
+        //--- associate the client port to the line port
+        //--- enable the client port
+        //
+        //Assumes only one "assignment" per logical-channel with index 1
+        //TODO check the OTN mapping of client ports into the line port frame specified by parameter "<allocation>"
+        if (rule.type == TerminalDeviceFlowRule.Type.CLIENT_INGRESS ||
+                rule.type == TerminalDeviceFlowRule.Type.CLIENT_EGRESS) {
+
+            String clientPortName;
+            String linePortName;
+            if (rule.type == TerminalDeviceFlowRule.Type.CLIENT_INGRESS) {
+                clientPortName = rule.inPort().toString();
+                linePortName = rule.outPort().toString();
+            } else {
+                clientPortName = rule.outPort().toString();
+                linePortName = rule.inPort().toString();
+            }
+
+            log.info("Sending CLIENT FlowRule to device {} CLIENT port: {}, LINE port {}",
+                    did(), clientPortName, linePortName);
+
+            try {
+                setLogicalChannelAssignment(session, OPERATION_ENABLE, clientPortName, linePortName,
+                        DEFAULT_ASSIGNMENT_INDEX, DEFAULT_ALLOCATION_INDEX);
+            } catch (NetconfException e) {
+                log.error("Error setting the logical channel assignment");
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    protected boolean removeFlowRule(NetconfSession session, TerminalDeviceFlowRule rule)
+            throws NetconfException {
+
+        //Configuration of LINE side, used for OpticalConnectivity intents
+        //--- configure central frequency to ZERO
+        //--- disable the line port
+        if (rule.type == TerminalDeviceFlowRule.Type.LINE_INGRESS ||
+                rule.type == TerminalDeviceFlowRule.Type.LINE_EGRESS) {
+
+            FlowRuleParser frp = new FlowRuleParser(rule);
+            String componentName = frp.getPortNumber().toString();
+
+            log.info("Removing LINE FlowRule device {} line port {}",
+                    did(), componentName);
+
+            try {
+                setLogicalChannel(session, OPERATION_DISABLE, componentName);
+            } catch (NetconfException e) {
+                log.error("Error disabling the logical channel line side");
+                return false;
+            }
+        }
+
+        //Configuration of CLIENT side, used for OpticalCircuit intents
+        //--- configure central frequency to ZERO
+        //--- disable the line port
+        if (rule.type == TerminalDeviceFlowRule.Type.CLIENT_INGRESS ||
+                rule.type == TerminalDeviceFlowRule.Type.CLIENT_EGRESS) {
+
+            String clientPortName;
+            String linePortName;
+            if (rule.type == TerminalDeviceFlowRule.Type.CLIENT_INGRESS) {
+                clientPortName = rule.inPort().toString();
+                linePortName = rule.outPort().toString();
+            } else {
+                clientPortName = rule.outPort().toString();
+                linePortName = rule.inPort().toString();
+            }
+
+            log.debug("Removing CLIENT FlowRule device {} client port: {}, line port {}",
+                    did(), clientPortName, linePortName);
+
+            try {
+                setLogicalChannelAssignment(session, OPERATION_DISABLE, clientPortName, linePortName,
+                        DEFAULT_ASSIGNMENT_INDEX, DEFAULT_ALLOCATION_INDEX);
+            } catch (NetconfException e) {
+                log.error("Error disabling the logical channel assignment");
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private List<FlowRule> fetchLineConnectionFromDevice(String channel, Frequency centralFreq) {
+        List<FlowRule> confirmedRules = new ArrayList<>();
+        FlowRule cacheAddRule;
+        FlowRule cacheDropRule;
+        NetconfSession session = getNetconfSession();
+
+        log.debug("fetchOpticalConnectionsFromDevice {} frequency {}", did(), centralFreq);
+
+        //Build the corresponding flow rule as expected
+        //Selector including port and ochSignal
+        //Treatment including port
+        PortNumber inputPortNumber = PortNumber.portNumber(channel);
+        PortNumber outputPortNumber = PortNumber.portNumber(channel);
+
+        log.debug("fetchOpticalConnectionsFromDevice {} port {}-{}", did(), inputPortNumber, outputPortNumber);
+
+        TrafficSelector selectorDrop = DefaultTrafficSelector.builder()
+                .matchInPort(inputPortNumber)
+                .add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID))
+                .add(Criteria.matchLambda(toOchSignal(centralFreq, 50.0)))
+                .build();
+
+        TrafficTreatment treatmentDrop = DefaultTrafficTreatment.builder()
+                .setOutput(outputPortNumber)
+                .build();
+
+        TrafficSelector selectorAdd = DefaultTrafficSelector.builder()
+                .matchInPort(inputPortNumber)
+                .build();
+
+        TrafficTreatment treatmentAdd = DefaultTrafficTreatment.builder()
+                .add(Instructions.modL0Lambda(toOchSignal(centralFreq, 50.0)))
+                .setOutput(outputPortNumber)
+                .build();
+
+        //Retrieved rules and cached rules are considered equal if both selector and treatment are equal
+        cacheAddRule = null;
+        cacheDropRule = null;
+        if (getConnectionCache().size(did()) != 0) {
+            cacheDropRule = getConnectionCache().get(did()).stream()
+                    .filter(r -> (r.selector().equals(selectorDrop) && r.treatment().equals(treatmentDrop)))
+                    .findFirst()
+                    .orElse(null);
+
+            cacheAddRule = getConnectionCache().get(did()).stream()
+                    .filter(r -> (r.selector().equals(selectorAdd) && r.treatment().equals(treatmentAdd)))
+                    .findFirst()
+                    .orElse(null);
+        }
+
+        //Include the DROP rule to the retrieved rules if found in cache
+        if ((cacheDropRule != null)) {
+            confirmedRules.add(cacheDropRule);
+            log.debug("fetchOpticalConnectionsFromDevice {} DROP LINE rule included in the cache {}",
+                    did(), cacheDropRule);
+        } else {
+            log.warn("fetchOpticalConnectionsFromDevice {} DROP LINE rule not included in cache", did());
+        }
+
+        //Include the ADD rule to the retrieved rules if found in cache
+        if ((cacheAddRule != null)) {
+            confirmedRules.add(cacheAddRule);
+            log.debug("fetchOpticalConnectionsFromDevice {} ADD LINE rule included in the cache {}",
+                    did(), cacheAddRule.selector());
+        } else {
+            log.warn("fetchOpticalConnectionsFromDevice {} ADD LINE rule not included in cache", did());
+        }
+
+        //If neither Add or Drop rules are present in the cache, remove configuration from the device
+        if ((cacheDropRule == null) && (cacheAddRule == null)) {
+            log.warn("fetchOpticalConnectionsFromDevice {} ADD and DROP rule not included in the cache", did());
+
+            FlowRule deviceDropRule = DefaultFlowRule.builder()
+                    .forDevice(data().deviceId())
+                    .makePermanent()
+                    .withSelector(selectorDrop)
+                    .withTreatment(treatmentDrop)
+                    .withCookie(DEFAULT_RULE_COOKIE)
+                    .withPriority(DEFAULT_RULE_PRIORITY)
+                    .build();
+
+            FlowRule deviceAddRule = DefaultFlowRule.builder()
+                    .forDevice(data().deviceId())
+                    .makePermanent()
+                    .withSelector(selectorAdd)
+                    .withTreatment(treatmentAdd)
+                    .withCookie(DEFAULT_RULE_COOKIE)
+                    .withPriority(DEFAULT_RULE_PRIORITY)
+                    .build();
+
+            try {
+                //TODO this is not required if allowExternalFlowRules
+                TerminalDeviceFlowRule addRule = new TerminalDeviceFlowRule(deviceAddRule, getLinePorts());
+                removeFlowRule(session, addRule);
+
+                TerminalDeviceFlowRule dropRule = new TerminalDeviceFlowRule(deviceDropRule, getLinePorts());
+                removeFlowRule(session, dropRule);
+            } catch (NetconfException e) {
+                openConfigError("Error removing LINE rule from device", e);
+            }
+        }
+        return confirmedRules;
+    }
+
+    private List<FlowRule> fetchClientConnectionFromDevice(PortNumber clientPortNumber, PortNumber linePortNumber) {
+        List<FlowRule> confirmedRules = new ArrayList<>();
+        FlowRule cacheAddRule;
+        FlowRule cacheDropRule;
+        NetconfSession session = getNetconfSession();
+
+        //Build the corresponding flow rule as expected
+        //Selector including port
+        //Treatment including port
+
+        log.debug("fetchClientConnectionsFromDevice {} client {} line {}", did(), clientPortNumber, linePortNumber);
+
+        TrafficSelector selectorDrop = DefaultTrafficSelector.builder()
+                .matchInPort(linePortNumber)
+                .build();
+
+        TrafficTreatment treatmentDrop = DefaultTrafficTreatment.builder()
+                .setOutput(clientPortNumber)
+                .build();
+
+        TrafficSelector selectorAdd = DefaultTrafficSelector.builder()
+                .matchInPort(clientPortNumber)
+                .build();
+
+        TrafficTreatment treatmentAdd = DefaultTrafficTreatment.builder()
+                .setOutput(linePortNumber)
+                .build();
+
+        //Retrieved rules and cached rules are considered equal if both selector and treatment are equal
+        cacheAddRule = null;
+        cacheDropRule = null;
+        if (getConnectionCache().size(did()) != 0) {
+            cacheDropRule = getConnectionCache().get(did()).stream()
+                    .filter(r -> (r.selector().equals(selectorDrop) && r.treatment().equals(treatmentDrop)))
+                    .findFirst()
+                    .orElse(null);
+
+            cacheAddRule = getConnectionCache().get(did()).stream()
+                    .filter(r -> (r.selector().equals(selectorAdd) && r.treatment().equals(treatmentAdd)))
+                    .findFirst()
+                    .orElse(null);
+        }
+
+        //Include the DROP rule to the retrieved rules if found in cache
+        if ((cacheDropRule != null)) {
+            confirmedRules.add(cacheDropRule);
+            log.debug("fetchClientConnectionsFromDevice {} DROP CLIENT rule in the cache {}",
+                    did(), cacheDropRule);
+        } else {
+            log.warn("fetchClientConnectionsFromDevice {} DROP CLIENT rule not found in cache", did());
+        }
+
+        //Include the ADD rule to the retrieved rules if found in cache
+        if ((cacheAddRule != null)) {
+            confirmedRules.add(cacheAddRule);
+            log.debug("fetchClientConnectionsFromDevice {} ADD CLIENT rule in the cache {}",
+                    did(), cacheAddRule);
+        } else {
+            log.warn("fetchClientConnectionsFromDevice {} ADD CLIENT rule not found in cache", did());
+        }
+
+        if ((cacheDropRule == null) && (cacheAddRule == null)) {
+            log.warn("fetchClientConnectionsFromDevice {} ADD and DROP rule not included in the cache", did());
+
+            FlowRule deviceDropRule = DefaultFlowRule.builder()
+                    .forDevice(data().deviceId())
+                    .makePermanent()
+                    .withSelector(selectorDrop)
+                    .withTreatment(treatmentDrop)
+                    .withCookie(DEFAULT_RULE_COOKIE)
+                    .withPriority(DEFAULT_RULE_PRIORITY)
+                    .build();
+
+            FlowRule deviceAddRule = DefaultFlowRule.builder()
+                    .forDevice(data().deviceId())
+                    .makePermanent()
+                    .withSelector(selectorAdd)
+                    .withTreatment(treatmentAdd)
+                    .withCookie(DEFAULT_RULE_COOKIE)
+                    .withPriority(DEFAULT_RULE_PRIORITY)
+                    .build();
+
+            try {
+                //TODO this is not required if allowExternalFlowRules
+                TerminalDeviceFlowRule addRule = new TerminalDeviceFlowRule(deviceAddRule, getLinePorts());
+                removeFlowRule(session, addRule);
+
+                TerminalDeviceFlowRule dropRule = new TerminalDeviceFlowRule(deviceDropRule, getLinePorts());
+                removeFlowRule(session, dropRule);
+            } catch (NetconfException e) {
+                openConfigError("Error removing CLIENT rule from device", e);
+            }
+        }
+        return confirmedRules;
+    }
+
+    /**
+     * Fetches list of connections from device.
+     *
+     * TODO manage allow external flow rules (allowExternalFlowRules)
+     * Currently removes from the device all connections that are not currently present in the DeviceConnectionCache.
+     *
+     * @return connections that are present on the device and in the DeviceConnectionCache.
+     */
+    private List<FlowRule> fetchConnectionsFromDevice() {
+        List<FlowRule> confirmedRules = new ArrayList<>();
+        String reply;
+        FlowRule cacheAddRule;
+        FlowRule cacheDropRule;
+        NetconfSession session = getNetconfSession();
+
+        //Get relevant information from the device
+        StringBuilder requestFilter = new StringBuilder();
+        requestFilter.append("<components xmlns='http://openconfig.net/yang/platform'>");
+        requestFilter.append("  <component>");
+        requestFilter.append("    <name/>");
+        requestFilter.append("    <oc-opt-term:optical-channel " +
+                "xmlns:oc-opt-term='http://openconfig.net/yang/terminal-device'>");
+        requestFilter.append("      <oc-opt-term:config/>");
+        requestFilter.append("    </oc-opt-term:optical-channel>");
+        requestFilter.append("  </component>");
+        requestFilter.append("</components>");
+        requestFilter.append("<terminal-device xmlns='http://openconfig.net/yang/terminal-device'>");
+        requestFilter.append("  <logical-channels>");
+        requestFilter.append("    <channel>");
+        requestFilter.append("      <index/>");
+        requestFilter.append("      <config>");
+        requestFilter.append("        <admin-state/>");
+        requestFilter.append("        <logical-channel-type/>");
+        requestFilter.append("      </config>");
+        requestFilter.append("      <logical-channel-assignments>");
+        requestFilter.append("        <assignment>");
+        requestFilter.append("          <config>");
+        requestFilter.append("            <logical-channel/>");
+        requestFilter.append("          </config>");
+        requestFilter.append("        </assignment>");
+        requestFilter.append("      </logical-channel-assignments>");
+        requestFilter.append("    </channel>");
+        requestFilter.append("  </logical-channels>");
+        requestFilter.append("</terminal-device>");
+
+        try {
+            reply = session.get(requestFilter.toString(), null);
+            //log.debug("TRANSPONDER CONNECTIONS - fetchConnectionsFromDevice {} reply {}", did(), reply);
+        } catch (NetconfException e) {
+            log.error("Failed to retrieve configuration details for device {}", handler().data().deviceId(), e);
+            return ImmutableList.of();
+        }
+
+        HierarchicalConfiguration cfg = XmlConfigParser.loadXml(new ByteArrayInputStream(reply.getBytes()));
+
+        List<HierarchicalConfiguration> logicalChannels =
+                cfg.configurationsAt("data.terminal-device.logical-channels.channel");
+
+        List<HierarchicalConfiguration> components =
+                cfg.configurationsAt("data.components.component");
+
+        //Retrieve the ENABLED line ports
+        List<String> enabledOpticalChannels = logicalChannels.stream()
+                .filter(r -> r.getString("config.logical-channel-type").equals(OC_TYPE_PROT_OTN))
+                .filter(r -> r.getString("config.admin-state").equals(OPERATION_ENABLE))
+                .map(r -> r.getString("index"))
+                .collect(Collectors.toList());
+
+        log.debug("fetchConnectionsFromDevice {} enabledOpticalChannelsIndex {}", did(), enabledOpticalChannels);
+
+        if (enabledOpticalChannels.size() != 0) {
+            for (String channel : enabledOpticalChannels) {
+                log.debug("fetchOpticalConnectionsFromDevice {} channel {}", did(), channel);
+
+                //Retrieve the corresponding central frequency from the associated component
+                //TODO correlate the components instead of relying on naming
+                Frequency centralFreq = components.stream()
+                        .filter(c -> c.getString("name").equals(PREFIX_CHANNEL + channel))
+                        .map(c -> c.getDouble("optical-channel.config.frequency"))
+                        .map(c -> Frequency.ofMHz(c))
+                        .findFirst()
+                        .orElse(null);
+
+                confirmedRules.addAll(fetchLineConnectionFromDevice(channel, centralFreq));
+            }
+        }
+
+        //Retrieve the ENABLED client ports
+        List<String> enabledClientChannels = logicalChannels.stream()
+                .filter(r -> r.getString("config.logical-channel-type").equals(OC_TYPE_PROT_ETH))
+                .filter(r -> r.getString("config.admin-state").equals(OPERATION_ENABLE))
+                .map(r -> r.getString("index"))
+                .collect(Collectors.toList());
+
+        log.debug("fetchClientConnectionsFromDevice {} enabledClientChannelsIndex {}", did(), enabledClientChannels);
+
+        if (enabledClientChannels.size() != 0) {
+            for (String clientPort : enabledClientChannels) {
+
+                log.debug("fetchClientConnectionsFromDevice {} channel {}", did(), clientPort);
+
+                String linePort = logicalChannels.stream()
+                    .filter(r -> r.getString("config.logical-channel-type").equals(OC_TYPE_PROT_ETH))
+                    .filter(r -> r.getString("config.admin-state").equals(OPERATION_ENABLE))
+                    .filter(r -> r.getString("index").equals(clientPort))
+                    .map(r -> r.getString("logical-channel-assignments.assignment.config.logical-channel"))
+                    .findFirst()
+                    .orElse(null);
+
+                //Build the corresponding flow rule as expected
+                //Selector including port
+                //Treatment including port
+                PortNumber clientPortNumber = PortNumber.portNumber(clientPort);
+                PortNumber linePortNumber = PortNumber.portNumber(linePort);
+
+                confirmedRules.addAll(fetchClientConnectionFromDevice(clientPortNumber, linePortNumber));
+            }
+        }
+
+        //Returns rules that are both on the device and on the cache
+        if (confirmedRules.size() != 0) {
+            log.info("fetchConnectionsFromDevice {} number of confirmed rules {}", did(), confirmedRules.size());
+            return confirmedRules;
+        } else {
+            return ImmutableList.of();
+        }
+    }
+
+    /**
+     * Convert start and end frequencies to OCh signal.
+     *
+     * FIXME: supports channel spacing 50 and 100
+     *
+     * @param central central frequency as double in THz
+     * @param width width of the channel arounf the central frequency as double in GHz
+     * @return OCh signal
+     */
+    public static OchSignal toOchSignal(Frequency central, double width) {
+        int slots = (int) (width / ChannelSpacing.CHL_12P5GHZ.frequency().asGHz());
+        int multiplier = 0;
+
+        double centralAsGHz = central.asGHz();
+
+        if (width == 50) {
+            multiplier = (int) ((centralAsGHz - Spectrum.CENTER_FREQUENCY.asGHz())
+                    / ChannelSpacing.CHL_50GHZ.frequency().asGHz());
+
+            return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, multiplier, slots);
+        }
+
+        if (width == 100) {
+            multiplier = (int) ((centralAsGHz - Spectrum.CENTER_FREQUENCY.asGHz())
+                    / ChannelSpacing.CHL_100GHZ.frequency().asGHz());
+
+            return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_100GHZ, multiplier, slots);
+        }
+
+        return null;
+    }
+
+    private List<PortNumber> getLinePorts() {
+        List<PortNumber> linePorts;
+
+        DeviceService deviceService = this.handler().get(DeviceService.class);
+        linePorts = deviceService.getPorts(data().deviceId()).stream()
+                .filter(p -> p.annotations().value(OdtnDeviceDescriptionDiscovery.PORT_TYPE)
+                        .equals(OdtnDeviceDescriptionDiscovery.OdtnPortType.LINE.value()))
+                .map(p -> p.number())
+                .collect(Collectors.toList());
+
+        return linePorts;
+
+    }
+}
diff --git a/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/TerminalDeviceFlowRule.java b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/TerminalDeviceFlowRule.java
new file mode 100644
index 0000000..fb6eead
--- /dev/null
+++ b/drivers/odtn-driver/src/main/java/org/onosproject/drivers/odtn/openconfig/TerminalDeviceFlowRule.java
@@ -0,0 +1,244 @@
+/*
+ * 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.openconfig;
+
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.OchSignalCriterion;
+import org.onosproject.net.flow.criteria.OchSignalTypeCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class TerminalDeviceFlowRule extends DefaultFlowRule {
+    private static final Logger log = LoggerFactory.getLogger(TerminalDeviceFlowRule.class);
+
+    public enum Type {
+        CLIENT_INGRESS,
+        CLIENT_EGRESS,
+        LINE_INGRESS,
+        LINE_EGRESS
+    }
+
+    //As generated by the OpticalConnectivityIntentCompiler
+    private static final int NUM_CRITERIA_LINE_EGRESS_RULE = 3;
+    private static final int NUM_INSTRUCTIONS_LINE_EGRESS_RULE = 1;
+    private static final int NUM_CRITERIA_LINE_INGRESS_RULE = 1;
+    private static final int NUM_INSTRUCTIONS_LINE_INGRESS_RULE = 2;
+
+    //As generated by the OpticalCoircuitIntentCompiler
+    private static final int NUM_CRITERIA_CLIENT_RULES = 1;
+    private static final int NUM_INSTRUCTIONS_CLIENT_RULES = 1;
+
+    public Type type;
+
+    private PortNumber inPortNumber;
+    private PortNumber outPortNumber;
+    private OchSignal ochSignal;
+    private String connectionName;
+
+
+    public TerminalDeviceFlowRule(FlowRule rule, List<PortNumber> linePorts) {
+        super(rule);
+
+        Set<Criterion> criteria = rule.selector().criteria();
+        List<Instruction> instructions = rule.treatment().immediate();
+
+        /*Rules for TerminalDevice are generated in OpticalPathIntentCompiler with two types of intents
+        --- OpticalConnectivity intent compilation generates following flow rules
+        OPTICAL LINE level at INGRESS node -- criteria: input port; output port, OChSignal;
+        OPTICAL LINE level at EGRESS node -- criteria: input port, OChSignal and OCh type; instruction: output port
+        --- OpticalCircuit intent compilation generates following flow rules
+        CLIENT PORT at INGRESS node -- criteria: input port (OduClt); instruction output port (Och)
+        CLIENT PORT at EGRESS node -- criteria: input port (Och); instruction output port (OduClt)*/
+
+        checkArgument((criteria.size() == NUM_CRITERIA_LINE_EGRESS_RULE) ||
+                (criteria.size() == NUM_CRITERIA_LINE_INGRESS_RULE) ||
+                (criteria.size() == NUM_CRITERIA_CLIENT_RULES),
+                "Wrong size of flow rule criteria for TerminalDevice size" + criteria.size());
+
+        checkArgument((instructions.size() == NUM_INSTRUCTIONS_LINE_EGRESS_RULE) ||
+                        (instructions.size() == NUM_INSTRUCTIONS_LINE_INGRESS_RULE) ||
+                        (instructions.size() == NUM_INSTRUCTIONS_CLIENT_RULES),
+                "Wrong size of flow rule instructions for TerminalDevice size " + instructions.size());
+
+        //This is EGRESS rule on the LINE side
+        if ((criteria.size() == NUM_CRITERIA_LINE_EGRESS_RULE) &&
+                (instructions.size() == NUM_INSTRUCTIONS_LINE_EGRESS_RULE)) {
+            log.debug("Building the TerminalDeviceFlowRule for LINE_EGRESS");
+            type = Type.LINE_EGRESS;
+
+            criteria.forEach(
+                    c -> checkArgument(c instanceof OchSignalCriterion ||
+                                    c instanceof OchSignalTypeCriterion ||
+                                    c instanceof PortCriterion,
+                            "Incompatible flow rule criteria for ADD TerminalDevice: " + criteria
+                    )
+            );
+            instructions.forEach(
+                    c -> checkArgument(c instanceof Instructions.OutputInstruction,
+                            "Incompatible flow rule instruction for ADD TerminalDevice: " + instructions
+                    )
+            );
+
+            ochSignal = criteria.stream()
+                    .filter(c -> c instanceof OchSignalCriterion)
+                    .map(c -> ((OchSignalCriterion) c).lambda())
+                    .findAny()
+                    .orElse(null);
+
+            inPortNumber = criteria.stream()
+                    .filter(c -> c instanceof PortCriterion)
+                    .map(c -> ((PortCriterion) c).port())
+                    .findAny()
+                    .orElse(null);
+
+            outPortNumber = ((Instructions.OutputInstruction) instructions.get(0)).port();
+
+            checkArgument(linePorts.contains(outPortNumber),
+                    "Incompatible output port for DROP TerminalDevice");
+
+        }
+
+        //This is INGRESS rule on the LINE side
+        if ((criteria.size() == NUM_CRITERIA_LINE_INGRESS_RULE) &&
+                (instructions.size() == NUM_INSTRUCTIONS_LINE_INGRESS_RULE)) {
+            log.debug("Building the TerminalDeviceFlowRule LINE_INGRESS");
+            type = Type.LINE_INGRESS;
+
+            criteria.forEach(
+                    c -> checkArgument(
+                            c instanceof PortCriterion,
+                            "Incompatible flow rule criteria for ADD TerminalDevice: " + criteria
+                    )
+            );
+            instructions.forEach(
+                    c -> checkArgument(c.type() == Instruction.Type.L0MODIFICATION ||
+                                    c.type() == Instruction.Type.OUTPUT,
+                            "Incompatible flow rule instruction for ADD TerminalDevice: " + instructions
+                    )
+            );
+
+            inPortNumber = criteria.stream()
+                    .filter(c -> c instanceof PortCriterion)
+                    .map(c -> ((PortCriterion) c).port())
+                    .findAny()
+                    .orElse(null);
+
+            checkArgument(linePorts.contains(inPortNumber),
+                    "Incompatible input port for DROP TerminalDevice");
+
+            ochSignal = instructions.stream()
+                    .filter(c -> c.type() == Instruction.Type.L0MODIFICATION)
+                    .map(c -> ((L0ModificationInstruction.ModOchSignalInstruction) c).lambda())
+                    .findAny()
+                    .orElse(null);
+
+            outPortNumber = instructions.stream()
+                    .filter(c -> c.type() == Instruction.Type.OUTPUT)
+                    .map(c -> ((Instructions.OutputInstruction) c).port())
+                    .findAny()
+                    .orElse(null);
+        }
+
+        //This is INGRESS or EGRESS rule on the CLIENT side
+        if ((criteria.size() == NUM_CRITERIA_CLIENT_RULES) &&
+                (instructions.size() == NUM_INSTRUCTIONS_CLIENT_RULES)) {
+
+            criteria.forEach(
+                    c -> checkArgument(
+                            c instanceof PortCriterion,
+                            "Incompatible flow rule criteria for ADD TerminalDevice: " + criteria
+                    )
+            );
+            instructions.forEach(
+                    c -> checkArgument(c.type() == Instruction.Type.OUTPUT,
+                            "Incompatible flow rule instruction for ADD TerminalDevice: " + instructions
+                    )
+            );
+
+            inPortNumber = criteria.stream()
+                    .filter(c -> c instanceof PortCriterion)
+                    .map(c -> ((PortCriterion) c).port())
+                    .findAny()
+                    .orElse(null);
+
+            outPortNumber = instructions.stream()
+                    .filter(c -> c.type() == Instruction.Type.OUTPUT)
+                    .map(c -> ((Instructions.OutputInstruction) c).port())
+                    .findAny()
+                    .orElse(null);
+
+            ochSignal = null;
+
+            if (linePorts.contains(outPortNumber)) {
+                type = Type.CLIENT_INGRESS;
+            } else {
+                type = Type.CLIENT_EGRESS;
+            }
+        }
+
+        if (type == Type.LINE_EGRESS) {
+            connectionName = "LineEgress-LinePort-" + inPortNumber.toString()
+                    + "-ochSig-" + ochSignal.centralFrequency().toString();
+        }
+        if (type == Type.LINE_INGRESS) {
+            connectionName = "LineIngress-LinePort-" + outPortNumber.toString()
+                    + "-ochSig-" + ochSignal.centralFrequency().toString();
+        }
+        if (type == Type.CLIENT_EGRESS) {
+            connectionName = "ClientEgress-LinePort-" + inPortNumber.toString()
+                    + "-ClientPort-" + outPortNumber.toString();
+        }
+        if (type == Type.CLIENT_INGRESS) {
+            connectionName = "ClientIngress-ClientPort-" + inPortNumber.toString()
+                    + "-LinePort-" + outPortNumber.toString();
+        }
+
+        log.info("TerminalFlowRule built with name {}", connectionName);
+    }
+
+    public PortNumber inPort() {
+        return inPortNumber;
+    }
+
+    public PortNumber outPort() {
+        return outPortNumber;
+    }
+
+    public OchSignal ochSignal() {
+        return ochSignal;
+    }
+
+    public String connectionName() {
+        return connectionName;
+    }
+}
+
diff --git a/drivers/odtn-driver/src/main/resources/odtn-drivers.xml b/drivers/odtn-driver/src/main/resources/odtn-drivers.xml
index 2028af7..dfc418a 100644
--- a/drivers/odtn-driver/src/main/resources/odtn-drivers.xml
+++ b/drivers/odtn-driver/src/main/resources/odtn-drivers.xml
@@ -45,6 +45,17 @@
                    impl="org.onosproject.drivers.odtn.openconfig.TerminalDeviceFlowRuleProgrammable"/>
     </driver>
 
+    <driver name="client-line-terminal-device"  manufacturer="CTTC" hwVersion="" swVersion="">
+        <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
+                   impl="org.onosproject.drivers.odtn.openconfig.ClientLineTerminalDeviceDiscovery"/>
+        <behaviour api ="org.onosproject.net.optical.OpticalDevice"
+                   impl="org.onosproject.net.optical.DefaultOpticalDevice"/>
+        <behaviour api ="org.onosproject.net.behaviour.LambdaQuery"
+                   impl="org.onosproject.drivers.odtn.openconfig.TerminalDeviceLambdaQuery"/>
+        <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
+                   impl="org.onosproject.drivers.odtn.openconfig.ClientLineTerminalDeviceFlowRuleProgrammable"/>
+    </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"/>
diff --git a/drivers/odtn-driver/src/test/resources/terminal-device.xml b/drivers/odtn-driver/src/test/resources/terminal-device.xml
new file mode 100644
index 0000000..0af7eaa
--- /dev/null
+++ b/drivers/odtn-driver/src/test/resources/terminal-device.xml
@@ -0,0 +1,1161 @@
+<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+	<terminal-device xmlns="http://openconfig.net/yang/terminal-device">
+		<logical-channels>
+			<!--Card1-->
+			<channel>
+				<!--Description: Client Port-->
+				<index>10103</index>
+				<config>
+					<index>10103</index>
+					<description>Logical channel 10103</description>
+					<admin-state>DISABLED</admin-state>
+					<rate-class xmlns:type="http://openconfig.net/yang/transport-types">type:TRIB_RATE_10G</rate-class>
+					<trib-protocol xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_10GE_LAN
+					</trib-protocol>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_ETHERNET
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<test-signal>0</test-signal>
+				</config>
+				<state>
+					<index>10103</index>
+					<description>Logical channel 10103</description>
+					<admin-state>DISABLED</admin-state>
+					<rate-class xmlns:type="http://openconfig.net/yang/transport-types">type:TRIB_RATE_10G</rate-class>
+					<trib-protocol xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_10GE_LAN
+					</trib-protocol>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_ETHERNET
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<test-signal>0</test-signal>
+					<link-state>UP</link-state>
+				</state>
+				<ingress>
+					<config>
+						<transceiver>transceiver-10103</transceiver>
+						<!--
+						<physical-channel></physical-channel>
+						-->
+					</config>
+					<state>
+						<transceiver>transceiver-10103</transceiver>
+						<!-- 
+						<physical-channel></physical-channel>
+						-->
+					</state>
+				</ingress>
+				<logical-channel-assignments>
+					<assignment>
+						<index>1</index>
+						<config>
+							<index>1</index>
+							<description>Logical channel assigned</description>
+							<assignment-type>LOGICAL_CHANNEL</assignment-type>
+							<logical-channel>10101</logical-channel>
+							<allocation>10</allocation>
+						</config>
+						<state>
+							<index>1</index>
+							<description>0</description>
+							<assignment-type>LOGICAL_CHANNEL</assignment-type>
+							<logical-channel>10101</logical-channel>
+							<allocation>10</allocation>
+						</state>
+					</assignment>
+				</logical-channel-assignments>
+			</channel>
+			<channel>
+				<!--Description: Client Port-->
+				<index>10104</index>
+				<config>
+					<index>10104</index>
+					<description>Logical channel 10104</description>
+					<admin-state>DISABLED</admin-state>
+					<rate-class xmlns:type="http://openconfig.net/yang/transport-types">type:TRIB_RATE_10G</rate-class>
+					<trib-protocol xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_10GE_LAN
+					</trib-protocol>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_ETHERNET
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<test-signal>0</test-signal>
+				</config>
+				<state>
+					<index>11104</index>
+					<description>Logical channel 10104</description>
+					<admin-state>DISABLED</admin-state>
+					<rate-class xmlns:type="http://openconfig.net/yang/transport-types">type:TRIB_RATE_10G</rate-class>
+					<trib-protocol xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_10GE_LAN
+					</trib-protocol>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_ETHERNET
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<test-signal>0</test-signal>
+					<link-state>DOWN</link-state>
+				</state>
+				<ingress>
+					<config>
+						<transceiver>transceiver-10104</transceiver>
+					</config>
+					<state>
+						<transceiver>transceiver-10104</transceiver>
+					</state>
+				</ingress>
+				<logical-channel-assignments>
+					<assignment>
+						<index>1</index>
+						<config>
+							<index>1</index>
+							<description>Logical channel assigned</description>
+							<assignment-type>LOGICAL_CHANNEL</assignment-type>
+							<logical-channel>10101</logical-channel>
+							<allocation>10</allocation>
+						</config>
+						<state>
+							<index>1</index>
+							<description>0</description>
+							<assignment-type>LOGICAL_CHANNEL</assignment-type>
+							<logical-channel>10101</logical-channel>
+							<allocation>10</allocation>
+						</state>
+					</assignment>
+				</logical-channel-assignments>
+			</channel>
+			<channel>
+				<!--Description: Client Port-->
+				<index>10105</index>
+				<config>
+					<index>10105</index>
+					<description>Logical channel 10105</description>
+					<admin-state>DISABLED</admin-state>
+					<rate-class xmlns:type="http://openconfig.net/yang/transport-types">type:TRIB_RATE_10G</rate-class>
+					<trib-protocol xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_10GE_LAN
+					</trib-protocol>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_ETHERNET
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<test-signal>0</test-signal>
+				</config>
+				<state>
+					<index>10105</index>
+					<description>Logical channel 10105</description>
+					<admin-state>DISABLED</admin-state>
+					<rate-class xmlns:type="http://openconfig.net/yang/transport-types">type:TRIB_RATE_10G</rate-class>
+					<trib-protocol xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_10GE_LAN
+					</trib-protocol>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_ETHERNET
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<test-signal>0</test-signal>
+					<link-state>DOWN</link-state>
+				</state>
+				<ingress>
+					<config>
+						<transceiver>transceiver-10105</transceiver>
+						<!--
+						<physical-channel></physical-channel>
+						-->
+					</config>
+					<state>
+						<transceiver>transceiver-10105</transceiver>
+						<!--
+						<physical-channel></physical-channel>
+						-->
+					</state>
+				</ingress>
+				<logical-channel-assignments>
+					<assignment>
+						<index>1</index>
+						<config>
+							<index>1</index>
+							<description>Logical channel assigned</description>
+							<assignment-type>LOGICAL_CHANNEL</assignment-type>
+							<logical-channel>10101</logical-channel>
+							<allocation>10</allocation>
+						</config>
+						<state>
+							<index>1</index>
+							<description>0</description>
+							<assignment-type>LOGICAL_CHANNEL</assignment-type>
+							<logical-channel>10101</logical-channel>
+							<allocation>10</allocation>
+						</state>
+					</assignment>
+				</logical-channel-assignments>
+			</channel>
+
+			<channel>
+				<!--Description: Line (OTN) Port-->
+				<index>10101</index>
+				<config>
+					<index>10101</index>
+					<description>Logical channel 10101</description>
+					<admin-state>DISABLED</admin-state>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_OTN
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+				</config>
+				<state>
+					<index>10101</index>
+					<description>Logical channel 10101</description>
+					<admin-state>DISABLED</admin-state>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_OTN
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<link-state>UP</link-state>
+				</state>
+				   <ingress>
+						<config>
+							<transceiver>channel-10101</transceiver>
+							<!--
+							<physical-channel></physical-channel>
+							-->
+						</config>
+						<state>
+							<transceiver>channel-10101</transceiver>
+							<!--
+							<physical-channel></physical-channel>
+							-->
+						</state>
+					</ingress>
+
+				<otn>
+					<config>
+						<tti-msg-expected>test1</tti-msg-expected>
+						<tti-msg-transmit>test1</tti-msg-transmit>
+					</config>
+					<state>
+						<tti-msg-expected>test1</tti-msg-expected>
+						<tti-msg-transmit>test1</tti-msg-transmit>
+						<tti-msg-auto>0</tti-msg-auto>
+						<tti-msg-recv>0</tti-msg-recv>
+						<rdi-msg>0</rdi-msg>
+						<errored-seconds>0</errored-seconds>
+						<severely-errored-seconds>0</severely-errored-seconds>
+						<unavailable-seconds>0</unavailable-seconds>
+						<code-violations>0</code-violations>
+						<fec-uncorrectable-words>0</fec-uncorrectable-words>
+						<fec-corrected-bytes>0</fec-corrected-bytes>
+						<fec-corrected-bits>0</fec-corrected-bits>
+						<background-block-errors>0</background-block-errors>
+						<pre-fec-ber>
+							<instant>0.0</instant>
+							<avg>0.0</avg>
+							<min>0.0</min>
+							<max>0.0</max>
+						</pre-fec-ber>
+						<post-fec-ber>
+							<instant>0</instant>
+							<avg>0</avg>
+							<min>0</min>
+							<max>0</max>
+						</post-fec-ber>
+						<q-value>
+							<instant>0</instant>
+							<avg>0</avg>
+							<min>0</min>
+							<max>0</max>
+							<interval>0</interval>
+						</q-value>
+						<esnr>
+							<instant>0</instant>
+							<avg>0</avg>
+							<min>0</min>
+							<max>0</max>
+							<interval>0</interval>
+						</esnr>	
+					</state>
+				</otn>
+				<logical-channel-assignments>
+					<assignment>
+						<index>1</index>
+						<config>
+							<index>1</index>
+							<description>Optical channel assigned 100</description>
+							<allocation>100</allocation>
+							<assignment-type>OPTICAL_CHANNEL</assignment-type>
+							<optical-channel>channel-10101</optical-channel>
+						</config>
+						<state>
+							<index>1</index>
+							<description>Optical channel assigned 100</description>
+							<allocation>100</allocation>
+							<assignment-type>OPTICAL_CHANNEL</assignment-type>
+							<optical-channel>channel-10101</optical-channel>
+						</state>
+					</assignment>
+				</logical-channel-assignments>
+			</channel>
+			<!--/Card1-->
+			<!--Card2-->
+			<channel>
+				<!--Description: Client Port-->
+				<index>10106</index>
+				<config>
+					<index>10106</index>
+					<description>Logical channel 10106</description>
+					<admin-state>DISABLED</admin-state>
+					<rate-class xmlns:type="http://openconfig.net/yang/transport-types">type:TRIB_RATE_10G</rate-class>
+					<trib-protocol xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_10GE_LAN
+					</trib-protocol>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_ETHERNET
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<test-signal>0</test-signal>
+				</config>
+				<state>
+					<index>10106</index>
+					<description>Logical channel 10106</description>
+					<admin-state>DISABLED</admin-state>
+					<rate-class xmlns:type="http://openconfig.net/yang/transport-types">type:TRIB_RATE_10G</rate-class>
+					<trib-protocol xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_10GE_LAN
+					</trib-protocol>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_ETHERNET
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<test-signal>0</test-signal>
+					<link-state>UP</link-state>
+				</state>
+				<ingress>
+					<config>
+						<transceiver>transceiver-10106</transceiver>
+						<!--
+						<physical-channel></physical-channel>
+						-->
+					</config>
+					<state>
+						<transceiver>transceiver-10106</transceiver>
+						<!--
+						<physical-channel></physical-channel>
+						-->
+					</state>
+				</ingress>
+				<logical-channel-assignments>
+					<assignment>
+						<index>1</index>
+						<config>
+							<index>1</index>
+							<description>Logical channel assigned</description>
+							<assignment-type>LOGICAL_CHANNEL</assignment-type>
+							<logical-channel>10102</logical-channel>
+							<allocation>10</allocation>
+						</config>
+						<state>
+							<index>1</index>
+							<description>0</description>
+							<assignment-type>LOGICAL_CHANNEL</assignment-type>
+							<logical-channel>10102</logical-channel>
+							<allocation>10</allocation>
+						</state>
+					</assignment>
+				</logical-channel-assignments>
+			</channel>
+			<channel>
+				<!--Description: Client Port-->
+				<index>10107</index>
+				<config>
+					<index>10107</index>
+					<description>Logical channel 10107</description>
+					<admin-state>DISABLED</admin-state>
+					<rate-class xmlns:type="http://openconfig.net/yang/transport-types">type:TRIB_RATE_10G</rate-class>
+					<trib-protocol xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_10GE_LAN
+					</trib-protocol>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_ETHERNET
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<test-signal>0</test-signal>
+				</config>
+				<state>
+					<index>11104</index>
+					<description>Logical channel 10107</description>
+					<admin-state>DISABLED</admin-state>
+					<rate-class xmlns:type="http://openconfig.net/yang/transport-types">type:TRIB_RATE_10G</rate-class>
+					<trib-protocol xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_10GE_LAN
+					</trib-protocol>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_ETHERNET
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<test-signal>0</test-signal>
+					<link-state>DOWN</link-state>
+				</state>
+				<ingress>
+					<config>
+						<transceiver>transceiver-10107</transceiver>
+					</config>
+					<state>
+						<transceiver>transceiver-10107</transceiver>
+					</state>
+				</ingress>
+				<logical-channel-assignments>
+					<assignment>
+						<index>1</index>
+						<config>
+							<index>1</index>
+							<description>Logical channel assigned</description>
+							<assignment-type>LOGICAL_CHANNEL</assignment-type>
+							<logical-channel>10102</logical-channel>
+							<allocation>10</allocation>
+						</config>
+						<state>
+							<index>1</index>
+							<description>0</description>
+							<assignment-type>LOGICAL_CHANNEL</assignment-type>
+							<logical-channel>10102</logical-channel>
+							<allocation>10</allocation>
+						</state>
+					</assignment>
+				</logical-channel-assignments>
+			</channel>
+			<channel>
+				<!--Description: Client Port-->
+				<index>10108</index>
+				<config>
+					<index>10108</index>
+					<description>Logical channel 10108</description>
+					<admin-state>DISABLED</admin-state>
+					<rate-class xmlns:type="http://openconfig.net/yang/transport-types">type:TRIB_RATE_10G</rate-class>
+					<trib-protocol xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_10GE_LAN
+					</trib-protocol>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_ETHERNET
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<test-signal>0</test-signal>
+				</config>
+				<state>
+					<index>10108</index>
+					<description>Logical channel 10108</description>
+					<admin-state>DISABLED</admin-state>
+					<rate-class xmlns:type="http://openconfig.net/yang/transport-types">type:TRIB_RATE_10G</rate-class>
+					<trib-protocol xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_10GE_LAN
+					</trib-protocol>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_ETHERNET
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<test-signal>0</test-signal>
+					<link-state>DOWN</link-state>
+				</state>
+				<ingress>
+					<config>
+						<transceiver>transceiver-10108</transceiver>
+						<!-- 
+						<physical-channel></physical-channel>
+						-->
+					</config>
+					<state>
+						<transceiver>transceiver-10108</transceiver>
+						<!-- 
+						<physical-channel></physical-channel>
+						-->
+					</state>
+				</ingress>
+				<logical-channel-assignments>
+					<assignment>
+						<index>1</index>
+						<config>
+							<index>1</index>
+							<description>Logical channel assigned</description>
+							<assignment-type>LOGICAL_CHANNEL</assignment-type>
+							<logical-channel>10102</logical-channel>
+							<allocation>10</allocation>
+						</config>
+						<state>
+							<index>1</index>
+							<description>0</description>
+							<assignment-type>LOGICAL_CHANNEL</assignment-type>
+							<logical-channel>10102</logical-channel>
+							<allocation>10</allocation>
+						</state>
+					</assignment>
+				</logical-channel-assignments>
+			</channel>
+
+			<channel>
+				<!--Description: Line (OTN) Port-->
+				<index>10102</index>
+				<config>
+					<index>10102</index>
+					<description>Logical channel 10102</description>
+					<admin-state>DISABLED</admin-state>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_OTN
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+				</config>
+				<state>
+					<index>10102</index>
+					<description>Logical channel 10102</description>
+					<admin-state>DISABLED</admin-state>
+					<logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_OTN
+					</logical-channel-type>
+					<loopback-mode>NONE</loopback-mode>
+					<link-state>UP</link-state>
+				</state>
+				   <ingress>
+						<config>
+							<transceiver>channel-10102</transceiver>
+							<!--
+							<physical-channel></physical-channel>
+							-->
+						</config>
+						<state>
+							<transceiver>channel-10102</transceiver>
+							<!--
+							<physical-channel></physical-channel>
+							-->
+						</state>
+					</ingress>
+
+				<otn>
+					<config>
+						<tti-msg-expected>test1</tti-msg-expected>
+						<tti-msg-transmit>test1</tti-msg-transmit>
+					</config>
+					<state>
+						<tti-msg-expected>test1</tti-msg-expected>
+						<tti-msg-transmit>test1</tti-msg-transmit>
+						<tti-msg-auto>0</tti-msg-auto>
+						<tti-msg-recv>0</tti-msg-recv>
+						<rdi-msg>0</rdi-msg>
+						<errored-seconds>0</errored-seconds>
+						<severely-errored-seconds>0</severely-errored-seconds>
+						<unavailable-seconds>0</unavailable-seconds>
+						<code-violations>0</code-violations>
+						<fec-uncorrectable-words>0</fec-uncorrectable-words>
+						<fec-corrected-bytes>0</fec-corrected-bytes>
+						<fec-corrected-bits>0</fec-corrected-bits>
+						<background-block-errors>0</background-block-errors>
+						<pre-fec-ber>
+							<instant>0.0</instant>
+							<avg>0.0</avg>
+							<min>0.0</min>
+							<max>0.0</max>
+						</pre-fec-ber>
+						<post-fec-ber>
+							<instant>0</instant>
+							<avg>0</avg>
+							<min>0</min>
+							<max>0</max>
+						</post-fec-ber>
+						<q-value>
+							<instant>0</instant>
+							<avg>0</avg>
+							<min>0</min>
+							<max>0</max>
+							<interval>0</interval>
+						</q-value>
+						<esnr>
+							<instant>0</instant>
+							<avg>0</avg>
+							<min>0</min>
+							<max>0</max>
+							<interval>0</interval>
+						</esnr>
+					</state>
+				</otn>
+				<logical-channel-assignments>
+					<assignment>
+						<index>1</index>
+						<config>
+							<index>1</index>
+							<description>Optical channel assigned 100</description>
+							<allocation>100</allocation>
+							<assignment-type>OPTICAL_CHANNEL</assignment-type>
+							<optical-channel>channel-10102</optical-channel>
+						</config>
+						<state>
+							<index>1</index>
+							<description>Optical channel assigned 100</description>
+							<allocation>100</allocation>
+							<assignment-type>OPTICAL_CHANNEL</assignment-type>
+							<optical-channel>channel-10102</optical-channel>
+						</state>
+					</assignment>
+				</logical-channel-assignments>
+			</channel>
+			<!--/Card2-->
+		</logical-channels>
+	</terminal-device>
+	<components xmlns="http://openconfig.net/yang/platform">
+		<component>
+			<name>device</name>
+			<config>
+				<name>device</name>
+			</config>
+			<state>
+				<name>DC-USA</name>
+				<mfg-name>SSSA-CNIT</mfg-name>
+	         		<hardware-version>1.0.0</hardware-version>
+        	 		<firmware-version>1.0.0</firmware-version>
+         			<software-version>1.0.0</software-version>
+         			<serial-no>610610</serial-no>
+         			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:OPERATING_SYSTEM</type>
+			</state>
+		</component>
+				<!--Card11-->
+		<component>
+			<name>port-10101</name>
+			<config>
+				<name>port-10101</name>
+			</config>
+			<state>
+				<name>port-10101</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:PORT</type>
+			</state>
+			<subcomponents>
+				<subcomponent>
+					<name>channel-10101</name>
+					<config>
+						<name>channel-10101</name>
+					</config>
+					<state>
+						<name>channel-10101</name>
+					</state>
+				</subcomponent>
+			</subcomponents>
+			<properties>
+				<property>
+					<name>onos-index</name>
+					<config>
+						<name>onos-index</name>
+						<value>10101</value>
+					</config>
+					<state>
+						<name>onos-index</name>
+						<value>10101</value>
+					</state>
+				</property>
+				<property>
+					<name>odtn-port-type</name>
+					<config>
+						<name>odtn-port-type</name>
+						<value>line</value>
+					</config>
+					<state>
+						<name>odtn-port-type</name>
+						<value>line</value>
+					</state>
+				</property>
+			</properties>
+		</component>
+		<component>
+			<name>port-10103</name>
+			<config>
+				<name>port-10103</name>
+			</config>
+			<state>
+				<name>port-10103</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:PORT</type>
+			</state>
+			<subcomponents>
+				<subcomponent>
+					<name>transceiver-10103</name>
+					<config>
+						<name>transceiver-10103</name>
+					</config>
+					<state>
+						<name>transceiver-10103</name>
+					</state>
+				</subcomponent>
+			</subcomponents>
+			<properties>
+				<property>
+					<name>onos-index</name>
+					<config>
+						<name>onos-index</name>
+						<value>10103</value>
+					</config>
+					<state>
+						<name>onos-index</name>
+						<value>10103</value>
+					</state>
+				</property>
+				<property>
+					<name>odtn-port-type</name>
+					<config>
+						<name>odtn-port-type</name>
+						<value>client</value>
+					</config>
+					<state>
+						<name>odtn-port-type</name>
+						<value>client</value>
+					</state>
+				</property>
+			</properties>
+		</component>
+		<component>
+			<name>port-10104</name>
+			<config>
+				<name>port-10104</name>
+			</config>
+			<state>
+				<name>port-10104</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:PORT</type>
+			</state>
+			<subcomponents>
+				<subcomponent>
+					<name>transceiver-10104</name>
+					<config>
+						<name>transceiver-10104</name>
+					</config>
+					<state>
+						<name>transceiver-10104</name>
+					</state>
+				</subcomponent>
+			</subcomponents>
+			<properties>
+				<property>
+					<name>onos-index</name>
+					<config>
+						<name>onos-index</name>
+						<value>10104</value>
+					</config>
+					<state>
+						<name>onos-index</name>
+						<value>10104</value>
+					</state>
+				</property>
+				<property>
+					<name>odtn-port-type</name>
+					<config>
+						<name>odtn-port-type</name>
+						<value>client</value>
+					</config>
+					<state>
+						<name>odtn-port-type</name>
+						<value>client</value>
+					</state>
+				</property>
+			</properties>
+		</component>
+		<component>
+			<name>port-10105</name>
+			<config>
+				<name>port-10105</name>
+			</config>
+			<state>
+				<name>port-10105</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:PORT</type>
+			</state>
+			<subcomponents>
+				<subcomponent>
+					<name>transceiver-10105</name>
+					<config>
+						<name>transceiver-10105</name>
+					</config>
+					<state>
+						<name>transceiver-10105</name>
+					</state>
+				</subcomponent>
+			</subcomponents>
+			<properties>
+				<property>
+					<name>onos-index</name>
+					<config>
+						<name>onos-index</name>
+						<value>10105</value>
+					</config>
+					<state>
+						<name>onos-index</name>
+						<value>10105</value>
+					</state>
+				</property>
+				<property>
+					<name>odtn-port-type</name>
+					<config>
+						<name>odtn-port-type</name>
+						<value>client</value>
+					</config>
+					<state>
+						<name>odtn-port-type</name>
+						<value>client</value>
+					</state>
+				</property>
+			</properties>
+		</component>
+		<component>
+			<name>transceiver-10103</name>
+			<config>
+				<name>transceiver-10103</name>
+			</config>
+			<state>
+				<name>transceiver-10103</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:TRANSCEIVER</type>
+			</state>
+		</component>
+		<component>
+			<name>transceiver-10104</name>
+			<config>
+				<name>transceiver-10104</name>
+			</config>
+			<state>
+				<name>transceiver-10104</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:TRANSCEIVER</type>
+			</state>
+		</component>
+		<component>
+			<name>transceiver-10105</name>
+			<config>
+				<name>transceiver-10105</name>
+			</config>
+			<state>
+				<name>transceiver-10105</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:TRANSCEIVER</type>
+			</state>
+		</component>
+		<component>
+			<name>channel-10101</name>
+			<config>
+				<name>channel-10101</name>
+			</config>
+			<state>
+				<name>channel-10101</name>
+      			<type xmlns:typex="http://openconfig.net/yang/transport-types">typex:OPTICAL_CHANNEL</type>
+			</state>
+			<optical-channel xmlns="http://openconfig.net/yang/terminal-device">
+				<config>
+					<frequency>191600000</frequency>
+					<target-output-power>100</target-output-power>
+					<operational-mode>0</operational-mode>
+					<line-port>port-10101</line-port>
+				</config>
+				<state>
+					<frequency>191600000</frequency>
+					<target-output-power>0</target-output-power>
+					<operational-mode>0</operational-mode>
+					<line-port>port-10101</line-port>
+					<group-id>1</group-id>
+					<output-power>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+						<interval>0</interval>
+					</output-power>
+					<input-power>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+						<interval>0</interval>
+					</input-power>
+					<laser-bias-current>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+						<interval>0</interval>
+					</laser-bias-current>
+					<chromatic-dispersion>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+					</chromatic-dispersion>
+					<polarization-mode-dispersion>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+					</polarization-mode-dispersion>
+					<second-order-polarization-mode-dispersion>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+					</second-order-polarization-mode-dispersion>
+					<polarization-dependent-loss>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+						<interval>0</interval>
+					</polarization-dependent-loss>
+				</state>
+			</optical-channel>
+		</component>
+		<!--/Card1-->
+
+		<!--Card12-->
+		<component>
+			<name>port-10102</name>
+			<config>
+				<name>port-10102</name>
+			</config>
+			<state>
+				<name>port-10102</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:PORT</type>
+			</state>
+			<subcomponents>
+				<subcomponent>
+					<name>channel-10102</name>
+					<config>
+						<name>channel-10102</name>
+					</config>
+					<state>
+						<name>channel-10102</name>
+					</state>
+				</subcomponent>
+			</subcomponents>
+			<properties>
+				<property>
+					<name>onos-index</name>
+					<config>
+						<name>onos-index</name>
+						<value>10102</value>
+					</config>
+					<state>
+						<name>onos-index</name>
+						<value>10102</value>
+					</state>
+				</property>
+				<property>
+					<name>odtn-port-type</name>
+					<config>
+						<name>odtn-port-type</name>
+						<value>line</value>
+					</config>
+					<state>
+						<name>odtn-port-type</name>
+						<value>line</value>
+					</state>
+				</property>
+			</properties>
+		</component>
+		<component>
+			<name>port-10106</name>
+			<config>
+				<name>port-10106</name>
+			</config>
+			<state>
+				<name>port-10106</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:PORT</type>
+			</state>
+			<subcomponents>
+				<subcomponent>
+					<name>transceiver-10106</name>
+					<config>
+						<name>transceiver-10106</name>
+					</config>
+					<state>
+						<name>transceiver-10106</name>
+					</state>
+				</subcomponent>
+			</subcomponents>
+			<properties>
+				<property>
+					<name>onos-index</name>
+					<config>
+						<name>onos-index</name>
+						<value>10106</value>
+					</config>
+					<state>
+						<name>onos-index</name>
+						<value>10106</value>
+					</state>
+				</property>
+				<property>
+					<name>odtn-port-type</name>
+					<config>
+						<name>odtn-port-type</name>
+						<value>client</value>
+					</config>
+					<state>
+						<name>odtn-port-type</name>
+						<value>client</value>
+					</state>
+				</property>
+			</properties>
+		</component>
+		<component>
+			<name>port-10107</name>
+			<config>
+				<name>port-10107</name>
+			</config>
+			<state>
+				<name>port-10107</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:PORT</type>
+			</state>
+			<subcomponents>
+				<subcomponent>
+					<name>transceiver-10107</name>
+					<config>
+						<name>transceiver-10107</name>
+					</config>
+					<state>
+						<name>transceiver-10107</name>
+					</state>
+				</subcomponent>
+			</subcomponents>
+			<properties>
+				<property>
+					<name>onos-index</name>
+					<config>
+						<name>onos-index</name>
+						<value>10107</value>
+					</config>
+					<state>
+						<name>onos-index</name>
+						<value>10107</value>
+					</state>
+				</property>
+				<property>
+					<name>odtn-port-type</name>
+					<config>
+						<name>odtn-port-type</name>
+						<value>client</value>
+					</config>
+					<state>
+						<name>odtn-port-type</name>
+						<value>client</value>
+					</state>
+				</property>
+			</properties>
+		</component>
+		<component>
+			<name>port-10108</name>
+			<config>
+				<name>port-10108</name>
+			</config>
+			<state>
+				<name>port-10108</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:PORT</type>
+			</state>
+			<subcomponents>
+				<subcomponent>
+					<name>transceiver-10108</name>
+					<config>
+						<name>transceiver-10108</name>
+					</config>
+					<state>
+						<name>transceiver-10108</name>
+					</state>
+				</subcomponent>
+			</subcomponents>
+			<properties>
+				<property>
+					<name>onos-index</name>
+					<config>
+						<name>onos-index</name>
+						<value>10108</value>
+					</config>
+					<state>
+						<name>onos-index</name>
+						<value>10108</value>
+					</state>
+				</property>
+				<property>
+					<name>odtn-port-type</name>
+					<config>
+						<name>odtn-port-type</name>
+						<value>client</value>
+					</config>
+					<state>
+						<name>odtn-port-type</name>
+						<value>client</value>
+					</state>
+				</property>
+			</properties>
+		</component>
+		<component>
+			<name>transceiver-10106</name>
+			<config>
+				<name>transceiver-10106</name>
+			</config>
+			<state>
+				<name>transceiver-10106</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:TRANSCEIVER</type>
+			</state>
+		</component>
+		<component>
+			<name>transceiver-10107</name>
+			<config>
+				<name>transceiver-10107</name>
+			</config>
+			<state>
+				<name>transceiver-10107</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:TRANSCEIVER</type>
+			</state>
+		</component>
+		<component>
+			<name>transceiver-10108</name>
+			<config>
+				<name>transceiver-10108</name>
+			</config>
+			<state>
+				<name>transceiver-10108</name>
+      			<type xmlns:typex="http://openconfig.net/yang/platform-types">typex:TRANSCEIVER</type>
+			</state>
+		</component>
+		<component>
+			<name>channel-10102</name>
+			<config>
+				<name>channel-10102</name>
+			</config>
+			<state>
+				<name>channel-10102</name>
+      			<type xmlns:typex="http://openconfig.net/yang/transport-types">typex:OPTICAL_CHANNEL</type>
+			</state>
+		    <optical-channel xmlns="http://openconfig.net/yang/terminal-device">
+				<config>
+					<frequency>191600000</frequency>
+					<target-output-power>100</target-output-power>
+					<operational-mode>0</operational-mode>
+					<line-port>port-10102</line-port>
+				</config>
+				<state>
+					<frequency>191600000</frequency>
+					<target-output-power>0</target-output-power>
+					<operational-mode>0</operational-mode>
+					<line-port>port-10102</line-port>
+					<group-id>1</group-id>
+					<output-power>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+						<interval>0</interval>
+					</output-power>
+					<input-power>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+						<interval>0</interval>
+					</input-power>
+					<laser-bias-current>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+						<interval>0</interval>
+					</laser-bias-current>
+					<chromatic-dispersion>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+					</chromatic-dispersion>
+					<polarization-mode-dispersion>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+					</polarization-mode-dispersion>
+					<second-order-polarization-mode-dispersion>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+					</second-order-polarization-mode-dispersion>
+					<polarization-dependent-loss>
+						<instant>0</instant>
+						<avg>0</avg>
+						<min>0</min>
+						<max>0</max>
+						<interval>0</interval>
+					</polarization-dependent-loss>
+				</state>
+			</optical-channel>
+		</component>
+		<!--/Card2-->
+	</components>
+</config>
+