[ONOS-5599] Recover OF multi part meter features stats message as part of handshake.

Change-Id: I047f1c9b1512b136cc441c8d0822ba2552f2d92e
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/AbstractOpenFlowSwitch.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/AbstractOpenFlowSwitch.java
index 465dd06..128863e 100644
--- a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/AbstractOpenFlowSwitch.java
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/AbstractOpenFlowSwitch.java
@@ -30,6 +30,7 @@
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
 import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsReply;
 import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleRequest;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
@@ -82,6 +83,8 @@
     protected OFFeaturesReply features;
     protected OFDescStatsReply desc;
 
+    protected OFMeterFeaturesStatsReply meterfeatures;
+
     // messagesPendingMastership is used as synchronization variable for
     // all mastership related changes. In this block, mastership (including
     // role update) will have either occurred or not.
@@ -242,6 +245,11 @@
     }
 
     @Override
+    public void setMeterFeaturesReply(OFMeterFeaturesStatsReply meterFeaturesReply) {
+        meterfeatures = meterFeaturesReply;
+    }
+
+    @Override
     public abstract Boolean supportNxRole();
 
     //************************
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 9cc0291..fc38aeb 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
@@ -23,6 +23,7 @@
 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
 import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsReply;
 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFVersion;
 
@@ -134,6 +135,12 @@
     void setFeaturesReply(OFFeaturesReply featuresReply);
 
     /**
+     *  Sets the meter features reply for this switch.
+     * @param meterFeaturesReply the meter features to set.
+     */
+    void setMeterFeaturesReply(OFMeterFeaturesStatsReply meterFeaturesReply);
+
+    /**
      * Sets the switch description.
      * @param desc the descriptions
      */
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 79eaa6b..930f5dd 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
@@ -56,6 +56,8 @@
 import org.projectfloodlight.openflow.protocol.OFHello;
 import org.projectfloodlight.openflow.protocol.OFHelloElem;
 import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsReply;
+import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsRequest;
 import org.projectfloodlight.openflow.protocol.OFPacketIn;
 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
@@ -102,6 +104,7 @@
     // Temporary storage for switch-features and port-description
     private OFFeaturesReply featuresReply;
     private List<OFPortDescStatsReply> portDescReplies;
+    private OFMeterFeaturesStatsReply meterFeaturesReply;
     //private OFPortDescStatsReply portDescReply;
     // a concurrent ArrayList to temporarily store port status messages
     // before we are ready to deal with them
@@ -357,8 +360,16 @@
                             h.getSwitchInfoString(),
                             m.getMissSendLen());
                 }
-                h.sendHandshakeDescriptionStatsRequest();
-                h.setState(WAIT_DESCRIPTION_STAT_REPLY);
+
+                if (h.ofVersion == OFVersion.OF_13) {
+                    // Meters were introduced in OpenFlow 1.3
+                    h.sendMeterFeaturesRequest();
+                    h.setState(WAIT_METER_FEATURES_REPLY);
+                }
+                else {
+                    h.sendHandshakeDescriptionStatsRequest();
+                    h.setState(WAIT_DESCRIPTION_STAT_REPLY);
+                }
             }
 
             @Override
@@ -432,6 +443,7 @@
                 h.sw.setFeaturesReply(h.featuresReply);
                 //h.sw.setPortDescReply(h.portDescReply);
                 h.sw.setPortDescReplies(h.portDescReplies);
+                h.sw.setMeterFeaturesReply(h.meterFeaturesReply);
                 h.sw.setConnected(true);
                 h.sw.setChannel(h.channel);
 //                boolean success = h.sw.connectSwitch();
@@ -557,6 +569,54 @@
 
         },
 
+        /**
+         * We are expecting a OF Multi Part Meter Features Stats Reply.
+         * Notice that this information is only available for switches running
+         * OpenFlow 1.3
+         */
+        WAIT_METER_FEATURES_REPLY(true) {
+            @Override
+            void processOFError(OFChannelHandler h, OFErrorMsg m)
+                    throws IOException {
+                // will never be called. We override processOFMessage
+            }
+
+            @Override
+            void processOFStatisticsReply(OFChannelHandler h,
+                                          OFStatsReply  m)
+                    throws IOException, SwitchStateException {
+                switch(m.getStatsType()) {
+                    case METER_FEATURES:
+
+                        log.debug("Received Meter Features");
+                        OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply)m;
+                        log.info("Received meter features from {} with max meters: {}",
+                                h.getSwitchInfoString(),
+                                ofmfsr.getFeatures().getMaxMeter());
+                        h.meterFeaturesReply = ofmfsr;
+                        h.sendHandshakeDescriptionStatsRequest();
+                        h.setState(WAIT_DESCRIPTION_STAT_REPLY);
+                        break;
+                    default:
+                        log.error("Unexpected OF Multi Part stats reply");
+                        illegalMessageReceived(h, m);
+                        break;
+                }
+            }
+
+            @Override
+            void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply  m)
+                    throws IOException, SwitchStateException {
+                illegalMessageReceived(h, m);
+            }
+
+            @Override
+            void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
+                    throws IOException {
+                h.pendingPortStatusMsg.add(m);
+            }
+        },
+
 
         /**
          * This controller is in MASTER role for this switch. We enter this state
@@ -1307,6 +1367,20 @@
         channel.write(Collections.singletonList(dreq));
     }
 
+    /**
+     * send a meter features request
+     * @throws IOException
+     */
+    private void sendMeterFeaturesRequest() throws IOException {
+        // Get meter features including the MaxMeters value available for the device
+        OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
+        OFMeterFeaturesStatsRequest mfreq = factory
+                .buildMeterFeaturesStatsRequest()
+                .setXid(handshakeTransactionIds--)
+                .build();
+        channel.write(Collections.singletonList(mfreq));
+    }
+
     private void sendHandshakeOFPortDescRequest() throws IOException {
         // Get port description for 1.3 switch
         OFPortDescStatsRequest preq = factory13
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OpenflowSwitchDriverAdapter.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OpenflowSwitchDriverAdapter.java
index 115c9ea..11c47e1 100644
--- a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OpenflowSwitchDriverAdapter.java
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OpenflowSwitchDriverAdapter.java
@@ -31,6 +31,7 @@
 import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
 import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsReply;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
 import org.projectfloodlight.openflow.protocol.OFVersion;
@@ -120,6 +121,11 @@
     }
 
     @Override
+    public void setMeterFeaturesReply(OFMeterFeaturesStatsReply meterFeaturesReply) {
+
+    }
+
+    @Override
     public void setSwitchDescription(OFDescStatsReply desc) {
 
     }