LINC driver and OF device provider report correct optical port types

Change-Id: I501ce5f6f53136254024ad7122a4fec0d17504e0
diff --git a/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitchImplLINC13.java b/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitchImplLINC13.java
index 1e270b0..7faee37 100644
--- a/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitchImplLINC13.java
+++ b/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitchImplLINC13.java
@@ -16,6 +16,7 @@
 package org.onosproject.driver.handshaker;
 
 import org.onosproject.net.Device;
+import com.google.common.collect.ImmutableSet;
 import org.onosproject.openflow.controller.OpenFlowOpticalSwitch;
 import org.onosproject.openflow.controller.PortDescPropertyType;
 import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
@@ -28,19 +29,29 @@
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFObject;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortDescPropOpticalTransport;
+import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFPortOptical;
 import org.projectfloodlight.openflow.protocol.OFStatsReply;
 import org.projectfloodlight.openflow.protocol.OFStatsType;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
+import org.projectfloodlight.openflow.types.OFPort;
 
 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;
 
 /**
  * LINC-OE Optical Emulator switch class.
+ *
+ * The LINC ROADM emulator exposes two types of ports: OCh ports connect to ports in the packet layer,
+ * while OMS ports connect to an OMS port on a neighbouring ROADM.
+ *
+ * LINC sends the tap ports (OCh for our purposes) in the regular port desc stats reply,
+ * while it sends *all* ports (both tap and WDM ports, i.e., OCh and OMS) in the experimenter port desc stats reply.
+ *
  */
 public class OFOpticalSwitchImplLINC13
  extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch {
@@ -48,7 +59,7 @@
     private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false);
     private long barrierXidToWaitFor = -1;
 
-    private OFCircuitPortsReply wPorts;
+    private List<OFPortOptical> opticalPorts;
 
     @Override
     public void startDriverHandshake() {
@@ -109,7 +120,7 @@
                 OFStatsReply stats = (OFStatsReply) m;
                 if (stats.getStatsType() == OFStatsType.EXPERIMENTER) {
                     log.warn("LINC-OE : Received stats reply message {}", m);
-                    wPorts = (OFCircuitPortsReply) m;
+                    createOpticalPortList((OFCircuitPortsReply) m);
                     driverHandshakeComplete.set(true);
                 }
                 break;
@@ -124,7 +135,6 @@
 
     public void processOFPortStatus(OFCircuitPortStatus ps) {
         log.debug("LINC-OE ..OF Port Status :", ps);
-
     }
 
     private void sendHandshakeOFExperimenterPortDescRequest() throws
@@ -147,7 +157,7 @@
      * @return List of ports
      */
     public List<OFPortDesc> getPorts() {
-        return ImmutableList.copyOf(super.getPorts());
+        return Collections.EMPTY_LIST;
     }
 
 
@@ -161,9 +171,87 @@
         return Device.Type.ROADM;
     }
 
+    /**
+     * Checks if given port is also part of the regular port desc stats, i.e., is the port a tap port.
+     *
+     * @param port given OF port
+     * @return true if the port is a tap (OCh), false otherwise (OMS port)
+     */
+    private boolean hasPort(OFPort port) {
+        for (OFPortDescStatsReply reply : this.ports) {
+            for (OFPortDesc p : reply.getEntries()) {
+                if (p.getPortNo().equals(port)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Creates an OpenFlow optical port based on the given port and transport type.
+     *
+     * @param port OpenFlow optical port
+     * @param type transport type
+     * @return OpenFlow optical port
+     */
+    private OFPortOptical createOpticalPort(OFPortOptical port, short type) {
+        List<OFPortDescPropOpticalTransport> descList = new ArrayList<>(port.getDesc().size());
+
+        for (OFPortDescPropOpticalTransport desc : port.getDesc()) {
+            OFPortDescPropOpticalTransport newDesc = desc.createBuilder()
+                    .setType(desc.getType())
+                    .setPortSignalType(type)
+                    .setPortType(desc.getPortType())
+                    .setReserved(desc.getReserved())
+                    .build();
+            descList.add(newDesc);
+        }
+
+        OFPortOptical newPort = port.createBuilder()
+                .setConfig(port.getConfig())
+                .setDesc(descList)
+                .setHwAddr(port.getHwAddr())
+                .setName(port.getName())
+                .setPortNo(port.getPortNo())
+                .setState(port.getState())
+                .build();
+
+        return newPort;
+    }
+
+    /**
+     * 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<>(wPorts.getEntries().size());
+
+        for (OFPortOptical p : wPorts.getEntries()) {
+            short signalType;
+
+            // FIXME: use constants once loxi has full optical extensions
+            if (hasPort(p.getPortNo())) {
+                signalType = 5;      // OCH port
+            } else {
+                signalType = 2;      // OMS port
+            }
+
+            opticalPorts.add(createOpticalPort(p, signalType));
+        }
+    }
+
     @Override
     public List<? extends OFObject> getPortsOf(PortDescPropertyType type) {
-        return ImmutableList.copyOf(wPorts.getEntries());
+        if (!type.equals(PortDescPropertyType.OPTICAL_TRANSPORT)) {
+            return Collections.EMPTY_LIST;
+        }
+
+        return opticalPorts;
     }
 
     @Override
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 9177e70..cb19dc5 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
@@ -19,7 +19,6 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
-
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -28,13 +27,17 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.ChassisId;
-import org.onosproject.cfg.ComponentConfigService;
 import org.onlab.util.Frequency;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onlab.util.Spectrum;
 import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.ChannelSpacing;
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.GridType;
 import org.onosproject.net.MastershipRole;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.OduSignalType;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.SparseAnnotations;
@@ -45,6 +48,7 @@
 import org.onosproject.net.device.DeviceProvider;
 import org.onosproject.net.device.DeviceProviderRegistry;
 import org.onosproject.net.device.DeviceProviderService;
+import org.onosproject.net.device.OchPortDescription;
 import org.onosproject.net.device.OmsPortDescription;
 import org.onosproject.net.device.PortDescription;
 import org.onosproject.net.device.PortStatistics;
@@ -64,6 +68,7 @@
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPortConfig;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortDescPropOpticalTransport;
 import org.projectfloodlight.openflow.protocol.OFPortFeatures;
 import org.projectfloodlight.openflow.protocol.OFPortOptical;
 import org.projectfloodlight.openflow.protocol.OFPortReason;
@@ -86,6 +91,7 @@
 import java.util.HashSet;
 import java.util.List;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Strings.isNullOrEmpty;
 import static org.onlab.util.Tools.get;
 import static org.onosproject.net.DeviceId.deviceId;
@@ -454,6 +460,8 @@
          * @return portDescription for the port.
          */
         private PortDescription buildPortDescription(PortDescPropertyType ptype, OFPortOptical port) {
+            checkArgument(port.getDesc().size() >= 1);
+
             // Minimally functional fixture. This needs to be fixed as we add better support.
             PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
 
@@ -469,6 +477,23 @@
                 // removable once 1.4+ support complete.
                 LOG.debug("Unsupported optical port properties");
             }
+
+            OFPortDescPropOpticalTransport desc = port.getDesc().get(0);
+            switch (desc.getPortSignalType()) {
+                // FIXME: use constants once loxi has full optical extensions
+                case 2:     // OMS port
+                    // Assume complete optical spectrum and 50 GHz grid
+                    // LINC-OE is only supported optical OF device for now
+                    return new OmsPortDescription(portNo, enabled,
+                            Spectrum.U_BAND_MIN, Spectrum.O_BAND_MAX, Frequency.ofGHz(50), annotations);
+                case 5:     // OCH port
+                    OchSignal signal = new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, 0, 4);
+                    return new OchPortDescription(portNo, enabled, OduSignalType.ODU4,
+                            true, signal, annotations);
+                default:
+                    break;
+            }
+
             return new DefaultPortDescription(portNo, enabled, FIBER, 0, annotations);
         }
 
@@ -486,12 +511,10 @@
             boolean enabled = true;
             SparseAnnotations annotations = makePortNameAnnotation(port.getName());
 
-            // Wavelength range: 1260 - 1630 nm (S160 data sheet)
-            // Grid is irrelevant for this type of switch
-            Frequency minFreq = Spectrum.O_BAND_MAX;
-            Frequency maxFreq = Spectrum.U_BAND_MIN;
-            Frequency grid = Frequency.ofGHz(100);
-            return new OmsPortDescription(portNo, enabled, minFreq, maxFreq, grid, annotations);
+            // S160 data sheet
+            // Wavelength range: 1260 - 1630 nm, grid is irrelevant for this type of switch
+            return new OmsPortDescription(portNo, enabled,
+                    Spectrum.U_BAND_MIN, Spectrum.O_BAND_MAX, Frequency.ofGHz(100), annotations);
         }
 
         private PortDescription buildPortDescription(OFPortStatus status) {
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/ConfigProvider.java b/web/api/src/main/java/org/onosproject/rest/resources/ConfigProvider.java
index cc05bff..dbd80cc 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/ConfigProvider.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/ConfigProvider.java
@@ -254,6 +254,7 @@
                                                   annotations);
             case FIBER:
                 // Currently, assume OMS when FIBER. Provide sane defaults.
+                annotations = annotations(node.get("annotations"));
                 return new OmsPortDescription(port, node.path("enabled").asBoolean(true),
                                               CENTER, CENTER.add(TOTAL),
                                               Frequency.ofGHz(100), annotations);