Flow stats collection optimization - Flag introduced to stop periodic collection and collect only in 3 scenarios (Flows Add/Delete/Mod, Switch Add/Change, Mastership Change)

Change-Id: I1ad143a416f34135a622818c60dbc97310fe905e
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitchListener.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitchListener.java
index 5ad5a4f..41ec50b 100644
--- a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitchListener.java
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitchListener.java
@@ -55,4 +55,11 @@
      * @param response role reply from the switch
      */
     void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response);
+
+    /**
+     * Notify that role of the switch changed to Master.
+     *
+     * @param dpid the switch for which the role is changed
+     */
+    default void roleChangedToMaster(Dpid dpid) {}
 }
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 8431068..9434c59 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
@@ -340,6 +340,7 @@
             }
             // perform role transition after clearing messages queue
             this.role = RoleState.MASTER;
+            this.agent.roleChangedToMaster(dpid);
         }
     }
 
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowAgent.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowAgent.java
index 4451b19..d6bda5d 100644
--- a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowAgent.java
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowAgent.java
@@ -124,4 +124,11 @@
      * @param listener the OpenFlow classifier listener
      */
     void removeClassifierListener(OpenFlowClassifierListener listener);
+
+    /**
+     * Notify that role of the switch changed to Master.
+     *
+     * @param dpid the switch for which the role is changed
+     */
+    default void roleChangedToMaster(Dpid dpid) {}
 }
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
index 9dd52cc..ae5d94e 100644
--- a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
@@ -949,6 +949,13 @@
         public void removeClassifierListener(OpenFlowClassifierListener listener) {
             ofClassifierListener.remove(listener);
         }
+
+        @Override
+        public void roleChangedToMaster(Dpid dpid) {
+            for (OpenFlowSwitchListener l : ofSwitchListener) {
+                l.roleChangedToMaster(dpid);
+            }
+        }
     }
 
     /**
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowStatsCollector.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowStatsCollector.java
index 638c195..4aa6b21 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowStatsCollector.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowStatsCollector.java
@@ -217,15 +217,21 @@
     }
 
     public synchronized void start() {
-        // Initially start polling quickly. Then drop down to configured value
         log.debug("Starting Stats collection thread for {}", sw.getStringId());
         loadCounter = new SlidingWindowCounter(HIGH_WINDOW);
-        pauseTask = new PauseTimerTask();
-        scheduledPauseTask = executorService.scheduleAtFixedRate(pauseTask, 1 * MS,
-                                            1 * MS, TimeUnit.MILLISECONDS);
-        pollTask = new PollTimerTask();
-        scheduledPollTask = executorService.scheduleAtFixedRate(pollTask, 1 * MS,
-                                            pollInterval * MS, TimeUnit.MILLISECONDS);
+        if (pollInterval > 0) {
+            pauseTask = new PauseTimerTask();
+            scheduledPauseTask = executorService.scheduleAtFixedRate(pauseTask, 1 * MS,
+                    1 * MS, TimeUnit.MILLISECONDS);
+            pollTask = new PollTimerTask();
+            // Initially start polling quickly. Then drop down to configured value
+            scheduledPollTask = executorService.scheduleAtFixedRate(pollTask, 1 * MS,
+                    pollInterval * MS, TimeUnit.MILLISECONDS);
+        } else {
+            // Trigger the poll only once
+            pollTask = new PollTimerTask();
+            executorService.schedule(pollTask, 0, TimeUnit.MILLISECONDS);
+        }
     }
 
     private synchronized void pause() {
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java
index c67f2e9..9874234 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java
@@ -112,6 +112,7 @@
     private long flowMissingXid = NO_FLOW_MISSING_XID;
 
     private FlowRuleService flowRuleService;
+    private boolean pollPeriodically = true;
 
     /**
      * Creates a new adaptive collector for the given switch and default cal_and_poll frequency.
@@ -128,6 +129,9 @@
         flowRuleService = get(FlowRuleService.class);
 
         initMemberVars(pollInterval);
+        if (pollInterval == -1) {
+            pollPeriodically = false;
+        }
     }
 
     /**
@@ -394,27 +398,49 @@
 
         isFirstTimeStart = true;
 
-        // Initially start polling quickly. Then drop down to configured value
         calAndShortFlowsTask = new CalAndShortFlowsTask();
-        calAndShortFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
-                calAndShortFlowsTask,
-                1,
-                calAndPollInterval,
-                TimeUnit.SECONDS);
-
         midFlowsTask = new MidFlowsTask();
-        midFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
-                midFlowsTask,
-                1,
-                midPollInterval,
-                TimeUnit.SECONDS);
-
         longFlowsTask = new LongFlowsTask();
-        longFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
-                longFlowsTask,
-                1,
-                longPollInterval,
-                TimeUnit.SECONDS);
+
+        if (pollPeriodically) {
+            // Initially start polling quickly. Then drop down to configured value
+
+            calAndShortFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+                    calAndShortFlowsTask,
+                    1,
+                    calAndPollInterval,
+                    TimeUnit.SECONDS);
+
+
+            midFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+                    midFlowsTask,
+                    1,
+                    midPollInterval,
+                    TimeUnit.SECONDS);
+
+
+            longFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+                    longFlowsTask,
+                    1,
+                    longPollInterval,
+                    TimeUnit.SECONDS);
+        } else {
+            // Trigger the polls only once
+            adaptiveFlowStatsScheduler.schedule(
+                    calAndShortFlowsTask,
+                    0,
+                    TimeUnit.SECONDS);
+
+            adaptiveFlowStatsScheduler.schedule(
+                    midFlowsTask,
+                    0,
+                    TimeUnit.SECONDS);
+
+            adaptiveFlowStatsScheduler.schedule(
+                    longFlowsTask,
+                    0,
+                    TimeUnit.SECONDS);
+        }
 
         log.info("Started");
     }
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
index c051c5e..b208b1e 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
@@ -105,6 +105,8 @@
 import static org.onosproject.provider.of.flow.impl.OsgiPropertyConstants.ADAPTIVE_FLOW_SAMPLING_DEFAULT;
 import static org.onosproject.provider.of.flow.impl.OsgiPropertyConstants.POLL_FREQUENCY;
 import static org.onosproject.provider.of.flow.impl.OsgiPropertyConstants.POLL_FREQUENCY_DEFAULT;
+import static org.onosproject.provider.of.flow.impl.OsgiPropertyConstants.POLL_STATS_PERIODICALLY;
+import static org.onosproject.provider.of.flow.impl.OsgiPropertyConstants.POLL_STATS_PERIODICALLY_DEFAULT;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -143,6 +145,9 @@
     /** Adaptive Flow Sampling is on or off. */
     private boolean adaptiveFlowSampling = ADAPTIVE_FLOW_SAMPLING_DEFAULT;
 
+    /** Poll Stats Periodically ON/OFF. */
+    private boolean pollStatsPeriodically = POLL_STATS_PERIODICALLY_DEFAULT;
+
     private FlowRuleProviderService providerService;
 
     private final InternalFlowProvider listener = new InternalFlowProvider();
@@ -203,7 +208,6 @@
         try {
             String s = get(properties, POLL_FREQUENCY);
             newFlowPollFrequency = isNullOrEmpty(s) ? flowPollFrequency : Integer.parseInt(s.trim());
-
         } catch (NumberFormatException | ClassCastException e) {
             newFlowPollFrequency = flowPollFrequency;
         }
@@ -212,22 +216,34 @@
             flowPollFrequency = newFlowPollFrequency;
             adjustRate();
         }
-
         log.info("Settings: flowPollFrequency={}", flowPollFrequency);
 
         boolean newAdaptiveFlowSampling;
         String s = get(properties, ADAPTIVE_FLOW_SAMPLING);
         newAdaptiveFlowSampling = isNullOrEmpty(s) ? adaptiveFlowSampling : Boolean.parseBoolean(s.trim());
-
         if (newAdaptiveFlowSampling != adaptiveFlowSampling) {
             // stop previous collector
             stopCollectors();
             adaptiveFlowSampling = newAdaptiveFlowSampling;
-            // create new collectors
-            createCollectors();
+            if (pollStatsPeriodically) {
+                // create new collectors
+                createCollectors();
+            }
         }
-
         log.info("Settings: adaptiveFlowSampling={}", adaptiveFlowSampling);
+
+        boolean newPollStatsPeriodically;
+        String flag = get(properties, POLL_STATS_PERIODICALLY);
+        newPollStatsPeriodically = isNullOrEmpty(flag) ? pollStatsPeriodically : Boolean.parseBoolean(flag.trim());
+        if (newPollStatsPeriodically != pollStatsPeriodically) {
+            // stop previous collector
+            stopCollectors();
+            pollStatsPeriodically = newPollStatsPeriodically;
+            if (pollStatsPeriodically) {
+                createCollectors();
+            }
+        }
+        log.info("Settings: pollStatsPeriodically={}", pollStatsPeriodically);
     }
 
     private Cache<Long, InternalCacheEntry> createBatchCache() {
@@ -325,6 +341,11 @@
         if (collector != null) {
             collector.recordEvents(events);
         }
+
+        if (!pollStatsPeriodically) {
+            log.debug("Triggering Flow/Table Stats, Flow Add/Del/Mod event, switch : {}", dpid.toString());
+            triggerStatsCollection(dpid);
+        }
     }
 
     @Override
@@ -419,12 +440,43 @@
         recordEvents(dpid, (batch.getOperations().size() + 1));
     }
 
+    private void triggerStatsCollection(Dpid dpid) {
+        OpenFlowSwitch sw = controller.getSwitch(dpid);
+        if (sw == null) {
+            return;
+        }
+
+        SwitchDataCollector sdc = adaptiveFlowSampling ? afsCollectors.get(dpid) : simpleCollectors.get(dpid);
+        if (sdc == null) {
+            if (adaptiveFlowSampling) {
+                sdc = new NewAdaptiveFlowStatsCollector(driverService, sw, -1);
+                afsCollectors.put(dpid, (NewAdaptiveFlowStatsCollector) sdc);
+            } else {
+                sdc = new FlowStatsCollector(executorService, sw, -1);
+                simpleCollectors.put(dpid, (FlowStatsCollector) sdc);
+            }
+        }
+        sdc.start();
+
+        TableStatisticsCollector tsc = tableStatsCollectors.get(dpid);
+        if (tsc == null) {
+            tsc = new TableStatisticsCollector(executorService, sw, -1);
+            tableStatsCollectors.put(dpid, tsc);
+        }
+        tsc.start();
+    }
+
     private class InternalFlowProvider
             implements OpenFlowSwitchListener, OpenFlowEventListener {
 
         @Override
         public void switchAdded(Dpid dpid) {
-            createCollector(controller.getSwitch(dpid));
+            if (pollStatsPeriodically) {
+                createCollector(controller.getSwitch(dpid));
+            } else {
+                log.debug("Triggering Flow/Table Stats, Switch: {} added, ", dpid.toString());
+                triggerStatsCollection(dpid);
+            }
         }
 
         @Override
@@ -645,6 +697,14 @@
             }
         }
 
+        @Override
+        public void roleChangedToMaster(Dpid dpid) {
+            if (!pollStatsPeriodically) {
+                log.debug("Triggering Flow/Table Stats, Mastership change: {}, ", dpid.toString());
+                triggerStatsCollection(dpid);
+            }
+        }
+
         private DriverHandler getDriver(DeviceId devId) {
             Driver driver = driverService.getDriver(devId);
             DriverHandler handler = new DefaultDriverHandler(new DefaultDriverData(driver, devId));
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OsgiPropertyConstants.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OsgiPropertyConstants.java
index 36022e8..26d3515 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OsgiPropertyConstants.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OsgiPropertyConstants.java
@@ -25,8 +25,10 @@
 
     public static final String POLL_FREQUENCY = "flowPollFrequency";
     public static final String ADAPTIVE_FLOW_SAMPLING = "adaptiveFlowSampling";
+    public static final String POLL_STATS_PERIODICALLY = "pollStatsPeriodically";
 
     public static final int POLL_FREQUENCY_DEFAULT = 5;
     public static final boolean ADAPTIVE_FLOW_SAMPLING_DEFAULT = false;
+    public static final boolean POLL_STATS_PERIODICALLY_DEFAULT = true;
 
 }
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java
index 466c91a..f2ddc72 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java
@@ -87,11 +87,16 @@
     }
 
     public synchronized void start() {
-        // Initially start polling quickly. Then drop down to configured value
         log.debug("Starting Table Stats collection thread for {}", sw.getStringId());
         task = new InternalTimerTask();
-        scheduledTask = executorService.scheduleAtFixedRate(task, 1 * MS,
-                                            pollInterval * MS, TimeUnit.MILLISECONDS);
+        if (pollInterval > 0) {
+            // Initially start polling quickly. Then drop down to configured value
+            scheduledTask = executorService.scheduleAtFixedRate(task, 1 * MS,
+                    pollInterval * MS, TimeUnit.MILLISECONDS);
+        } else {
+            // Trigger the poll only once
+            executorService.schedule(task, 0, TimeUnit.MILLISECONDS);
+        }
     }
 
     public synchronized void stop() {