Improve oplink roadm handshaker and PowerConfig driver

Change-Id: I326e3a6b405669d67ec08b0d23f3f164023efab6
diff --git a/drivers/optical/src/main/java/org/onosproject/driver/optical/handshaker/OplinkRoadm.java b/drivers/optical/src/main/java/org/onosproject/driver/optical/handshaker/OplinkRoadm.java
deleted file mode 100644
index 273469b..0000000
--- a/drivers/optical/src/main/java/org/onosproject/driver/optical/handshaker/OplinkRoadm.java
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
- * Copyright 2016-present 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.driver.optical.handshaker;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.onosproject.drivers.optical.OpticalAdjacencyLinkService;
-import org.onosproject.net.Annotations;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultAnnotations;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
-import org.onosproject.net.Port;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DefaultPortDescription;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.device.PortDescription;
-import org.onosproject.net.link.DefaultLinkDescription;
-import org.onosproject.net.link.LinkService;
-import org.onosproject.net.optical.OpticalAnnotations;
-import org.onosproject.openflow.controller.Dpid;
-import org.onosproject.openflow.controller.OpenFlowOpticalSwitch;
-import org.onosproject.openflow.controller.PortDescPropertyType;
-import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
-import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted;
-import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeCompleted;
-import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted;
-import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
-import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply;
-import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest;
-import org.projectfloodlight.openflow.protocol.OFExpExtAdId;
-import org.projectfloodlight.openflow.protocol.OFExpPortAdidOtn;
-import org.projectfloodlight.openflow.protocol.OFExpPortAdjacency;
-import org.projectfloodlight.openflow.protocol.OFExpPortAdjacencyId;
-import org.projectfloodlight.openflow.protocol.OFExpPortAdjacencyReply;
-import org.projectfloodlight.openflow.protocol.OFExpPortAdjacencyRequest;
-import org.projectfloodlight.openflow.protocol.OFMessage;
-import org.projectfloodlight.openflow.protocol.OFObject;
-import org.projectfloodlight.openflow.protocol.OFOplinkPortPower;
-import org.projectfloodlight.openflow.protocol.OFPortDesc;
-import org.projectfloodlight.openflow.protocol.OFPortOptical;
-import org.projectfloodlight.openflow.protocol.OFStatsReply;
-import org.projectfloodlight.openflow.protocol.OFStatsRequest;
-import org.projectfloodlight.openflow.protocol.OFStatsType;
-import org.projectfloodlight.openflow.protocol.OFType;
-import org.projectfloodlight.openflow.protocol.OFOplinkPortPowerRequest;
-import org.projectfloodlight.openflow.protocol.OFOplinkPortPowerReply;
-
-
-/**
- * Driver for Oplink single WSS 8D ROADM.
- *
- * Driver implements custom handshaker and supports for Optical channel Port based on OpenFlow OTN extension.
- * The device consists of Och ports, and performances wavelength cross-connect among the ports.
- */
-public class OplinkRoadm extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch {
-
-    private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false);
-    private List<OFPortOptical> opticalPorts;
-
-    @Override
-    public List<? extends OFObject> getPortsOf(PortDescPropertyType type) {
-        return ImmutableList.copyOf(opticalPorts);
-    }
-
-    @Override
-    /**
-     * Returns a list of standard (Ethernet) ports.
-     *
-     * @return List of ports
-     */
-    public List<OFPortDesc> getPorts() {
-        return Collections.EMPTY_LIST;
-    }
-
-    @Override
-    public Set<PortDescPropertyType> getPortTypes() {
-        return ImmutableSet.of(PortDescPropertyType.OPTICAL_TRANSPORT);
-    }
-
-    @Override
-    public Boolean supportNxRole() {
-        return false;
-    }
-
-    @Override
-    public void startDriverHandshake() {
-        log.warn("Starting driver handshake for sw {}", getStringId());
-        if (startDriverHandshakeCalled) {
-            throw new SwitchDriverSubHandshakeAlreadyStarted();
-        }
-        startDriverHandshakeCalled = true;
-        try {
-            sendHandshakeOFExperimenterPortDescRequest();
-        } catch (IOException e) {
-            log.error("OPLK ROADM exception while sending experimenter port desc:", e);
-        }
-    }
-
-    @Override
-    public boolean isDriverHandshakeComplete() {
-        return driverHandshakeComplete.get();
-    }
-
-    @Override
-    public void processDriverHandshakeMessage(OFMessage m) {
-
-        if (!startDriverHandshakeCalled) {
-            throw new SwitchDriverSubHandshakeNotStarted();
-        }
-
-        if (driverHandshakeComplete.get()) {
-            throw new SwitchDriverSubHandshakeCompleted(m);
-        }
-
-        switch (m.getType()) {
-            case BARRIER_REPLY:
-                log.debug("OPLK ROADM Received barrier response");
-                break;
-            case ERROR:
-                log.error("Switch {} Error {}", getStringId(), m);
-                break;
-            case FEATURES_REPLY:
-                break;
-            case FLOW_REMOVED:
-                break;
-            case GET_ASYNC_REPLY:
-                break;
-            case PACKET_IN:
-                break;
-            case PORT_STATUS:
-                processOFPortStatus((OFCircuitPortStatus) m);
-                break;
-            case QUEUE_GET_CONFIG_REPLY:
-                break;
-            case ROLE_REPLY:
-                break;
-            case STATS_REPLY:
-                OFStatsReply stats = (OFStatsReply) m;
-                if (stats.getStatsType() == OFStatsType.EXPERIMENTER) {
-                    log.warn("OPLK ROADM : Received multipart (port desc) reply message {}", m);
-                    //OTN Optical extension 1.0 port-desc
-                    createOpticalPortList((OFCircuitPortsReply) m);
-                    driverHandshakeComplete.set(true);
-                }
-                break;
-            default:
-                log.warn("Received message {} during switch-driver " +
-                                 "subhandshake " + "from switch {} ... " +
-                                 "Ignoring message", m,
-                         getStringId());
-
-        }
-    }
-
-    private void processOFPortStatus(OFCircuitPortStatus ps) {
-        log.debug("OPLK ROADM ..OF Port Status :", ps);
-    }
-
-    @Override
-    public Device.Type deviceType() {
-        return Device.Type.ROADM;
-    }
-
-    @Override
-    public final void sendMsg(OFMessage m) {
-        List<OFMessage> messages = new ArrayList<>();
-        messages.add(m);
-
-        if (m.getType() == OFType.STATS_REQUEST) {
-            OFStatsRequest sr = (OFStatsRequest) m;
-            log.debug("OPLK ROADM rebuilding stats request type {}", sr.getStatsType());
-            switch (sr.getStatsType()) {
-                case PORT:
-                    //add Oplink experiment stats message to get the port's current power
-                    OFOplinkPortPowerRequest powerRequest = this.factory().buildOplinkPortPowerRequest()
-                            .setXid(sr.getXid())
-                            .setFlags(sr.getFlags())
-                            .build();
-                    messages.add(powerRequest);
-                    // add experiment message to get adjacent ports
-                    OFExpPortAdjacencyRequest adjacencyRequest = this.factory().buildExpPortAdjacencyRequest()
-                            .setXid(sr.getXid())
-                            .setFlags(sr.getFlags())
-                            .build();
-                    messages.add(adjacencyRequest);
-                    break;
-                default:
-                    break;
-            }
-        } else {
-            log.debug("OPLK ROADM sends msg:{}, as is", m.getType());
-        }
-
-        for (OFMessage message : messages) {
-            super.sendMsg(message);
-        }
-    }
-
-    private void sendHandshakeOFExperimenterPortDescRequest() throws IOException {
-        // send multi part message for port description for optical switches
-        OFCircuitPortsRequest circuitPortsRequest = factory()
-                .buildCircuitPortsRequest().setXid(getNextTransactionId())
-                .build();
-        log.info("OPLK ROADM : Sending experimented circuit port stats " +
-                 "message " +
-                 "{}",
-                 circuitPortsRequest);
-        this.sendHandshakeMessage(circuitPortsRequest);
-    }
-
-    /**
-     * Builds list of OFPortOptical ports based on the multi-part circuit ports reply.
-     * Ensure the optical transport port's signal type is configured correctly.
-     *
-     * @param wPorts OF reply with circuit ports
-     */
-    private void createOpticalPortList(OFCircuitPortsReply wPorts) {
-        opticalPorts = new ArrayList<>();
-        opticalPorts.addAll(wPorts.getEntries());
-    }
-
-    @Override
-    public List<PortDescription> processExpPortStats(OFMessage msg) {
-        if (msg instanceof OFOplinkPortPowerReply) {
-            return buildPortPowerDescriptions(((OFOplinkPortPowerReply) msg).getEntries());
-        } else if (msg instanceof OFExpPortAdjacencyReply) {
-            return buildPortAdjacencyDescriptions(((OFExpPortAdjacencyReply) msg).getEntries());
-        }
-        return Collections.emptyList();
-    }
-
-    private List<PortDescription> buildPortPowerDescriptions(List<OFOplinkPortPower> portPowers) {
-        DeviceService deviceService = this.handler().get(DeviceService.class);
-        List<Port> ports = deviceService.getPorts(this.data().deviceId());
-        HashMap<Long, OFOplinkPortPower> powerMap = new HashMap<>(portPowers.size());
-        portPowers.forEach(power -> powerMap.put((long) power.getPort(), power));
-        final List<PortDescription> portDescs = new ArrayList<>();
-        for (Port port : ports) {
-            DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
-            builder.putAll(port.annotations());
-            OFOplinkPortPower power = powerMap.get(port.number().toLong());
-            if (power != null) {
-                builder.set(OpticalAnnotations.CURRENT_POWER, Long.toString(power.getPowerValue()));
-            }
-            portDescs.add(new DefaultPortDescription(port.number(), port.isEnabled(),
-                    port.type(), port.portSpeed(), builder.build()));
-        }
-        return portDescs;
-    }
-
-    private OplinkPortAdjacency getNeighbor(OFExpPortAdjacency ad) {
-        for (OFExpPortAdjacencyId adid : ad.getProperties()) {
-            List<OFExpExtAdId> otns = adid.getAdId();
-            if (otns != null && !otns.isEmpty()) {
-                OFExpPortAdidOtn otn = (OFExpPortAdidOtn) otns.get(0);
-                // ITU-T G.7714 ETH MAC Format (in second 16 bytes of the following)
-                // |---------------------------------------------------------------------------|
-                // | Other format (16 bytes)                                                   |
-                // |---------------------------------------------------------------------------|
-                // | Header (2 bytes) | ID (4 bits) | MAC (6 bytes) | Port (4 bytes) | Unused  |
-                // |---------------------------------------------------------------------------|
-                ChannelBuffer buffer = ChannelBuffers.buffer(32);
-                otn.getOpspec().write32Bytes(buffer);
-                long mac = buffer.getLong(18) << 4 >>> 16;
-                int port = (int) (buffer.getLong(24) << 4 >>> 32);
-                // Oplink does not use the 4 most significant bytes of Dpid so Dpid can be
-                // constructed from MAC address
-                return new OplinkPortAdjacency(DeviceId.deviceId(Dpid.uri(new Dpid(mac))),
-                        PortNumber.portNumber(port));
-            }
-        }
-        return null;
-    }
-
-    private List<PortDescription> buildPortAdjacencyDescriptions(List<OFExpPortAdjacency> portAds) {
-        DeviceService deviceService = this.handler().get(DeviceService.class);
-        List<Port> ports = deviceService.getPorts(this.data().deviceId());
-
-        // Map port's number with port's adjacency
-        HashMap<Long, OFExpPortAdjacency> adMap = new HashMap<>(portAds.size());
-        portAds.forEach(ad -> adMap.put((long) ad.getPortNo().getPortNumber(), ad));
-
-        List<PortDescription> portDescs = new ArrayList<>();
-        for (Port port : ports) {
-            DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
-            Annotations oldAnnotations = port.annotations();
-            builder.putAll(oldAnnotations);
-            OFExpPortAdjacency ad = adMap.get(port.number().toLong());
-            if (ad != null) {
-                // neighbor discovered, add to port descriptions
-                OplinkPortAdjacency neighbor = getNeighbor(ad);
-                String newId = neighbor.getDeviceId().toString();
-                String newPort = neighbor.getPort().toString();
-                // Check if annotation already exists
-                if (!newId.equals(oldAnnotations.value(OpticalAnnotations.NEIGHBOR_ID)) ||
-                    !newPort.equals(oldAnnotations.value(OpticalAnnotations.NEIGHBOR_PORT))) {
-                    builder.set(OpticalAnnotations.NEIGHBOR_ID, newId);
-                    builder.set(OpticalAnnotations.NEIGHBOR_PORT, newPort);
-                }
-                addLink(port.number(), neighbor);
-            } else {
-                // no neighbors found
-                builder.remove(OpticalAnnotations.NEIGHBOR_ID);
-                builder.remove(OpticalAnnotations.NEIGHBOR_PORT);
-                removeLink(port.number());
-            }
-            portDescs.add(new DefaultPortDescription(port.number(), port.isEnabled(),
-                    port.type(), port.portSpeed(), builder.build()));
-        }
-        return portDescs;
-    }
-
-    private void addLink(PortNumber portNumber, OplinkPortAdjacency neighbor) {
-        ConnectPoint dst = new ConnectPoint(handler().data().deviceId(), portNumber);
-        ConnectPoint src = new ConnectPoint(neighbor.getDeviceId(), neighbor.getPort());
-        OpticalAdjacencyLinkService adService =
-                this.handler().get(OpticalAdjacencyLinkService.class);
-        adService.linkDetected(new DefaultLinkDescription(src, dst, Link.Type.OPTICAL));
-    }
-
-    // Remove incoming link with port if there are any.
-    private void removeLink(PortNumber portNumber) {
-        ConnectPoint dst = new ConnectPoint(handler().data().deviceId(), portNumber);
-        // Check so only incoming links are removed
-        Set<Link> links = this.handler().get(LinkService.class).getIngressLinks(dst);
-        if (!links.isEmpty()) {
-            OpticalAdjacencyLinkService adService =
-                    this.handler().get(OpticalAdjacencyLinkService.class);
-            adService.linksVanished(dst);
-        }
-    }
-
-    private class OplinkPortAdjacency {
-        private DeviceId deviceId;
-        private PortNumber portNumber;
-
-        public OplinkPortAdjacency(DeviceId deviceId, PortNumber portNumber) {
-            this.deviceId = deviceId;
-            this.portNumber = portNumber;
-        }
-
-        public DeviceId getDeviceId() {
-            return deviceId;
-        }
-
-        public PortNumber getPort() {
-            return portNumber;
-        }
-    }
-}
diff --git a/drivers/optical/src/main/java/org/onosproject/driver/optical/handshaker/OplinkRoadmHandshaker.java b/drivers/optical/src/main/java/org/onosproject/driver/optical/handshaker/OplinkRoadmHandshaker.java
new file mode 100644
index 0000000..d805f97
--- /dev/null
+++ b/drivers/optical/src/main/java/org/onosproject/driver/optical/handshaker/OplinkRoadmHandshaker.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2016-present 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.driver.optical.handshaker;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.onosproject.net.Device;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.openflow.controller.OpenFlowOpticalSwitch;
+import org.onosproject.openflow.controller.PortDescPropertyType;
+import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
+import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted;
+import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeCompleted;
+import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted;
+import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
+import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply;
+import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest;
+import org.projectfloodlight.openflow.protocol.OFExpPortAdjacencyReply;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFObject;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortOptical;
+import org.projectfloodlight.openflow.protocol.OFStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsRequest;
+import org.projectfloodlight.openflow.protocol.OFStatsType;
+import org.projectfloodlight.openflow.protocol.OFType;
+import org.projectfloodlight.openflow.protocol.OFOplinkPortPowerReply;
+
+import static org.onosproject.net.Device.Type;
+
+/**
+ * Driver for Oplink single WSS 8D ROADM.
+ *
+ * Driver implements custom handshaker and supports optical channel Port based on OpenFlow OTN extension.
+ * The device consists of Och ports, and performances wavelength cross-connect among the ports.
+ */
+public class OplinkRoadmHandshaker extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch {
+
+    private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false);
+    private List<OFPortOptical> opticalPorts = new ArrayList<>();
+    private OplinkHandshakerUtil oplinkUtil = new OplinkHandshakerUtil(this);
+
+    @Override
+    public List<? extends OFObject> getPortsOf(PortDescPropertyType type) {
+        // Expected type is OPTICAL_TRANSPORT
+        if (type == PortDescPropertyType.OPTICAL_TRANSPORT) {
+            return ImmutableList.copyOf(opticalPorts);
+        }
+        // Any other type, return empty
+        log.warn("Unexpected port description property type: {}", type);
+        return ImmutableList.of();
+    }
+
+    /**
+     * Returns a list of standard (Ethernet) ports.
+     *
+     * @return List of ports
+     */
+    @Override
+    public List<OFPortDesc> getPorts() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public Set<PortDescPropertyType> getPortTypes() {
+        return ImmutableSet.of(PortDescPropertyType.OPTICAL_TRANSPORT);
+    }
+
+    @Override
+    public Boolean supportNxRole() {
+        return false;
+    }
+
+    @Override
+    public void startDriverHandshake() {
+        log.info("Starting driver handshake for sw {}", getStringId());
+        if (startDriverHandshakeCalled) {
+            throw new SwitchDriverSubHandshakeAlreadyStarted();
+        }
+        startDriverHandshakeCalled = true;
+        try {
+            sendHandshakeOFExperimenterPortDescRequest();
+        } catch (IOException e) {
+            log.error("OPLK ROADM exception while sending experimenter port desc:", e);
+        }
+    }
+
+    @Override
+    public boolean isDriverHandshakeComplete() {
+        return driverHandshakeComplete.get();
+    }
+
+    @Override
+    public void processDriverHandshakeMessage(OFMessage m) {
+
+        if (!startDriverHandshakeCalled) {
+            throw new SwitchDriverSubHandshakeNotStarted();
+        }
+
+        if (driverHandshakeComplete.get()) {
+            throw new SwitchDriverSubHandshakeCompleted(m);
+        }
+
+        switch (m.getType()) {
+            case BARRIER_REPLY:
+                log.debug("OPLK ROADM Received barrier response");
+                break;
+            case ERROR:
+                log.error("Switch {} Error {}", getStringId(), m);
+                break;
+            case FEATURES_REPLY:
+                break;
+            case FLOW_REMOVED:
+                break;
+            case GET_ASYNC_REPLY:
+                break;
+            case PACKET_IN:
+                break;
+            case PORT_STATUS:
+                processOFPortStatus((OFCircuitPortStatus) m);
+                break;
+            case QUEUE_GET_CONFIG_REPLY:
+                break;
+            case ROLE_REPLY:
+                break;
+            case STATS_REPLY:
+                OFStatsReply stats = (OFStatsReply) m;
+                if (stats.getStatsType() == OFStatsType.EXPERIMENTER) {
+                    log.debug("OPLK ROADM : Received multipart (port desc) reply message {}", m);
+                    //OTN Optical extension 1.0 port-desc
+                    createOpticalPortList((OFCircuitPortsReply) m);
+                    driverHandshakeComplete.set(true);
+                }
+                break;
+            default:
+                log.warn("Received message {} during switch-driver " +
+                        "subhandshake from switch {} ... " +
+                        "Ignoring message", m, getStringId());
+
+        }
+    }
+
+    @Override
+    public Device.Type deviceType() {
+        return Type.ROADM;
+    }
+
+    @Override
+    public final void sendMsg(OFMessage m) {
+        List<OFMessage> messages = new ArrayList<>();
+        messages.add(m);
+
+        if (m.getType() == OFType.STATS_REQUEST) {
+            OFStatsRequest sr = (OFStatsRequest) m;
+            log.debug("OPLK ROADM rebuilding stats request type {}", sr.getStatsType());
+            switch (sr.getStatsType()) {
+                case PORT:
+                    // add Oplink experiment message to get the port's current power
+                    messages.add(oplinkUtil.buildPortPowerRequest());
+                    // add experiment message to get adjacent ports
+                    messages.add(oplinkUtil.buildPortAdjacencyRequest());
+                    break;
+                default:
+                    break;
+            }
+        } else {
+            log.debug("OPLK ROADM sends msg:{}, as is", m.getType());
+        }
+
+        super.sendMsg(messages);
+    }
+
+    @Override
+    public List<PortDescription> processExpPortStats(OFMessage msg) {
+        if (msg instanceof OFOplinkPortPowerReply) {
+            return oplinkUtil.buildPortPowerDescriptions(((OFOplinkPortPowerReply) msg).getEntries());
+        } else if (msg instanceof OFExpPortAdjacencyReply) {
+            return oplinkUtil.buildPortAdjacencyDescriptions(((OFExpPortAdjacencyReply) msg).getEntries());
+        }
+        return Collections.emptyList();
+    }
+
+    private void processOFPortStatus(OFCircuitPortStatus ps) {
+        log.debug("OPLK ROADM ..OF Port Status :", ps);
+    }
+
+    private void sendHandshakeOFExperimenterPortDescRequest() throws IOException {
+        // Send multipart message for port description for optical switches
+        OFCircuitPortsRequest circuitPortsRequest = oplinkUtil.buildCircuitPortsRequest();
+        log.debug("OPLK ROADM : Sending experimented port description message {}", circuitPortsRequest);
+        sendHandshakeMessage(circuitPortsRequest);
+    }
+
+    /**
+     * Builds list of OFPortOptical ports based on the multi-part circuit ports reply.
+     * Ensure the optical transport port's signal type is configured correctly.
+     *
+     * @param wPorts OF reply with circuit ports
+     */
+    private void createOpticalPortList(OFCircuitPortsReply wPorts) {
+        opticalPorts.addAll(wPorts.getEntries());
+    }
+
+}
diff --git a/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkEdfaPowerConfig.java b/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkEdfaPowerConfig.java
index 554df14..4538258 100644
--- a/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkEdfaPowerConfig.java
+++ b/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkEdfaPowerConfig.java
@@ -16,21 +16,12 @@
 
 package org.onosproject.driver.optical.power;
 
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.Optional;
 
 import com.google.common.collect.Range;
 import org.onosproject.net.driver.AbstractHandlerBehaviour;
-import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.behaviour.PowerConfig;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.optical.OpticalAnnotations;
-import org.onosproject.openflow.controller.Dpid;
-import org.onosproject.openflow.controller.OpenFlowController;
-import org.onosproject.openflow.controller.OpenFlowSwitch;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Port Power (Gain and attenuation) implementation for Oplink EDFA device.
@@ -43,148 +34,31 @@
 public class OplinkEdfaPowerConfig extends AbstractHandlerBehaviour
                                     implements PowerConfig<Object> {
 
-    /**
-     * Input and ouput port number
-     * Note:
-     * These port number configurations are just in use for a short time.
-     * In the future, the port number and direction type would be obtained from physical device.
-     */
-    private static final int LINE_IN_WEST = 1;
-    private static final int LINE_OUT_WEST = 2;
-    private static final int LINE_IN_EAST = 3;
-    private static final int LINE_OUT_EAST = 4;
-
-    /**
-     * Power threshold of each port, magnified 100 times
-     * Note:
-     * These threshold configurations are just in use for a short time.
-     * In the future, the power threshold would be obtained from physical device.
-     */
-    private static final long POWER_IN_WEST_LOW_THRES = -1900L;
-    private static final long POWER_IN_WEST_HIGH_THRES = 0L;
-    private static final long POWER_IN_EAST_LOW_THRES = -3100L;
-    private static final long POWER_IN_EAST_HIGH_THRES = 700L;
-    private static final long POWER_OUT_LOW_THRES = 0L;
-    private static final long POWER_OUT_HIGH_THRES = 1900L;
-
-    // Transaction id to use.
-    private final AtomicInteger xidCounter = new AtomicInteger(0);
-    // Log
-    protected final Logger log = LoggerFactory.getLogger(getClass());
+    // oplink power config utility
+    private OplinkPowerConfigUtil oplinkUtil = new OplinkPowerConfigUtil(this);
 
     @Override
-    public Optional<Long> getTargetPower(PortNumber portNum, Object component) {
-        Long returnVal = getTargetPortPower(portNum);
-        return Optional.ofNullable(returnVal);
+    public Optional<Long> getTargetPower(PortNumber port, Object component) {
+        return Optional.ofNullable(oplinkUtil.getTargetPower(port, component));
     }
 
     @Override
-    public Optional<Long> currentPower(PortNumber portNum, Object component) {
-        Long returnVal = getCurrentPortPower(portNum);
-        return Optional.ofNullable(returnVal);
+    public Optional<Long> currentPower(PortNumber port, Object component) {
+        return Optional.ofNullable(oplinkUtil.getCurrentPower(port, component));
     }
 
     @Override
-    public void setTargetPower(PortNumber portNum, Object component, long power) {
-        setTargetPortPower(portNum, power);
+    public void setTargetPower(PortNumber port, Object component, long power) {
+        oplinkUtil.setTargetPower(port, component, power);
     }
 
     @Override
     public Optional<Range<Long>> getTargetPowerRange(PortNumber port, Object component) {
-        Range<Long> range = getTargetPortPowerRange(port);
-        return Optional.ofNullable(range);
+        return Optional.ofNullable(oplinkUtil.getTargetPowerRange(port, component));
     }
 
     @Override
     public Optional<Range<Long>> getInputPowerRange(PortNumber port, Object component) {
-        Range<Long> range = getInputPortPowerRange(port);
-        return Optional.ofNullable(range);
-    }
-
-    private OpenFlowSwitch getOpenFlowDevice() {
-        final OpenFlowController controller = handler().get(OpenFlowController.class);
-        final Dpid dpid = Dpid.dpid(data().deviceId().uri());
-        OpenFlowSwitch sw = controller.getSwitch(dpid);
-        if (sw == null || !sw.isConnected()) {
-            log.warn("OpenFlow handshaker driver not found or device is not connected");
-            return null;
-        }
-        return sw;
-    }
-
-    private Long getPowerFromPort(PortNumber portNum, String annotation) {
-        // Check if switch is connected, otherwise do not return value in store, which is obsolete.
-        if (getOpenFlowDevice() == null) {
-            // Warning already exists in method getOpenFlowDevice()
-            return null;
-        }
-        DeviceService deviceService = handler().get(DeviceService.class);
-        Port port = deviceService.getPort(data().deviceId(), portNum);
-        if (port == null) {
-            log.warn("Unexpected port: {}", portNum);
-            return null;
-        }
-        String power = port.annotations().value(annotation);
-        if (power == null) {
-            log.warn("Cannot get {} from port {}.", annotation, portNum);
-            return null;
-        }
-        return Long.valueOf(power);
-    }
-
-    private Long getTargetPortPower(PortNumber portNum) {
-        return getPowerFromPort(portNum, OpticalAnnotations.TARGET_POWER);
-    }
-
-    private Long getCurrentPortPower(PortNumber portNum) {
-        return getPowerFromPort(portNum, OpticalAnnotations.CURRENT_POWER);
-    }
-
-    private void setTargetPortPower(PortNumber portNum, long power) {
-        OpenFlowSwitch device = getOpenFlowDevice();
-        // Check if switch is connected, otherwise do not return value in store, which is obsolete.
-        if (device == null) {
-            // Warning already exists in method getOpenFlowDevice()
-            return;
-        }
-        device.sendMsg(device.factory().buildOplinkPortPowerSet()
-                .setXid(xidCounter.getAndIncrement())
-                .setPort((int) portNum.toLong())
-                .setPowerValue((int) power)
-                .build());
-    }
-
-    // Returns the acceptable target range for an output Port, null otherwise
-    private Range<Long> getTargetPortPowerRange(PortNumber port) {
-        long portNum = port.toLong();
-        // FIXME
-        // Short time hard code, we will use port direction type instead in the future.
-        // And more, the power range will be also obtained from device configuration.
-        switch ((int) portNum) {
-            case LINE_OUT_EAST:
-            case LINE_OUT_WEST:
-                return Range.closed(POWER_OUT_LOW_THRES, POWER_OUT_HIGH_THRES);
-            default:
-                // Unexpected port. Do not need warning here for port polling.
-                return null;
-        }
-    }
-
-    // Returns the working input power range for an input port, null if the port
-    // is not an input port.
-    private Range<Long> getInputPortPowerRange(PortNumber port) {
-        long portNum = port.toLong();
-        // FIXME
-        // Short time hard code, we will use port direction type instead in the future.
-        // And more, the power range will be also obtained from device configuration.
-        switch ((int) portNum) {
-            case LINE_IN_EAST:
-                return Range.closed(POWER_IN_EAST_LOW_THRES, POWER_IN_EAST_HIGH_THRES);
-            case LINE_IN_WEST:
-                return Range.closed(POWER_IN_WEST_LOW_THRES, POWER_IN_WEST_HIGH_THRES);
-            default:
-                // Unexpected port. Do not need warning here for port polling.
-                return null;
-        }
+        return Optional.ofNullable(oplinkUtil.getInputPowerRange(port, component));
     }
 }
diff --git a/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkPowerConfigUtil.java b/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkPowerConfigUtil.java
new file mode 100644
index 0000000..a24df09
--- /dev/null
+++ b/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkPowerConfigUtil.java
@@ -0,0 +1,560 @@
+/*
+ * Copyright 2016-present 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.driver.optical.power;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.List;
+
+import com.google.common.collect.Range;
+
+import org.onosproject.driver.extensions.OplinkAttenuation;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.Direction;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.HandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowRule;
+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.Criterion;
+import org.onosproject.net.flow.criteria.OchSignalCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.optical.OpticalAnnotations;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowController;
+import org.onosproject.openflow.controller.OpenFlowOpticalSwitch;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.PortDescPropertyType;
+import org.projectfloodlight.openflow.protocol.OFObject;
+import org.projectfloodlight.openflow.protocol.OFPortOptical;
+
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.net.Device.Type;
+
+/**
+ * Oplink power config utility.
+ */
+public class OplinkPowerConfigUtil {
+
+    // Parent driver handler behaviour
+    private HandlerBehaviour behaviour;
+    // Transaction id to use.
+    private final AtomicInteger xidCounter = new AtomicInteger(0);
+    // Log
+    private final Logger log = getLogger(getClass());
+
+    // Component type
+    private enum ComponentType {
+        NONE,
+        PORT,
+        CHANNEL
+    }
+
+    // Port properties for oplink devices, currently supports EDFA and ROADM.
+    // This type is mapped to OFPortDescPropOpticalTransport#getPortType() value.
+    private enum PortDescType {
+        NONE,
+        PA_LINE_IN,
+        PA_LINE_OUT,
+        BA_LINE_IN,
+        BA_LINE_OUT,
+        EXP_IN,
+        EXP_OUT,
+        AUX_IN,
+        AUX_OUT,
+    }
+
+    /**
+     * Power threshold of each port, in 0.01 dB
+     * Note:
+     * These threshold configurations are just in use for a short time.
+     * In the future, the power threshold would be obtained from physical device.
+     */
+    // EDFA
+    private static final long EDFA_POWER_IN_WEST_LOW_THRES = -1900L;
+    private static final long EDFA_POWER_IN_WEST_HIGH_THRES = 0L;
+    private static final long EDFA_POWER_IN_EAST_LOW_THRES = -3100L;
+    private static final long EDFA_POWER_IN_EAST_HIGH_THRES = 700L;
+    private static final long EDFA_POWER_OUT_LOW_THRES = 0L;
+    private static final long EDFA_POWER_OUT_HIGH_THRES = 1900L;
+    // ROADM
+    private static final long ROADM_POWER_LINE_IN_LOW_THRES = -3000L;
+    private static final long ROADM_POWER_LINE_IN_HIGH_THRES = 2350L;
+    private static final long ROADM_POWER_LINE_OUT_LOW_THRES = 0L;
+    private static final long ROADM_POWER_LINE_OUT_HIGH_THRES = 2350L;
+    private static final long ROADM_POWER_OTHER_IN_LOW_THRES = -1500L;
+    private static final long ROADM_POWER_OTHER_IN_HIGH_THRES = 2000L;
+    private static final long ROADM_POWER_OTHER_OUT_LOW_THRES = -600L;
+    private static final long ROADM_POWER_OTHER_OUT_HIGH_THRES = 1500L;
+    private static final long ROADM_MIN_ATTENUATION = 0L;
+    private static final long ROADM_MAX_ATTENUATION = 2500L;
+
+    /**
+     * Create a new OplinkPowerConfigUtil.
+     * @param behaviour driver handler behaviour
+     */
+    public OplinkPowerConfigUtil(HandlerBehaviour behaviour) {
+        this.behaviour = behaviour;
+    }
+
+    /**
+     * Obtains specified port/channel target power.
+     *
+     * @param port the port number
+     * @param component the port component
+     * @return target power value in .01 dBm
+     */
+    public Long getTargetPower(PortNumber port, Object component) {
+        switch (getComponentType(component)) {
+            case PORT:
+                return getPortPower(port, OpticalAnnotations.TARGET_POWER);
+            case CHANNEL:
+                return getChannelAttenuation(port, (OchSignal) component);
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Obtains specified port/channel current power.
+     *
+     * @param port the port number
+     * @param component the port component
+     * @return current power value in .01 dBm
+     */
+    public Long getCurrentPower(PortNumber port, Object component) {
+        switch (getComponentType(component)) {
+            case PORT:
+                return getPortPower(port, OpticalAnnotations.CURRENT_POWER);
+            case CHANNEL:
+                return getCurrentChannelPower(port, (OchSignal) component);
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Sets specified port target power or channel attenuation.
+     *
+     * @param port the port number
+     * @param component the port component
+     * @param power target power in .01 dBm
+     */
+    public void setTargetPower(PortNumber port, Object component, long power) {
+        switch (getComponentType(component)) {
+            case PORT:
+                setPortPower(port, power);
+                break;
+            case CHANNEL:
+                setChannelAttenuation(port, (OchSignal) component, power);
+               break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Returns the acceptable target range for an output port/channel, null otherwise.
+     *
+     * @param port the port number
+     * @param component the port component
+     * @return power range
+     */
+    public Range<Long> getTargetPowerRange(PortNumber port, Object component) {
+        switch (getComponentType(component)) {
+            case PORT:
+                return getTargetPortPowerRange(port);
+            case CHANNEL:
+                return getChannelAttenuationRange(port);
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Returns the working input power range for an input port, null otherwise.
+     *
+     * @param port the port number
+     * @param component the port component
+     * @return power range
+     */
+    public Range<Long> getInputPowerRange(PortNumber port, Object component) {
+        switch (getComponentType(component)) {
+            case PORT:
+                return getInputPortPowerRange(port);
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Returns specified component type.
+     *
+     * @param component the port component
+     * @return component type
+     */
+    private ComponentType getComponentType(Object component) {
+        if (component == null || component instanceof Direction) {
+            return ComponentType.PORT;
+        } else if (component instanceof OchSignal) {
+            return ComponentType.CHANNEL;
+        }
+        return ComponentType.NONE;
+    }
+
+    /**
+     * Returns current switch known to this OF controller.
+     *
+     * @return current switch
+     */
+    private OpenFlowSwitch getOpenFlowDevice() {
+        final DriverHandler handler = behaviour.handler();
+        final OpenFlowController controller = handler.get(OpenFlowController.class);
+        final Dpid dpid = Dpid.dpid(handler.data().deviceId().uri());
+        OpenFlowSwitch sw = controller.getSwitch(dpid);
+        if (sw == null || !sw.isConnected()) {
+            log.warn("OpenFlow handshaker driver not found or device is not connected, dpid = {}", dpid);
+            return null;
+        }
+        return sw;
+    }
+
+    /**
+     * Find oplink port description type from optical ports.
+     *
+     * @param opsw switch
+     * @param portNum the port number
+     * @return port oplink port description type
+     */
+    private PortDescType getPortDescType(OpenFlowOpticalSwitch opsw, PortNumber portNum) {
+        for (PortDescPropertyType type : opsw.getPortTypes()) {
+            List<? extends OFObject> portsOf = opsw.getPortsOf(type);
+            for (OFObject op : portsOf) {
+                if (op instanceof OFPortOptical) {
+                    OFPortOptical opticalPort = (OFPortOptical) op;
+                    if ((long) opticalPort.getPortNo().getPortNumber() == portNum.toLong()) {
+                        return PortDescType.values()[opticalPort.getDesc().get(0).getPortType()];
+                    }
+                }
+            }
+        }
+        return PortDescType.NONE;
+    }
+
+    /**
+     * Returns the target port power range.
+     *
+     * @param portNum the port number
+     * @return power range
+     */
+    private Range<Long> getTargetPortPowerRange(PortNumber portNum) {
+        OpenFlowSwitch ofs = getOpenFlowDevice();
+        if (ofs == null) {
+            return null;
+        }
+        PortDescType portType = getPortDescType((OpenFlowOpticalSwitch) ofs, portNum);
+        Type devType = ofs.deviceType();
+        // FIXME
+        // Short time hard code.
+        // The power range will be obtained from physical device in the future.
+        switch (devType) {
+            case OPTICAL_AMPLIFIER:
+                if (portType == PortDescType.PA_LINE_OUT || portType == PortDescType.BA_LINE_OUT) {
+                    return Range.closed(EDFA_POWER_OUT_LOW_THRES, EDFA_POWER_OUT_HIGH_THRES);
+                }
+                break;
+            case ROADM:
+                if (portType == PortDescType.PA_LINE_OUT) {
+                    return Range.closed(ROADM_POWER_LINE_OUT_LOW_THRES, ROADM_POWER_LINE_OUT_HIGH_THRES);
+                } else if (portType == PortDescType.EXP_OUT || portType == PortDescType.AUX_OUT) {
+                    return Range.closed(ROADM_POWER_OTHER_OUT_LOW_THRES, ROADM_POWER_OTHER_OUT_HIGH_THRES);
+                }
+                break;
+            default:
+                log.warn("Unexpected device type: {}", devType);
+                break;
+        }
+        // Unexpected port or device type. Do not need warning here for port polling.
+        return null;
+    }
+
+    /**
+     * Returns the input port power range.
+     *
+     * @param portNum the port number
+     * @return power range
+     */
+    private Range<Long> getInputPortPowerRange(PortNumber portNum) {
+        OpenFlowSwitch ofs = getOpenFlowDevice();
+        if (ofs == null) {
+            return null;
+        }
+        PortDescType portType = getPortDescType((OpenFlowOpticalSwitch) ofs, portNum);
+        Type devType = ofs.deviceType();
+        // FIXME
+        // Short time hard code.
+        // The port type and power range will be obtained from physical device in the future.
+        switch (devType) {
+            case OPTICAL_AMPLIFIER:
+                if (portType == PortDescType.PA_LINE_IN) {
+                    return Range.closed(EDFA_POWER_IN_WEST_LOW_THRES, EDFA_POWER_IN_WEST_HIGH_THRES);
+                } else if (portType == PortDescType.BA_LINE_IN) {
+                    return Range.closed(EDFA_POWER_IN_EAST_LOW_THRES, EDFA_POWER_IN_EAST_HIGH_THRES);
+                }
+                break;
+            case ROADM:
+                if (portType == PortDescType.PA_LINE_IN) {
+                    return Range.closed(ROADM_POWER_LINE_IN_LOW_THRES, ROADM_POWER_LINE_IN_HIGH_THRES);
+                } else if (portType == PortDescType.EXP_IN || portType == PortDescType.AUX_IN) {
+                    return Range.closed(ROADM_POWER_OTHER_IN_LOW_THRES, ROADM_POWER_OTHER_IN_HIGH_THRES);
+                }
+                break;
+            default:
+                log.warn("Unexpected device type: {}", devType);
+                break;
+        }
+        // Unexpected port or device type. Do not need warning here for port polling.
+        return null;
+    }
+
+    /**
+     * Returns the acceptable attenuation range for a connection (represented as
+     * a flow with attenuation instruction). Port can be either the input or
+     * output port of the connection. Returns null if the connection does not
+     * support attenuation.
+     *
+     * @param portNum the port number
+     * @return attenuation range
+     */
+    private Range<Long> getChannelAttenuationRange(PortNumber portNum) {
+        OpenFlowSwitch ofs = getOpenFlowDevice();
+        if (ofs == null) {
+            return null;
+        }
+        if (ofs.deviceType() != Type.ROADM) {
+            return null;
+        }
+        PortDescType portType = getPortDescType((OpenFlowOpticalSwitch) ofs, portNum);
+        // Short time hard code.
+        // The port type and attenuation range will be obtained from physical device in the future.
+        if (portType == PortDescType.PA_LINE_OUT || portType == PortDescType.EXP_IN ||
+                portType == PortDescType.AUX_IN) {
+            return Range.closed(ROADM_MIN_ATTENUATION, ROADM_MAX_ATTENUATION);
+        }
+        // Unexpected port. Do not need warning here for port polling.
+        return null;
+    }
+
+    /**
+     * Find specified port power from port description.
+     *
+     * @param portNum the port number
+     * @param annotation annotation in port description
+     * @return power value in 0.01 dBm
+     */
+    private Long getPortPower(PortNumber portNum, String annotation) {
+        // Check if switch is connected, otherwise do not return value in store, which is obsolete.
+        if (getOpenFlowDevice() == null) {
+            // Warning already exists in method getOpenFlowDevice()
+            return null;
+        }
+        final DriverHandler handler = behaviour.handler();
+        DeviceService deviceService = handler.get(DeviceService.class);
+        Port port = deviceService.getPort(handler.data().deviceId(), portNum);
+        if (port == null) {
+            log.warn("Unexpected port: {}", portNum);
+            return null;
+        }
+        String power = port.annotations().value(annotation);
+        if (power == null) {
+            log.warn("Cannot get {} from port {}.", annotation, portNum);
+            return null;
+        }
+        return Long.valueOf(power);
+    }
+
+    /**
+     * Sets specified port power value.
+     *
+     * @param portNum the port number
+     * @param power power value
+     */
+    private void setPortPower(PortNumber portNum, long power) {
+        OpenFlowSwitch device = getOpenFlowDevice();
+        // Check if switch is connected
+        if (device == null) {
+            return;
+        }
+        device.sendMsg(device.factory().buildOplinkPortPowerSet()
+                .setXid(xidCounter.getAndIncrement())
+                .setPort((int) portNum.toLong())
+                .setPowerValue((int) power)
+                .build());
+    }
+
+    /**
+     * Gets specified channel attenuation.
+     *
+     * @param portNum the port number
+     * @param och channel signal
+     * @return atteuation in 0.01 dB
+     */
+    private Long getChannelAttenuation(PortNumber portNum, OchSignal och) {
+        FlowEntry flowEntry = findFlow(portNum, och);
+        if (flowEntry == null) {
+            return null;
+        }
+        List<Instruction> instructions = flowEntry.treatment().allInstructions();
+        for (Instruction ins : instructions) {
+            if (ins.type() != Instruction.Type.EXTENSION) {
+                continue;
+            }
+            ExtensionTreatment ext = ((Instructions.ExtensionInstructionWrapper) ins).extensionInstruction();
+            if (ext.type() == ExtensionTreatmentType.ExtensionTreatmentTypes.OPLINK_ATTENUATION.type()) {
+                return (long) ((OplinkAttenuation) ext).getAttenuation();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Sets specified channle attenuation.
+     *
+     * @param portNum the port number
+     * @param och channel signal
+     * @param power attenuation in 0.01 dB
+     */
+    private void setChannelAttenuation(PortNumber portNum, OchSignal och, long power) {
+        FlowEntry flowEntry = findFlow(portNum, och);
+        if (flowEntry == null) {
+            log.warn("Target channel power not set");
+            return;
+        }
+        final DriverHandler handler = behaviour.handler();
+        for (Instruction ins : flowEntry.treatment().allInstructions()) {
+            if (ins.type() != Instruction.Type.EXTENSION) {
+                continue;
+            }
+            ExtensionTreatment ext = ((Instructions.ExtensionInstructionWrapper) ins).extensionInstruction();
+            if (ext.type() == ExtensionTreatmentType.ExtensionTreatmentTypes.OPLINK_ATTENUATION.type()) {
+                ((OplinkAttenuation) ext).setAttenuation((int) power);
+                FlowRuleService service = handler.get(FlowRuleService.class);
+                service.applyFlowRules(flowEntry);
+                return;
+            }
+        }
+        addAttenuation(flowEntry, power);
+    }
+
+    /**
+     * Gets specified channle current power.
+     *
+     * @param portNum the port number
+     * @param och channel signal
+     * @return power value in 0.01 dBm
+     */
+    private Long getCurrentChannelPower(PortNumber portNum, OchSignal och) {
+        FlowEntry flowEntry = findFlow(portNum, och);
+        if (flowEntry != null) {
+            // TODO put somewhere else if possible
+            // We put channel power in packets
+            return flowEntry.packets();
+        }
+        return null;
+    }
+
+    /**
+     * Find matching flow on device.
+     *
+     * @param portNum the port number
+     * @param och channel signal
+     * @return flow entry
+     */
+    private FlowEntry findFlow(PortNumber portNum, OchSignal och) {
+        final DriverHandler handler = behaviour.handler();
+        FlowRuleService service = handler.get(FlowRuleService.class);
+        Iterable<FlowEntry> flowEntries = service.getFlowEntries(handler.data().deviceId());
+
+        // Return first matching flow
+        for (FlowEntry entry : flowEntries) {
+            TrafficSelector selector = entry.selector();
+            OchSignalCriterion entrySigid =
+                    (OchSignalCriterion) selector.getCriterion(Criterion.Type.OCH_SIGID);
+            // Check channel
+            if (entrySigid != null && och.equals(entrySigid.lambda())) {
+                // Check input port
+                PortCriterion entryPort =
+                        (PortCriterion) selector.getCriterion(Criterion.Type.IN_PORT);
+                if (entryPort != null && portNum.equals(entryPort.port())) {
+                    return entry;
+                }
+
+                // Check output port
+                TrafficTreatment treatment = entry.treatment();
+                for (Instruction instruction : treatment.allInstructions()) {
+                    if (instruction.type() == Instruction.Type.OUTPUT &&
+                        ((Instructions.OutputInstruction) instruction).port().equals(portNum)) {
+                        return entry;
+                    }
+                }
+            }
+        }
+        log.warn("No matching flow found");
+        return null;
+    }
+
+    /**
+     * Replace flow with new flow containing Oplink attenuation extension instruction. Also resets metrics.
+     *
+     * @param flowEntry flow entry
+     * @param power power value
+     */
+    private void addAttenuation(FlowEntry flowEntry, long power) {
+        FlowRule.Builder flowBuilder = new DefaultFlowRule.Builder()
+                .withCookie(flowEntry.id().value())
+                .withPriority(flowEntry.priority())
+                .forDevice(flowEntry.deviceId())
+                .forTable(flowEntry.tableId());
+        if (flowEntry.isPermanent()) {
+            flowBuilder.makePermanent();
+        } else {
+            flowBuilder.makeTemporary(flowEntry.timeout());
+        }
+        flowBuilder.withSelector(flowEntry.selector());
+        // Copy original instructions and add attenuation instruction
+        TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+        flowEntry.treatment().allInstructions().forEach(ins -> treatmentBuilder.add(ins));
+        final DriverHandler handler = behaviour.handler();
+        treatmentBuilder.add(Instructions.extension(new OplinkAttenuation((int) power), handler.data().deviceId()));
+        flowBuilder.withTreatment(treatmentBuilder.build());
+
+        FlowRuleService service = handler.get(FlowRuleService.class);
+        service.applyFlowRules(flowBuilder.build());
+    }
+}
diff --git a/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkRoadmPowerConfig.java b/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkRoadmPowerConfig.java
index 3a7dc0c..4904168 100644
--- a/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkRoadmPowerConfig.java
+++ b/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkRoadmPowerConfig.java
@@ -16,38 +16,12 @@
 
 package org.onosproject.driver.optical.power;
 
-import java.util.List;
 import java.util.Optional;
 
 import com.google.common.collect.Range;
-import org.onosproject.driver.extensions.OplinkAttenuation;
-import org.onosproject.net.OchSignal;
 import org.onosproject.net.driver.AbstractHandlerBehaviour;
-import org.onosproject.net.Direction;
-import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.behaviour.PowerConfig;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.DefaultFlowRule;
-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.Criterion;
-import org.onosproject.net.flow.criteria.OchSignalCriterion;
-import org.onosproject.net.flow.criteria.PortCriterion;
-import org.onosproject.net.flow.instructions.ExtensionTreatment;
-import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
-import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions;
-import org.onosproject.net.optical.OpticalAnnotations;
-import org.onosproject.openflow.controller.Dpid;
-import org.onosproject.openflow.controller.OpenFlowController;
-import org.onosproject.openflow.controller.OpenFlowSwitch;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Port Power (Gain and attenuation) implementation for Oplink 1-SLOT-8D ROADM.
@@ -60,314 +34,31 @@
 public class OplinkRoadmPowerConfig extends AbstractHandlerBehaviour
                                     implements PowerConfig<Object> {
 
-    private static final int LINE_IN = 1;
-    private static final int LINE_OUT = 2;
-    private static final int AUX_OUT_1 = 3;
-    private static final int AUX_OUT_2 = 4;
-    private static final int EXPRESS_OUT_1 = 5;
-    private static final int EXPRESS_OUT_2 = 6;
-    private static final int EXPRESS_OUT_3 = 7;
-    private static final int EXPRESS_OUT_4 = 8;
-    private static final int EXPRESS_OUT_5 = 9;
-    private static final int EXPRESS_OUT_6 = 10;
-    private static final int EXPRESS_OUT_7 = 11;
-    private static final int AUX_IN_1 = 12;
-    private static final int AUX_IN_2 = 13;
-    private static final int EXPRESS_IN_1 = 14;
-    private static final int EXPRESS_IN_2 = 15;
-    private static final int EXPRESS_IN_3 = 16;
-    private static final int EXPRESS_IN_4 = 17;
-    private static final int EXPRESS_IN_5 = 18;
-    private static final int EXPRESS_IN_6 = 19;
-    private static final int EXPRESS_IN_7 = 20;
+    // oplink power config utility
+    private OplinkPowerConfigUtil oplinkUtil = new OplinkPowerConfigUtil(this);
 
-    protected final Logger log = LoggerFactory.getLogger(getClass());
-
-    // Component type
-    private enum Type {
-        NONE,
-        PORT,
-        CHANNEL
-    }
-
-    // Get the type if component is valid
-    private Type getType(Object component) {
-        if (component == null || component instanceof Direction) {
-            return Type.PORT;
-        } else if (component instanceof OchSignal) {
-            return Type.CHANNEL;
-        } else {
-            return Type.NONE;
-        }
-    }
-
-    private OpenFlowSwitch getOpenFlowDevice() {
-        final OpenFlowController controller = this.handler().get(OpenFlowController.class);
-        final Dpid dpid = Dpid.dpid(this.data().deviceId().uri());
-        OpenFlowSwitch sw = controller.getSwitch(dpid);
-        if (sw == null || !sw.isConnected()) {
-            return null;
-        } else {
-            return sw;
-        }
-    }
-
-    // Find matching flow on device
-    private FlowEntry findFlow(PortNumber portNum, OchSignal och) {
-        FlowRuleService service = this.handler().get(FlowRuleService.class);
-        Iterable<FlowEntry> flowEntries = service.getFlowEntries(this.data().deviceId());
-
-        // Return first matching flow
-        for (FlowEntry entry : flowEntries) {
-            TrafficSelector selector = entry.selector();
-            OchSignalCriterion entrySigid =
-                    (OchSignalCriterion) selector.getCriterion(Criterion.Type.OCH_SIGID);
-            // Check channel
-            if (entrySigid != null && och.equals(entrySigid.lambda())) {
-                // Check input port
-                PortCriterion entryPort =
-                        (PortCriterion) selector.getCriterion(Criterion.Type.IN_PORT);
-                if (entryPort != null && portNum.equals(entryPort.port())) {
-                    return entry;
-                }
-
-                // Check output port
-                TrafficTreatment treatment = entry.treatment();
-                for (Instruction instruction : treatment.allInstructions()) {
-                    if (instruction.type() == Instruction.Type.OUTPUT &&
-                        ((Instructions.OutputInstruction) instruction).port().equals(portNum)) {
-                        return entry;
-                    }
-                }
-            }
-        }
-        log.warn("No matching flow found");
-        return null;
+    @Override
+    public Optional<Long> getTargetPower(PortNumber port, Object component) {
+        return Optional.ofNullable(oplinkUtil.getTargetPower(port, component));
     }
 
     @Override
-    public Optional<Long> getTargetPower(PortNumber portNum, Object component) {
-        Long returnVal = null;
-        // Check if switch is connected, otherwise do not return value in store,
-        // which is obsolete.
-        if (getOpenFlowDevice() != null) {
-            switch (getType(component)) {
-                case PORT:
-                    // Will be implemented in the future.
-                    break;
-                case CHANNEL:
-                    returnVal = getChannelAttenuation(portNum, (OchSignal) component);
-                    break;
-                default:
-                    break;
-            }
-        }
-        return Optional.ofNullable(returnVal);
+    public Optional<Long> currentPower(PortNumber port, Object component) {
+        return Optional.ofNullable(oplinkUtil.getCurrentPower(port, component));
     }
 
     @Override
-    public Optional<Long> currentPower(PortNumber portNum, Object component) {
-        Long returnVal = null;
-        // Check if switch is connected, otherwise do not return value in store,
-        // which is obsolete.
-        if (getOpenFlowDevice() != null) {
-            switch (getType(component)) {
-                case PORT:
-                    returnVal = getCurrentPortPower(portNum);
-                    break;
-                case CHANNEL:
-                    returnVal = getCurrentChannelPower(portNum, (OchSignal) component);
-                    break;
-                default:
-                    break;
-            }
-        }
-        return Optional.ofNullable(returnVal);
-    }
-
-    @Override
-    public void setTargetPower(PortNumber portNum, Object component, long power) {
-        if (getOpenFlowDevice() != null) {
-            switch (getType(component)) {
-                case PORT:
-                    setTargetPortPower(portNum, power);
-                    break;
-                case CHANNEL:
-                    setChannelAttenuation(portNum, (OchSignal) component, power);
-                    break;
-                default:
-                    break;
-            }
-        } else {
-            log.warn("OpenFlow handshaker driver not found or device is not connected");
-        }
+    public void setTargetPower(PortNumber port, Object component, long power) {
+        oplinkUtil.setTargetPower(port, component, power);
     }
 
     @Override
     public Optional<Range<Long>> getTargetPowerRange(PortNumber port, Object component) {
-        Range<Long> range = null;
-        switch (getType(component)) {
-            case PORT:
-                range = getTargetPortPowerRange(port);
-                break;
-            case CHANNEL:
-                range = getChannelAttenuationRange(port);
-                break;
-            default:
-                break;
-        }
-        return Optional.ofNullable(range);
+        return Optional.ofNullable(oplinkUtil.getTargetPowerRange(port, component));
     }
 
     @Override
     public Optional<Range<Long>> getInputPowerRange(PortNumber port, Object component) {
-        Range<Long> range = null;
-        switch (getType(component)) {
-            case PORT:
-                range = getInputPortPowerRange(port);
-                break;
-            default:
-                break;
-        }
-        return Optional.ofNullable(range);
-    }
-
-    private Long getChannelAttenuation(PortNumber portNum, OchSignal och) {
-        FlowEntry flowEntry = findFlow(portNum, och);
-        if (flowEntry != null) {
-            List<Instruction> instructions = flowEntry.treatment().allInstructions();
-            for (Instruction ins : instructions) {
-                if (ins.type() == Instruction.Type.EXTENSION) {
-                    ExtensionTreatment ext = ((Instructions.ExtensionInstructionWrapper) ins).extensionInstruction();
-                    if (ext.type() == ExtensionTreatmentType.ExtensionTreatmentTypes.OPLINK_ATTENUATION.type()) {
-                        return (long) ((OplinkAttenuation) ext).getAttenuation();
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
-    private Long getCurrentPortPower(PortNumber portNum) {
-        DeviceService deviceService = this.handler().get(DeviceService.class);
-        Port port = deviceService.getPort(this.data().deviceId(), portNum);
-        if (port != null) {
-            String currentPower = port.annotations().value(OpticalAnnotations.CURRENT_POWER);
-            if (currentPower != null) {
-                return Long.valueOf(currentPower);
-            }
-        }
-        return null;
-    }
-
-    private Long getCurrentChannelPower(PortNumber portNum, OchSignal och) {
-        FlowEntry flowEntry = findFlow(portNum, och);
-        if (flowEntry != null) {
-            // TODO put somewhere else if possible
-            // We put channel power in packets
-            return flowEntry.packets();
-        }
-        return null;
-    }
-
-    private void setTargetPortPower(PortNumber portNum, long power) {
-        OpenFlowSwitch device = getOpenFlowDevice();
-        device.sendMsg(device.factory().buildOplinkPortPowerSet()
-                .setXid(0)
-                .setPort((int) portNum.toLong())
-                .setPowerValue((int) power)
-                .build());
-    }
-
-    private void setChannelAttenuation(PortNumber portNum, OchSignal och, long power) {
-        FlowEntry flowEntry = findFlow(portNum, och);
-        if (flowEntry != null) {
-            List<Instruction> instructions = flowEntry.treatment().allInstructions();
-            for (Instruction ins : instructions) {
-                if (ins.type() == Instruction.Type.EXTENSION) {
-                    ExtensionTreatment ext = ((Instructions.ExtensionInstructionWrapper) ins).extensionInstruction();
-                    if (ext.type() == ExtensionTreatmentType.ExtensionTreatmentTypes.OPLINK_ATTENUATION.type()) {
-                        ((OplinkAttenuation) ext).setAttenuation((int) power);
-                        FlowRuleService service = this.handler().get(FlowRuleService.class);
-                        service.applyFlowRules(flowEntry);
-                        return;
-                    }
-                }
-            }
-            addAttenuation(flowEntry, power);
-        } else {
-            log.warn("Target channel power not set");
-        }
-    }
-
-    // Replace flow with new flow containing Oplink attenuation extension instruction. Also resets
-    // metrics.
-    private void addAttenuation(FlowEntry flowEntry, long power) {
-        FlowRule.Builder flowBuilder = new DefaultFlowRule.Builder();
-        flowBuilder.withCookie(flowEntry.id().value());
-        flowBuilder.withPriority(flowEntry.priority());
-        flowBuilder.forDevice(flowEntry.deviceId());
-        flowBuilder.forTable(flowEntry.tableId());
-        if (flowEntry.isPermanent()) {
-            flowBuilder.makePermanent();
-        } else {
-            flowBuilder.makeTemporary(flowEntry.timeout());
-        }
-
-        flowBuilder.withSelector(flowEntry.selector());
-
-        // Copy original instructions and add attenuation instruction
-        TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
-        flowEntry.treatment().allInstructions().forEach(ins -> treatmentBuilder.add(ins));
-        treatmentBuilder.add(Instructions.extension(new OplinkAttenuation((int) power), this.data().deviceId()));
-        flowBuilder.withTreatment(treatmentBuilder.build());
-
-        FlowRuleService service = this.handler().get(FlowRuleService.class);
-        service.applyFlowRules(flowBuilder.build());
-    }
-
-    // Returns the acceptable target range for an output Port, null otherwise
-    private Range<Long> getTargetPortPowerRange(PortNumber port) {
-        Range<Long> range = null;
-        long num = port.toLong();
-        if (num == LINE_OUT) {
-            range = Range.closed(100L, 2040L);
-        } else if (num >= AUX_OUT_1 && num <= EXPRESS_OUT_7) {
-            range = Range.closed(-680L, 1530L);
-        }
-        return range;
-    }
-
-    // Returns the acceptable attenuation range for a connection (represented as
-    // a flow with attenuation instruction). Port can be either the input or
-    // output port of the connection. Returns null if the connection does not
-    // support attenuation.
-    private Range<Long> getChannelAttenuationRange(PortNumber port) {
-        Range<Long> range = null;
-        long num = port.toLong();
-        // Only connections from AuxIn to LineOut or ExpressIn to LineOut support
-        // attenuation.
-        if (num == LINE_OUT ||
-            num >= AUX_IN_1 && num <= EXPRESS_IN_7) {
-            range = Range.closed(0L, 2550L);
-        }
-        return range;
-    }
-
-    // Returns the working input power range for an input port, null if the port
-    // is not an input port.
-    private Range<Long> getInputPortPowerRange(PortNumber port) {
-        Range<Long> range = null;
-        long portNum = port.toLong();
-        if (portNum == LINE_IN) {
-            // TODO implement support for IR and ER range
-            // only supports LR right now
-            range = Range.closed(-2600L, 540L);
-        } else if (portNum == AUX_IN_1 || portNum == AUX_IN_2) {
-            range = Range.closed(-1250L, 1590L);
-        } else if (portNum >= EXPRESS_IN_1 && portNum <= EXPRESS_IN_7) {
-            range = Range.closed(-1420L, 1420L);
-        }
-        return range;
+        return Optional.ofNullable(oplinkUtil.getInputPowerRange(port, component));
     }
 }
diff --git a/drivers/optical/src/main/java/org/onosproject/driver/optical/query/OplinkRoadmLambdaQuery.java b/drivers/optical/src/main/java/org/onosproject/driver/optical/query/OplinkRoadmLambdaQuery.java
index d797979..1532a80 100644
--- a/drivers/optical/src/main/java/org/onosproject/driver/optical/query/OplinkRoadmLambdaQuery.java
+++ b/drivers/optical/src/main/java/org/onosproject/driver/optical/query/OplinkRoadmLambdaQuery.java
@@ -36,13 +36,13 @@
 
 public class OplinkRoadmLambdaQuery extends AbstractHandlerBehaviour implements LambdaQuery {
 
-    private static final int LAMBDA_COUNT = 88;
-    private static final int CENTER_OFFSET = 29;
+    private static final int MIN_CHANNEL = -28;
+    private static final int MAX_CHANNEL = 59;
 
     @Override
     public Set<OchSignal> queryLambdas(PortNumber port) {
-        return IntStream.rangeClosed(1, LAMBDA_COUNT)
-                .mapToObj(x -> OchSignal.newDwdmSlot(ChannelSpacing.CHL_50GHZ, x - CENTER_OFFSET))
+        return IntStream.rangeClosed(MIN_CHANNEL, MAX_CHANNEL)
+                .mapToObj(x -> OchSignal.newDwdmSlot(ChannelSpacing.CHL_50GHZ, x))
                 .collect(Collectors.toSet());
     }
 }
\ No newline at end of file
diff --git a/drivers/optical/src/main/resources/optical-drivers.xml b/drivers/optical/src/main/resources/optical-drivers.xml
index 65ed00f..ca4016f 100644
--- a/drivers/optical/src/main/resources/optical-drivers.xml
+++ b/drivers/optical/src/main/resources/optical-drivers.xml
@@ -51,7 +51,7 @@
             manufacturer="Oplink a Molex company" hwVersion="ROADM"
             swVersion="of-agent">
         <behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver"
-                   impl="org.onosproject.driver.optical.handshaker.OplinkRoadm"/>
+                   impl="org.onosproject.driver.optical.handshaker.OplinkRoadmHandshaker"/>
         <behaviour api="org.onosproject.net.behaviour.LambdaQuery"
                    impl="org.onosproject.driver.optical.query.OplinkRoadmLambdaQuery"/>
         <behaviour api="org.onosproject.net.optical.OpticalDevice"