Additional monitoring stats in server driver

This patch also performs some refactoring to make the
JSON parameters exchanged between the driver and the device
homogeneous (i.e., following the [a-z][A-Z]* pattern).

Code reviewed and minor refactoring.
Avoid exception when timing statistics are not present.
Handle device re-connections.
Server also reports a hardware queue index per core.

Addressed code reviewer's comments.

Change-Id: I6c9d0bbd5884267ee2fdb69bf50809694994c56d
Signed-off-by: Georgios Katsikas <katsikas.gp@gmail.com>
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/BasicServerDriver.java b/drivers/server/src/main/java/org/onosproject/drivers/server/BasicServerDriver.java
index 4fffe6e..d1cb020 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/BasicServerDriver.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/BasicServerDriver.java
@@ -194,6 +194,22 @@
     }
 
     /**
+     * Raise a connect event by setting the
+     * activity flag of this device.
+     *
+     * @param device a device to connect
+     */
+    protected void raiseDeviceReconnect(RestSBDevice device) {
+        // Already done!
+        if (device.isActive()) {
+            return;
+        }
+
+        log.debug("Setting device {} active", device.deviceId());
+        device.setActive(true);
+    }
+
+    /**
      * Upon a failure to contact a device, the driver
      * raises a disconnect event by resetting the
      * activity flag of this device.
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 10203a3..d52f115 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
@@ -110,8 +110,8 @@
     private static final String PARAM_HW_VENDOR        = "hwVersion";
     private static final String PARAM_SW_VENDOR        = "swVersion";
     private static final String PARAM_SERIAL           = "serial";
-    private static final String PARAM_TIMING_STATS     = "timing_stats";
-    private static final String PARAM_TIMING_AUTOSCALE = "autoscale_timing_stats";
+    private static final String PARAM_TIMING_STATS     = "timingStats";
+    private static final String PARAM_TIMING_AUTOSCALE = "autoScaleTimingStats";
 
     private static final String NIC_PARAM_NAME             = "name";
     private static final String NIC_PARAM_PORT_INDEX       = "index";
@@ -137,20 +137,29 @@
     /**
      * CPU statistics.
      */
-    private static final String CPU_PARAM_ID        = "id";
-    private static final String CPU_PARAM_VENDOR    = "vendor";
-    private static final String CPU_PARAM_FREQUENCY = "frequency";
-    private static final String CPU_PARAM_LOAD      = "load";
-    private static final String CPU_PARAM_STATUS    = "busy";
-    private static final String CPU_STATS_BUSY_CPUS = "busyCpus";
-    private static final String CPU_STATS_FREE_CPUS = "freeCpus";
+    private static final String CPU_PARAM_ID         = "id";
+    private static final String CPU_PARAM_VENDOR     = "vendor";
+    private static final String CPU_PARAM_FREQUENCY  = "frequency";
+    private static final String CPU_PARAM_LOAD       = "load";
+    private static final String CPU_PARAM_QUEUE      = "queue";
+    private static final String CPU_PARAM_STATUS     = "busy";
+    private static final String CPU_PARAM_THROUGHPUT = "throughput";
+    private static final String CPU_PARAM_LATENCY    = "latency";
+    private static final String MON_PARAM_UNIT       = "unit";
+    private static final String MON_PARAM_BUSY_CPUS  = "busyCpus";
+    private static final String MON_PARAM_FREE_CPUS  = "freeCpus";
+    private static final String MON_PARAM_MIN        = "min";
+    private static final String MON_PARAM_AVERAGE    = "average";
+    private static final String MON_PARAM_MEDIAN     = "median";
+    private static final String MON_PARAM_MAX        = "max";
 
     /**
      * Timing statistics.
      */
-    private static final String TIMING_PARAM_PARSE     = "parse";
-    private static final String TIMING_PARAM_LAUNCH    = "launch";
-    private static final String TIMING_PARAM_AUTOSCALE = "autoscale";
+    private static final String TIMING_PARAM_PARSE     = "parseTime";
+    private static final String TIMING_PARAM_LAUNCH    = "launchTime";
+    private static final String TIMING_PARAM_DEPLOY    = "deployTime";
+    private static final String TIMING_PARAM_AUTOSCALE = "autoScaleTime";
 
     /**
      * Auxiliary constants.
@@ -350,6 +359,9 @@
         );
         checkNotNull(dev, DEVICE_NULL);
 
+        // Set alive
+        raiseDeviceReconnect(dev);
+
         // Updates the controller with the complete device information
         getController().removeDevice(deviceId);
         getController().addDevice((RestSBDevice) dev);
@@ -512,7 +524,7 @@
      * @param deviceId the device ID to be queried
      * @return global monitoring statistics
      */
-     private MonitoringStatistics getGlobalMonitoringStatistics(DeviceId deviceId) {
+     public MonitoringStatistics getGlobalMonitoringStatistics(DeviceId deviceId) {
         // Monitoring statistics to return
         MonitoringStatistics monStats = null;
 
@@ -562,8 +574,8 @@
         }
 
         // Get high-level CPU statistics
-        int busyCpus = objNode.path(CPU_STATS_BUSY_CPUS).asInt();
-        int freeCpus = objNode.path(CPU_STATS_FREE_CPUS).asInt();
+        int busyCpus = objNode.path(MON_PARAM_BUSY_CPUS).asInt();
+        int freeCpus = objNode.path(MON_PARAM_FREE_CPUS).asInt();
 
         // Get a list of CPU statistics per core
         Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
@@ -581,11 +593,13 @@
         statsBuilder.setDeviceId(deviceId)
                 .setTimingStatistics(timinsgStats)
                 .setCpuStatistics(cpuStats)
-                .setNicStatistics(nicStats)
-                .build();
+                .setNicStatistics(nicStats);
 
         monStats = statsBuilder.build();
 
+        // When a device reports monitoring data, it means it is alive
+        raiseDeviceReconnect(device);
+
         log.debug("Global monitoring statistics: {}", monStats.toString());
 
         return monStats;
@@ -689,11 +703,13 @@
         statsBuilder.setDeviceId(deviceId)
                 .setTimingStatistics(timinsgStats)
                 .setCpuStatistics(cpuStats)
-                .setNicStatistics(nicStats)
-                .build();
+                .setNicStatistics(nicStats);
 
         monStats = statsBuilder.build();
 
+        // When a device reports monitoring data, it means it is alive
+        raiseDeviceReconnect(device);
+
         log.debug("Monitoring statistics: {}", monStats.toString());
 
         return monStats;
@@ -720,22 +736,62 @@
         for (JsonNode cn : cpuNode) {
             ObjectNode cpuObjNode = (ObjectNode) cn;
 
+            // CPU statistics builder
+            DefaultCpuStatistics.Builder cpuBuilder = DefaultCpuStatistics.builder();
+
+            // Throughput statistics are optional
+            JsonNode throughputNode = cpuObjNode.get(CPU_PARAM_THROUGHPUT);
+            if (throughputNode != null) {
+                String throughputUnit = get(throughputNode, MON_PARAM_UNIT);
+                if (!Strings.isNullOrEmpty(throughputUnit)) {
+                    cpuBuilder.setThroughputUnit(throughputUnit);
+                }
+                float averageThroughput = (float) 0;
+                if (throughputNode.get(MON_PARAM_AVERAGE) != null) {
+                    averageThroughput = throughputNode.path(MON_PARAM_AVERAGE).floatValue();
+                }
+                cpuBuilder.setAverageThroughput(averageThroughput);
+            }
+
+            // Latency statistics are optional
+            JsonNode latencyNode = cpuObjNode.get(CPU_PARAM_LATENCY);
+            if (latencyNode != null) {
+                String latencyUnit = get(latencyNode, MON_PARAM_UNIT);
+                if (!Strings.isNullOrEmpty(latencyUnit)) {
+                    cpuBuilder.setLatencyUnit(latencyUnit);
+                }
+                float minLatency = (float) 0;
+                if (latencyNode.get(MON_PARAM_MIN) != null) {
+                    minLatency = latencyNode.path(MON_PARAM_MIN).floatValue();
+                }
+                float medianLatency = (float) 0;
+                if (latencyNode.get(MON_PARAM_MEDIAN) != null) {
+                    medianLatency = latencyNode.path(MON_PARAM_MEDIAN).floatValue();
+                }
+                float maxLatency = (float) 0;
+                if (latencyNode.get(MON_PARAM_MAX) != null) {
+                    maxLatency = latencyNode.path(MON_PARAM_MAX).floatValue();
+                }
+
+                cpuBuilder.setMinLatency(minLatency)
+                    .setMedianLatency(medianLatency)
+                    .setMaxLatency(maxLatency);
+            }
+
             // CPU ID with its load and status
-            int   cpuId    = cpuObjNode.path(CPU_PARAM_ID).asInt();
-            float cpuLoad  = cpuObjNode.path(CPU_PARAM_LOAD).floatValue();
+            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();
 
-            // Incorporate these statistics into an object
-            DefaultCpuStatistics.Builder cpuBuilder =
-                DefaultCpuStatistics.builder();
-
+            // This is mandatory information
             cpuBuilder.setDeviceId(deviceId)
                     .setId(cpuId)
                     .setLoad(cpuLoad)
-                    .setIsBusy(isBusy)
-                    .build();
+                    .setQueue(queueId)
+                    .setIsBusy(isBusy);
 
-            // We have statistics for this CPU core
+            // We have all the statistics for this CPU core
             cpuStats.add(cpuBuilder.build());
         }
 
@@ -790,8 +846,7 @@
             long txErrors  = nicObjNode.path(NIC_STATS_TX_ERRORS).asLong();
 
             // Incorporate these statistics into an object
-            DefaultPortStatistics.Builder nicBuilder =
-                DefaultPortStatistics.builder();
+            DefaultPortStatistics.Builder nicBuilder = DefaultPortStatistics.builder();
 
             nicBuilder.setDeviceId(deviceId)
                     .setPort((int) portNumber)
@@ -802,8 +857,7 @@
                     .setPacketsRxDropped(rxDropped)
                     .setPacketsRxErrors(rxErrors)
                     .setPacketsTxDropped(txDropped)
-                    .setPacketsTxErrors(txErrors)
-                    .build();
+                    .setPacketsTxErrors(txErrors);
 
             // We have statistics for this NIC
             nicStats.add(nicBuilder.build());
@@ -813,9 +867,9 @@
     }
 
     /**
-     * Parse the input JSON object, looking for timing-related
-     * statistics. Upon success, construct and return a
-     * timing statistics object.
+     * Parse the input JSON object, looking for timing-related statistics.
+     * Upon success, return a timing statistics object with the advertized values.
+     * Upon failure, return a timing statistics object with zero-initialized values.
      *
      * @param objNode input JSON node with timing statistics information
      * @return TimingStatistics object or null
@@ -827,33 +881,56 @@
             return timinsgStats;
         }
 
+        // If no timing statistics are present, then send zeros
+        if (objNode.get(PARAM_TIMING_STATS) == null) {
+            return getZeroTimingStatistics();
+        }
+
+        DefaultTimingStatistics.Builder timingBuilder = DefaultTimingStatistics.builder();
+
         // Get timing statistics
         JsonNode timingNode = objNode.path(PARAM_TIMING_STATS);
         ObjectNode timingObjNode = (ObjectNode) timingNode;
 
+        // The unit of timing statistics
+        String timingStatsUnit = get(timingNode, MON_PARAM_UNIT);
+        if (!Strings.isNullOrEmpty(timingStatsUnit)) {
+            timingBuilder.setUnit(timingStatsUnit);
+        }
+
         // Time (ns) to parse the controller's deployment instruction
-        long parsingTime = timingObjNode.path(TIMING_PARAM_PARSE).asLong();
+        long parsingTime = 0;
+        if (timingObjNode.get(TIMING_PARAM_PARSE) != null) {
+            parsingTime = timingObjNode.path(TIMING_PARAM_PARSE).asLong();
+        }
         // Time (ns) to do the deployment
-        long launchingTime = timingObjNode.path(TIMING_PARAM_LAUNCH).asLong();
-        // Total time (ns)
-        long totalTime = parsingTime + launchingTime;
+        long launchingTime = 0;
+        if (timingObjNode.get(TIMING_PARAM_LAUNCH) != null) {
+            launchingTime = timingObjNode.path(TIMING_PARAM_LAUNCH).asLong();
+        }
+        // Deployment time (ns) equals to time to parse + time to launch
+        long deployTime = 0;
+        if (timingObjNode.get(TIMING_PARAM_DEPLOY) != null) {
+            deployTime = timingObjNode.path(TIMING_PARAM_DEPLOY).asLong();
+        }
+        checkArgument(deployTime == parsingTime + launchingTime, "Inconsistent timing statistics");
+
+        timingBuilder.setParsingTime(parsingTime)
+                    .setLaunchingTime(launchingTime);
 
         // Get autoscale timing statistics
         JsonNode autoscaleTimingNode = objNode.path(PARAM_TIMING_AUTOSCALE);
+        if (autoscaleTimingNode == null) {
+            return timingBuilder.build();
+        }
+
         ObjectNode autoscaleTimingObjNode = (ObjectNode) autoscaleTimingNode;
-
         // Time (ns) to autoscale a server's load
-        long autoscaleTime = autoscaleTimingObjNode.path(
-            TIMING_PARAM_AUTOSCALE
-        ).asLong();
-
-        DefaultTimingStatistics.Builder timingBuilder =
-            DefaultTimingStatistics.builder();
-
-        timingBuilder.setParsingTime(parsingTime)
-                    .setLaunchingTime(launchingTime)
-                    .setAutoscaleTime(autoscaleTime)
-                    .build();
+        long autoscaleTime = 0;
+        if (autoscaleTimingObjNode.get(TIMING_PARAM_AUTOSCALE) != null) {
+            autoscaleTimingObjNode.path(TIMING_PARAM_AUTOSCALE).asLong();
+        }
+        timingBuilder.setAutoscaleTime(autoscaleTime);
 
         return timingBuilder.build();
     }
@@ -866,13 +943,11 @@
      * @return TimingStatistics object
      */
     private TimingStatistics getZeroTimingStatistics() {
-        DefaultTimingStatistics.Builder zeroTimingBuilder =
-            DefaultTimingStatistics.builder();
+        DefaultTimingStatistics.Builder zeroTimingBuilder = DefaultTimingStatistics.builder();
 
         zeroTimingBuilder.setParsingTime(0)
                          .setLaunchingTime(0)
-                         .setAutoscaleTime(0)
-                         .build();
+                         .setAutoscaleTime(0);
 
         return zeroTimingBuilder.build();
     }
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 f9c1989..2f5305d 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
@@ -17,10 +17,15 @@
 package org.onosproject.drivers.server.impl.stats;
 
 import org.onosproject.drivers.server.stats.CpuStatistics;
+import org.onosproject.drivers.server.stats.MonitoringUnit;
 
 import org.onosproject.net.DeviceId;
 import com.google.common.base.MoreObjects;
 
+import java.util.Optional;
+
+import static org.onosproject.drivers.server.stats.MonitoringUnit.LatencyUnit;
+import static org.onosproject.drivers.server.stats.MonitoringUnit.ThroughputUnit;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkArgument;
 
@@ -32,6 +37,9 @@
     private static final float MIN_CPU_LOAD = (float) 0.0;
     private static final float MAX_CPU_LOAD = (float) 1.0;
 
+    private static final LatencyUnit DEF_LATENCY_UNIT = LatencyUnit.NANO_SECOND;
+    private static final ThroughputUnit DEF_THROUGHPUT_UNIT = ThroughputUnit.MBPS;
+
     // Upper limit of CPU cores in one machine
     public static final int MAX_CPU_NB = 512;
 
@@ -39,27 +47,46 @@
 
     private final int id;
     private final float load;
+    private final int queue;
     private final boolean isBusy;
+    private final Optional<MonitoringUnit> throughputUnit;
+    private final Optional<Float> averageThroughput;
+    private final Optional<MonitoringUnit> latencyUnit;
+    private final Optional<Float> minLatency;
+    private final Optional<Float> medianLatency;
+    private final Optional<Float> maxLatency;
 
-    private DefaultCpuStatistics(
-            DeviceId deviceId,
-            int      id,
-            float    load,
-            boolean  isBusy) {
+    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, boolean isBusy,
+            MonitoringUnit throughputUnit, float averageThroughput, MonitoringUnit latencyUnit,
+            float minLatency, float medianLatency, float maxLatency) {
         checkNotNull(deviceId, "Device ID is NULL");
-        checkArgument(
-            (id >= 0) && (id < MAX_CPU_NB),
-            "Invalid CPU core ID " + String.valueOf(id) + ", not in [0, " + String.valueOf(MAX_CPU_NB - 1) + "]"
-        );
-        checkArgument(
-            (load >= MIN_CPU_LOAD) && (load <= MAX_CPU_LOAD),
-            "Invalid CPU load " + Float.toString(load) + ", not in [" + MIN_CPU_LOAD + ", " + MAX_CPU_LOAD + "]"
-        );
+        checkArgument((id >= 0) && (id < MAX_CPU_NB),
+            "Invalid CPU core ID " + String.valueOf(id) + ", not in [0, " + String.valueOf(MAX_CPU_NB - 1) + "]");
+        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.throughputUnit = (throughputUnit == null) ?
+                Optional.empty() : Optional.ofNullable(throughputUnit);
+        this.averageThroughput = (averageThroughput < 0) ?
+                Optional.empty() : Optional.ofNullable(averageThroughput);
+        this.latencyUnit = (latencyUnit == null) ?
+                Optional.empty() : Optional.ofNullable(latencyUnit);
+        this.minLatency = (minLatency < 0) ?
+                Optional.empty() : Optional.ofNullable(minLatency);
+        this.medianLatency = (medianLatency < 0) ?
+                Optional.empty() : Optional.ofNullable(medianLatency);
+        this.maxLatency = (maxLatency < 0) ?
+                Optional.empty() : Optional.ofNullable(maxLatency);
     }
 
     // Constructor for serializer
@@ -67,7 +94,15 @@
         this.deviceId = null;
         this.id       = 0;
         this.load     = 0;
+        this.queue    = 0;
         this.isBusy   = false;
+
+        this.throughputUnit = null;
+        this.averageThroughput = null;
+        this.latencyUnit = null;
+        this.minLatency = null;
+        this.medianLatency = null;
+        this.maxLatency = null;
     }
 
     /**
@@ -90,18 +125,60 @@
     }
 
     @Override
+    public int queue() {
+        return this.queue;
+    }
+
+    @Override
     public boolean busy() {
         return this.isBusy;
     }
 
     @Override
+    public Optional<MonitoringUnit> throughputUnit() {
+        return this.throughputUnit;
+    }
+
+    @Override
+    public Optional<Float> averageThroughput() {
+        return this.averageThroughput;
+    }
+
+    @Override
+    public Optional<MonitoringUnit> latencyUnit() {
+        return this.latencyUnit;
+    }
+
+    @Override
+    public Optional<Float> minLatency() {
+        return this.minLatency;
+    }
+
+    @Override
+    public Optional<Float> medianLatency() {
+        return this.medianLatency;
+    }
+
+    @Override
+    public Optional<Float> maxLatency() {
+        return this.maxLatency;
+    }
+
+    @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)
                 .omitNullValues()
                 .add("device", deviceId)
                 .add("id",     id())
                 .add("load",   load())
+                .add("queue",  queue())
                 .add("isBusy", busy())
+                .add("throughputUnit", throughputUnit.orElse(null))
+                .add("averageThroughput", averageThroughput.orElse(null))
+                .add("latencyUnit", latencyUnit.orElse(null))
+                .add("minLatency", minLatency.orElse(null))
+                .add("medianLatency", medianLatency.orElse(null))
+                .add("maxLatency", maxLatency.orElse(null))
                 .toString();
     }
 
@@ -109,8 +186,16 @@
 
         DeviceId deviceId;
         int      id;
-        float    load;
-        boolean  isBusy;
+        float    load = 0;
+        int      queue = -1;
+        boolean  isBusy = false;
+
+        MonitoringUnit throughputUnit = DEF_THROUGHPUT_UNIT;
+        float averageThroughput = -1;
+        MonitoringUnit latencyUnit = DEF_LATENCY_UNIT;
+        float minLatency = -1;
+        float medianLatency = -1;
+        float maxLatency = -1;
 
         private Builder() {
 
@@ -131,7 +216,7 @@
         /**
          * Sets the CPU ID.
          *
-         * @param id the CPU ID
+         * @param id CPU ID
          * @return builder object
          */
         public Builder setId(int id) {
@@ -153,6 +238,18 @@
         }
 
         /**
+         * Sets the hardware queue ID associated with this core.
+         *
+         * @param queue hardware queue ID
+         * @return builder object
+         */
+        public Builder setQueue(int queue) {
+            this.queue = queue;
+
+            return this;
+        }
+
+        /**
          * Sets the CPU status (free or busy).
          *
          * @param isBusy CPU status
@@ -165,17 +262,87 @@
         }
 
         /**
+         * Sets the throughput unit.
+         *
+         * @param throughputUnitStr throughput unit as a string
+         * @return builder object
+         */
+        public Builder setThroughputUnit(String throughputUnitStr) {
+            this.throughputUnit = ThroughputUnit.getByName(throughputUnitStr);
+
+            return this;
+        }
+
+        /**
+         * Sets the average throughput.
+         *
+         * @param averageThroughput average throughput
+         * @return builder object
+         */
+        public Builder setAverageThroughput(float averageThroughput) {
+            this.averageThroughput = averageThroughput;
+
+            return this;
+        }
+
+        /**
+         * Sets the latency unit.
+         *
+         * @param latencyUnitStr latency unit as a string
+         * @return builder object
+         */
+        public Builder setLatencyUnit(String latencyUnitStr) {
+            this.latencyUnit = LatencyUnit.getByName(latencyUnitStr);
+
+            return this;
+        }
+
+        /**
+         * Sets the minimum latency.
+         *
+         * @param minLatency minimum latency
+         * @return builder object
+         */
+        public Builder setMinLatency(float minLatency) {
+            this.minLatency = minLatency;
+
+            return this;
+        }
+
+        /**
+         * Sets the median latency.
+         *
+         * @param medianLatency median latency
+         * @return builder object
+         */
+        public Builder setMedianLatency(float medianLatency) {
+            this.medianLatency = medianLatency;
+
+            return this;
+        }
+
+        /**
+         * Sets the maximum latency.
+         *
+         * @param maxLatency maximum latency
+         * @return builder object
+         */
+        public Builder setMaxLatency(float maxLatency) {
+            this.maxLatency = maxLatency;
+
+            return this;
+        }
+
+        /**
          * Creates a DefaultCpuStatistics object.
          *
          * @return DefaultCpuStatistics object
          */
         public DefaultCpuStatistics build() {
             return new DefaultCpuStatistics(
-                deviceId,
-                id,
-                load,
-                isBusy
-            );
+                deviceId, id, load, queue, isBusy,
+                throughputUnit, averageThroughput,
+                latencyUnit, minLatency, medianLatency, maxLatency);
         }
     }
 
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultTimingStatistics.java b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultTimingStatistics.java
index 8256c5f..0fe253d 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultTimingStatistics.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultTimingStatistics.java
@@ -16,29 +16,39 @@
 
 package org.onosproject.drivers.server.impl.stats;
 
+import org.onosproject.drivers.server.stats.MonitoringUnit;
 import org.onosproject.drivers.server.stats.TimingStatistics;
 
+import com.google.common.base.Strings;
 import com.google.common.base.MoreObjects;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.stats.MonitoringUnit.LatencyUnit;
 
 /**
  * Default implementation for timing statistics.
  */
 public final class DefaultTimingStatistics implements TimingStatistics {
 
+    private static final LatencyUnit DEF_UNIT = LatencyUnit.NANO_SECOND;
+
+    private final MonitoringUnit unit;
     private final long deployCommandParsingTime;
     private final long deployCommandLaunchingTime;
     private long autoscaleTime;
 
     private DefaultTimingStatistics(
+            MonitoringUnit unit,
             long parsingTime,
             long launchingTime,
             long autoscaleTime) {
+        checkNotNull(unit, "Time statistics unit is null");
         checkArgument(parsingTime   >= 0, "Parsing time is negative");
         checkArgument(launchingTime >= 0, "Launching time is negative");
         checkArgument(autoscaleTime >= 0, "Autoscale time is negative");
 
+        this.unit = unit;
         this.deployCommandParsingTime   = parsingTime;
         this.deployCommandLaunchingTime = launchingTime;
         this.autoscaleTime = autoscaleTime;
@@ -46,6 +56,7 @@
 
     // Constructor for serializer
     private DefaultTimingStatistics() {
+        this.unit = null;
         this.deployCommandParsingTime   = 0;
         this.deployCommandLaunchingTime = 0;
         this.autoscaleTime = 0;
@@ -61,6 +72,11 @@
     }
 
     @Override
+    public MonitoringUnit unit() {
+        return this.unit;
+    }
+
+    @Override
     public long deployCommandParsingTime() {
         return this.deployCommandParsingTime;
     }
@@ -84,15 +100,17 @@
     public String toString() {
         return MoreObjects.toStringHelper(this)
                 .omitNullValues()
-                .add("parsing",   this.deployCommandParsingTime())
-                .add("launching", this.deployCommandLaunchingTime())
-                .add("total",     this.totalDeploymentTime())
-                .add("autoscale", this.autoscaleTime())
+                .add("unit", this.unit().toString())
+                .add("parsingTime", this.deployCommandParsingTime())
+                .add("launchingTime", this.deployCommandLaunchingTime())
+                .add("deploymentTime", this.totalDeploymentTime())
+                .add("autoScaleTime", this.autoscaleTime())
                 .toString();
     }
 
     public static final class Builder {
 
+        MonitoringUnit unit = DEF_UNIT;
         long deployCommandParsingTime;
         long deployCommandLaunchingTime;
         long autoscaleTime;
@@ -102,6 +120,20 @@
         }
 
         /**
+         * Sets time statistics unit.
+         *
+         * @param unitStr time statistics unit as a string
+         * @return builder object
+         */
+        public Builder setUnit(String unitStr) {
+            if (!Strings.isNullOrEmpty(unitStr)) {
+                this.unit = LatencyUnit.getByName(unitStr);
+            }
+
+            return this;
+        }
+
+        /**
          * Sets parsing time.
          *
          * @param parsingTime parsing time
@@ -144,6 +176,7 @@
          */
         public DefaultTimingStatistics build() {
             return new DefaultTimingStatistics(
+                unit,
                 deployCommandParsingTime,
                 deployCommandLaunchingTime,
                 autoscaleTime
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 9ea22b3..f30134d 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
@@ -16,6 +16,8 @@
 
 package org.onosproject.drivers.server.stats;
 
+import java.util.Optional;
+
 /**
  * CPU statistics API.
  */
@@ -38,10 +40,63 @@
     float load();
 
     /**
+     * Returns the hardware queue identifier associated with this CPU core.
+     *
+     * @return hardware queue identifier
+     */
+    int queue();
+
+    /**
      * Returns the status (true=busy, false=free) of a CPU core.
      *
      * @return boolean CPU core status
      */
     boolean busy();
 
+    /**
+     * Returns the unit of throughput values.
+     *
+     * @return throughput monitoring unit
+     */
+    Optional<MonitoringUnit> throughputUnit();
+
+    /**
+     * Returns the average throughput of this CPU core,
+     * expressed in throughputUnit() monitoring units.
+     *
+     * @return average throughput of a CPU core
+     */
+    Optional<Float> averageThroughput();
+
+    /**
+     * Returns the unit of latency values.
+     *
+     * @return latency monitoring unit
+     */
+    Optional<MonitoringUnit> latencyUnit();
+
+    /**
+     * Returns the minimum latency incurred by a CPU core,
+     * expressed in latencyUnit() monitoring units.
+     *
+     * @return minimum latency incurred by a CPU core
+     */
+    Optional<Float> minLatency();
+
+    /**
+     * Returns the median latency incurred by a CPU core,
+     * expressed in latencyUnit() monitoring units.
+     *
+     * @return median latency incurred by a CPU core
+     */
+    Optional<Float> medianLatency();
+
+    /**
+     * Returns the maximum latency incurred by a CPU core,
+     * expressed in latencyUnit() monitoring units.
+     *
+     * @return maximum latency incurred by a CPU core
+     */
+    Optional<Float> maxLatency();
+
 }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/stats/MonitoringUnit.java b/drivers/server/src/main/java/org/onosproject/drivers/server/stats/MonitoringUnit.java
new file mode 100644
index 0000000..0528a69
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/stats/MonitoringUnit.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.drivers.server.stats;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Representation of a monitoring unit.
+ */
+public interface MonitoringUnit {
+
+    /**
+     * Throughput-related monitoring units.
+     */
+    public enum ThroughputUnit implements MonitoringUnit {
+
+        BPS("bps"),
+        KBPS("kbps"),
+        MBPS("mbps"),
+        GBPS("gbps");
+
+        private String throughputUnit;
+
+        // Statically maps throughput monitoring units to enum types
+        private static final Map<String, MonitoringUnit> MAP =
+            new HashMap<String, MonitoringUnit>();
+        static {
+            for (ThroughputUnit tu : ThroughputUnit.values()) {
+                MAP.put(tu.toString().toLowerCase(), (MonitoringUnit) tu);
+            }
+        }
+
+        private ThroughputUnit(String throughputUnit) {
+            this.throughputUnit = throughputUnit;
+        }
+
+        public static MonitoringUnit getByName(String tu) {
+            tu = tu.toLowerCase();
+            return MAP.get(tu);
+        }
+
+        @Override
+        public String toString() {
+            return this.throughputUnit;
+        }
+
+    };
+
+    /**
+     * Latency-related monitoring units.
+     */
+    public enum LatencyUnit implements MonitoringUnit {
+
+        NANO_SECOND("ns"),
+        MICRO_SECOND("us"),
+        MILLI_SECOND("ms"),
+        SECOND("s");
+
+        private String latencyUnit;
+
+        // Statically maps latency monitoring units to enum types
+        private static final Map<String, MonitoringUnit> MAP =
+            new HashMap<String, MonitoringUnit>();
+        static {
+            for (LatencyUnit lu : LatencyUnit.values()) {
+                MAP.put(lu.toString().toLowerCase(), (MonitoringUnit) lu);
+            }
+        }
+
+        private LatencyUnit(String latencyUnit) {
+            this.latencyUnit = latencyUnit;
+        }
+
+        public static MonitoringUnit getByName(String lu) {
+            lu = lu.toLowerCase();
+            return MAP.get(lu);
+        }
+
+        @Override
+        public String toString() {
+            return this.latencyUnit;
+        }
+
+    };
+
+    String toString();
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/stats/TimingStatistics.java b/drivers/server/src/main/java/org/onosproject/drivers/server/stats/TimingStatistics.java
index 102b186..9cf46dd 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/stats/TimingStatistics.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/stats/TimingStatistics.java
@@ -22,6 +22,13 @@
 public interface TimingStatistics {
 
     /**
+     * Returns the unit of timing statistics.
+     *
+     * @return timing statistics' unit
+     */
+    MonitoringUnit unit();
+
+    /**
      * Time (ns) to parse the controller's deployment instruction.
      *
      * @return time in nanoseconds to parse a 'deploy' command