Added ability to poll flow counters in BMv2

Also fixed few minor things here and there.

Change-Id: Ib5e6a92de46870f52510cd6fad0cef8da022bb62
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java
index 3f7a3ec..7d78fe3 100644
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java
@@ -48,12 +48,15 @@
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
+import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
+
 /**
  * Flow rule programmable device behaviour implementation for BMv2.
  */
@@ -65,7 +68,7 @@
 
     // There's no Bmv2 client method to poll flow entries from the device. Use a local store.
     // FIXME: this information should be distributed across instances of the cluster.
-    private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, FlowEntry>>
+    private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, TimestampedFlowRule>>
             ENTRIES_MAP = Maps.newConcurrentMap();
 
     // Cache model objects instead of parsing the JSON each time.
@@ -106,10 +109,28 @@
                 ENTRIES_MAP.forEach((key, value) -> {
                     if (key.getLeft() == deviceId && key.getMiddle() == table.name()
                             && value != null) {
+                        long entryId = value.getKey();
                         // Filter entries_map for this device and table.
-                        if (installedEntryIds.contains(value.getKey())) {
+                        if (installedEntryIds.contains(entryId)) {
                             // Entry is installed.
-                            entryList.add(value.getRight());
+                            long bytes = 0L;
+                            long packets = 0L;
+                            if (table.hasCounters()) {
+                                // Read counter values from device.
+                                try {
+                                    Pair<Long, Long> counterValue = deviceClient.readTableEntryCounter(table.name(),
+                                                                                                       entryId);
+                                    bytes = counterValue.getLeft();
+                                    packets = counterValue.getRight();
+                                } catch (Bmv2RuntimeException e) {
+                                    LOG.warn("Unable to get counter values for entry {} of table {} of device {}: {}",
+                                             entryId, table.name(), deviceId, e.toString());
+                                }
+                            }
+                            TimestampedFlowRule tsRule = value.getRight();
+                            FlowEntry entry = new DefaultFlowEntry(tsRule.rule(), ADDED,
+                                                                   tsRule.lifeInSeconds(), packets, bytes);
+                            entryList.add(entry);
                         } else {
                             // No such entry on device, can remove from local store.
                             ENTRIES_MAP.remove(key);
@@ -183,16 +204,14 @@
                                 // Tentatively delete entry before re-adding.
                                 // It might not exist on device due to inconsistencies.
                                 deviceClient.deleteTableEntry(bmv2Entry.tableName(), entryId);
+                                value = null;
                             } catch (Bmv2RuntimeException e) {
                                 // Silently drop exception as we can probably fix this by re-adding the entry.
                             }
                         }
                         // Add entry.
                         entryId = deviceClient.addTableEntry(bmv2Entry);
-                        // TODO: evaluate flow entry life, bytes and packets
-                        FlowEntry flowEntry = new DefaultFlowEntry(
-                                rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0);
-                        value = Pair.of(entryId, flowEntry);
+                        value = Pair.of(entryId, new TimestampedFlowRule(rule));
                     } else {
                         // Remove entry
                         if (value == null) {
@@ -258,4 +277,22 @@
     private enum Operation {
         APPLY, REMOVE
     }
+
+    private class TimestampedFlowRule {
+        private final FlowRule rule;
+        private final Date addedDate;
+
+        public TimestampedFlowRule(FlowRule rule) {
+            this.rule = rule;
+            this.addedDate = new Date();
+        }
+
+        public FlowRule rule() {
+            return rule;
+        }
+
+        public long lifeInSeconds() {
+            return (new Date().getTime() - addedDate.getTime()) / 1000;
+        }
+    }
 }
\ No newline at end of file