Implement PowerConfig for Oplink Devices

Change-Id: I939126580f8d3cdcdbcd9a46f6ee5cacbd25051d
diff --git a/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java b/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java
index 00efb4a..4235ed1 100644
--- a/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java
+++ b/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java
@@ -15,6 +15,8 @@
  */
 package org.onosproject.net;
 
+import com.google.common.annotations.Beta;
+
 /**
  * Collection of keys for annotation.
  * <p>
@@ -110,6 +112,18 @@
     public static final String STATIC_LAMBDA = "staticLambda";
 
     /**
+     * Annotation key for optical port's target power.
+     */
+    @Beta
+    public static final String TARGET_POWER = "targetPower";
+
+    /**
+     * Annotation key for optical port's current power.
+     */
+    @Beta
+    public static final String CURRENT_POWER = "currentPower";
+
+    /**
      * Annotation key for the static port.
      */
     public static final String STATIC_PORT = "staticPort";
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
index 13c238b..cf1a179 100644
--- 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-present Open Networking Laboratory
+ * 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.
@@ -25,7 +25,14 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.Device;
+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.openflow.controller.OpenFlowOpticalSwitch;
 import org.onosproject.openflow.controller.PortDescPropertyType;
 import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
@@ -37,10 +44,16 @@
 import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest;
 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.
@@ -49,6 +62,7 @@
  * 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;
 
@@ -159,7 +173,26 @@
     @Override
     public final void sendMsg(OFMessage m) {
         OFMessage newMsg = m;
-        //Stub for later enhancement.
+
+        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:
+                    //replace with Oplink experiment stats message to get the port current power
+                    OFOplinkPortPowerRequest pRequest = this.factory().buildOplinkPortPowerRequest()
+                            .setXid(sr.getXid())
+                            .setFlags(sr.getFlags())
+                            .build();
+                    newMsg = pRequest;
+                    break;
+                default:
+                    break;
+            }
+        } else {
+            log.debug("OPLK ROADM sends msg:{}, as is", m.getType());
+        }
+
         super.sendMsg(newMsg);
     }
 
@@ -185,4 +218,38 @@
         opticalPorts = new ArrayList<>();
         opticalPorts.addAll(wPorts.getEntries());
     }
+
+    @Override
+    public List<PortDescription> processExpPortStats(OFMessage msg) {
+        if (msg instanceof OFOplinkPortPowerReply) {
+            return buildPortPowerDescriptions(((OFOplinkPortPowerReply) msg).getEntries());
+        }
+        return Collections.emptyList();
+    }
+
+    private OFOplinkPortPower getPortPower(List<OFOplinkPortPower> portPowers, PortNumber portNum) {
+        for (OFOplinkPortPower power : portPowers) {
+            if (power.getPort() == portNum.toLong()) {
+                return power;
+            }
+        }
+        return null;
+    }
+
+    private List<PortDescription> buildPortPowerDescriptions(List<OFOplinkPortPower> portPowers) {
+        DeviceService deviceService = this.handler().get(DeviceService.class);
+        List<Port> ports = deviceService.getPorts(this.data().deviceId());
+        final List<PortDescription> portDescs = new ArrayList<>();
+        for (Port port : ports) {
+            DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
+            builder.putAll(port.annotations());
+            OFOplinkPortPower power = getPortPower(portPowers, port.number());
+            if (power != null) {
+                builder.set(AnnotationKeys.CURRENT_POWER, Long.toString(power.getPowerValue()));
+            }
+            portDescs.add(new DefaultPortDescription(port.number(), port.isEnabled(),
+                    port.type(), port.portSpeed(), builder.build()));
+        }
+        return portDescs;
+    }
 }
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
new file mode 100644
index 0000000..47a699d
--- /dev/null
+++ b/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkRoadmPowerConfig.java
@@ -0,0 +1,94 @@
+/*
+ * 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.driver.optical.power;
+
+import java.util.Optional;
+
+import org.onosproject.net.AnnotationKeys;
+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.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 ROADM.
+ *
+ * An Oplink ROADM port exposes OchSignal resources.
+ * Optical Power can be set at port level or channel/wavelength level (attenuation).
+ *
+ */
+
+public class OplinkRoadmPowerConfig extends AbstractHandlerBehaviour
+                                    implements PowerConfig<Direction> {
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    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;
+        }
+    }
+
+    @Override
+    public Optional<Long> getTargetPower(PortNumber portNum, Direction component) {
+        // Will be implemented in the future.
+        return Optional.empty();
+    }
+
+    @Override
+    public Optional<Long> currentPower(PortNumber portNum, Direction component) {
+        Long returnVal = null;
+        // Check if switch is connected, otherwise do not return value in store,
+        // which is obsolete.
+        if (getOpenFlowDevice() != null) {
+            DeviceService deviceService = this.handler().get(DeviceService.class);
+            Port port = deviceService.getPort(this.data().deviceId(), portNum);
+            if (port != null) {
+                String currentPower = port.annotations().value(AnnotationKeys.CURRENT_POWER);
+                if (currentPower != null) {
+                    returnVal = Long.valueOf(currentPower);
+                }
+            }
+        }
+        return Optional.ofNullable(returnVal);
+    }
+
+    @Override
+    public void setTargetPower(PortNumber portNum, Direction component, long power) {
+        OpenFlowSwitch device = getOpenFlowDevice();
+        if (device != null) {
+            device.sendMsg(device.factory().buildOplinkPortPowerSet()
+                    .setXid(0)
+                    .setPort((int) portNum.toLong())
+                    .setPowerValue((int) power)
+                    .build());
+        } else {
+            log.warn("OpenFlow handshaker driver not found or device is not connected");
+        }
+    }
+}
diff --git a/drivers/optical/src/main/java/org/onosproject/driver/optical/power/package-info.java b/drivers/optical/src/main/java/org/onosproject/driver/optical/power/package-info.java
new file mode 100644
index 0000000..db6378ef
--- /dev/null
+++ b/drivers/optical/src/main/java/org/onosproject/driver/optical/power/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implementations of the power config behaviours for optical devices.
+ */
+package org.onosproject.driver.optical.power;
diff --git a/drivers/optical/src/main/resources/optical-drivers.xml b/drivers/optical/src/main/resources/optical-drivers.xml
index c87a8cd..505db69 100644
--- a/drivers/optical/src/main/resources/optical-drivers.xml
+++ b/drivers/optical/src/main/resources/optical-drivers.xml
@@ -56,6 +56,8 @@
                    impl="org.onosproject.driver.optical.query.OplinkRoadmLambdaQuery"/>
         <behaviour api="org.onosproject.net.optical.OpticalDevice"
                    impl="org.onosproject.net.optical.DefaultOpticalDevice"/>
+        <behaviour api="org.onosproject.net.behaviour.PowerConfig"
+                   impl="org.onosproject.driver.optical.power.OplinkRoadmPowerConfig"/>
     </driver>
 
 </drivers>
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowOpticalSwitch.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowOpticalSwitch.java
index 34d4f88..42f8638 100644
--- a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowOpticalSwitch.java
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowOpticalSwitch.java
@@ -15,8 +15,11 @@
  */
 package org.onosproject.openflow.controller;
 
+import java.util.Collections;
 import java.util.List;
 
+import org.onosproject.net.device.PortDescription;
+import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
 
 import com.google.common.annotations.Beta;
@@ -38,4 +41,15 @@
     @Beta
     @Override
     abstract List<OFPortDesc> getPorts();
+
+    /**
+     * Returns updated PortDescriptions built from experimenter message
+     * received from device.
+     *
+     * @param msg OpenFlow message from device.
+     * @return List of updated PortDescriptions.
+     */
+    default List<PortDescription> processExpPortStats(OFMessage msg) {
+        return Collections.emptyList();
+    }
 }
diff --git a/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java b/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
index 1f6a888..177cf1a 100644
--- a/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
+++ b/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
@@ -547,7 +547,7 @@
 
         private PortDescription buildPortDescription(PortDescPropertyType ptype, OFObject port,
                 OpenFlowOpticalSwitch opsw) {
-            if (port instanceof  OFPortOptical) {
+            if (port instanceof OFPortOptical) {
                 return buildPortDescription(ptype, (OFPortOptical) port, opsw);
             }
             return buildPortDescription(ptype, (OFExpPort) port);
@@ -833,6 +833,16 @@
                                     statsEntries.clear();
                                 }
                             }
+                        } else if (((OFStatsReply) msg).getStatsType() == OFStatsType.EXPERIMENTER) {
+                            OpenFlowSwitch sw = controller.getSwitch(dpid);
+                            if (sw instanceof OpenFlowOpticalSwitch) {
+                                // Optical switch uses experimenter stats message to update power
+                                List<PortDescription> portDescs =
+                                        ((OpenFlowOpticalSwitch) sw).processExpPortStats(msg);
+                                if (!portDescs.isEmpty()) {
+                                    providerService.updatePorts(DeviceId.deviceId(Dpid.uri(dpid)), portDescs);
+                                }
+                            }
                         }
                         break;
                     case ERROR: