[ONOS-6607] Get active flow entries count in FlowRuleService

Change-Id: I68b4d916f92427c06a82d3622fcc05738f64541c
diff --git a/core/api/src/main/java/org/onosproject/net/flow/FlowRuleService.java b/core/api/src/main/java/org/onosproject/net/flow/FlowRuleService.java
index 37a7c54..017ba979 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/FlowRuleService.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/FlowRuleService.java
@@ -143,4 +143,14 @@
      * @return collection of flow table statistics
      */
     Iterable<TableStatisticsEntry> getFlowTableStatistics(DeviceId deviceId);
+
+    /**
+     * Returns number of flow rules in ADDED state for specified device.
+     *
+     * @param deviceId device identifier
+     * @return number of flow rules in ADDED state
+     */
+    default long getActiveFlowRuleCount(DeviceId deviceId) {
+        return 0;
+    }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/flow/FlowRuleStore.java b/core/api/src/main/java/org/onosproject/net/flow/FlowRuleStore.java
index f1cb90c..0281009 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/FlowRuleStore.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/FlowRuleStore.java
@@ -136,4 +136,12 @@
      * @return the flow table statistics
      */
     Iterable<TableStatisticsEntry> getTableStatistics(DeviceId deviceId);
+
+    /**
+     * Returns number of flow rules in ADDED state for specified device.
+     *
+     * @param deviceId the device ID
+     * @return number of flow rules in ADDED state
+     */
+    long getActiveFlowRuleCount(DeviceId deviceId);
 }
diff --git a/core/common/src/test/java/org/onosproject/store/trivial/SimpleFlowRuleStore.java b/core/common/src/test/java/org/onosproject/store/trivial/SimpleFlowRuleStore.java
index 81c3105..a51c4ad 100644
--- a/core/common/src/test/java/org/onosproject/store/trivial/SimpleFlowRuleStore.java
+++ b/core/common/src/test/java/org/onosproject/store/trivial/SimpleFlowRuleStore.java
@@ -22,6 +22,7 @@
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
+import com.google.common.collect.Streams;
 import com.google.common.util.concurrent.SettableFuture;
 
 import org.apache.felix.scr.annotations.Activate;
@@ -390,6 +391,13 @@
         return ImmutableList.copyOf(tableStats);
     }
 
+    @Override
+    public long getActiveFlowRuleCount(DeviceId deviceId) {
+        return Streams.stream(getTableStatistics(deviceId))
+                .mapToLong(TableStatisticsEntry::activeFlowEntries)
+                .sum();
+    }
+
     private static final class TimeoutFuture
             implements RemovalListener<Integer, SettableFuture<CompletedBatchOperation>> {
         @Override
diff --git a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
index 7e601f2..6c47560 100644
--- a/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
@@ -704,6 +704,11 @@
         return store.getTableStatistics(deviceId);
     }
 
+    @Override
+    public long getActiveFlowRuleCount(DeviceId deviceId) {
+        return store.getActiveFlowRuleCount(deviceId);
+    }
+
     private class InternalDeviceListener implements DeviceListener {
         @Override
         public void event(DeviceEvent event) {
diff --git a/core/store/dist/src/main/java/org/onosproject/store/flow/impl/DistributedFlowRuleStore.java b/core/store/dist/src/main/java/org/onosproject/store/flow/impl/DistributedFlowRuleStore.java
index af388bab..0f7e980 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/flow/impl/DistributedFlowRuleStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/flow/impl/DistributedFlowRuleStore.java
@@ -31,6 +31,7 @@
  import java.util.concurrent.atomic.AtomicReference;
  import java.util.stream.Collectors;
 
+ import com.google.common.collect.Streams;
  import org.apache.felix.scr.annotations.Activate;
  import org.apache.felix.scr.annotations.Component;
  import org.apache.felix.scr.annotations.Deactivate;
@@ -926,6 +927,13 @@
         return ImmutableList.copyOf(tableStats);
     }
 
+    @Override
+    public long getActiveFlowRuleCount(DeviceId deviceId) {
+        return Streams.stream(getTableStatistics(deviceId))
+                .mapToLong(TableStatisticsEntry::activeFlowEntries)
+                .sum();
+    }
+
     private class InternalTableStatsListener
         implements EventuallyConsistentMapListener<DeviceId, List<TableStatisticsEntry>> {
         @Override
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java
index 5dc2d5e..4a64e3b 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java
@@ -331,4 +331,28 @@
         return ok(root).build();
     }
 
+    /**
+     * Gets sum of active entries in all tables for all devices.
+     *
+     * @onos.rsModel StatisticsFlowsActiveEntries
+     * @return 200 OK with JSON encoded array of active entry count per device
+     */
+    @GET
+    @Path("flows/activeentries")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getActiveEntriesCountPerDevice() {
+        final FlowRuleService service = get(FlowRuleService.class);
+        final Iterable<Device> devices = get(DeviceService.class).getDevices();
+        final ObjectNode root = mapper().createObjectNode();
+        final ArrayNode rootArrayNode = root.putArray("statistics");
+        for (final Device device : devices) {
+            long activeEntries = service.getActiveFlowRuleCount(device.id());
+            final ObjectNode entry = mapper().createObjectNode();
+            entry.put("device", device.id().toString());
+            entry.put("activeEntries", activeEntries);
+            rootArrayNode.add(entry);
+        }
+
+        return ok(root).build();
+    }
 }
diff --git a/web/api/src/main/resources/definitions/StatisticsFlowsActiveEntries.json b/web/api/src/main/resources/definitions/StatisticsFlowsActiveEntries.json
new file mode 100644
index 0000000..9a677d7
--- /dev/null
+++ b/web/api/src/main/resources/definitions/StatisticsFlowsActiveEntries.json
@@ -0,0 +1,38 @@
+{
+  "type": "object",
+  "title": "statistics",
+  "required": [
+    "statistics"
+  ],
+  "properties": {
+    "statistics": {
+      "type": "array",
+      "required": [
+        "statistics"
+      ],
+      "xml": {
+        "name": "statistics",
+        "wrapped": true
+      },
+      "items": {
+        "type": "object",
+        "title": "statistics",
+        "required": [
+          "device",
+          "activeEntries"
+        ],
+        "properties": {
+          "device": {
+            "type": "string",
+            "example": "of:0000000000000001"
+          },
+          "activeEntries": {
+            "type": "integer",
+            "format": "int64",
+            "example": 0
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file