Add support for OF1.4 devices

- part of ONOS-6386

Change-Id: I2e03496ae6f2fdcda5800e2208533faf07790e3d
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowSwitchDriver.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowSwitchDriver.java
index fc38aeb..e6e6fca 100644
--- a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowSwitchDriver.java
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowSwitchDriver.java
@@ -188,6 +188,9 @@
 
     /**
      * Does this switch support Nicira Role messages.
+     * <p>
+     * Only relevant if this Device is OpenFlow 1.0.
+     *
      * @return true if supports, false otherwise.
      */
     Boolean supportNxRole();
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java
index 5fce725..b854898 100644
--- a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java
@@ -69,8 +69,17 @@
 
     protected static final Logger log = LoggerFactory.getLogger(Controller.class);
 
+   /**
+    * @deprecated in 1.10.0
+    */
+    @Deprecated
     protected static final OFFactory FACTORY13 = OFFactories.getFactory(OFVersion.OF_13);
+    /**
+     * @deprecated in 1.10.0
+     */
+    @Deprecated
     protected static final OFFactory FACTORY10 = OFFactories.getFactory(OFVersion.OF_10);
+
     private static final boolean TLS_DISABLED = false;
     private static final short MIN_KS_LENGTH = 6;
 
@@ -97,6 +106,7 @@
 
     // Perf. related configuration
     protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
+
     private DriverService driverService;
     private boolean enableOfTls = TLS_DISABLED;
 
@@ -104,11 +114,21 @@
     // Getters/Setters
     // ***************
 
+    /**
+     * @return OF1.0 factory
+     * @deprecated in 1.10.0
+     */
+    @Deprecated
     public OFFactory getOFMessageFactory10() {
         return FACTORY10;
     }
 
 
+    /**
+     * @return OF1.3 factory
+     * @deprecated in 1.10.0
+     */
+    @Deprecated
     public OFFactory getOFMessageFactory13() {
         return FACTORY13;
     }
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java
index 6649fcd..76019fd 100644
--- a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java
@@ -21,6 +21,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.RejectedExecutionException;
 
@@ -47,6 +48,7 @@
 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
 import org.projectfloodlight.openflow.protocol.OFErrorType;
 import org.projectfloodlight.openflow.protocol.OFExperimenter;
+import org.projectfloodlight.openflow.protocol.OFFactories;
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
 import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
@@ -119,7 +121,12 @@
 
     //Indicates the openflow version used by this switch
     protected OFVersion ofVersion;
+
+    // deprecated in 1.10.0
+    @Deprecated
     protected OFFactory factory13;
+    // deprecated in 1.10.0
+    @Deprecated
     protected OFFactory factory10;
 
     /** transaction Ids to use during handshake. Since only one thread
@@ -137,8 +144,8 @@
         this.state = ChannelState.INIT;
         this.pendingPortStatusMsg = new CopyOnWriteArrayList<>();
         this.portDescReplies = new ArrayList<>();
-        factory13 = controller.getOFMessageFactory13();
-        factory10 = controller.getOFMessageFactory10();
+        factory13 = OFFactories.getFactory(OFVersion.OF_13);
+        factory10 = OFFactories.getFactory(OFVersion.OF_10);
         duplicateDpidFound = Boolean.FALSE;
     }
 
@@ -200,20 +207,20 @@
                 // we are just checking the version number.
                 if (m.getVersion().getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
                     log.debug("Received {} Hello from {} - switching to OF "
-                            + "version 1.3", m.getVersion(),
+                            + "version 1.3+", m.getVersion(),
                             h.channel.getRemoteAddress());
+                    h.ofVersion = m.getVersion();
                     h.sendHandshakeHelloMessage();
-                    h.ofVersion = OFVersion.OF_13;
                 } else if (m.getVersion().getWireVersion() >= OFVersion.OF_10.getWireVersion()) {
                     log.debug("Received {} Hello from {} - switching to OF "
                             + "version 1.0", m.getVersion(),
                             h.channel.getRemoteAddress());
+                    h.ofVersion = m.getVersion();
                     OFHello hi =
-                            h.factory10.buildHello()
+                            OFFactories.getFactory(h.ofVersion).buildHello()
                                     .setXid(h.handshakeTransactionIds--)
                                     .build();
                     h.channel.write(Collections.singletonList(hi));
-                    h.ofVersion = OFVersion.OF_10;
                 } else {
                     log.error("Received Hello of version {} from switch at {}. "
                             + "This controller works with OF1.0 and OF1.3 "
@@ -367,8 +374,17 @@
                             m.getMissSendLen());
                 }
 
-                // TODO should this check if 1.3 or later?
-                if (h.ofVersion == OFVersion.OF_13) {
+                nextState(h);
+            }
+
+            /**
+             * Transition to next state based on OF version.
+             *
+             * @param h current channel handler
+             * @throws IOException
+             */
+            private void nextState(OFChannelHandler h) throws IOException {
+                if (h.ofVersion.getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
                     // Meters were introduced in OpenFlow 1.3
                     h.sendMeterFeaturesRequest();
                     h.setState(WAIT_METER_FEATURES_REPLY);
@@ -399,6 +415,19 @@
 
             @Override
             void processOFError(OFChannelHandler h, OFErrorMsg m) {
+                if (m.getErrType() == OFErrorType.BAD_REQUEST) {
+                    OFBadRequestErrorMsg badRequest = (OFBadRequestErrorMsg) m;
+                    if (badRequest.getCode() == OFBadRequestCode.BAD_TYPE) {
+                         log.debug("{} does not support GetConfig, moving on", h.getSwitchInfoString());
+                         try {
+                            nextState(h);
+                            return;
+                        } catch (IOException e) {
+                            log.error("Exception thrown transitioning to next", e);
+                            logErrorDisconnect(h, m);
+                        }
+                    }
+                }
                 logErrorDisconnect(h, m);
             }
 
@@ -1058,8 +1087,7 @@
                         h.channel.getRemoteAddress());
                 return;
             }
-            OFFactory factory = (h.ofVersion == OFVersion.OF_13) ?
-                    h.controller.getOFMessageFactory13() : h.controller.getOFMessageFactory10();
+            OFFactory factory = OFFactories.getFactory(h.ofVersion);
                     OFEchoReply reply = factory
                             .buildEchoReply()
                             .setXid(m.getXid())
@@ -1248,7 +1276,7 @@
     @Override
     public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
             throws Exception {
-        OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
+        OFFactory factory = OFFactories.getFactory(ofVersion);
         OFMessage m = factory.buildEchoRequest().build();
         log.debug("Sending Echo Request on idle channel: {}",
                 e.getChannel().getPipeline().getLast());
@@ -1335,16 +1363,21 @@
         // The OF protocol requires us to start things off by sending the highest
         // version of the protocol supported.
 
-        // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
+        // bitmap represents OF1.0, OF1.3, and OF1.4
         // see Sec. 7.5.1 of the OF1.3.4 spec
-        U32 bitmap = U32.ofRaw(0x00000012);
-        OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
+        U32 bitmap = U32.ofRaw((0b1 << OFVersion.OF_10.getWireVersion()) |
+                               (0b1 << OFVersion.OF_13.getWireVersion()) |
+                               (0b1 << OFVersion.OF_14.getWireVersion()));
+        OFVersion version = Optional.ofNullable(ofVersion).orElse(OFVersion.OF_13);
+        OFHelloElem hem = OFFactories.getFactory(version)
+                .buildHelloElemVersionbitmap()
                 .setBitmaps(Collections.singletonList(bitmap))
                 .build();
-        OFMessage.Builder mb = factory13.buildHello()
+        OFMessage.Builder mb = OFFactories.getFactory(version)
+                .buildHello()
                 .setXid(this.handshakeTransactionIds--)
                 .setElements(Collections.singletonList(hem));
-        log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
+        log.info("Sending {} Hello to {}", version, channel.getRemoteAddress());
         channel.write(Collections.singletonList(mb.build()));
     }
 
@@ -1353,7 +1386,7 @@
      * @throws IOException
      */
     private void sendHandshakeFeaturesRequestMessage() throws IOException {
-        OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
+        OFFactory factory = OFFactories.getFactory(ofVersion);
         log.debug("Sending FEATURES_REQUEST to {}", channel.getRemoteAddress());
         OFMessage m = factory.buildFeaturesRequest()
                 .setXid(this.handshakeTransactionIds--)
@@ -1367,7 +1400,7 @@
      * @throws IOException
      */
     private void sendHandshakeSetConfig() throws IOException {
-        OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
+        OFFactory factory = OFFactories.getFactory(ofVersion);
         log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
         List<OFMessage> msglist = new ArrayList<>(3);
 
@@ -1406,7 +1439,7 @@
      */
     private void sendHandshakeDescriptionStatsRequest() throws IOException {
         // Get Description to set switch-specific flags
-        OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
+        OFFactory factory = OFFactories.getFactory(ofVersion);
         log.debug("Sending DESC_STATS_REQUEST to {}", channel.getRemoteAddress());
         OFDescStatsRequest dreq = factory
                 .buildDescStatsRequest()
@@ -1422,7 +1455,7 @@
      */
     private void sendMeterFeaturesRequest() throws IOException {
         // Get meter features including the MaxMeters value available for the device
-        OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
+        OFFactory factory = OFFactories.getFactory(ofVersion);
         log.debug("Sending METER_FEATURES_REQUEST to {}", channel.getRemoteAddress());
         OFMeterFeaturesStatsRequest mfreq = factory
                 .buildMeterFeaturesStatsRequest()
@@ -1433,8 +1466,8 @@
 
     private void sendHandshakeOFPortDescRequest() throws IOException {
         log.debug("Sending OF_PORT_DESC_REQUEST to {}", channel.getRemoteAddress());
-        // Get port description for 1.3 switch
-        OFPortDescStatsRequest preq = factory13
+        // Get port description for 1.3+ switch
+        OFPortDescStatsRequest preq = OFFactories.getFactory(ofVersion)
                 .buildPortDescStatsRequest()
                 .setXid(handshakeTransactionIds--)
                 .build();
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 7fd344d..f9858a4 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
@@ -35,6 +35,7 @@
 import org.onosproject.net.ChannelSpacing;
 import org.onosproject.net.CltSignalType;
 import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultAnnotations.Builder;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.GridType;
@@ -83,6 +84,8 @@
 import org.projectfloodlight.openflow.protocol.OFObject;
 import org.projectfloodlight.openflow.protocol.OFPortConfig;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortDescPropEthernet;
+import org.projectfloodlight.openflow.protocol.OFPortDescPropOptical;
 import org.projectfloodlight.openflow.protocol.OFPortDescPropOpticalTransport;
 import org.projectfloodlight.openflow.protocol.OFPortFeatures;
 import org.projectfloodlight.openflow.protocol.OFPortMod;
@@ -110,6 +113,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Optional;
 import java.util.Properties;
 import java.util.Set;
 import java.util.Timer;
@@ -137,6 +141,63 @@
 
     private static final Logger LOG = getLogger(OpenFlowDeviceProvider.class);
 
+    // TODO Some duplicate with one defined in OpticalAnnotations
+    // slice out optical specific handling and consolidate.
+    /**
+     * Annotation key for minimum frequency in Hz.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_MIN_FREQ_HZ = "minFrequency";
+
+    /**
+     * Annotation key for maximum frequency in Hz.
+     * Value is expected be an integer.
+     */
+    public static final String AK_MAX_FREQ_HZ = "maxFrequency";
+
+    /**
+     * Annotation key for grid in Hz.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_GRID_HZ = "grid";
+
+
+    /**
+     * Annotation key for minimum frequency in Hz.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_TX_MIN_FREQ_HZ = "txMinFrequency";
+
+    /**
+     * Annotation key for maximum frequency in Hz.
+     * Value is expected be an integer.
+     */
+    public static final String AK_TX_MAX_FREQ_HZ = "txMaxFrequency";
+
+    /**
+     * Annotation key for grid in Hz.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_TX_GRID_HZ = "txGrid";
+
+    /**
+     * Annotation key for minimum frequency in Hz.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_RX_MIN_FREQ_HZ = "rxMinFrequency";
+
+    /**
+     * Annotation key for maximum frequency in Hz.
+     * Value is expected be an integer.
+     */
+    public static final String AK_RX_MAX_FREQ_HZ = "rxMaxFrequency";
+
+    /**
+     * Annotation key for grid in Hz.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_RX_GRID_HZ = "rxGrid";
+
     //TODO consider renaming KBPS and MBPS (as they are used to convert by division)
     private static final long KBPS = 1_000;
     private static final long MBPS = 1_000 * 1_000;
@@ -383,6 +444,7 @@
                     .set(AnnotationKeys.CHANNEL_ID, sw.channelId())
                     .set(AnnotationKeys.MANAGEMENT_ADDRESS, sw.channelId().split(":")[0]);
 
+            // FIXME following ignores driver specified by name
             Driver driver = driverService.getDriver(sw.manufacturerDescription(),
                     sw.hardwareDescription(),
                     sw.softwareDescription());
@@ -494,9 +556,12 @@
             if (!((Device.Type.ROADM.equals(sw.deviceType())) ||
                     (Device.Type.OTN.equals(sw.deviceType())) ||
                     (Device.Type.OPTICAL_AMPLIFIER.equals(sw.deviceType())))) {
-                  sw.getPorts().forEach(port -> portDescs.add(buildPortDescription(port)));
+                // build regular (=non-optical) Device ports
+                sw.getPorts().forEach(port -> portDescs.add(buildPortDescription(port)));
             }
 
+            // TODO handle Optical Device, but plain OF devices(1.4 and later)
+
             OpenFlowOpticalSwitch opsw;
             switch (sw.deviceType()) {
                 case ROADM:
@@ -612,7 +677,7 @@
             PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
             boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN)
                     && !port.getConfig().contains(OFPortConfig.PORT_DOWN);
-            SparseAnnotations annotations = makePortAnnotation(port.getName(), port.getHwAddr().toString());
+            SparseAnnotations annotations = makePortAnnotation(port.getName(), port.getHwAddr().toString()).build();
 
             OFExpPortDescPropOpticalTransport firstProp = port.getProperties().get(0);
             OFPortOpticalTransportSignalType sigType = firstProp.getPortSignalType();
@@ -672,14 +737,14 @@
         }
 
         /**
-         * Creates an annotation for the port name if one is available.
+         * Creates an annotation builder for the port name if one is available.
          *
          * @param portName the port name
          * @param portMac the port mac
-         * @return annotation containing port name and/or port MAC if any of
+         * @return annotation builder containing port name and/or port MAC if any of
          *          the two is found, empty otherwise
          */
-        private SparseAnnotations makePortAnnotation(String portName, String portMac) {
+        private DefaultAnnotations.Builder makePortAnnotation(String portName, String portMac) {
             DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
             String pName = Strings.emptyToNull(portName);
             String pMac = Strings.emptyToNull(portMac);
@@ -689,8 +754,96 @@
             if (pMac != null) {
                 builder.set(AnnotationKeys.PORT_MAC, pMac);
             }
-            //Returns an empty annotation if both pName and pMac are null
-            return builder.build();
+            return builder;
+        }
+
+        private String mhzToAnnotation(long freqMhz) {
+            // annotations is in Hz
+            return Long.toString(freqMhz * 1_000_000);
+        }
+
+        private PortDescription buildPortDescription14(OFPortDesc port) {
+            PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
+            boolean enabled =
+                    !port.getState().contains(OFPortState.LINK_DOWN) &&
+                    !port.getConfig().contains(OFPortConfig.PORT_DOWN);
+            Builder annotations = makePortAnnotation(port.getName(),
+                                                     port.getHwAddr().toString());
+
+            Optional<OFPortDescPropEthernet> ether = port.getProperties().stream()
+                .filter(OFPortDescPropEthernet.class::isInstance)
+                .map(OFPortDescPropEthernet.class::cast)
+                .findAny();
+            if (ether.isPresent()) {
+                // ethernet port
+                // TODO parse other part of OFPortDescPropEthernet if necessary
+                return new DefaultPortDescription(portNo,
+                                                  enabled,
+                                                  COPPER,
+                                                  portSpeed(port),
+                                                  annotations.build());
+            }
+
+            Optional<OFPortDescPropOptical> optical = port.getProperties().stream()
+                    .filter(OFPortDescPropOptical.class::isInstance)
+                    .map(OFPortDescPropOptical.class::cast)
+                    .findAny();
+            if (optical.isPresent()) {
+                // optical port
+
+                // FIXME is there a generic way to access OFOpticalPortFeaturesSerializerVer14
+
+                // wire value for OFOpticalPortFeatures.USE_FREQ
+                final long useFreq = 0x8;
+                if ((optical.get().getSupported() & useFreq) != 0) {
+                    // unit is in Frequency Mhz
+                    long rxMinFreq = optical.get().getRxMinFreqLmda();
+                    long rxMaxFreq = optical.get().getRxMaxFreqLmda();
+                    long txMinFreq = optical.get().getTxMinFreqLmda();
+                    long txMaxFreq = optical.get().getTxMaxFreqLmda();
+
+                    annotations.set(AK_RX_MIN_FREQ_HZ, mhzToAnnotation(rxMinFreq));
+                    annotations.set(AK_RX_MAX_FREQ_HZ, mhzToAnnotation(rxMaxFreq));
+
+                    annotations.set(AK_TX_MIN_FREQ_HZ, mhzToAnnotation(txMinFreq));
+                    annotations.set(AK_TX_MAX_FREQ_HZ, mhzToAnnotation(txMaxFreq));
+
+                    // FIXME pretty confident this is not going to happen
+                    // unless Device models Tx/Rx ports as separate port
+                    if (rxMinFreq == txMinFreq) {
+                        annotations.set(AK_MIN_FREQ_HZ,
+                                        mhzToAnnotation(rxMinFreq));
+                    }
+                    if (rxMaxFreq == txMaxFreq) {
+                        annotations.set(AK_MAX_FREQ_HZ,
+                                        mhzToAnnotation(rxMaxFreq));
+                    }
+
+                } else {
+                    // unit is in Lambda nm * 100
+                    // TODO implement
+                    LOG.trace("Optical parameter specified in Lambda not supported yet", portNo);
+                }
+                // TODO parse other part of OFPortDescPropOptical
+                // Tx/Rx tunable, ...
+                // Power is configurable or now
+
+                // TODO How to determine appropriate port type?
+
+                return new DefaultPortDescription(portNo,
+                                                  enabled,
+                                                  FIBER,
+                                                  portSpeed(port),
+                                                  annotations.build());
+            }
+
+            // fall back default
+            return new DefaultPortDescription(portNo,
+                                              enabled,
+                                              COPPER,
+                                              portSpeed(port),
+                                              annotations.build());
+
         }
 
         /**
@@ -700,12 +853,15 @@
          * @return portDescription for the port.
          */
         private PortDescription buildPortDescription(OFPortDesc port) {
+            if (port.getVersion().wireVersion >= OFVersion.OF_14.getWireVersion()) {
+                return buildPortDescription14(port);
+            }
             PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
             boolean enabled =
                     !port.getState().contains(OFPortState.LINK_DOWN) &&
                             !port.getConfig().contains(OFPortConfig.PORT_DOWN);
             Port.Type type = port.getCurr().contains(OFPortFeatures.PF_FIBER) ? FIBER : COPPER;
-            SparseAnnotations annotations = makePortAnnotation(port.getName(), port.getHwAddr().toString());
+            SparseAnnotations annotations = makePortAnnotation(port.getName(), port.getHwAddr().toString()).build();
             return new DefaultPortDescription(portNo, enabled, type,
                                               portSpeed(port), annotations);
         }
@@ -727,7 +883,7 @@
 
             boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN)
                     && !port.getConfig().contains(OFPortConfig.PORT_DOWN);
-            SparseAnnotations annotations = makePortAnnotation(port.getName(), port.getHwAddr().toString());
+            SparseAnnotations annotations = makePortAnnotation(port.getName(), port.getHwAddr().toString()).build();
 
             if (port.getVersion() == OFVersion.OF_13
                     && ptype == PortDescPropertyType.OPTICAL_TRANSPORT) {
@@ -805,7 +961,7 @@
 
             // FIXME when Calient OF agent reports port status
             boolean enabled = true;
-            SparseAnnotations annotations = makePortAnnotation(name, port.getHwAddr().toString());
+            SparseAnnotations annotations = makePortAnnotation(name, port.getHwAddr().toString()).build();
 
             // S160 data sheet
             // Wavelength range: 1260 - 1630 nm, grid is irrelevant for this type of switch
@@ -820,18 +976,34 @@
             } else {
                 PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
                 Port.Type type = port.getCurr().contains(OFPortFeatures.PF_FIBER) ? FIBER : COPPER;
-                SparseAnnotations annotations = makePortAnnotation(port.getName(), port.getHwAddr().toString());
+                SparseAnnotations annotations = makePortAnnotation(port.getName(), port.getHwAddr().toString()).build();
                 return new DefaultPortDescription(portNo, false, true, type,
                                                   portSpeed(port), annotations);
             }
         }
 
+        /**
+         * Returns port speed in Mbps.
+         *
+         * @param port description to parse
+         * @return port speed in Mbps
+         */
         private long portSpeed(OFPortDesc port) {
+            if (port.getVersion().getWireVersion() >= OFVersion.OF_14.getWireVersion()) {
+                // OFPortDescPropEthernet
+                return port.getProperties().stream()
+                        .filter(OFPortDescPropEthernet.class::isInstance)
+                        .map(OFPortDescPropEthernet.class::cast)
+                        .mapToLong(OFPortDescPropEthernet::getCurrSpeed)
+                        .map(kbps -> kbps / KBPS)
+                        .findAny()
+                        .orElse(PortSpeed.SPEED_NONE.getSpeedBps() / MBPS);
+            }
             if (port.getVersion() == OFVersion.OF_13) {
                 // Note: getCurrSpeed() returns a value in kbps (this also applies to OF_11 and OF_12)
                 return port.getCurrSpeed() / KBPS;
             }
-
+            // < OF1.3
             PortSpeed portSpeed = PortSpeed.SPEED_NONE;
             for (OFPortFeatures feat : port.getCurr()) {
                 portSpeed = PortSpeed.max(portSpeed, feat.getPortSpeed());
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
index 02ebebe..6ed4cca 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
@@ -133,6 +133,7 @@
         case OF_10:
             return new FlowModBuilderVer10(flowRule, factory, xid, driverService);
         case OF_13:
+        case OF_14:
             return new FlowModBuilderVer13(flowRule, factory, xid, driverService);
         default:
             throw new UnsupportedOperationException(