Small extensions in server driver

First extension is the ability to prevent the installation
of a full wildcard rule, when only one rule is present per
NIC. Such a rule will redirect all the system's load on one
core, which is undesirable.
Second, we introduce the ability to detect the duration of
load on a core by converting the boolean field isBusy to
a timestamp busySince.

Addressed code review suggestions.

Change-Id: I631cf322ee3724d9f1f97246d4189b5b2a008a76
Signed-off-by: Georgios Katsikas <katsikas.gp@gmail.com>
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/FlowRuleProgrammableServerImpl.java b/drivers/server/src/main/java/org/onosproject/drivers/server/FlowRuleProgrammableServerImpl.java
index 3dc7d59..34947cb 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/FlowRuleProgrammableServerImpl.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/FlowRuleProgrammableServerImpl.java
@@ -251,16 +251,26 @@
 
         rules.forEach(rule -> {
             if (!(rule instanceof FlowEntry)) {
-                NicFlowRule nicRule = (NicFlowRule) rule;
-                String tcId = nicRule.trafficClassId();
+                NicFlowRule nicRule = null;
 
-                // Create a bucket of flow rules for this traffic class
-                if (!rulesPerTc.containsKey(tcId)) {
-                    rulesPerTc.put(tcId, Sets.<FlowRule>newConcurrentHashSet());
+                // Only NicFlowRules are accepted
+                try {
+                    nicRule = (NicFlowRule) rule;
+                } catch (ClassCastException cEx) {
+                    log.warn("Skipping rule not crafted for NIC: {}", rule);
                 }
 
-                Set<FlowRule> tcRuleSet = rulesPerTc.get(tcId);
-                tcRuleSet.add(nicRule);
+                if (nicRule != null) {
+                    String tcId = nicRule.trafficClassId();
+
+                    // Create a bucket of flow rules for this traffic class
+                    if (!rulesPerTc.containsKey(tcId)) {
+                        rulesPerTc.put(tcId, Sets.<FlowRule>newConcurrentHashSet());
+                    }
+
+                    Set<FlowRule> tcRuleSet = rulesPerTc.get(tcId);
+                    tcRuleSet.add(nicRule);
+                }
             }
         });
 
@@ -322,6 +332,11 @@
 
         for (FlowRule rule : rules) {
             NicFlowRule nicRule = (NicFlowRule) rule;
+            if (nicRule.isFullWildcard() && (rules.size() > 1)) {
+                log.warn("Skipping wildcard rule: {}", nicRule);
+                continue;
+            }
+
             long coreIndex = nicRule.cpuCoreIndex();
 
             // Keep the ID of the target NIC
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/ServerDevicesDiscovery.java b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerDevicesDiscovery.java
index b4440e8..3ff7f05 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/ServerDevicesDiscovery.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerDevicesDiscovery.java
@@ -781,14 +781,14 @@
             int cpuId = cpuObjNode.path(CPU_PARAM_ID).asInt();
             float cpuLoad = cpuObjNode.path(CPU_PARAM_LOAD).floatValue();
             int queueId = cpuObjNode.path(CPU_PARAM_QUEUE).asInt();
-            boolean isBusy = cpuObjNode.path(CPU_PARAM_STATUS).booleanValue();
+            int busySince = cpuObjNode.path(CPU_PARAM_STATUS).asInt();
 
             // This is mandatory information
             cpuBuilder.setDeviceId(deviceId)
                     .setId(cpuId)
                     .setLoad(cpuLoad)
                     .setQueue(queueId)
-                    .setIsBusy(isBusy);
+                    .setBusySince(busySince);
 
             // We have all the statistics for this CPU core
             cpuStats.add(cpuBuilder.build());
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultNicFlowRule.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultNicFlowRule.java
index 019b875..8f50986 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultNicFlowRule.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultNicFlowRule.java
@@ -169,7 +169,7 @@
         }
 
         // This action provides basic rule match counters
-        this.actions.add(new NicRuleAction(NicRuleAction.Action.COUNT));
+        // this.actions.add(new NicRuleAction(NicRuleAction.Action.COUNT));
     }
 
     @Override
@@ -307,6 +307,16 @@
     }
 
     @Override
+    public boolean isFullWildcard() {
+        if (((ipv4SrcAddress() != null) && !ipv4SrcAddress().isZero()) ||
+            ((ipv4DstAddress() != null) && !ipv4DstAddress().isZero()) ||
+            (ipv4Protocol() > 0) || (sourcePort() > 0) || (destinationPort() > 0)) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
     public Set<NicRuleAction> actions() {
         return actions;
     }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/FlowRxFilterValue.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/FlowRxFilterValue.java
index 64c792b..c2430c0 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/FlowRxFilterValue.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/FlowRxFilterValue.java
@@ -26,26 +26,29 @@
 public final class FlowRxFilterValue extends RxFilterValue
         implements Comparable {
 
-    private long cpuCoreId;
+    private long value;
     private String flowRule;
 
     /**
      * Constructs a flow-based Rx filter.
+     *
+     * @param cpuId CPU ID of the server this tag will lead to
      */
-    public FlowRxFilterValue() {
-        super();
+    public FlowRxFilterValue(int cpuId) {
+        super(cpuId);
         setValue(0);
         setRule("");
     }
 
     /**
-     * Constructs a flow-based Rx filter with CPU core ID.
+     * Constructs a flow-based Rx filter with physical CPU core ID.
      *
-     * @param cpuCoreId a CPU core ID when the flow ends up
+     * @param value Flow tag
+     * @param cpuId CPU ID of the server this tag will lead to
      */
-    public FlowRxFilterValue(long cpuCoreId) {
-        super();
-        setValue(cpuCoreId);
+    public FlowRxFilterValue(long value, int cpuId) {
+        super(cpuId);
+        setValue(value);
         setRule("");
     }
 
@@ -53,12 +56,13 @@
      * Constructs a flow-based Rx filter with CPU core ID
      * and an associated rule.
      *
-     * @param cpuCoreId a CPU core ID
+     * @param value Flow tag
+     * @param cpuId CPU ID of the server this tag will lead to
      * @param flowRule a flow rule as a string
      */
-    public FlowRxFilterValue(long cpuCoreId, String flowRule) {
-        super();
-        setValue(cpuCoreId);
+    public FlowRxFilterValue(long value, int cpuId, String flowRule) {
+        super(cpuId);
+        setValue(value);
         setRule(flowRule);
     }
 
@@ -68,7 +72,7 @@
      * @param other a source FlowRxFilterValue object
      */
     public FlowRxFilterValue(FlowRxFilterValue other) {
-        super();
+        super(other.cpuId);
         setValue(other.value());
         setRule(other.rule());
     }
@@ -79,18 +83,18 @@
      * @return Flow Rx filter value
      */
     public long value() {
-        return this.cpuCoreId;
+        return this.value;
     }
 
     /**
      * Sets the value of this Rx filter.
      *
-     * @param cpuCoreId a CPU core ID for this Rx filter
+     * @param value a CPU core ID for this Rx filter
      */
-    public void setValue(long cpuCoreId) {
-        checkArgument(cpuCoreId >= 0,
+    private void setValue(long value) {
+        checkArgument(value >= 0,
             "NIC flow Rx filter has invalid CPU core ID");
-        this.cpuCoreId = cpuCoreId;
+        this.value = value;
     }
 
     /**
@@ -115,7 +119,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(this.cpuCoreId, this.flowRule);
+        return Objects.hash(this.value, this.flowRule, this.cpuId);
     }
 
     @Override
@@ -130,8 +134,8 @@
 
         FlowRxFilterValue other = (FlowRxFilterValue) obj;
 
-        return (this.value() == other.value()) &&
-                this.rule().equals(other.rule());
+        return this.value() == other.value() &&
+                this.rule().equals(other.rule()) && ((RxFilterValue) this).equals(other);
     }
 
     @Override
@@ -147,15 +151,15 @@
         if (other instanceof FlowRxFilterValue) {
             FlowRxFilterValue otherRxVal = (FlowRxFilterValue) other;
 
-            long thisCoreId  = this.value();
-            long otherCoreId = otherRxVal.value();
+            long thisCpuId  = this.value();
+            long otherCpuId = otherRxVal.value();
 
-            if (thisCoreId > otherCoreId) {
+            if (thisCpuId > otherCpuId) {
                 return 1;
-            } else if (thisCoreId < otherCoreId) {
+            } else if (thisCpuId < otherCpuId) {
                 return -1;
             } else {
-                return 0;
+                return this.cpuId - otherRxVal.cpuId;
             }
         }
 
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MacRxFilterValue.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MacRxFilterValue.java
index 2676f65..25b28bb 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MacRxFilterValue.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MacRxFilterValue.java
@@ -30,9 +30,10 @@
 
     /**
      * Constructs a MAC-based Rx filter.
+     * @param cpuId CPU ID of the server this tag will lead to
      */
-    public MacRxFilterValue() {
-        super();
+    public MacRxFilterValue(int cpuId) {
+        super(cpuId);
         this.mac = null;
     }
 
@@ -40,9 +41,10 @@
      * Constructs a MAC-based Rx filter with specific MAC address.
      *
      * @param mac a MAC address to use as a filter
+     * @param cpuId CPU ID of the server this tag will lead to
      */
-    public MacRxFilterValue(MacAddress mac) {
-        super();
+    public MacRxFilterValue(MacAddress mac, int cpuId) {
+        super(cpuId);
         setValue(mac);
     }
 
@@ -52,7 +54,7 @@
      * @param other a source MacRxFilterValue object
      */
     public MacRxFilterValue(MacRxFilterValue other) {
-        super();
+        super(other.cpuId);
         setValue(other.value());
     }
 
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MplsRxFilterValue.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MplsRxFilterValue.java
index 2b5112f..ddc1930c 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MplsRxFilterValue.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MplsRxFilterValue.java
@@ -30,9 +30,11 @@
 
     /**
      * Constructs an MPLS-based Rx filter.
+     *
+     * @param cpuId CPU ID of the server this tag will lead to
      */
-    public MplsRxFilterValue() {
-        super();
+    public MplsRxFilterValue(int cpuId) {
+        super(cpuId);
         this.mplsLabel = null;
     }
 
@@ -40,9 +42,10 @@
      * Constructs an MPLS-based Rx filter with specific label.
      *
      * @param mplsLabel an MPLS label to use as a filter
+     * @param cpuId CPU ID of the server this tag will lead to
      */
-    public MplsRxFilterValue(MplsLabel mplsLabel) {
-        super();
+    public MplsRxFilterValue(MplsLabel mplsLabel, int cpuId) {
+        super(cpuId);
         setValue(mplsLabel);
     }
 
@@ -52,7 +55,7 @@
      * @param other a source MplsRxFilterValue object
      */
     public MplsRxFilterValue(MplsRxFilterValue other) {
-        super();
+        super(other.cpuId);
         setValue(other.value());
     }
 
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicFlowRule.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicFlowRule.java
index 1215b6f..9083395 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicFlowRule.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicFlowRule.java
@@ -206,6 +206,13 @@
     boolean hasTransport();
 
     /**
+     * Returns whether this rule is a full wildcard or not.
+     *
+     * @return boolean full wildcard status
+     */
+    boolean isFullWildcard();
+
+    /**
      * Returns the set of actions of this rule.
      *
      * @return rule's set of actions
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/RssRxFilterValue.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/RssRxFilterValue.java
index 08f2a46..2d54a81 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/RssRxFilterValue.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/RssRxFilterValue.java
@@ -28,9 +28,11 @@
 
     /**
      * Constructs an RSS-based Rx filter.
+     *
+     * @param cpuId CPU ID of the server this tag will lead to
      */
-    public RssRxFilterValue() {
-        super();
+    public RssRxFilterValue(int cpuId) {
+        super(cpuId);
         setValue(0);
     }
 
@@ -38,9 +40,10 @@
      * Constructs an RSS-based Rx filter with specific hash.
      *
      * @param rssHash a hash value
+     * @param cpuId CPU ID of the server this tag will lead to
      */
-    public RssRxFilterValue(int rssHash) {
-        super();
+    public RssRxFilterValue(int rssHash, int cpuId) {
+        super(cpuId);
         setValue(rssHash);
     }
 
@@ -50,7 +53,7 @@
      * @param other a source RssRxFilterValue object
      */
     public RssRxFilterValue(RssRxFilterValue other) {
-        super();
+        super(other.cpuId);
         setValue(other.value());
     }
 
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/RxFilterValue.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/RxFilterValue.java
index 10ebe04..dceff82 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/RxFilterValue.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/RxFilterValue.java
@@ -15,12 +15,50 @@
  */
 package org.onosproject.drivers.server.devices.nic;
 
+import java.util.Objects;
+
 /**
  * The base class that holds the value of a NIC's Rx filter.
  */
 public abstract class RxFilterValue {
 
-    public RxFilterValue() {
+    /* CPU id of the server this tag will lead to */
+    protected int cpuId;
+
+    /**
+     * Constructs an Rx filter value.
+     *
+     * @param cpuId CPU ID of the server this tag will lead to
+     */
+    public RxFilterValue(int cpuId) {
+        this.cpuId = cpuId;
+    }
+
+    /**
+     * Returns the CPU ID that corresponds to this Rx filter value.
+     *
+     * @return CPU ID of the server this tag will lead to
+     */
+    public int cpuId() {
+        return this.cpuId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(this.cpuId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if ((obj == null) || (!(obj instanceof RxFilterValue))) {
+            return false;
+        }
+
+        return cpuId == ((RxFilterValue) obj).cpuId;
     }
 
 }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/VlanRxFilterValue.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/VlanRxFilterValue.java
index 18a99be..8e16321 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/VlanRxFilterValue.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/VlanRxFilterValue.java
@@ -30,9 +30,10 @@
 
     /**
      * Constructs a VLAN-based Rx filter.
+     * @param cpuId CPU id of the server this tag will lead to
      */
-    public VlanRxFilterValue() {
-        super();
+    public VlanRxFilterValue(int cpuId) {
+        super(cpuId);
         this.vlanId = VlanId.NONE;
     }
 
@@ -40,9 +41,10 @@
      * Constructs a VLAN-based Rx filter with specific ID.
      *
      * @param vlanId a VLAN ID to use as a filter
+     * @param cpuId CPU id of the server this tag will lead to
      */
-    public VlanRxFilterValue(VlanId vlanId) {
-        super();
+    public VlanRxFilterValue(VlanId vlanId, int cpuId) {
+        super(cpuId);
         setValue(vlanId);
     }
 
@@ -52,7 +54,7 @@
      * @param other a source VlanRxFilterValue object
      */
     public VlanRxFilterValue(VlanRxFilterValue other) {
-        super();
+        super(other.cpuId);
         setValue(other.value());
     }
 
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultCpuStatistics.java b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultCpuStatistics.java
index 99e6c84..f2b6968 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultCpuStatistics.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultCpuStatistics.java
@@ -48,7 +48,7 @@
     private final int id;
     private final float load;
     private final int queue;
-    private final boolean isBusy;
+    private final int busySince;
     private final Optional<MonitoringUnit> throughputUnit;
     private final Optional<Float> averageThroughput;
     private final Optional<MonitoringUnit> latencyUnit;
@@ -56,11 +56,11 @@
     private final Optional<Float> averageLatency;
     private final Optional<Float> maxLatency;
 
-    private DefaultCpuStatistics(DeviceId deviceId, int id, float load, int queue, boolean isBusy) {
-        this(deviceId, id, load, queue, isBusy, null, -1, null, -1, -1, -1);
+    private DefaultCpuStatistics(DeviceId deviceId, int id, float load, int queue, int busySince) {
+        this(deviceId, id, load, queue, busySince, null, -1, null, -1, -1, -1);
     }
 
-    private DefaultCpuStatistics(DeviceId deviceId, int id, float load, int queue, boolean isBusy,
+    private DefaultCpuStatistics(DeviceId deviceId, int id, float load, int queue, int busySince,
             MonitoringUnit throughputUnit, float averageThroughput, MonitoringUnit latencyUnit,
             float minLatency, float averageLatency, float maxLatency) {
         checkNotNull(deviceId, "Device ID is NULL");
@@ -69,11 +69,11 @@
         checkArgument((load >= MIN_CPU_LOAD) && (load <= MAX_CPU_LOAD),
             "Invalid CPU load " + Float.toString(load) + ", not in [" + MIN_CPU_LOAD + ", " + MAX_CPU_LOAD + "]");
 
-        this.deviceId = deviceId;
-        this.id       = id;
-        this.load     = load;
-        this.queue    = queue;
-        this.isBusy   = isBusy;
+        this.deviceId  = deviceId;
+        this.id        = id;
+        this.load      = load;
+        this.queue     = queue;
+        this.busySince = busySince;
 
         this.throughputUnit = (throughputUnit == null) ?
                 Optional.empty() : Optional.ofNullable(throughputUnit);
@@ -91,11 +91,11 @@
 
     // Constructor for serializer
     private DefaultCpuStatistics() {
-        this.deviceId = null;
-        this.id       = 0;
-        this.load     = 0;
-        this.queue    = 0;
-        this.isBusy   = false;
+        this.deviceId  = null;
+        this.id        = 0;
+        this.load      = 0;
+        this.queue     = 0;
+        this.busySince = -1;
 
         this.throughputUnit = null;
         this.averageThroughput = null;
@@ -131,7 +131,12 @@
 
     @Override
     public boolean busy() {
-        return this.isBusy;
+        return this.busySince >= 0;
+    }
+
+    @Override
+    public int busySince() {
+        return this.busySince;
     }
 
     @Override
@@ -172,7 +177,7 @@
                 .add("id",     id())
                 .add("load",   load())
                 .add("queue",  queue())
-                .add("isBusy", busy())
+                .add("busySince", busySince())
                 .add("throughputUnit", throughputUnit.orElse(null))
                 .add("averageThroughput", averageThroughput.orElse(null))
                 .add("latencyUnit", latencyUnit.orElse(null))
@@ -188,7 +193,7 @@
         int      id;
         float    load = 0;
         int      queue = -1;
-        boolean  isBusy = false;
+        int      busySince = -1;
 
         MonitoringUnit throughputUnit = DEF_THROUGHPUT_UNIT;
         float averageThroughput = -1;
@@ -250,13 +255,13 @@
         }
 
         /**
-         * Sets the CPU status (free or busy).
+         * Sets the CPU status (free or busy since some ms).
          *
-         * @param isBusy CPU status
+         * @param busySince CPU busy time in ms, -1 if not busy
          * @return builder object
          */
-        public Builder setIsBusy(boolean isBusy) {
-            this.isBusy = isBusy;
+        public Builder setBusySince(int busySince) {
+            this.busySince = busySince;
 
             return this;
         }
@@ -340,7 +345,7 @@
          */
         public DefaultCpuStatistics build() {
             return new DefaultCpuStatistics(
-                deviceId, id, load, queue, isBusy,
+                deviceId, id, load, queue, busySince,
                 throughputUnit, averageThroughput,
                 latencyUnit, minLatency, averageLatency, maxLatency);
         }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/stats/CpuStatistics.java b/drivers/server/src/main/java/org/onosproject/drivers/server/stats/CpuStatistics.java
index c43b826..5296d57 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/stats/CpuStatistics.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/stats/CpuStatistics.java
@@ -54,6 +54,14 @@
     boolean busy();
 
     /**
+     * Returns the amount of time in ms since the CPU has been busy,
+     * or a negative value if the CPU is idle.
+     *
+     * @return int time in ms since the CPU has been busy
+     */
+    int busySince();
+
+    /**
      * Returns the unit of throughput values.
      *
      * @return throughput monitoring unit