[Emu] openTAM: FlowStatisticManager, DistributedFlowStatisticStore, get-flow-stats CLI Implementation and NewAdaptiveFlowStatsCollector update and typo

 - GetFlowStatistics.java
   .Fixed function name typo: immediateLoad()
 - SummaryFlowEntryWithLoad.java
   .Added javadoc
 - TypedFlowEntryWithLoad.java
   .Added javadoc,
   .and replace checknotnull and throw NullPointerException in typedPollInterval() at line 104

Change-Id: I23d2eaf234d0affeb5f927275148d9165c66c774
diff --git a/cli/src/main/java/org/onosproject/cli/Comparators.java b/cli/src/main/java/org/onosproject/cli/Comparators.java
index b0cbbdd..1df2f04 100644
--- a/cli/src/main/java/org/onosproject/cli/Comparators.java
+++ b/cli/src/main/java/org/onosproject/cli/Comparators.java
@@ -25,6 +25,8 @@
 import org.onosproject.net.Port;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.group.Group;
+
+import org.onosproject.net.statistic.TypedFlowEntryWithLoad;
 import org.onosproject.net.topology.TopologyCluster;
 
 import java.util.Comparator;
@@ -115,4 +117,12 @@
     public static final Comparator<Interface> INTERFACES_COMPARATOR = (intf1, intf2) ->
             CONNECT_POINT_COMPARATOR.compare(intf1.connectPoint(), intf2.connectPoint());
 
+    public static final Comparator<TypedFlowEntryWithLoad> TYPEFLOWENTRY_WITHLOAD_COMPARATOR =
+            new Comparator<TypedFlowEntryWithLoad>() {
+                @Override
+                public int compare(TypedFlowEntryWithLoad fe1, TypedFlowEntryWithLoad fe2) {
+                    long delta = fe1.load().rate() -  fe2.load().rate();
+                    return delta == 0 ? 0 : (delta > 0 ? -1 : +1);
+                }
+            };
 }
diff --git a/cli/src/main/java/org/onosproject/cli/net/GetFlowStatistics.java b/cli/src/main/java/org/onosproject/cli/net/GetFlowStatistics.java
new file mode 100644
index 0000000..cafe87f
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/net/GetFlowStatistics.java
@@ -0,0 +1,323 @@
+/*

+ * 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.cli.net;

+

+import org.apache.karaf.shell.commands.Argument;

+import org.apache.karaf.shell.commands.Command;

+import org.apache.karaf.shell.commands.Option;

+import org.onosproject.cli.AbstractShellCommand;

+import org.onosproject.net.ConnectPoint;

+import org.onosproject.net.Device;

+import org.onosproject.net.DeviceId;

+import org.onosproject.net.Port;

+import org.onosproject.net.PortNumber;

+import org.onosproject.net.device.DeviceService;

+import org.onosproject.net.flow.TypedStoredFlowEntry;

+import org.onosproject.net.flow.instructions.Instruction;

+import org.onosproject.net.statistic.FlowStatisticService;

+import org.onosproject.net.statistic.SummaryFlowEntryWithLoad;

+import org.onosproject.net.statistic.TypedFlowEntryWithLoad;

+

+import java.util.List;

+import java.util.Map;

+

+import static org.onosproject.net.DeviceId.deviceId;

+import static org.onosproject.net.PortNumber.portNumber;

+

+/**

+ * Fetches flow statistics with a flow type and instruction type.

+ */

+@Command(scope = "onos", name = "get-flow-stats",

+        description = "Fetches flow stats for a connection point with given flow type and instruction type")

+public class GetFlowStatistics extends AbstractShellCommand {

+    @Argument(index = 0, name = "devicePort",

+            description = "Device[/Port] connectPoint Description",

+            required = true, multiValued = false)

+    String devicePort = null;

+

+    @Option(name = "-s", aliases = "--summary",

+            description = "Show flow stats summary",

+            required = false, multiValued = false)

+    boolean showSummary = true; // default summary

+

+    @Option(name = "-a", aliases = "--all",

+            description = "Show flow stats all",

+            required = false, multiValued = false)

+    boolean showAll = false;

+

+    @Option(name = "-t", aliases = "--topn",

+            description = "Show flow stats topn",

+            required = false, multiValued = false)

+    String showTopn = null;

+

+    @Option(name = "-f", aliases = "--flowType",

+            description = "Flow live type, It includes IMMEDIATE, SHORT, MID, LONG, UNKNOWN"

+                          + ", and is valid with -a or -t option only",

+            required = false, multiValued = false)

+    String flowLiveType = null;

+

+    @Option(name = "-i", aliases = "--instructionType",

+            description = "Flow instruction type, It includes DROP, OUTPUT, GROUP, L0MODIFICATION, L2MODIFICATION,"

+                    + " TABLE, L3MODIFICATION, METADATA"

+                    + ", and is valid with -a or -t option only",

+            required = false, multiValued = false)

+    String instructionType = null;

+

+    @Override

+    protected void execute() {

+        DeviceService deviceService = get(DeviceService.class);

+        FlowStatisticService flowStatsService = get(FlowStatisticService.class);

+

+        String deviceURI = getDeviceId(devicePort);

+        String portURI = getPortNumber(devicePort);

+

+        DeviceId ingressDeviceId = deviceId(deviceURI);

+        PortNumber ingressPortNumber;

+        if (portURI.length() == 0) {

+            ingressPortNumber = null;

+        } else {

+            ingressPortNumber = portNumber(portURI);

+        }

+

+        Device device = deviceService.getDevice(ingressDeviceId);

+        if (device == null) {

+            error("No such device %s", ingressDeviceId.uri());

+            return;

+        }

+

+        if (ingressPortNumber != null) {

+            Port port = deviceService.getPort(ingressDeviceId, ingressPortNumber);

+            if (port == null) {

+                error("No such port %s on device %s", portURI, ingressDeviceId.uri());

+                return;

+            }

+        }

+

+        if (flowLiveType != null) {

+            flowLiveType = flowLiveType.toUpperCase();

+        }

+        if (instructionType != null) {

+            instructionType = instructionType.toUpperCase();

+        }

+

+        // convert String to FlowLiveType and check validity

+        TypedStoredFlowEntry.FlowLiveType inLiveType;

+        if (flowLiveType == null) {

+            inLiveType = null;

+        } else {

+            inLiveType = getFlowLiveType(flowLiveType);

+            if (inLiveType == null) {

+                error("Invalid flow live type [%s] error", flowLiveType);

+                return;

+            }

+        }

+        // convert String to InstructionType and check validity

+        Instruction.Type inInstructionType;

+        if (instructionType == null) {

+            inInstructionType = null;

+        } else {

+            inInstructionType = getInstructionType(instructionType);

+            if (inInstructionType == null) {

+                error("Invalid instruction type [%s] error", instructionType);

+                return;

+            }

+        }

+

+        if (showTopn != null) {

+            int topn = Integer.parseInt(showTopn);

+

+            if (topn <= 0) {

+                topn = 100; //default value

+            } else if (topn > 1000) {

+                topn = 1000; //max value

+            }

+

+            // print show topn head line with type

+            print("deviceId=%s, show TOPN=%s flows, live type=%s, instruction type=%s",

+                    deviceURI,

+                    Integer.toString(topn),

+                    flowLiveType == null ? "ALL" : flowLiveType,

+                    instructionType == null ? "ALL" : instructionType);

+            if (ingressPortNumber == null) {

+                Map<ConnectPoint, List<TypedFlowEntryWithLoad>> typedFlowLoadMap =

+                          flowStatsService.loadTopnByType(device, inLiveType, inInstructionType, topn);

+                // print all ports topn flows load for a given device

+                for (ConnectPoint cp : typedFlowLoadMap.keySet()) {

+                    printPortFlowsLoad(cp, typedFlowLoadMap.get(cp));

+                }

+            } else {

+                List<TypedFlowEntryWithLoad> typedFlowLoad =

+                        flowStatsService.loadTopnByType(device, ingressPortNumber, inLiveType, inInstructionType, topn);

+                // print device/port topn flows load

+                ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);

+                printPortFlowsLoad(cp, typedFlowLoad);

+            }

+        } else if (showAll) { // is true?

+            // print show all head line with type

+            print("deviceId=%s, show ALL flows, live type=%s, instruction type=%s",

+                    deviceURI,

+                    flowLiveType == null ? "ALL" : flowLiveType,

+                    instructionType == null ? "ALL" : instructionType);

+            if (ingressPortNumber == null) {

+                Map<ConnectPoint, List<TypedFlowEntryWithLoad>> typedFlowLoadMap =

+                        flowStatsService.loadAllByType(device, inLiveType, inInstructionType);

+                // print all ports all flows load for a given device

+                for (ConnectPoint cp : typedFlowLoadMap.keySet()) {

+                    printPortFlowsLoad(cp, typedFlowLoadMap.get(cp));

+                }

+            } else {

+                List<TypedFlowEntryWithLoad> typedFlowLoad =

+                        flowStatsService.loadAllByType(device, ingressPortNumber, inLiveType, inInstructionType);

+                // print device/port all flows load

+                ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);

+                printPortFlowsLoad(cp, typedFlowLoad);

+            }

+        } else { // if (showSummary == true) //always is true

+            // print show summary head line

+            print("deviceId=%s, show SUMMARY flows", deviceURI);

+            if (ingressPortNumber == null) {

+                Map<ConnectPoint, SummaryFlowEntryWithLoad> summaryFlowLoadMap =

+                        flowStatsService.loadSummary(device);

+                // print all ports flow load summary for a given device

+                for (ConnectPoint cp : summaryFlowLoadMap.keySet()) {

+                    printPortSummaryLoad(cp, summaryFlowLoadMap.get(cp));

+                }

+            } else {

+                SummaryFlowEntryWithLoad summaryFlowLoad =

+                        flowStatsService.loadSummary(device, ingressPortNumber);

+                // print device/port flow load summary

+                ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);

+                printPortSummaryLoad(cp, summaryFlowLoad);

+            }

+        }

+    }

+

+    /**

+     * Extracts the port number portion of the ConnectPoint.

+     *

+     * @param deviceString string representing the device/port

+     * @return port number as a string, empty string if the port is not found

+     */

+    private String getPortNumber(String deviceString) {

+        if (deviceString == null) {

+            return "";

+        }

+

+        int slash = deviceString.indexOf('/');

+        if (slash <= 0) {

+            return ""; // return when no port number

+        }

+        return deviceString.substring(slash + 1, deviceString.length());

+    }

+

+    /**

+     * Extracts the device ID portion of the ConnectPoint.

+     *

+     * @param deviceString string representing the device/port

+     * @return device ID string

+     */

+    private String getDeviceId(String deviceString) {

+        if (deviceString == null) {

+            return "";

+        }

+

+        int slash = deviceString.indexOf('/');

+        if (slash <= 0) {

+            return deviceString; // return only included device ID

+        }

+        return deviceString.substring(0, slash);

+    }

+

+    /**

+     * converts string of flow live type to FloeLiveType enum.

+     *

+     * @param liveType string representing the flow live type

+     * @return TypedStoredFlowEntry.FlowLiveType

+     */

+    private TypedStoredFlowEntry.FlowLiveType getFlowLiveType(String liveType) {

+        String liveTypeUC = liveType.toUpperCase();

+

+        if (liveTypeUC.equals("IMMEDIATE")) {

+            return TypedStoredFlowEntry.FlowLiveType.IMMEDIATE_FLOW;

+        } else if (liveTypeUC.equals("SHORT")) {

+            return TypedStoredFlowEntry.FlowLiveType.SHORT_FLOW;

+        } else if (liveTypeUC.equals("MID")) {

+            return TypedStoredFlowEntry.FlowLiveType.MID_FLOW;

+        } else if (liveTypeUC.equals("LONG")) {

+            return TypedStoredFlowEntry.FlowLiveType.LONG_FLOW;

+        } else if (liveTypeUC.equals("UNKNOWN")) {

+            return TypedStoredFlowEntry.FlowLiveType.UNKNOWN_FLOW;

+        } else {

+            return null; // flow live type error

+        }

+    }

+

+    /**

+     * converts string of instruction type to Instruction type enum.

+     *

+     * @param instType string representing the instruction type

+     * @return Instruction.Type

+     */

+    private Instruction.Type getInstructionType(String instType) {

+        String instTypeUC = instType.toUpperCase();

+

+        if (instTypeUC.equals("DROP")) {

+            return Instruction.Type.DROP;

+        } else if (instTypeUC.equals("OUTPUT")) {

+            return Instruction.Type.OUTPUT;

+        } else if (instTypeUC.equals("GROUP")) {

+            return Instruction.Type.GROUP;

+        } else if (instTypeUC.equals("L0MODIFICATION")) {

+            return Instruction.Type.L0MODIFICATION;

+        } else if (instTypeUC.equals("L2MODIFICATION")) {

+            return Instruction.Type.L2MODIFICATION;

+        } else if (instTypeUC.equals("TABLE")) {

+            return Instruction.Type.TABLE;

+        } else if (instTypeUC.equals("L3MODIFICATION")) {

+            return Instruction.Type.L3MODIFICATION;

+        } else if (instTypeUC.equals("METADATA")) {

+            return Instruction.Type.METADATA;

+        } else {

+             return null; // instruction type error

+        }

+    }

+

+    private void printPortFlowsLoad(ConnectPoint cp, List<TypedFlowEntryWithLoad> typedFlowLoad) {

+       print("  deviceId/Port=%s/%s, %s flows", cp.elementId(), cp.port(), typedFlowLoad.size());

+        for (TypedFlowEntryWithLoad tfel: typedFlowLoad) {

+            TypedStoredFlowEntry tfe =  tfel.typedStoredFlowEntry();

+            print("    flowId=%s, state=%s, liveType=%s, life=%s -> %s",

+                    Long.toHexString(tfe.id().value()),

+                    tfe.state(),

+                    tfe.flowLiveType(),

+                    tfe.life(),

+                    tfel.load().isValid() ? tfel.load() : "Load{rate=0, NOT VALID}");

+        }

+    }

+

+    private void printPortSummaryLoad(ConnectPoint cp, SummaryFlowEntryWithLoad summaryFlowLoad) {

+        print("  deviceId/Port=%s/%s, Total=%s, Immediate=%s, Short=%s, Mid=%s, Long=%s, Unknown=%s",

+                cp.elementId(),

+                cp.port(),

+                summaryFlowLoad.totalLoad().isValid() ? summaryFlowLoad.totalLoad() : "Load{rate=0, NOT VALID}",

+                summaryFlowLoad.immediateLoad().isValid() ? summaryFlowLoad.immediateLoad() : "Load{rate=0, NOT VALID}",

+                summaryFlowLoad.shortLoad().isValid() ? summaryFlowLoad.shortLoad() : "Load{rate=0, NOT VALID}",

+                summaryFlowLoad.midLoad().isValid() ? summaryFlowLoad.midLoad() : "Load{rate=0, NOT VALID}",

+                summaryFlowLoad.longLoad().isValid() ? summaryFlowLoad.longLoad() : "Load{rate=0, NOT VALID}",

+                summaryFlowLoad.unknownLoad().isValid() ? summaryFlowLoad.unknownLoad() : "Load{rate=0, NOT VALID}");

+    }

+}

diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 459ffa9..8c56a49 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -222,6 +222,12 @@
             </completers>
         </command>
         <command>
+            <action class="org.onosproject.cli.net.GetFlowStatistics"/>
+            <completers>
+                <ref component-id="deviceIdCompleter"/>
+            </completers>
+        </command>
+        <command>
             <action class="org.onosproject.cli.net.AddMultiPointToSinglePointIntentCommand"/>
             <completers>
                 <ref component-id="connectPointCompleter"/>
@@ -333,7 +339,6 @@
         <command>
             <action class="org.onosproject.cli.net.InterfacesListCommand"/>
         </command>
-
         <command>
             <action class="org.onosproject.cli.net.GroupsListCommand"/>
         </command>