Miscellaneous fixes for reading Linc-OE port types.

 - Keep track of created PortDescriptions so that they can be replayed when
   configurations don't stick
 - Push topology configs to all cluster members (Temporary hack until
   Configs are made Mastership-aware)
 - Port type consistency for Optical ports - default to FIBER port type

Change-Id: Ib2c9e2839c212d2998206bd0106490b2b38446a9
diff --git a/core/api/src/main/java/org/onosproject/net/DefaultAnnotations.java b/core/api/src/main/java/org/onosproject/net/DefaultAnnotations.java
index 13e74b8..3911d0e 100644
--- a/core/api/src/main/java/org/onosproject/net/DefaultAnnotations.java
+++ b/core/api/src/main/java/org/onosproject/net/DefaultAnnotations.java
@@ -189,6 +189,11 @@
         throw new IllegalArgumentException("Expecting HashMap instance");
     }
 
+    @Override
+    public String toString() {
+        return (map == null) ? "null" : map.toString();
+    }
+
     /**
      * Facility for gradually building model annotations.
      */
diff --git a/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java b/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java
index 7f45343..5ea27af 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java
@@ -1023,11 +1023,12 @@
                     continue;
                 }
                 annotations = merge(annotations, otherPortDesc.value().annotations());
-                switch (otherPortDesc.value().type()) {
+                PortDescription other = otherPortDesc.value();
+                switch (other.type()) {
                     case OMS:
                         OmsPortDescription omsPortDesc = (OmsPortDescription) otherPortDesc.value();
                         updated = new OmsPort(device, number, isEnabled, omsPortDesc.minFrequency(),
-                                omsPortDesc.maxFrequency(), omsPortDesc.grid());
+                                omsPortDesc.maxFrequency(), omsPortDesc.grid(), annotations);
                         break;
                     case OCH:
                         OchPortDescription ochPortDesc = (OchPortDescription) otherPortDesc.value();
@@ -1039,7 +1040,8 @@
                         updated = new OduCltPort(device, number, isEnabled, oduCltPortDesc.signalType(), annotations);
                         break;
                     default:
-                        updated = new DefaultPort(device, number, isEnabled, annotations);
+                        updated = new DefaultPort(
+                                device, number, isEnabled, other.type(), other.portSpeed(), annotations);
                 }
                 newest = otherPortDesc.timestamp();
             }
@@ -1047,7 +1049,10 @@
         if (portDesc == null) {
             return updated == null ? new DefaultPort(device, number, false, annotations) : updated;
         }
-        return updated == null ? new DefaultPort(device, number, isEnabled, annotations) : updated;
+        PortDescription current = portDesc.value();
+        return updated == null
+                ? new DefaultPort(device, number, isEnabled, current.type(), current.portSpeed(), annotations)
+                : updated;
     }
 
     /**
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 2d9f4bb..5e62e09 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
@@ -342,7 +342,6 @@
             if (sw.isOptical()) {
                 OpenFlowOpticalSwitch opsw = (OpenFlowOpticalSwitch) sw;
                 opsw.getPortTypes().forEach(type -> {
-                    LOG.debug("ports: {}", opsw.getPortsOf(type));
                     opsw.getPortsOf(type).forEach(
                         op -> {
                             portDescs.add(buildPortDescription(type, (OFPortOptical) op));
@@ -398,10 +397,10 @@
         private PortDescription buildPortDescription(PortDescPropertyType ptype, OFPortOptical port) {
             // Minimally functional fixture. This needs to be fixed as we add better support.
             PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
+
             boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN)
                     && !port.getConfig().contains(OFPortConfig.PORT_DOWN);
             SparseAnnotations annotations = makePortNameAnnotation(port.getName());
-            Port.Type type = FIBER;
 
             if (port.getVersion() == OFVersion.OF_13
                     && ptype == PortDescPropertyType.OPTICAL_TRANSPORT) {
@@ -411,7 +410,7 @@
                 // removable once 1.4+ support complete.
                 LOG.debug("Unsupported optical port properties");
             }
-            return new DefaultPortDescription(portNo, enabled, type, 0, annotations);
+            return new DefaultPortDescription(portNo, enabled, FIBER, 0, annotations);
         }
 
         private PortDescription buildPortDescription(OFPortStatus status) {
diff --git a/tools/test/topos/opticalUtils.py b/tools/test/topos/opticalUtils.py
index 52a54b2..5b47d53 100644
--- a/tools/test/topos/opticalUtils.py
+++ b/tools/test/topos/opticalUtils.py
@@ -445,12 +445,12 @@
             sleep(SLEEP_TIME)
 
         info('*** Pushing Topology.json to ONOS\n')
-        output = quietRun('%s/tools/test/bin/onos-topo-cfg %s Topology.json' % (LINCSwitch.onosDir, LINCSwitch.controllers[ 0 ].ip), shell=True)
-
-        # successful output contains the two characters '{}'
-        # if there is more output than this, there is an issue
-        if output.strip('{}'):
-            warn('***WARNING: Could not push topology file to ONOS: %s\n' % output)
+        for index in range(len(LINCSwitch.controllers)):
+            output = quietRun('%s/tools/test/bin/onos-topo-cfg %s Topology.json &' % (LINCSwitch.onosDir, LINCSwitch.controllers[ index ].ip), shell=True)
+            # successful output contains the two characters '{}'
+            # if there is more output than this, there is an issue
+            if output.strip('{}'):
+                warn('***WARNING: Could not push topology file to ONOS: %s\n' % output)
 
     @staticmethod
     def waitStarted(net, timeout=TIMEOUT):
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 edbe310..d0ae044 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
@@ -16,13 +16,16 @@
 package org.onosproject.rest.resources;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 
 import org.onlab.packet.ChassisId;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onlab.util.Frequency;
+import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.ChannelSpacing;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultAnnotations;
@@ -40,6 +43,7 @@
 import org.onosproject.net.OduSignalType;
 import org.onosproject.net.OmsPort;
 import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.SparseAnnotations;
 import org.onosproject.net.device.DefaultDeviceDescription;
 import org.onosproject.net.device.DefaultPortDescription;
@@ -71,6 +75,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -113,6 +118,7 @@
 
     private DeviceListener deviceEventCounter = new DeviceEventCounter();
     private List<ConnectPoint> connectPoints = Lists.newArrayList();
+    private Map<ConnectPoint, PortDescription> descriptions = Maps.newHashMap();
 
     /**
      * Creates a new configuration provider.
@@ -216,24 +222,29 @@
     // Parses the given node with port information.
     private PortDescription parsePort(JsonNode node) {
         Port.Type type = Port.Type.valueOf(node.path("type").asText("COPPER"));
+        PortNumber port = portNumber(node.path("port").asLong(0));
+        String portName = Strings.emptyToNull(port.name());
+        SparseAnnotations annotations  = null;
+        if (portName != null) {
+            annotations = DefaultAnnotations.builder()
+                    .set(AnnotationKeys.PORT_NAME, portName).build();
+        }
         switch (type) {
             case COPPER:
-                return new DefaultPortDescription(portNumber(node.path("port").asLong(0)),
-                        node.path("enabled").asBoolean(true),
-                        type, node.path("speed").asLong(1_000));
+                return new DefaultPortDescription(port, node.path("enabled").asBoolean(true),
+                                                  type, node.path("speed").asLong(1_000),
+                                                  annotations);
             case FIBER:
                 // Currently, assume OMS when FIBER. Provide sane defaults.
-                return new OmsPortDescription(portNumber(node.path("port").asLong(0)),
-                        node.path("enabled").asBoolean(true),
-                        CENTER,
-                        CENTER.add(TOTAL),
-                        Frequency.ofGHz(100));
+                return new OmsPortDescription(port, node.path("enabled").asBoolean(true),
+                                              CENTER, CENTER.add(TOTAL),
+                                              Frequency.ofGHz(100), annotations);
             default:
                 log.warn("{}: Unsupported Port Type");
         }
-        return new DefaultPortDescription(portNumber(node.path("port").asLong(0)),
-                                          node.path("enabled").asBoolean(true),
-                                          type, node.path("speed").asLong(1_000));
+        return new DefaultPortDescription(port, node.path("enabled").asBoolean(true),
+                                          type, node.path("speed").asLong(1_000),
+                                          annotations);
     }
 
     // Parses the given JSON and provides links as configured.
@@ -267,17 +278,12 @@
     }
 
     private void updatePorts(ConnectPoint src, ConnectPoint dst, SparseAnnotations annotations) {
-        DeviceId srcId = src.deviceId();
-        DeviceId dstId = dst.deviceId();
-        Port srcPort = deviceService.getPort(srcId, src.port());
-        Port dstPort = deviceService.getPort(dstId, dst.port());
-
         final String linkType = annotations.value("optical.type");
         if ("cross-connect".equals(linkType)) {
             String value = annotations.value("bandwidth").trim();
             try {
                 double bw = Double.parseDouble(value);
-                updateOchPort(bw, srcPort, dstPort, srcId, dstId);
+                updateOchPort(bw, src, dst);
             } catch (NumberFormatException e) {
                 log.warn("Invalid bandwidth ({}), can't configure port(s)", value);
                 return;
@@ -286,7 +292,7 @@
             String value = annotations.value("optical.waves").trim();
             try {
                 int numChls = Integer.parseInt(value);
-                updateOMSPorts(numChls, srcPort, dstPort, srcId, dstId);
+                updateOMSPorts(numChls, src, dst);
             } catch (NumberFormatException e) {
                 log.warn("Invalid channel ({}), can't configure port(s)", value);
                 return;
@@ -295,10 +301,10 @@
     }
 
     // uses 'bandwidth' annotation to determine the channel spacing.
-    private void updateOchPort(double bw, Port srcPort, Port dstPort, DeviceId srcId, DeviceId dstId) {
-        Device src = deviceService.getDevice(srcId);
-        Device dst = deviceService.getDevice(dstId);
-        // bandwidth in MHz (assuming Hz - linc is not clear if Hz or b).
+    private void updateOchPort(double bw, ConnectPoint srcCp, ConnectPoint dstCp) {
+        Device src = deviceService.getDevice(srcCp.deviceId());
+        Device dst = deviceService.getDevice(dstCp.deviceId());
+        // bandwidth in MHz (assuming Hz - linc is not clear if that or Mb).
         Frequency spacing = Frequency.ofMHz(bw);
         // channel bandwidth is smaller than smallest standard channel spacing.
         ChannelSpacing chsp = null;
@@ -319,20 +325,21 @@
         }
         OchSignal signal = new OchSignal(GridType.DWDM, chsp, 1, 1);
         if (src.type() == Device.Type.ROADM) {
-            PortDescription portDesc = new OchPortDescription(srcPort.number(), srcPort.isEnabled(),
+            PortDescription portDesc = new OchPortDescription(srcCp.port(), true,
                     OduSignalType.ODU4, true, signal);
-            deviceProviderService.portStatusChanged(srcId, portDesc);
+            descriptions.put(srcCp, portDesc);
+            deviceProviderService.portStatusChanged(srcCp.deviceId(), portDesc);
         }
         if (dst.type() == Device.Type.ROADM) {
-            PortDescription portDesc = new OchPortDescription(dstPort.number(), dstPort.isEnabled(),
+            PortDescription portDesc = new OchPortDescription(dstCp.port(), true,
                     OduSignalType.ODU4, true, signal);
-            deviceProviderService.portStatusChanged(dstId, portDesc);
+            descriptions.put(dstCp, portDesc);
+            deviceProviderService.portStatusChanged(dstCp.deviceId(), portDesc);
         }
     }
 
-    private void updateOMSPorts(int numChls, Port srcPort, Port dstPort, DeviceId srcId, DeviceId dstId) {
-        // round down to largest slot that allows numChl channels to fit into C
-        // band range
+    private void updateOMSPorts(int numChls, ConnectPoint srcCp, ConnectPoint dstCp) {
+        // round down to largest slot that allows numChl channels to fit into C band range
         ChannelSpacing chl = null;
         Frequency perChl = TOTAL.floorDivision(numChls);
         for (int i = 0; i < ChannelSpacing.values().length; i++) {
@@ -352,10 +359,12 @@
         Frequency min = CENTER.add(grid);
         Frequency max = CENTER.add(grid.multiply(numChls));
 
-        PortDescription srcPortDesc = new OmsPortDescription(srcPort.number(), srcPort.isEnabled(), min, max, grid);
-        PortDescription dstPortDesc = new OmsPortDescription(dstPort.number(), dstPort.isEnabled(), min, max, grid);
-        deviceProviderService.portStatusChanged(srcId, srcPortDesc);
-        deviceProviderService.portStatusChanged(dstId, dstPortDesc);
+        PortDescription srcPortDesc = new OmsPortDescription(srcCp.port(), true, min, max, grid);
+        PortDescription dstPortDesc = new OmsPortDescription(dstCp.port(), true, min, max, grid);
+        descriptions.put(srcCp, srcPortDesc);
+        descriptions.put(dstCp, dstPortDesc);
+        deviceProviderService.portStatusChanged(srcCp.deviceId(), srcPortDesc);
+        deviceProviderService.portStatusChanged(dstCp.deviceId(), dstPortDesc);
     }
 
     // Parses the given JSON and provides hosts as configured.
@@ -440,8 +449,12 @@
         }
     }
 
-    // Creates a port description from the specified connection point.
+    // Creates a port description from the specified connection point if none created earlier.
     private PortDescription description(ConnectPoint cp) {
+        PortDescription saved = descriptions.get(cp);
+        if (saved != null) {
+            return saved;
+        }
         Port p = deviceService.getPort(cp.deviceId(), cp.port());
         if (p == null) {
             return new DefaultPortDescription(cp.port(), true);