Add driver for oplink netconf device.

Change-Id: I3c8a2e5ba3f79df48a2212b25664821891b6d2ae
diff --git a/drivers/oplink/BUCK b/drivers/oplink/BUCK
new file mode 100755
index 0000000..bc52ab2
--- /dev/null
+++ b/drivers/oplink/BUCK
@@ -0,0 +1,36 @@
+COMPILE_DEPS = [
+    '//lib:CORE_DEPS',
+    '//drivers/default:onos-drivers-default',
+    '//drivers/utilities:onos-drivers-utilities',
+    '//protocols/netconf/api:onos-protocols-netconf-api',
+    '//apps/optical-model:onos-apps-optical-model',
+]
+
+TEST_DEPS = [
+    '//lib:TEST_ADAPTERS',
+    '//core/api:onos-api-tests',
+]
+
+BUNDLES = [
+    ':onos-drivers-oplink',
+    '//drivers/utilities:onos-drivers-utilities',
+]
+
+osgi_jar_with_tests (
+    deps = COMPILE_DEPS,
+    test_deps = TEST_DEPS,
+    resources_root = 'src/main/resources',
+    resources = glob(['src/main/resources/**']),
+)
+
+onos_app(
+    app_name = 'org.onosproject.drivers.oplink',
+    title = 'Oplink Device Drivers',
+    category = 'Drivers',
+    url = 'http://onosproject.org',
+    description = 'ONOS Oplink Device Drivers application.',
+    included_bundles = BUNDLES,
+    required_apps = [
+        'org.onosproject.netconf', 'org.onosproject.optical-model'
+    ],
+)
\ No newline at end of file
diff --git a/drivers/oplink/features.xml b/drivers/oplink/features.xml
new file mode 100755
index 0000000..ed1f431
--- /dev/null
+++ b/drivers/oplink/features.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+
+<!--
+  ~ Copyright 2016 Open Networking Laboratory
+  ~
+  ~ 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.
+  -->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
+    <feature name="${project.artifactId}" version="${project.version}"
+             description="${project.description}">
+        <feature>onos-api</feature>
+        <bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
+
+        <bundle>mvn:${project.groupId}/onos-drivers-utilities/${project.version}</bundle>
+
+        <bundle>mvn:${project.groupId}/onos-netconf-api/${project.version}</bundle>
+    </feature>
+</features>
diff --git a/drivers/oplink/pom.xml b/drivers/oplink/pom.xml
new file mode 100755
index 0000000..71bd788
--- /dev/null
+++ b/drivers/oplink/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2016 Open Networking Laboratory
+  ~
+  ~ 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>onos-drivers-general</artifactId>
+        <groupId>org.onosproject</groupId>
+        <version>1.10.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <description>Oplink device drivers</description>
+
+    <artifactId>onos-drivers-oplink</artifactId>
+    <packaging>bundle</packaging>
+
+    <properties>
+        <onos.app.name>org.onosproject.drivers.oplink</onos.app.name>
+        <onos.app.origin>Oplink</onos.app.origin>
+        <onos.app.category>Drivers</onos.app.category>
+        <onos.app.title>Oplink Device Drivers</onos.app.title>
+        <onos.app.url>http://onosproject.org</onos.app.url>
+        <onos.app.requires>
+            org.onosproject.drivers,
+            org.onosproject.netconf,
+            org.onosproject.optical-model
+        </onos.app.requires>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-optical-model</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-netconf-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-drivers-utilities</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-drivers</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkCrossConnect.java b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkCrossConnect.java
new file mode 100644
index 0000000..1ffc5e1
--- /dev/null
+++ b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkCrossConnect.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.oplink;
+
+import org.onosproject.net.PortNumber;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Oplink cross connect data unit.
+ * The requirement members are input port, output port, wavelength channel and channel attenuation.
+ */
+public class OplinkCrossConnect {
+
+    private final PortNumber inPort;
+    private final PortNumber outPort;
+    private final int channel;
+    private final int attenuation;
+
+    /**
+     * OplinkConnection structure.
+     * @param inPort the input port
+     * @param outPort the output port
+     * @param channel the channel
+     * @param attenuation the attenuation
+     */
+    public OplinkCrossConnect(PortNumber inPort, PortNumber outPort, int channel, int attenuation) {
+        this.inPort = inPort;
+        this.outPort = outPort;
+        this.channel = channel;
+        this.attenuation = attenuation;
+    }
+
+    /**
+     * Returns the input port of the cross connect.
+     * @return input port
+     */
+    public PortNumber getInPort() {
+        return inPort;
+    }
+
+    /**
+     * Returns the output port of the cross connect.
+     * @return output port
+     */
+    public PortNumber getOutPort() {
+        return outPort;
+    }
+
+    /**
+     * Returns the channel number of the cross connect.
+     * @return channel number
+     */
+    public int getChannel() {
+        return channel;
+    }
+
+    /**
+     * Returns the channel attenuation of the cross connect.
+     * @return attenuation
+     */
+    public int getAttenuation() {
+        return attenuation;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("inPort", inPort)
+                .add("outPort", outPort)
+                .add("channel", channel)
+                .add("attenuation", attenuation)
+                .toString();
+    }
+}
\ No newline at end of file
diff --git a/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkDriversLoader.java b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkDriversLoader.java
new file mode 100755
index 0000000..cf56174
--- /dev/null
+++ b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkDriversLoader.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.oplink;
+
+import org.apache.felix.scr.annotations.Component;
+import org.onosproject.net.driver.AbstractDriverLoader;
+import org.onosproject.net.optical.OpticalDevice;
+
+/**
+ * Loader for Oplink device drivers.
+ */
+@Component(immediate = true)
+public class OplinkDriversLoader extends AbstractDriverLoader {
+
+    // OSGI: help bundle plugin discover runtime package dependency.
+    @SuppressWarnings("unused")
+    private OpticalDevice optical;
+
+    public OplinkDriversLoader() {
+        super("/oplink-drivers.xml");
+    }
+}
diff --git a/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkNetconfUtility.java b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkNetconfUtility.java
new file mode 100644
index 0000000..f474a5c
--- /dev/null
+++ b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkNetconfUtility.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.oplink;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+
+import java.io.IOException;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Netconf utility for oplink netconf drivers.
+ */
+public final class OplinkNetconfUtility {
+
+    // public used nodes
+    public static final String CFG_TAR_RUNNING = "running";
+    public static final String CFG_MODE_MERGE = "merge";
+    public static final String CFG_MODE_REMOVE = "remove";
+    public static final String KEY_XMLNS = "xmlns=\"http://com/att/device\"";
+    public static final String KEY_DATA = "data";
+    public static final String KEY_OPENOPTICALDEV = "open-optical-device";
+    public static final String KEY_CONNS = "connections";
+    public static final String KEY_CONNID = "connection-id";
+    public static final String KEY_PORTS = "ports";
+    public static final String KEY_PORTID = "port-id";
+    public static final String KEY_PORT = "port";
+    public static final String KEY_PORTDIRECT = "port-direction";
+    public static final String KEY_CHATT = "attenuation";
+    public static final String KEY_DATA_CONNS = String.format("%s.%s.%s", KEY_DATA, KEY_OPENOPTICALDEV, KEY_CONNS);
+    public static final String KEY_DATA_PORTS = String.format("%s.%s.%s", KEY_DATA, KEY_OPENOPTICALDEV, KEY_PORTS);
+    public static final String KEY_OPENOPTICALDEV_XMLNS = String.format("%s %s", KEY_OPENOPTICALDEV, KEY_XMLNS);
+
+    private OplinkNetconfUtility() {
+    }
+
+    /**
+     * Retrieves session reply information for get config operation.
+     *
+     * @param handler parent driver handler
+     * @param filter the filter string of xml content
+     * @return the reply string
+     */
+    public static String netconfGetConfig(DriverHandler handler, String filter) {
+        NetconfController controller = checkNotNull(handler.get(NetconfController.class));
+        NetconfSession session = controller.getNetconfDevice(handler.data().deviceId()).getSession();
+        String reply;
+        try {
+            reply = session.getConfig(CFG_TAR_RUNNING, filter);
+        } catch (IOException e) {
+            throw new RuntimeException(new NetconfException("Failed to retrieve configuration.", e));
+        }
+        return reply;
+    }
+
+    /**
+     * Retrieves session reply information for edit config operation.
+     *
+     * @param handler parent driver handler
+     * @param mode selected mode to change the configuration
+     * @param cfg the new configuration to be set
+     * @return the reply string
+     */
+    public static boolean netconfEditConfig(DriverHandler handler, String mode, String cfg) {
+        NetconfController controller = checkNotNull(handler.get(NetconfController.class));
+        NetconfSession session = controller.getNetconfDevice(handler.data().deviceId()).getSession();
+        boolean reply = false;
+        try {
+            reply = session.editConfig(CFG_TAR_RUNNING, mode, cfg);
+        } catch (IOException e) {
+            throw new RuntimeException(new NetconfException("Failed to edit configuration.", e));
+        }
+        return reply;
+    }
+
+    /**
+     * Retrieves specified node hierarchical configuration from the xml information.
+     *
+     * @param content the xml information
+     * @param key the configuration key node
+     * @return the hierarchical configuration
+     */
+    public static HierarchicalConfiguration configAt(String content, String key) {
+        HierarchicalConfiguration cfg = XmlConfigParser.loadXmlString(content);
+        HierarchicalConfiguration info;
+        try {
+            info = cfg.configurationAt(key);
+        } catch (Exception e) {
+            // Accept null for information polling
+            return null;
+        }
+        return info;
+    }
+
+    /**
+     * Makes a xml format sentence.
+     *
+     * @param node the node name
+     * @param content the node content
+     * @return the xml format sentence
+     */
+    public static String xml(String node, String content) {
+        return String.format("<%s>%s</%s>", node, content, node);
+    }
+
+    /**
+     * Makes a xml format open tag.
+     *
+     * @param node the node name
+     * @return the xml head format string
+     */
+    public static String xmlOpen(String node) {
+        return String.format("<%s>", node);
+    }
+
+    /**
+     * Makes a xml format close tag.
+     *
+     * @param node the node name
+     * @return the xml end format string
+     */
+    public static String xmlClose(String node) {
+        return String.format("</%s>", node);
+    }
+
+    /**
+     * Makes a xml format empty tag.
+     *
+     * @param node the node name
+     * @return the xml format of empty tag
+     */
+    public static String xmlEmpty(String node) {
+        return String.format("<%s/>", node);
+    }
+}
diff --git a/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalDeviceDescription.java b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalDeviceDescription.java
new file mode 100755
index 0000000..8897793
--- /dev/null
+++ b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalDeviceDescription.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.oplink;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceDescriptionDiscovery;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.slf4j.Logger;
+
+import java.util.List;
+
+import static org.onosproject.drivers.oplink.OplinkOpticalUtility.CHANNEL_SPACING;
+import static org.onosproject.drivers.oplink.OplinkOpticalUtility.START_CENTER_FREQ;
+import static org.onosproject.drivers.oplink.OplinkOpticalUtility.STOP_CENTER_FREQ;
+import static org.onosproject.net.optical.device.OmsPortHelper.omsPortDescription;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.drivers.oplink.OplinkNetconfUtility.*;
+
+/**
+ * Retrieves the ports from an Oplink optical netconf device.
+ */
+public class OplinkOpticalDeviceDescription extends AbstractHandlerBehaviour
+        implements DeviceDescriptionDiscovery {
+
+    private static final String KEY_PORTNAME = "port-name";
+    private static final String PORT_DIRECTION = "direction";
+    // log
+    private static final Logger log = getLogger(OplinkOpticalDeviceDescription.class);
+
+    @Override
+    public DeviceDescription discoverDeviceDetails() {
+        log.debug("No description to be added for device");
+        //TODO to be implemented if needed.
+        return null;
+    }
+
+    @Override
+    public List<PortDescription> discoverPortDetails() {
+        log.debug("Port description to be added for device {}", data().deviceId());
+        String reply = netconfGetConfig(handler(), getPortsFilter());
+        List<PortDescription> descriptions = parsePorts(reply);
+        return ImmutableList.copyOf(descriptions);
+    }
+
+    private String getPortsFilter() {
+        return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+                .append(xmlEmpty(KEY_PORTS))
+                .append(xmlClose(KEY_OPENOPTICALDEV))
+                .toString();
+    }
+
+    private List<PortDescription> parsePorts(String content) {
+        HierarchicalConfiguration cfg = XmlConfigParser.loadXmlString(content);
+        List<PortDescription> portDescriptions = Lists.newArrayList();
+        List<HierarchicalConfiguration> subtrees = cfg.configurationsAt(KEY_DATA_PORTS);
+        for (HierarchicalConfiguration portConfig : subtrees) {
+            portDescriptions.add(parsePort(portConfig));
+        }
+        return portDescriptions;
+    }
+
+    private PortDescription parsePort(HierarchicalConfiguration cfg) {
+        PortNumber portNumber = PortNumber.portNumber(cfg.getLong(KEY_PORTID));
+        HierarchicalConfiguration portInfo = cfg.configurationAt(KEY_PORT);
+        DefaultAnnotations annotations = DefaultAnnotations.builder()
+                .set(AnnotationKeys.PORT_NAME, portInfo.getString(KEY_PORTNAME))
+                .set(PORT_DIRECTION, portInfo.getString(KEY_PORTDIRECT))
+                .build();
+        return omsPortDescription(portNumber,
+                                  true,
+                                  START_CENTER_FREQ,
+                                  STOP_CENTER_FREQ,
+                                  CHANNEL_SPACING.frequency(),
+                                  annotations);
+    }
+}
diff --git a/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalFlowRuleProgrammable.java b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalFlowRuleProgrammable.java
new file mode 100644
index 0000000..01446f2
--- /dev/null
+++ b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalFlowRuleProgrammable.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.oplink;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.onosproject.drivers.utilities.XmlConfigParser;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleProgrammable;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.onosproject.drivers.oplink.OplinkNetconfUtility.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Flow rule programmable behaviour for oplink optical netconf devices.
+ */
+public class OplinkOpticalFlowRuleProgrammable
+        extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
+
+    // key
+    public static final String KEY_CHID = "wavelength-id";
+    public static final String KEY_SRC = "source";
+    public static final String KEY_DST = "destination";
+    public static final String KEY_SRC_PORTID = String.format("%s.%s", KEY_SRC, KEY_PORTID);
+    public static final String KEY_SRC_CHID = String.format("%s.%s", KEY_SRC, KEY_CHID);
+    public static final String KEY_DST_PORTID = String.format("%s.%s", KEY_DST, KEY_PORTID);
+
+    // log
+    private static final Logger log = getLogger(OplinkOpticalFlowRuleProgrammable.class);
+
+    @Override
+    public Collection<FlowEntry> getFlowEntries() {
+        return parseConnections();
+    }
+
+    @Override
+    public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
+        return applyConnections(rules);
+    }
+
+    @Override
+    public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
+        return removeConnections(rules);
+    }
+
+    private String getConnectionsFilter() {
+        return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+                .append(xmlEmpty(KEY_CONNS))
+                .append(xmlClose(KEY_OPENOPTICALDEV))
+                .toString();
+    }
+
+    private Collection<FlowEntry> parseConnections() {
+        log.debug("Fetch connections...");
+        String reply = netconfGetConfig(handler(), getConnectionsFilter());
+        HierarchicalConfiguration cfg = XmlConfigParser.loadXmlString(reply);
+        List<HierarchicalConfiguration> subtrees = cfg.configurationsAt(KEY_DATA_CONNS);
+        Collection<FlowEntry> list = new ArrayList<>();
+        for (HierarchicalConfiguration connection : subtrees) {
+            list.add(new DefaultFlowEntry(parseConnection(connection), FlowEntry.FlowEntryState.ADDED));
+        }
+        return list;
+    }
+
+    private FlowRule parseConnection(HierarchicalConfiguration cfg) {
+        return OplinkOpticalUtility.toFlowRule(this,
+                                               PortNumber.portNumber(cfg.getString(KEY_SRC_PORTID)),
+                                               PortNumber.portNumber(cfg.getString(KEY_DST_PORTID)),
+                                               cfg.getInt(KEY_SRC_CHID));
+
+    }
+
+    private Collection<FlowRule> applyConnections(Collection<FlowRule> rules) {
+        return rules.stream()
+                .filter(c -> applyConnection(c))
+                .collect(Collectors.toList());
+    }
+
+    private boolean applyConnection(FlowRule rule) {
+        log.debug("Applying connection {}", rule);
+        OplinkCrossConnect crossConnect = OplinkOpticalUtility.fromFlowRule(this, rule);
+        // Build xml
+        String connID = Integer.toString(crossConnect.getChannel());
+        String cfg = new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+                .append(xmlOpen(KEY_CONNS))
+                .append(xml(KEY_CONNID, connID))
+                .append(xmlOpen(KEY_SRC))
+                .append(xml(KEY_PORTID, crossConnect.getInPort().name()))
+                .append(xml(KEY_CHID, connID))
+                .append(xmlClose(KEY_SRC))
+                .append(xmlOpen(KEY_DST))
+                .append(xml(KEY_PORTID, crossConnect.getOutPort().name()))
+                .append(xmlClose(KEY_DST))
+                .append(xml(KEY_CHATT, Integer.toString(crossConnect.getAttenuation())))
+                .append(xmlClose(KEY_CONNS))
+                .append(xmlClose(KEY_OPENOPTICALDEV))
+                .toString();
+        return netconfEditConfig(handler(), CFG_MODE_MERGE, cfg);
+    }
+
+    private Collection<FlowRule> removeConnections(Collection<FlowRule> rules) {
+        return rules.stream()
+                .filter(c -> removeConnection(c))
+                .collect(Collectors.toList());
+    }
+
+    private boolean removeConnection(FlowRule rule) {
+        log.debug("Removing connection {}", rule);
+        OplinkCrossConnect crossConnect = OplinkOpticalUtility.fromFlowRule(this, rule);
+        // Build xml
+        String connID = Integer.toString(crossConnect.getChannel());
+        String cfg = new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+                .append(xmlOpen(KEY_CONNS))
+                .append(xml(KEY_CONNID, connID))
+                .append(xmlOpen(KEY_SRC))
+                .append(xml(KEY_PORTID, crossConnect.getInPort().name()))
+                .append(xml(KEY_CHID, connID))
+                .append(xmlClose(KEY_SRC))
+                .append(xmlClose(KEY_CONNS))
+                .append(xmlClose(KEY_OPENOPTICALDEV))
+                .toString();
+        return netconfEditConfig(handler(), CFG_MODE_REMOVE, cfg);
+    }
+}
diff --git a/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalLambdaQuery.java b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalLambdaQuery.java
new file mode 100644
index 0000000..a28d04d
--- /dev/null
+++ b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalLambdaQuery.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.oplink;
+
+import org.onlab.util.GuavaCollectors;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.LambdaQuery;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+
+import java.util.Set;
+import java.util.stream.IntStream;
+
+import static org.onlab.util.Spectrum.CENTER_FREQUENCY;
+import static org.onosproject.drivers.oplink.OplinkOpticalUtility.CHANNEL_SPACING;
+import static org.onosproject.drivers.oplink.OplinkOpticalUtility.STOP_CENTER_FREQ;
+import static org.onosproject.drivers.oplink.OplinkOpticalUtility.START_CENTER_FREQ;
+
+/**
+ * Lambda query implementation for Oplink netconf devices in C band.
+ *
+ * 96 lambdas with 50GHz width (fixed grid).
+ *
+ * Channel id: Nominal central frequency = 193100 GHz + spacingMultiplier * channelSpacing).
+ * Channel (-35 to 60): starting from 191350 GHz to 196100 GHz, Increment by 50GHz.
+ */
+public class OplinkOpticalLambdaQuery extends AbstractHandlerBehaviour implements LambdaQuery {
+
+    private static final int MIN_CHANNEL = (int) Math.round(
+            START_CENTER_FREQ.subtract(CENTER_FREQUENCY).asHz() / CHANNEL_SPACING.frequency().asHz());
+    private static final int MAX_CHANNEL = (int) Math.round(
+            STOP_CENTER_FREQ.subtract(CENTER_FREQUENCY).asHz() / CHANNEL_SPACING.frequency().asHz());
+
+    @Override
+    public Set<OchSignal> queryLambdas(PortNumber port) {
+        return IntStream.rangeClosed(MIN_CHANNEL, MAX_CHANNEL)
+                .mapToObj(x -> OchSignal.newDwdmSlot(CHANNEL_SPACING, x))
+                .collect(GuavaCollectors.toImmutableSet());
+    }
+}
diff --git a/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalPowerConfig.java b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalPowerConfig.java
new file mode 100755
index 0000000..92847f9
--- /dev/null
+++ b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalPowerConfig.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.oplink;
+
+import com.google.common.collect.Range;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.onosproject.driver.extensions.OplinkAttenuation;
+import org.onosproject.net.Direction;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.PowerConfig;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.slf4j.Logger;
+
+import java.util.Optional;
+
+import static org.onosproject.drivers.oplink.OplinkOpticalUtility.MAX_ATTENUATION;
+import static org.onosproject.drivers.oplink.OplinkOpticalUtility.MIN_ATTENUATION;
+import static org.onosproject.drivers.oplink.OplinkOpticalUtility.POWER_MULTIPLIER;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.drivers.oplink.OplinkNetconfUtility.*;
+
+/**
+ * Get current or target port/channel power from an Oplink optical netconf device.
+ * Set target port power or channel attenuation to an optical netconf device.
+ */
+public class OplinkOpticalPowerConfig<T> extends AbstractHandlerBehaviour
+        implements PowerConfig<T> {
+
+    // key
+    public static final String KEY_CHNUM = "wavelength-number";
+    public static final String KEY_CHPWR = "wavelength-power";
+    public static final String KEY_CHSTATS = "wavelength-stats";
+    public static final String KEY_OCMSTATS = "ocm-stats";
+    public static final String KEY_PORTDIRECT_RX = "rx";
+    public static final String KEY_PORTDIRECT_TX = "tx";
+    public static final String KEY_PORTTARPWR = "port-target-power";
+    public static final String KEY_PORTCURPWR = "port-current-power";
+    public static final String KEY_PORTPROPERTY = "port-property";
+    public static final String KEY_PORTPWRCAPMINRX = "port-power-capability-min-rx";
+    public static final String KEY_PORTPWRCAPMAXRX = "port-power-capability-max-rx";
+    public static final String KEY_PORTPWRCAPMINTX = "port-power-capability-min-tx";
+    public static final String KEY_PORTPWRCAPMAXTX = "port-power-capability-max-tx";
+    public static final String KEY_PORTS_PORT = String.format("%s.%s", KEY_DATA_PORTS, KEY_PORT);
+    public static final String KEY_PORTS_PORT_PROPERTY = String.format("%s.%s", KEY_PORTS_PORT, KEY_PORTPROPERTY);
+    // log
+    private static final Logger log = getLogger(OplinkOpticalPowerConfig.class);
+
+
+    @Override
+    public Optional<Long> getTargetPower(PortNumber port, T component) {
+        return Optional.ofNullable(acquireTargetPower(port, component));
+    }
+
+    @Override
+    public void setTargetPower(PortNumber port, T component, long power) {
+        if (component instanceof OchSignal) {
+            setChannelTargetPower(port, (OchSignal) component, power);
+        } else {
+            setPortTargetPower(port, power);
+        }
+    }
+
+    @Override
+    public Optional<Long> currentPower(PortNumber port, T component) {
+        return Optional.ofNullable(acquireCurrentPower(port, component));
+    }
+
+    @Override
+    public Optional<Range<Long>> getTargetPowerRange(PortNumber port, T component) {
+        return Optional.ofNullable(getTxPowerRange(port, component));
+    }
+
+    @Override
+    public Optional<Range<Long>> getInputPowerRange(PortNumber port, T component) {
+        return Optional.ofNullable(getRxPowerRange(port, component));
+    }
+
+    private String getPortPowerFilter(PortNumber port, String selection) {
+        return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+                .append(xmlOpen(KEY_PORTS))
+                .append(xml(KEY_PORTID, Long.toString(port.toLong())))
+                .append(xmlOpen(KEY_PORT))
+                .append(xmlEmpty(selection))
+                .append(xmlClose(KEY_PORT))
+                .append(xmlClose(KEY_PORTS))
+                .append(xmlClose(KEY_OPENOPTICALDEV))
+                .toString();
+    }
+
+    private String getChannelPowerFilter(PortNumber port, OchSignal channel) {
+        return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+                .append(xmlOpen(KEY_PORTS))
+                .append(xml(KEY_PORTID, Long.toString(port.toLong())))
+                .append(xmlOpen(KEY_PORT))
+                .append(xmlOpen(KEY_OCMSTATS))
+                .append(xml(KEY_CHNUM, Integer.toString(channel.spacingMultiplier())))
+                .append(xmlEmpty(KEY_CHSTATS))
+                .append(xmlClose(KEY_OCMSTATS))
+                .append(xmlClose(KEY_PORT))
+                .append(xmlClose(KEY_PORTS))
+                .append(xmlClose(KEY_OPENOPTICALDEV))
+                .toString();
+    }
+
+    private String getChannelAttenuationFilter(PortNumber port, OchSignal channel) {
+        return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+                .append(xmlOpen(KEY_CONNS))
+                .append(xml(KEY_CONNID, Integer.toString(channel.spacingMultiplier())))
+                .append(xmlEmpty(KEY_CHATT))
+                .append(xmlClose(KEY_CONNS))
+                .append(xmlClose(KEY_OPENOPTICALDEV))
+                .toString();
+    }
+
+    private String getPowerRangeFilter(PortNumber port, String direction) {
+        return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+                .append(xmlOpen(KEY_PORTS))
+                .append(xml(KEY_PORTID, Long.toString(port.toLong())))
+                .append(xmlOpen(KEY_PORT))
+                .append(xml(KEY_PORTDIRECT, direction))
+                .append(xmlEmpty(KEY_PORTPROPERTY))
+                .append(xmlClose(KEY_PORT))
+                .append(xmlClose(KEY_PORTS))
+                .append(xmlClose(KEY_OPENOPTICALDEV))
+                .toString();
+    }
+
+    private Long acquireTargetPower(PortNumber port, T component) {
+        if (component instanceof OchSignal) {
+            return acquireChannelAttenuation(port, (OchSignal) component);
+        }
+        log.debug("Get port{} target power...", port);
+        return acquirePortPower(port, KEY_PORTTARPWR);
+    }
+
+    private Long acquireCurrentPower(PortNumber port, T component) {
+        if (component instanceof OchSignal) {
+            return acquireChannelPower(port, (OchSignal) component);
+        }
+        log.debug("Get port{} current power...", port);
+        return acquirePortPower(port, KEY_PORTCURPWR);
+    }
+
+    private Long acquirePortPower(PortNumber port, String selection) {
+        String reply = netconfGetConfig(handler(), getPortPowerFilter(port, selection));
+        HierarchicalConfiguration info = configAt(reply, KEY_PORTS_PORT);
+        if (info == null) {
+            return null;
+        }
+        return (long) (info.getDouble(selection) * POWER_MULTIPLIER);
+    }
+
+    private Long acquireChannelAttenuation(PortNumber port, OchSignal channel) {
+        log.debug("Get port{} channel{} attenuation...", port, channel.channelSpacing());
+        String reply = netconfGetConfig(handler(), getChannelAttenuationFilter(port, channel));
+        HierarchicalConfiguration info = configAt(reply, KEY_CONNS);
+        if (info == null) {
+            return null;
+        }
+        return (long) (info.getDouble(KEY_CHATT) * POWER_MULTIPLIER);
+    }
+
+    private Long acquireChannelPower(PortNumber port, OchSignal channel) {
+        log.debug("Get port{} channel{} power...", port, channel.channelSpacing());
+        String reply = netconfGetConfig(handler(), getChannelPowerFilter(port, channel));
+        HierarchicalConfiguration info = configAt(reply, KEY_DATA_CONNS);
+        if (info == null) {
+            return null;
+        }
+        return (long) (info.getDouble(KEY_CHPWR) * POWER_MULTIPLIER);
+    }
+
+    private boolean setPortTargetPower(PortNumber port, long power) {
+        log.debug("Set port{} target power...", port);
+        String cfg = new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+                .append(xmlOpen(KEY_PORTS))
+                .append(xml(KEY_PORTID, Long.toString(port.toLong())))
+                .append(xmlOpen(KEY_PORT))
+                .append(xml(KEY_PORTTARPWR, Long.toString(power)))
+                .append(xmlClose(KEY_PORT))
+                .append(xmlClose(KEY_PORTS))
+                .append(xmlClose(KEY_OPENOPTICALDEV))
+                .toString();
+        return netconfEditConfig(handler(), CFG_MODE_MERGE, cfg);
+    }
+
+    private boolean setChannelTargetPower(PortNumber port, OchSignal channel, long power) {
+        log.debug("Set port{} channel{} attenuation.", port, channel.channelSpacing());
+        FlowRuleService service = handler().get(FlowRuleService.class);
+        Iterable<FlowEntry> entries = service.getFlowEntries(data().deviceId());
+        for (FlowEntry entry : entries) {
+            OplinkCrossConnect crossConnect  = OplinkOpticalUtility.fromFlowRule(this, entry);
+            // The channel port might be input port or output port.
+            if ((port.equals(crossConnect.getInPort()) || port.equals(crossConnect.getOutPort())) &&
+                    channel.spacingMultiplier() == crossConnect.getChannel()) {
+                log.debug("Flow is found, modify the flow with attenuation.");
+                // Modify attenuation in treatment
+                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                        .setOutput(crossConnect.getOutPort())
+                        .extension(new OplinkAttenuation((int) power), data().deviceId())
+                        .build();
+                // Apply the new flow rule
+                service.applyFlowRules(DefaultFlowRule.builder()
+                        .forDevice(data().deviceId())
+                        .makePermanent()
+                        .withSelector(entry.selector())
+                        .withTreatment(treatment)
+                        .withPriority(entry.priority())
+                        .withCookie(entry.id().value())
+                        .build());
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private Range<Long> getPowerRange(PortNumber port, String directionKey, String minKey, String maxKey) {
+        String reply = netconfGetConfig(handler(), getPowerRangeFilter(port, directionKey));
+        HierarchicalConfiguration info = configAt(reply, KEY_PORTS_PORT_PROPERTY);
+        if (info == null) {
+            return null;
+        }
+        long minPower = (long) (info.getDouble(minKey) * POWER_MULTIPLIER);
+        long maxPower = (long) (info.getDouble(maxKey) * POWER_MULTIPLIER);
+        return Range.closed(minPower, maxPower);
+    }
+
+    private Range<Long> getTxPowerRange(PortNumber port, T component) {
+        if (component instanceof Direction) {
+            log.debug("Get target port{} power range...", port);
+            return getPowerRange(port, KEY_PORTDIRECT_TX, KEY_PORTPWRCAPMINTX, KEY_PORTPWRCAPMAXTX);
+        } else {
+            log.debug("Get channel attenuation range...");
+            return Range.closed(MIN_ATTENUATION, MAX_ATTENUATION);
+        }
+    }
+
+    private Range<Long> getRxPowerRange(PortNumber port, T component) {
+        log.debug("Get input port{} power range...", port);
+        return getPowerRange(port, KEY_PORTDIRECT_RX, KEY_PORTPWRCAPMINRX, KEY_PORTPWRCAPMAXRX);
+    }
+}
diff --git a/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalUtility.java b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalUtility.java
new file mode 100644
index 0000000..9785224
--- /dev/null
+++ b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalUtility.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.oplink;
+
+import org.onlab.util.Frequency;
+import org.onosproject.core.CoreService;
+import org.onosproject.driver.extensions.OplinkAttenuation;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.GridType;
+import org.onosproject.net.Lambda;
+import org.onosproject.net.OchSignalType;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.driver.HandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.OchSignalCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Oplink optical utilities.
+ */
+public final class OplinkOpticalUtility {
+
+    // Lambda information supported by device.
+    public static final GridType GRID_TYPE = GridType.DWDM;
+    public static final int SLOT_GRANULARITY = 4;
+    // Channel spacing is 50GHz, 12.5GHz * 4
+    public static final ChannelSpacing CHANNEL_SPACING = ChannelSpacing.CHL_50GHZ;
+    // Start frequency supported by device.
+    public static final Frequency START_CENTER_FREQ = Frequency.ofGHz(191_350);
+    // Stop frequency supported by device.
+    public static final Frequency STOP_CENTER_FREQ = Frequency.ofGHz(196_100);
+
+    // Power multiply factor, the power accuracy supported by device is 0.01dBm.
+    // Transforms the double typed number to long typed power for ONOS.
+    public static final int POWER_MULTIPLIER = 100;
+    // Attenuation range supported by device, [0, 25dB].
+    public static final long MIN_ATTENUATION = 0L;
+    public static final long MAX_ATTENUATION = 2500L;
+
+    // Default attenuation value if the attenuation instruction is not found.
+    private static final int DEFAULT_ATT = 0;
+    // Default flow priority for an extra flow.
+    private static final int DEFAULT_PRIORITY = 88;
+    // Default application name.
+    private static final String DEFAULT_APP = "org.onosproject.drivers.oplink";
+
+    private OplinkOpticalUtility() {
+    }
+
+    /**
+     * Transforms a flow FlowRule object to an OplinkCrossConnect object.
+     * @param behaviour the parent driver handler
+     * @param rule FlowRule object
+     * @return cross connect object
+     */
+    public static OplinkCrossConnect fromFlowRule(HandlerBehaviour behaviour, FlowRule rule) {
+        // TrafficSelector
+        Set<Criterion> criterions = rule.selector().criteria();
+        int channel = criterions.stream()
+                .filter(c -> c instanceof OchSignalCriterion)
+                .map(c -> ((OchSignalCriterion) c).lambda().spacingMultiplier())
+                .findAny()
+                .orElse(null);
+        PortNumber inPort = criterions.stream()
+                .filter(c -> c instanceof PortCriterion)
+                .map(c -> ((PortCriterion) c).port())
+                .findAny()
+                .orElse(null);
+        // TrafficTreatment
+        List<Instruction> instructions = rule.treatment().immediate();
+        PortNumber outPort = instructions.stream()
+                .filter(c -> c instanceof Instructions.OutputInstruction)
+                .map(c -> ((Instructions.OutputInstruction) c).port())
+                .findAny()
+                .orElse(null);
+        int attenuation = instructions.stream()
+                .filter(c -> c instanceof Instructions.ExtensionInstructionWrapper)
+                .map(c -> ((Instructions.ExtensionInstructionWrapper) c).extensionInstruction())
+                .filter(c -> c instanceof OplinkAttenuation)
+                .map(c -> ((OplinkAttenuation) c).getAttenuation())
+                .findAny()
+                .orElse(DEFAULT_ATT);
+        return new OplinkCrossConnect(inPort, outPort, channel, attenuation);
+    }
+
+    /**
+     * Finds the FlowRule from flow rule store by the given cross connect information.
+     * Returns an extra flow to remove the flow by ONOS if not found.
+     * @param behaviour the parent driver handler
+     * @param cfg cross connect information
+     * @return the flow rule
+     */
+    public static FlowRule toFlowRule(HandlerBehaviour behaviour, OplinkCrossConnect cfg) {
+        return toFlowRule(behaviour, cfg.getInPort(), cfg.getOutPort(), cfg.getChannel());
+    }
+
+    /**
+     * Finds the FlowRule from flow rule store by the given ports and channel.
+     * Returns an extra flow to remove the flow by ONOS if not found.
+     * @param behaviour the parent driver handler
+     * @param inPort the input port
+     * @param outPort the output port
+     * @param channel the specified channel
+     * @return the flow rule
+     */
+    public static FlowRule toFlowRule(HandlerBehaviour behaviour, PortNumber inPort,
+                                      PortNumber outPort, Integer channel) {
+        FlowRuleService service = behaviour.handler().get(FlowRuleService.class);
+        Iterable<FlowEntry> entries = service.getFlowEntries(behaviour.data().deviceId());
+        // Try to Find the flow from flow rule store.
+        for (FlowEntry entry : entries) {
+            Set<Criterion> criterions = entry.selector().criteria();
+            // input port
+            PortNumber ip = criterions.stream()
+                    .filter(c -> c instanceof PortCriterion)
+                    .map(c -> ((PortCriterion) c).port())
+                    .findAny()
+                    .orElse(null);
+            // channel
+            Integer ch = criterions.stream()
+                    .filter(c -> c instanceof OchSignalCriterion)
+                    .map(c -> ((OchSignalCriterion) c).lambda().spacingMultiplier())
+                    .findAny()
+                    .orElse(null);
+            // output port
+            PortNumber op = entry.treatment().immediate().stream()
+                    .filter(c -> c instanceof Instructions.OutputInstruction)
+                    .map(c -> ((Instructions.OutputInstruction) c).port())
+                    .findAny()
+                    .orElse(null);
+            if (inPort.equals(ip) && channel.equals(ch) && outPort.equals(op)) {
+                // Find the flow.
+                return entry;
+            }
+        }
+        // Cannot find the flow from store. So report an extra flow to remove the flow by ONOS.
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPort(inPort)
+                .add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID))
+                .add(Criteria.matchLambda(Lambda.ochSignal(GRID_TYPE, CHANNEL_SPACING, channel, SLOT_GRANULARITY)))
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(outPort)
+                .build();
+        return DefaultFlowRule.builder()
+                .forDevice(behaviour.data().deviceId())
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(DEFAULT_PRIORITY)
+                .fromApp(behaviour.handler().get(CoreService.class).getAppId(DEFAULT_APP))
+                .build();
+
+    }
+}
diff --git a/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/package-info.java b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/package-info.java
new file mode 100755
index 0000000..b22120a
--- /dev/null
+++ b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Package for oplink device drivers.
+ */
+package org.onosproject.drivers.oplink;
diff --git a/drivers/oplink/src/main/resources/oplink-drivers.xml b/drivers/oplink/src/main/resources/oplink-drivers.xml
new file mode 100755
index 0000000..5baf905
--- /dev/null
+++ b/drivers/oplink/src/main/resources/oplink-drivers.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2016 Open Networking Laboratory
+  ~
+  ~ 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.
+  -->
+<drivers>
+    <driver name="oplink-netconf" manufacturer="Oplink a Molex company" hwVersion="x86-1.0" swVersion="sdn-1.0">
+        <behaviour api="org.onosproject.net.optical.OpticalDevice"
+                   impl="org.onosproject.net.optical.DefaultOpticalDevice"/>
+        <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
+                   impl="org.onosproject.drivers.oplink.OplinkOpticalDeviceDescription"/>
+        <behaviour api="org.onosproject.net.behaviour.PowerConfig"
+                   impl="org.onosproject.drivers.oplink.OplinkOpticalPowerConfig"/>
+        <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
+                   impl="org.onosproject.drivers.oplink.OplinkOpticalFlowRuleProgrammable"/>
+        <behaviour api="org.onosproject.net.behaviour.LambdaQuery"
+                   impl="org.onosproject.drivers.oplink.OplinkOpticalLambdaQuery"/>
+    </driver>
+</drivers>
diff --git a/drivers/pom.xml b/drivers/pom.xml
index 72f90d1..dadca4c 100644
--- a/drivers/pom.xml
+++ b/drivers/pom.xml
@@ -49,6 +49,7 @@
         <module>lisp</module>
         <module>flowspec</module>
         <module>microsemi</module>
+        <module>oplink</module>
     </modules>
 
     <!--<properties>
diff --git a/modules.defs b/modules.defs
index 90c2178..71f70c0 100644
--- a/modules.defs
+++ b/modules.defs
@@ -98,6 +98,7 @@
     '//drivers/huawei:onos-drivers-huawei-oar',
 #   Removed until we can handle v1.12 of onos-yang-tools - in the mean time build manually with Maven
 #   '//drivers/microsemi/ea1000driver:onos-drivers-microsemi-ea1000driver-oar',
+    '//drivers/oplink:onos-drivers-oplink-oar',
 ]
 
 ONOS_PROVIDERS = [