CORD-13:Table Statistics support along with CLI and REST

Change-Id: Ic7facc73754c4b1e7c9c5a9eecd217f89a67e135
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 949c657..4c38d7a 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
@@ -21,6 +21,7 @@
 import com.google.common.cache.RemovalNotification;
 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;
@@ -32,6 +33,7 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.flow.CompletedBatchOperation;
+import org.onosproject.net.flow.DefaultTableStatisticsEntry;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.FlowRuleBatchEntry;
@@ -40,6 +42,7 @@
 import org.onosproject.net.flow.FlowRuleProvider;
 import org.onosproject.net.flow.FlowRuleProviderRegistry;
 import org.onosproject.net.flow.FlowRuleProviderService;
+import org.onosproject.net.flow.TableStatisticsEntry;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.net.statistic.DefaultLoad;
@@ -58,6 +61,8 @@
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
 import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
+import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
+import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 import org.projectfloodlight.openflow.protocol.OFPortStatus;
 import org.projectfloodlight.openflow.protocol.OFStatsReply;
@@ -70,6 +75,7 @@
 import java.util.Dictionary;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.Timer;
@@ -121,6 +127,8 @@
 
     // NewAdaptiveFlowStatsCollector Set
     private final Map<Dpid, NewAdaptiveFlowStatsCollector> afsCollectors = Maps.newHashMap();
+    private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
+    private final Map<Dpid, TableStatisticsCollector> tableStatsCollectors = Maps.newHashMap();
 
     /**
      * Creates an OpenFlow host provider.
@@ -214,6 +222,9 @@
             fsc.start();
             simpleCollectors.put(new Dpid(sw.getId()), fsc);
         }
+        TableStatisticsCollector tsc = new TableStatisticsCollector(timer, sw, flowPollFrequency);
+        tsc.start();
+        tableStatsCollectors.put(new Dpid(sw.getId()), tsc);
     }
 
     private void stopCollectors() {
@@ -225,17 +236,19 @@
             simpleCollectors.values().forEach(FlowStatsCollector::stop);
             simpleCollectors.clear();
         }
+        tableStatsCollectors.values().forEach(TableStatisticsCollector::stop);
+        tableStatsCollectors.clear();
     }
 
     private void adjustRate() {
         DefaultLoad.setPollInterval(flowPollFrequency);
-
         if (adaptiveFlowSampling) {
             // NewAdaptiveFlowStatsCollector calAndPollInterval
             afsCollectors.values().forEach(fsc -> fsc.adjustCalAndPollInterval(flowPollFrequency));
         } else {
             simpleCollectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency));
         }
+        tableStatsCollectors.values().forEach(tsc -> tsc.adjustPollInterval(flowPollFrequency));
     }
 
     @Override
@@ -384,6 +397,10 @@
                     collector.stop();
                 }
             }
+            TableStatisticsCollector tsc = tableStatsCollectors.remove(dpid);
+            if (tsc != null) {
+                tsc.stop();
+            }
         }
 
         @Override
@@ -413,6 +430,8 @@
                 case STATS_REPLY:
                     if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW) {
                         pushFlowMetrics(dpid, (OFFlowStatsReply) msg);
+                    } else if (((OFStatsReply) msg).getStatsType() == OFStatsType.TABLE) {
+                        pushTableStatistics(dpid, (OFTableStatsReply) msg);
                     }
                     break;
                 case BARRIER_REPLY:
@@ -473,7 +492,6 @@
         private void pushFlowMetrics(Dpid dpid, OFFlowStatsReply replies) {
 
             DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
-            OpenFlowSwitch sw = controller.getSwitch(dpid);
 
             List<FlowEntry> flowEntries = replies.getEntries().stream()
                     .map(entry -> new FlowEntryBuilder(dpid, entry).build())
@@ -512,6 +530,31 @@
                 providerService.pushFlowMetrics(did, flowEntries);
             }
         }
+
+        private void pushTableStatistics(Dpid dpid, OFTableStatsReply replies) {
+
+            DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
+            List<TableStatisticsEntry> tableStatsEntries = replies.getEntries().stream()
+                    .map(entry -> buildTableStatistics(did, entry))
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toList());
+            providerService.pushTableStatistics(did, tableStatsEntries);
+        }
+
+        private TableStatisticsEntry buildTableStatistics(DeviceId deviceId,
+                                                          OFTableStatsEntry ofEntry) {
+            TableStatisticsEntry entry = null;
+            if (ofEntry != null) {
+                entry = new DefaultTableStatisticsEntry(deviceId,
+                                                        ofEntry.getTableId().getValue(),
+                                                        ofEntry.getActiveCount(),
+                                                        ofEntry.getLookupCount().getValue(),
+                                                        ofEntry.getMatchedCount().getValue());
+            }
+
+            return entry;
+
+        }
     }
 
     /**
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
new file mode 100644
index 0000000..922a470
--- /dev/null
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.provider.of.flow.impl;
+
+import org.onlab.util.SharedExecutors;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFTableStatsRequest;
+import org.slf4j.Logger;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Collects Table statistics for the specified switch.
+ */
+class TableStatisticsCollector {
+
+    private final Logger log = getLogger(getClass());
+
+    public static final int SECONDS = 1000;
+
+    private final OpenFlowSwitch sw;
+    private Timer timer;
+    private TimerTask task;
+
+    private int pollInterval;
+
+    /**
+     * Creates a new table statistics collector for the given switch and poll frequency.
+     *
+     * @param timer        timer to use for scheduling
+     * @param sw           switch to pull
+     * @param pollInterval poll frequency in seconds
+     */
+    TableStatisticsCollector(Timer timer, OpenFlowSwitch sw, int pollInterval) {
+        this.timer = timer;
+        this.sw = sw;
+        this.pollInterval = pollInterval;
+    }
+
+    /**
+     * Adjusts poll frequency.
+     *
+     * @param pollInterval poll frequency in seconds
+     */
+    synchronized void adjustPollInterval(int pollInterval) {
+        this.pollInterval = pollInterval;
+        task.cancel();
+        task = new InternalTimerTask();
+        timer.scheduleAtFixedRate(task, pollInterval * SECONDS, pollInterval * 1000);
+    }
+
+    private class InternalTimerTask extends TimerTask {
+        @Override
+        public void run() {
+            if (sw.getRole() == RoleState.MASTER) {
+                log.trace("Collecting stats for {}", sw.getStringId());
+                OFTableStatsRequest request = sw.factory().buildTableStatsRequest()
+                        .build();
+                sw.sendMsg(request);
+            }
+        }
+    }
+
+    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();
+        SharedExecutors.getTimer().scheduleAtFixedRate(task, 1 * SECONDS,
+                                                       pollInterval * SECONDS);
+    }
+
+    public synchronized void stop() {
+        log.debug("Stopping Table Stats collection thread for {}", sw.getStringId());
+        task.cancel();
+        task = null;
+    }
+
+}