Server device driver extensions

 * Additional behaviours implemented
    * BasicSystemOperations
    * DeviceHandshaker
    * InterfaceConfig
    * PortAdmin
    * QueueConfig
    * TableStatisticsDiscovery
    * DeviceSystemStatisticsQuery
 * New device abstractions on CPU cache and main memory
 * Additional statistics (main memory)
 * New UI with Memory statistics (based on the old gui)
 * Constants decoupled from implementations
 * More builders used for interface implementations
 * Style fixes and refactoring

Change-Id: Ie54ed0fb4760456cfd6339c74b36486dd3c20f87
Signed-off-by: Georgios Katsikas <katsikas.gp@gmail.com>
(cherry picked from commit 740d3281b09f2517c32a302cf12e16e3d98830fe)
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 d1cb020..5e6781d 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
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.onosproject.drivers.server;
 
 import org.onosproject.drivers.server.devices.RestServerSBDevice;
@@ -28,11 +29,13 @@
 import com.fasterxml.jackson.databind.JsonNode;
 
 import java.util.EnumSet;
-import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
-import static org.slf4j.LoggerFactory.getLogger;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_CONTROLLER_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_HANDLER_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_NULL;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * The basic functionality of the server driver.
@@ -42,24 +45,6 @@
     private final Logger log = getLogger(getClass());
 
     /**
-     * Resource endpoints of the server agent (REST server-side).
-     */
-    public    static final MediaType  JSON = MediaType.valueOf(MediaType.APPLICATION_JSON);
-    protected static final String ROOT_URL = "";
-    protected static final String SLASH = "/";
-    public    static final String BASE_URL = ROOT_URL + SLASH + "metron";
-
-    /**
-     * Common parameters to be exchanged with the server's agent.
-     */
-    public static final String PARAM_ID             = "id";
-    public static final String PARAM_NICS           = "nics";
-    public static final String PARAM_CPUS           = "cpus";
-    public static final String NIC_PARAM_RX_FILTER  = "rxFilter";
-    public static final String NIC_PARAM_RX_METHOD  = "method";
-
-
-    /**
      * Successful HTTP status codes.
      */
     private static final int STATUS_OK = Response.Status.OK.getStatusCode();
@@ -67,20 +52,11 @@
     private static final int STATUS_ACCEPTED = Response.Status.ACCEPTED.getStatusCode();
 
     /**
-     * Messages for error handlers.
-     */
-    protected static final String MASTERSHIP_NULL = "Mastership service is null";
-    protected static final String CONTROLLER_NULL = "RestSB controller is null";
-    protected static final String DEVICE_ID_NULL  = "Device ID cannot be null";
-    protected static final String HANDLER_NULL    = "Handler cannot be null";
-    protected static final String DEVICE_NULL     = "Device cannot be null";
-
-    /**
      * A unique controller that handles the REST-based communication.
      */
     protected static RestSBController controller = null;
     protected static DriverHandler       handler = null;
-    private static final Object CONTROLLER_LOCK = new Object();
+    protected static final Object CONTROLLER_LOCK = new Object();
 
     public BasicServerDriver() {};
 
@@ -90,9 +66,11 @@
      * @return DriverHandler instance
      */
     protected DriverHandler getHandler() {
-        synchronized (CONTROLLER_LOCK) {
-            handler = handler();
-            checkNotNull(handler, HANDLER_NULL);
+        if (handler == null) {
+            synchronized (CONTROLLER_LOCK) {
+                handler = super.handler();
+                checkNotNull(handler, MSG_HANDLER_NULL);
+            }
         }
 
         return handler;
@@ -107,7 +85,7 @@
         synchronized (CONTROLLER_LOCK) {
             if (controller == null) {
                 controller = getHandler().get(RestSBController.class);
-                checkNotNull(controller, CONTROLLER_NULL);
+                checkNotNull(controller, MSG_CONTROLLER_NULL);
             }
         }
 
@@ -115,6 +93,25 @@
     }
 
     /**
+     * Retrieve an instance of the REST SB device, given its ID.
+     *
+     * @param deviceId a device ID
+     * @return RestSBDevice instance
+     */
+    public RestSBDevice getDevice(DeviceId deviceId) {
+        return getController().getDevice(deviceId);
+    }
+
+    /**
+     * Retrieve a REST SB device ID.
+     *
+     * @return a device Id
+     */
+    public DeviceId getDeviceId() {
+        return getHandler().data().deviceId();
+    }
+
+    /**
      * Finds the NIC name that corresponds to a device's port number.
      *
      * @param deviceId a device ID
@@ -132,7 +129,7 @@
         } catch (ClassCastException ccEx) {
             return null;
         }
-        checkNotNull(device, DEVICE_NULL);
+        checkNotNull(device, MSG_DEVICE_NULL);
 
         return device.portNameFromNumber(port);
     }
@@ -194,6 +191,16 @@
     }
 
     /**
+     * Check whether a server device is active or not.
+     *
+     * @param device a device to check
+     * @return boolean status (true for active or false for inactive)
+     */
+    protected boolean deviceIsActive(RestSBDevice device) {
+        return device.isActive();
+    }
+
+    /**
      * Raise a connect event by setting the
      * activity flag of this device.
      *
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/Constants.java b/drivers/server/src/main/java/org/onosproject/drivers/server/Constants.java
new file mode 100644
index 0000000..aa594ae
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/Constants.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2020-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;
+
+import org.onosproject.drivers.server.devices.cpu.CpuCoreId;
+import org.onosproject.drivers.server.devices.cpu.CpuDevice;
+import org.onosproject.drivers.server.devices.cpu.CpuVendor;
+import org.onosproject.drivers.server.devices.memory.MemoryModuleDevice;
+import org.onosproject.drivers.server.devices.nic.NicDevice;
+import org.onosproject.drivers.server.devices.nic.NicRxFilter.RxFilter;
+import org.onosproject.drivers.server.stats.CpuStatistics;
+
+import javax.ws.rs.core.MediaType;
+
+/**
+ * Provides constants used by the server device driver.
+ */
+public final class Constants {
+
+    private Constants() {
+
+    }
+
+    /**
+     * Generic parameters to be exchanged with the server's agent.
+     */
+    public static final String PARAM_AUTOSCALE              = "autoScale";
+    public static final String PARAM_CAPACITY               = "capacity";
+    public static final String PARAM_CHASSIS_ID             = "chassisId";
+    public static final String PARAM_CONNECTION_STATUS      = "connectionStatus";
+    public static final String PARAM_CPU_CACHES             = "cpuCaches";
+    public static final String PARAM_CPU_CACHE_HIERARCHY    = "cpuCacheHierarchy";
+    public static final String PARAM_CPUS                   = "cpus";
+    public static final String PARAM_HW_VENDOR              = "hwVersion";
+    public static final String PARAM_ID                     = "id";
+    public static final String PARAM_MANUFACTURER           = "manufacturer";
+    public static final String PARAM_MEMORY                 = "memory";
+    public static final String PARAM_MEMORY_HIERARCHY       = "memoryHierarchy";
+    public static final String PARAM_MEMORY_MODULES         = "modules";
+    public static final String PARAM_NAME                   = "name";
+    public static final String PARAM_NICS                   = "nics";
+    public static final String PARAM_QUEUES                 = "queues";
+    public static final String PARAM_RULES                  = "rules";
+    public static final String PARAM_RULE_CONTENT           = "content";
+    public static final String PARAM_SPEED                  = "speed";
+    public static final String PARAM_SPEED_CONF             = "speedConfigured";
+    public static final String PARAM_SW_VENDOR              = "swVersion";
+    public static final String PARAM_SERIAL                 = "serial";
+    public static final String PARAM_STATUS                 = "status";
+    public static final String PARAM_TIME                   = "time";
+    public static final String PARAM_TIMING_STATS           = "timingStats";
+    public static final String PARAM_TIMING_STATS_AUTOSCALE = "timingStatsAutoscale";
+    public static final String PARAM_TYPE                   = "type";
+
+    /**
+     * Controller configuration parameters.
+     */
+    public static final String PARAM_CTRL      = "controllers";
+    public static final String PARAM_CTRL_IP   = "ip";
+    public static final String PARAM_CTRL_PORT = "port";
+    public static final String PARAM_CTRL_TYPE = "type";
+
+    /**
+     * CPU parameters.
+     */
+    public static final String PARAM_CPU_CORES   = "cores";
+    public static final String PARAM_CPU_ID_LOG  = "logicalId";
+    public static final String PARAM_CPU_ID_PHY  = "physicalId";
+    public static final String PARAM_CPU_SOCKET  = "socket";
+    public static final String PARAM_CPU_SOCKETS = "sockets";
+    public static final String PARAM_CPU_VENDOR  = "vendor";
+    public static final String PARAM_CPUS_MAX    = "maxCpus";
+
+    /**
+     * CPU cache parameters.
+     */
+    public static final String PARAM_CPU_CACHE_LEVEL    = "level";
+    public static final String PARAM_CPU_CACHE_LEVELS   = "levels";
+    public static final String PARAM_CPU_CACHE_SHARED   = "shared";
+    public static final String PARAM_CPU_CACHE_LINE_LEN = "lineLength";
+    public static final String PARAM_CPU_CACHE_POLICY   = "policy";
+    public static final String PARAM_CPU_CACHE_SETS     = "sets";
+    public static final String PARAM_CPU_CACHE_TYPE     = "type";
+    public static final String PARAM_CPU_CACHE_WAYS     = "ways";
+
+    /**
+     * Memory parameters.
+     */
+    public static final String PARAM_MEMORY_STATS_FREE  = "free";
+    public static final String PARAM_MEMORY_STATS_TOTAL = "total";
+    public static final String PARAM_MEMORY_STATS_USED  = "used";
+    public static final String PARAM_MEMORY_WIDTH_DATA  = "dataWidth";
+    public static final String PARAM_MEMORY_WIDTH_TOTAL = "totalWidth";
+
+    /**
+     * NIC parameters.
+     */
+    public static final String PARAM_NIC_HW_ADDR              = "hwAddr";
+    public static final String PARAM_NIC_MAX_RATE             = "maxRate";
+    public static final String PARAM_NIC_PORT                 = "port";
+    public static final String PARAM_NIC_PORT_TYPE            = "portType";
+    public static final String PARAM_NIC_PORT_TYPE_COPPER     = "copper";
+    public static final String PARAM_NIC_PORT_TYPE_FIBER      = "fiber";
+    public static final String PARAM_NIC_PORT_STATUS          = "portStatus";
+    public static final String PARAM_NIC_RX_FILTER            = "rxFilter";
+    public static final String PARAM_NIC_RX_FILTER_FD         = "flow";
+    /* Rx filtering methods usually implemented by NICs in commodity servers. */
+    public static final String PARAM_NIC_RX_METHOD            = "method";
+    public static final String PARAM_NIC_RX_METHOD_FLOW       = "flow";
+    public static final String PARAM_NIC_RX_METHOD_MAC        = "mac";
+    public static final String PARAM_NIC_RX_METHOD_MPLS       = "mpls";
+    public static final String PARAM_NIC_RX_METHOD_RSS        = "rss";
+    public static final String PARAM_NIC_RX_METHOD_VLAN       = "vlan";
+    public static final String PARAM_NIC_RX_METHOD_VALUES     = "values";
+    public static final String PARAM_NIC_TABLE                = "table";
+    public static final String PARAM_NIC_TABLE_ACTIVE_ENTRIES = "activeEntries";
+    public static final String PARAM_NIC_TABLE_PKTS_LOOKED_UP = "pktsLookedUp";
+    public static final String PARAM_NIC_TABLE_PKTS_MATCHED   = "pktsMatched";
+    public static final String PARAM_NIC_TABLE_MAX_SIZE       = "maxSize";
+
+    /**
+     * NIC statistics' parameters.
+     */
+    public static final String PARAM_NIC_STATS_RX_COUNT  = "rxCount";
+    public static final String PARAM_NIC_STATS_RX_BYTES  = "rxBytes";
+    public static final String PARAM_NIC_STATS_RX_DROPS  = "rxDropped";
+    public static final String PARAM_NIC_STATS_RX_ERRORS = "rxErrors";
+    public static final String PARAM_NIC_STATS_TX_COUNT  = "txCount";
+    public static final String PARAM_NIC_STATS_TX_BYTES  = "txBytes";
+    public static final String PARAM_NIC_STATS_TX_DROPS  = "txDropped";
+    public static final String PARAM_NIC_STATS_TX_ERRORS = "txErrors";
+
+    /**
+     * CPU statistics' parameters.
+     */
+    public static final String PARAM_CPU_FREQUENCY  = "frequency";
+    public static final String PARAM_CPU_LATENCY    = "latency";
+    public static final String PARAM_CPU_LOAD       = "load";
+    public static final String PARAM_CPU_QUEUE      = "queue";
+    public static final String PARAM_CPU_STATUS     = "busy";
+    public static final String PARAM_CPU_THROUGHPUT = "throughput";
+
+    /**
+     * Other monitoring statistics' parameters.
+     */
+    public static final String PARAM_MON_AVERAGE    = "average";
+    public static final String PARAM_MON_BUSY_CPUS  = "busyCpus";
+    public static final String PARAM_MON_FREE_CPUS  = "freeCpus";
+    public static final String PARAM_MON_MAX        = "max";
+    public static final String PARAM_MON_MIN        = "min";
+    public static final String PARAM_MON_UNIT       = "unit";
+
+    /**
+     * Timing statistics' parameters.
+     */
+    public static final String PARAM_TIMING_AUTOSCALE = "autoScaleTime";
+    public static final String PARAM_TIMING_DEPLOY    = "deployTime";
+    public static final String PARAM_TIMING_LAUNCH    = "launchTime";
+    public static final String PARAM_TIMING_PARSE     = "parseTime";
+
+    /**
+     * Resource API endpoints.
+     */
+    public static final MediaType JSON = MediaType.valueOf(MediaType.APPLICATION_JSON);
+    public static final String SLASH = "/";
+    public static final String URL_ROOT = "";
+    public static final String URL_BASE = URL_ROOT + SLASH + "metron";
+
+    public static final String URL_CONTROLLERS_GET        = URL_BASE + SLASH + "controllers";
+    public static final String URL_CONTROLLERS_SET        = URL_BASE + SLASH + "controllers";
+    public static final String URL_CONTROLLERS_DEL        = URL_BASE + SLASH + "controllers_delete";
+    public static final String URL_NIC_LINK_DISCOVERY     = URL_BASE + SLASH + "nic_link_disc";
+    public static final String URL_NIC_PORT_ADMIN         = URL_BASE + SLASH + "nic_ports";
+    public static final String URL_NIC_QUEUE_ADMIN        = URL_BASE + SLASH + "nic_queues";
+    public static final String URL_RULE_MANAGEMENT        = URL_BASE + SLASH + "rules";
+    public static final String URL_RULE_TABLE_STATS       = URL_BASE + SLASH + "rules_table_stats";
+    public static final String URL_SERVICE_CHAINS_STATS   = URL_BASE + SLASH + "service_chains_stats";  // + /ID
+    public static final String URL_SRV_GLOBAL_STATS       = URL_BASE + SLASH + "server_stats";
+    public static final String URL_SRV_PROBE_CONNECT      = URL_BASE + SLASH + "server_connect";
+    public static final String URL_SRV_PROBE_DISCONNECT   = URL_BASE + SLASH + "server_disconnect";
+    public static final String URL_SRV_RESOURCE_DISCOVERY = URL_BASE + SLASH + "server_resources";
+    public static final String URL_SRV_TIME_DISCOVERY     = URL_BASE + SLASH + "server_time";
+
+    /**
+     * Messages for error handlers.
+     */
+    public static final String MSG_CONTROLLER_NULL = "RestSB controller is NULL";
+    public static final String MSG_DEVICE_NULL     = "Device cannot be NULL";
+    public static final String MSG_DEVICE_ID_NULL  = "Device ID cannot be NULL";
+    public static final String MSG_HANDLER_NULL    = "Handler cannot be NULL";
+    public static final String MSG_MASTERSHIP_NULL = "Mastership service is NULL";
+    public static final String MSG_RESPONSE_NULL   = "Server's response is NULL";
+
+    public static final String MSG_CPU_CACHE_CAPACITY_NEGATIVE =
+        "CPU cache capacity (in kilo bytes) must be a positive integer";
+    public static final String MSG_CPU_CACHE_CAPACITY_CORE_NEGATIVE = "Per core CPU cache capacity must be positive";
+    public static final String MSG_CPU_CACHE_CAPACITY_LLC_NEGATIVE = "LLC capacity must be positive";
+    public static final String MSG_CPU_CACHE_CAPACITY_TOTAL_NEGATIVE = "Total CPU cache capacity must be positive";
+    public static final String MSG_CPU_CACHE_HIERARCHY_NULL = "CPU cache hierarchy cannot be NULL";
+    public static final String MSG_CPU_CACHE_ID_NULL = "CPU cache ID cannot be NULL";
+    public static final String MSG_CPU_CACHE_INSERTION_FAILED = "Failed to insert basic CPU cache into the hierarchy";
+    public static final String MSG_CPU_CACHE_LEVELS_EXCEEDED =
+        "Exceeded the typical number of levels of a CPU cache hierarchy";
+    public static final String MSG_CPU_CACHE_LEVEL_NULL = "CPU cache level cannot be NULL";
+    public static final String MSG_CPU_CACHE_LEVELS_NEGATIVE =
+        "Number of CPU cache levels must be positive";
+    public static final String MSG_CPU_CACHE_LINE_NEGATIVE =
+        "CPU cache line length (in bytes) must be a positive integer";
+    public static final String MSG_CPU_CACHE_POLICY_NULL = "PU cache policy cannot be NULL";
+    public static final String MSG_CPU_CACHE_SETS_NEGATIVE = "CPU cache sets must be a positive integer";
+    public static final String MSG_CPU_CACHE_TYPE_NULL = "CPU cache type cannot be NULL";
+    public static final String MSG_CPU_CACHE_WAYS_NEGATIVE = "CPU cache ways must be a positive integer";
+
+    public static final String MSG_CPU_CORE_ID_NULL = "CPU core ID cannot be NULL";
+    public static final String MSG_CPU_CORE_NEGATIVE = "CPU core ID must be in [0, " +
+        String.valueOf(CpuCoreId.MAX_CPU_CORE_NB - 1) + "]";
+    public static final String MSG_CPU_CORES_NEGATIVE = "Number of CPU cores must be positive";
+    public static final String MSG_CPU_LIST_NULL = "Device's set of CPUs cannot be NULL";
+    public static final String MSG_CPU_LOAD_NEGATIVE = "CPU load must be in [" + CpuStatistics.MIN_CPU_LOAD +
+        ", " + CpuStatistics.MAX_CPU_LOAD + "]";
+    public static final String MSG_CPU_FREQUENCY_NEGATIVE = "CPU core frequency must be positive" +
+        " and less or equal than " + CpuDevice.MAX_FREQUENCY_MHZ + " MHz";
+    public static final String MSG_CPU_SOCKET_NEGATIVE = "CPU socket ID must be in [0, " +
+        String.valueOf(CpuCoreId.MAX_CPU_SOCKET_NB - 1) + "]";
+    public static final String MSG_CPU_SOCKETS_NEGATIVE = "Number of CPU sockets must be positive";
+    public static final String MSG_CPU_VENDOR_NULL = "Unsupported CPU vendor" +
+        " Choose one in: " + BasicServerDriver.enumTypesToString(CpuVendor.class);
+
+    public static final String MSG_CONVERSION_TO_BITS = "Invalid conversion to bits";
+    public static final String MSG_CONVERSION_TO_BYTES = "Invalid conversion to bytes";
+
+    public static final String MSG_MEM_CAPACITY_NEGATIVE = "Total memory capacity must be positive";
+    public static final String MSG_MEM_HIERARCHY_EMPTY = "Memory hierarchy cannot be empty";
+    public static final String MSG_MEM_HIERARCHY_NULL = "Memory hierarchy cannot be NULL";
+    public static final String MSG_MEM_MANUFACTURER_NULL = "Memory manufacturer cannot be NULL";
+    public static final String MSG_MEM_MODULE_NULL = "Memory module cannot be NULL";
+    public static final String MSG_MEM_SERIAL_NB_NULL = "Memory serial number cannot be NULL";
+    public static final String MSG_MEM_SIZE_NEGATIVE = "Memory size must be positive";
+    public static final String MSG_MEM_SPEED_CONF_NEGATIVE = "Configured memory speed must be positive" +
+        " and less or equal than total speed";
+    public static final String MSG_MEM_SPEED_NEGATIVE = "Memory speed must be positive and less or equal than " +
+            MemoryModuleDevice.MAX_SPEED_MTS + " MT/s";
+    public static final String MSG_MEM_TYPE_NULL = "Memory type cannot be NULL";
+    public static final String MSG_MEM_WIDTH_DATA_NEGATIVE = "Memory data width must be positive";
+    public static final String MSG_MEM_WIDTH_TOTAL_NEGATIVE = "Memory total width must be positive";
+
+    public static final String MSG_NIC_FLOW_FILTER_MAC_NULL = "MAC address of NIC Rx filter cannot be NULL";
+    public static final String MSG_NIC_FLOW_FILTER_MECH_NULL = "NIC flow Rx filter mechanism cannot be NULL";
+    public static final String MSG_NIC_FLOW_FILTER_MPLS_NULL = "MPLS label of NIC Rx filter cannot be NULL";
+    public static final String MSG_NIC_FLOW_FILTER_NEGATIVE = "NIC flow Rx filter has invalid CPU core ID";
+    public static final String MSG_NIC_FLOW_FILTER_NULL = "NIC flow Rx filter cannot be NULL";
+    public static final String MSG_NIC_FLOW_FILTER_RSS_NEGATIVE = "RSS NIC Rx filter cannot be negative";
+    public static final String MSG_NIC_FLOW_FILTER_VLAN_NULL = "VLAN ID of NIC Rx filter cannot be NULL";
+    public static final String MSG_NIC_FLOW_RULE_ACTION_VAL_NULL = "NIC rule action value cannot be NULL";
+    public static final String MSG_NIC_FLOW_RULE_ACTION_TYPE_NULL = "NIC rule action type cannot be NULL";
+    public static final String MSG_NIC_FLOW_RULE_CORE_ID_NEGATIVE = "NIC rule's CPU core index must not be negative";
+    public static final String MSG_NIC_FLOW_RULE_IFACE_NEGATIVE = "NIC rule's interface number must be positive";
+    public static final String MSG_NIC_FLOW_RULE_IFACE_NULL = "NIC rule's interface name cannot be NULL";
+    public static final String MSG_NIC_FLOW_RULE_NULL = "NIC flow Rx filter rule cannot be NULL";
+    public static final String MSG_NIC_FLOW_RULE_SCOPE_NULL = "NIC rule's scope is NULL or empty";
+    public static final String MSG_NIC_FLOW_RULE_TC_ID_NULL = "NIC rule's traffic class ID is NULL or empty";
+
+    public static final String MSG_NIC_LIST_NULL = "Device's set of NICs cannot be NULL";
+    public static final String MSG_NIC_NAME_NULL = "NIC name cannot be empty or NULL";
+    public static final String MSG_NIC_MAC_NULL = "NIC MAC address cannot be NULL";
+    public static final String MSG_NIC_PORT_NUMBER_NEGATIVE = "NIC port number cannot be negative";
+    public static final String MSG_NIC_PORT_TYPE_NULL = "NIC port type cannot be NULL";
+    public static final String MSG_NIC_RX_FILTER_NULL = "Unsupported NIC Rx filter" +
+        " Choose one in: " + BasicServerDriver.enumTypesToString(RxFilter.class);
+    public static final String MSG_NIC_RX_FILTERS_NULL = "NIC Rx filters' list is NULL";
+    public static final String MSG_NIC_SPEED_NEGATIVE = "NIC speed must be positive and less or equal than " +
+        NicDevice.MAX_SPEED + " Mbps";
+    public static final String MSG_NIC_TABLE_SIZE_NEGATIVE    = "Invalid NIC table size";
+    public static final String MSG_NIC_TABLE_INDEX_NEGATIVE   = "Invalid NIC table index";
+    public static final String MSG_NIC_TABLE_COUNTER_NEGATIVE = "Invalid NIC table counter";
+
+    public static final String MSG_STATS_CPU_NULL = "CPU statistics are NULL";
+    public static final String MSG_STATS_CPU_CACHE_NULL = "CPU cache statistics are NULL";
+    public static final String MSG_STATS_MEMORY_FREE_NEGATIVE = "Free memory must be positive";
+    public static final String MSG_STATS_MEMORY_NULL = "Memory statistics are NULL";
+    public static final String MSG_STATS_MEMORY_TOTAL_NEGATIVE = "Total memory must be positive";
+    public static final String MSG_STATS_MEMORY_USED_NEGATIVE = "Used memory must be positive";
+    public static final String MSG_STATS_NIC_NULL = "NIC statistics are NULL";
+    public static final String MSG_STATS_TIMING_NULL = "Timing statistics are NULL";
+    public static final String MSG_STATS_TIMING_AUTO_SCALE_NEGATIVE = "Auto-scale time must be positive";
+    public static final String MSG_STATS_TIMING_DEPLOY_INCONSISTENT = "Deploy time must be equal to the" +
+        " summary of parsing and launching times";
+    public static final String MSG_STATS_TIMING_LAUNCH_NEGATIVE = "Launching time must be positive";
+    public static final String MSG_STATS_TIMING_PARSE_NEGATIVE = "Parsing time must be positive";
+    public static final String MSG_STATS_UNIT_NULL = "Statistics unit is NULL";
+
+    public static final String MSG_UI_DATA_CPU_NULL = "No CPU data to visualize";
+    public static final String MSG_UI_DATA_LATENCY_NULL = "No latency data to visualize";
+    public static final String MSG_UI_DATA_MEMORY_NULL = "No memory data to visualize";
+    public static final String MSG_UI_DATA_THROUGHPUT_NULL = "No throughput data to visualize";
+    public static final String MSG_UI_SUBMETRIC_NULL = "UI submetric is NULL";
+
+}
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 7542698..dbfe36b 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
@@ -16,12 +16,6 @@
 
 package org.onosproject.drivers.server;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.collect.Sets;
-
 import org.onosproject.drivers.server.devices.nic.NicFlowRule;
 import org.onosproject.drivers.server.devices.nic.NicRxFilter.RxFilter;
 import org.onosproject.net.DeviceId;
@@ -35,6 +29,13 @@
 
 import org.slf4j.Logger;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
+import com.google.common.collect.Sets;
+
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -47,10 +48,20 @@
 import java.util.concurrent.ConcurrentHashMap;
 import javax.ws.rs.ProcessingException;
 
-import com.google.common.base.Strings;
-
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.JSON;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_ID_NULL;
+import static org.onosproject.drivers.server.Constants.PARAM_ID;
+import static org.onosproject.drivers.server.Constants.PARAM_CPUS;
+import static org.onosproject.drivers.server.Constants.PARAM_NICS;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_RX_FILTER;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_RX_FILTER_FD;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_RX_METHOD;
+import static org.onosproject.drivers.server.Constants.PARAM_RULES;
+import static org.onosproject.drivers.server.Constants.PARAM_RULE_CONTENT;
+import static org.onosproject.drivers.server.Constants.SLASH;
+import static org.onosproject.drivers.server.Constants.URL_RULE_MANAGEMENT;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -58,35 +69,26 @@
  * converting ONOS FlowRule objetcs into
  * network interface card (NIC) rules and vice versa.
  */
-public class FlowRuleProgrammableServerImpl extends BasicServerDriver
+public class FlowRuleProgrammableServerImpl
+        extends BasicServerDriver
         implements FlowRuleProgrammable {
 
     private final Logger log = getLogger(getClass());
 
     /**
-     * Resource endpoints of the server agent (REST server-side).
-     */
-    private static final String RULE_MANAGEMENT_URL = BASE_URL + SLASH + "rules";
-
-    /**
-     * Parameters to be exchanged with the server's agent.
-     */
-    private static final String PARAM_RULES        = "rules";
-    private static final String PARAM_CPU_ID       = "cpuId";
-    private static final String PARAM_CPU_RULES    = "cpuRules";
-    private static final String PARAM_RULE_ID      = "ruleId";
-    private static final String PARAM_RULE_CONTENT = "ruleContent";
-    private static final String PARAM_RX_FILTER_FD = "flow";
-
-    /**
      * Driver's property to specify how many rules the controller can remove at once.
      */
     private static final String RULE_DELETE_BATCH_SIZE_PROPERTY = "ruleDeleteBatchSize";
 
+    public FlowRuleProgrammableServerImpl() {
+        super();
+        log.debug("Started");
+    }
+
     @Override
     public Collection<FlowEntry> getFlowEntries() {
-        DeviceId deviceId = getHandler().data().deviceId();
-        checkNotNull(deviceId, DEVICE_ID_NULL);
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
 
         // Expected FlowEntries installed through ONOS
         FlowRuleService flowService = getHandler().get(FlowRuleService.class);
@@ -95,7 +97,7 @@
         // Hit the path that provides the server's flow rules
         InputStream response = null;
         try {
-            response = getController().get(deviceId, RULE_MANAGEMENT_URL, JSON);
+            response = getController().get(deviceId, URL_RULE_MANAGEMENT, JSON);
         } catch (ProcessingException pEx) {
             log.error("Failed to get NIC flow entries from device: {}", deviceId);
             return Collections.EMPTY_LIST;
@@ -127,7 +129,7 @@
         for (JsonNode scNode : scsNode) {
             String scId = get(scNode, PARAM_ID);
             String rxFilter = get(
-                scNode.path(NIC_PARAM_RX_FILTER), NIC_PARAM_RX_METHOD);
+                scNode.path(PARAM_NIC_RX_FILTER), PARAM_NIC_RX_METHOD);
 
             // Only Flow-based RxFilter is permitted
             if (RxFilter.getByName(rxFilter) != RxFilter.FLOW) {
@@ -142,12 +144,12 @@
 
                 // Each NIC can dispatch to multiple CPU cores
                 for (JsonNode cpuNode : cpusNode) {
-                    String cpuId = get(cpuNode, PARAM_CPU_ID);
-                    JsonNode rulesNode = cpuNode.path(PARAM_CPU_RULES);
+                    String cpuId = get(cpuNode, PARAM_ID);
+                    JsonNode rulesNode = cpuNode.path(PARAM_RULES);
 
                     // Multiple rules might correspond to each CPU core
                     for (JsonNode ruleNode : rulesNode) {
-                        long ruleId = ruleNode.path(PARAM_RULE_ID).asLong();
+                        long ruleId = ruleNode.path(PARAM_ID).asLong();
                         String ruleContent = get(ruleNode, PARAM_RULE_CONTENT);
 
                         // Search for this rule ID in ONOS's store
@@ -159,7 +161,7 @@
                         // Rule trully present in the data plane => Add
                         } else {
                             actualFlowEntries.add(new DefaultFlowEntry(
-                                    r, FlowEntry.FlowEntryState.ADDED, 0, 0, 0));
+                                r, FlowEntry.FlowEntryState.ADDED, 0, 0, 0));
                         }
                     }
                 }
@@ -171,8 +173,8 @@
 
     @Override
     public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
-        DeviceId deviceId = getHandler().data().deviceId();
-        checkNotNull(deviceId, DEVICE_ID_NULL);
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
 
         // Set of truly-installed rules to be reported
         Set<FlowRule> installedRules = Sets.<FlowRule>newConcurrentHashSet();
@@ -195,8 +197,8 @@
 
     @Override
     public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
-        DeviceId deviceId = getHandler().data().deviceId();
-        checkNotNull(deviceId, DEVICE_ID_NULL);
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
 
         int ruleDeleteBatchSize = getRuleDeleteBatchSizeProperty(deviceId);
 
@@ -258,7 +260,7 @@
                 try {
                     nicRule = (NicFlowRule) rule;
                 } catch (ClassCastException cEx) {
-                    log.warn("Skipping rule not crafted for NIC: {}", rule);
+                    log.warn("Skipping flow rule not crafted for NIC: {}", rule);
                 }
 
                 if (nicRule != null) {
@@ -319,12 +321,12 @@
         ObjectNode scsObjNode = mapper.createObjectNode();
 
         // Add the service chain's traffic class ID that requested these rules
-        scsObjNode.put(BasicServerDriver.PARAM_ID, trafficClassId);
+        scsObjNode.put(PARAM_ID, trafficClassId);
 
         // Create the object node to host the Rx filter method
         ObjectNode methodObjNode = mapper.createObjectNode();
-        methodObjNode.put(BasicServerDriver.NIC_PARAM_RX_METHOD, PARAM_RX_FILTER_FD);
-        scsObjNode.put(BasicServerDriver.NIC_PARAM_RX_FILTER, methodObjNode);
+        methodObjNode.put(PARAM_NIC_RX_METHOD, PARAM_NIC_RX_FILTER_FD);
+        scsObjNode.put(PARAM_NIC_RX_FILTER, methodObjNode);
 
         // Map each core to an array of rule IDs and rules
         Map<Long, ArrayNode> cpuObjSet =
@@ -336,7 +338,7 @@
         while (it.hasNext()) {
             NicFlowRule nicRule = (NicFlowRule) it.next();
             if (nicRule.isFullWildcard() && (rulesToInstall > 1)) {
-                log.warn("Skipping wildcard rule: {}", nicRule);
+                log.warn("Skipping wildcard flow rule: {}", nicRule);
                 it.remove();
                 continue;
             }
@@ -347,7 +349,7 @@
             if (nic == null) {
                 nic = findNicInterfaceWithPort(deviceId, nicRule.interfaceNumber());
                 checkArgument(!Strings.isNullOrEmpty(nic),
-                    "Attempted to install rules in an invalid NIC");
+                    "Attempted to install flow rules in an invalid NIC");
             }
 
             // Create a JSON array for this CPU core
@@ -383,7 +385,7 @@
 
             ObjectNode cpuObjNode = mapper.createObjectNode();
             cpuObjNode.put("cpuId", coreIndex);
-            cpuObjNode.putArray(PARAM_CPU_RULES).addAll(ruleArrayNode);
+            cpuObjNode.putArray(PARAM_RULES).addAll(ruleArrayNode);
 
             cpusArrayNode.add(cpuObjNode);
         }
@@ -396,7 +398,7 @@
 
         // Post the NIC rules to the server
         int response = getController().post(
-            deviceId, RULE_MANAGEMENT_URL,
+            deviceId, URL_RULE_MANAGEMENT,
             new ByteArrayInputStream(sendObjNode.toString().getBytes()), JSON);
 
         // Upon an error, return an empty set of rules
@@ -427,7 +429,7 @@
         // Try to remove the rules, although server might be unreachable
         try {
             response = getController().delete(deviceId,
-                RULE_MANAGEMENT_URL + SLASH + ruleIds, null, JSON);
+                URL_RULE_MANAGEMENT + SLASH + ruleIds, null, JSON);
         } catch (Exception ex) {
             log.error("Failed to remove NIC flow rule batch with {} rules from device {}", ruleCount, deviceId);
             return false;
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/ServerBasicSystemOperations.java b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerBasicSystemOperations.java
new file mode 100644
index 0000000..eb4536f
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerBasicSystemOperations.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2020-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;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.BasicSystemOperations;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.protocol.rest.RestSBDevice;
+
+import org.slf4j.Logger;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import javax.ws.rs.ProcessingException;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.CompletableFuture.completedFuture;
+import static org.onosproject.drivers.server.Constants.JSON;
+import static org.onosproject.drivers.server.Constants.MSG_HANDLER_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_ID_NULL;
+import static org.onosproject.drivers.server.Constants.PARAM_TIME;
+import static org.onosproject.drivers.server.Constants.URL_SRV_TIME_DISCOVERY;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of basic system operations' behaviour for server devices.
+ */
+public class ServerBasicSystemOperations
+        extends BasicServerDriver
+        implements BasicSystemOperations {
+
+    private final Logger log = getLogger(getClass());
+
+    public ServerBasicSystemOperations() {
+        super();
+        log.debug("Started");
+    }
+
+    @Override
+    public DriverHandler handler() {
+        return super.getHandler();
+    }
+
+    @Override
+    public void setHandler(DriverHandler handler) {
+        checkNotNull(handler, MSG_HANDLER_NULL);
+        this.handler = handler;
+    }
+
+    @Override
+    public CompletableFuture<Boolean> reboot() {
+        throw new UnsupportedOperationException("Reboot operation not supported");
+    }
+
+    @Override
+    public CompletableFuture<Long> time() {
+        // Retrieve the device ID from the handler
+        DeviceId deviceId = super.getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
+
+        // Get the device
+        RestSBDevice device = super.getDevice(deviceId);
+        checkNotNull(device, MSG_DEVICE_NULL);
+
+        // Hit the path that provides the server's time
+        InputStream response = null;
+        try {
+            response = getController().get(deviceId, URL_SRV_TIME_DISCOVERY, JSON);
+        } catch (ProcessingException pEx) {
+            log.error("Failed to get the time of device: {}", deviceId);
+            return null;
+        }
+
+        // Load the JSON into object
+        ObjectMapper mapper = new ObjectMapper();
+        Map<String, Object> jsonMap = null;
+        JsonNode jsonNode = null;
+        try {
+            jsonMap  = mapper.readValue(response, Map.class);
+            jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
+        } catch (IOException ioEx) {
+            log.error("Failed to discover the device details of: {}", deviceId);
+            return null;
+        }
+
+        if (jsonNode == null) {
+            log.error("Failed to discover the device details of: {}", deviceId);
+            return null;
+        }
+
+        long time = jsonNode.path(PARAM_TIME).asLong();
+        checkArgument(time > 0, "Invalid time format: {}", time);
+
+        return completedFuture(new Long(time));
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/ServerControllerConfig.java b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerControllerConfig.java
index 3ffbc91..184b8b7 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/ServerControllerConfig.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerControllerConfig.java
@@ -39,31 +39,29 @@
 import javax.ws.rs.ProcessingException;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.JSON;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_ID_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_MASTERSHIP_NULL;
+import static org.onosproject.drivers.server.Constants.PARAM_CTRL;
+import static org.onosproject.drivers.server.Constants.PARAM_CTRL_IP;
+import static org.onosproject.drivers.server.Constants.PARAM_CTRL_PORT;
+import static org.onosproject.drivers.server.Constants.PARAM_CTRL_TYPE;
+import static org.onosproject.drivers.server.Constants.SLASH;
+import static org.onosproject.drivers.server.Constants.URL_CONTROLLERS_GET;
+import static org.onosproject.drivers.server.Constants.URL_CONTROLLERS_DEL;
+import static org.onosproject.drivers.server.Constants.URL_CONTROLLERS_SET;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Sets, gets, and removes controller configuration
- * from a commodity server (i.e., a REST device).
+ * Implementation of controller configuration behaviour for server devices.
  */
-public class ServerControllerConfig extends BasicServerDriver
+public class ServerControllerConfig
+        extends BasicServerDriver
         implements ControllerConfig {
 
     private final Logger log = getLogger(getClass());
 
     /**
-     * Resource endpoints of the server agent (REST server-side).
-     */
-    private static final String CONTROLLERS_CONF_URL = BASE_URL + SLASH + "controllers";
-
-    /**
-     * Parameters to be exchanged with the server's agent.
-     */
-    private static final String PARAM_CTRL      = "controllers";
-    private static final String PARAM_CTRL_IP   = "ip";
-    private static final String PARAM_CTRL_PORT = "port";
-    private static final String PARAM_CTRL_TYPE = "type";
-
-    /**
      * Constructs controller configuration for server.
      */
     public ServerControllerConfig() {
@@ -75,11 +73,11 @@
     public List<ControllerInfo> getControllers() {
         List<ControllerInfo> controllers = Lists.newArrayList();
 
-        DeviceId deviceId = getHandler().data().deviceId();
-        checkNotNull(deviceId, DEVICE_ID_NULL);
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
 
         MastershipService mastershipService = getHandler().get(MastershipService.class);
-        checkNotNull(deviceId, MASTERSHIP_NULL);
+        checkNotNull(deviceId, MSG_MASTERSHIP_NULL);
 
         if (!mastershipService.isLocalMaster(deviceId)) {
             log.warn(
@@ -92,7 +90,7 @@
         // Hit the path that provides the server's controllers
         InputStream response = null;
         try {
-            response = getController().get(deviceId, CONTROLLERS_CONF_URL, JSON);
+            response = getController().get(deviceId, URL_CONTROLLERS_GET, JSON);
         } catch (ProcessingException pEx) {
             log.error("Failed to get controllers of device: {}", deviceId);
             return controllers;
@@ -117,9 +115,9 @@
             return controllers;
         }
 
+        // Fetch controllers' array
         JsonNode ctrlNode = objNode.path(PARAM_CTRL);
 
-        // Fetch controller objects
         for (JsonNode cn : ctrlNode) {
             ObjectNode ctrlObjNode = (ObjectNode) cn;
 
@@ -156,11 +154,11 @@
 
     @Override
     public void setControllers(List<ControllerInfo> controllers) {
-        DeviceId deviceId = getHandler().data().deviceId();
-        checkNotNull(deviceId, DEVICE_ID_NULL);
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
 
         MastershipService mastershipService = getHandler().get(MastershipService.class);
-        checkNotNull(deviceId, MASTERSHIP_NULL);
+        checkNotNull(deviceId, MSG_MASTERSHIP_NULL);
 
         if (!mastershipService.isLocalMaster(deviceId)) {
             log.warn(
@@ -189,7 +187,7 @@
 
         // Post the controllers to the device
         int response = getController().post(
-            deviceId, CONTROLLERS_CONF_URL,
+            deviceId, URL_CONTROLLERS_SET,
             new ByteArrayInputStream(sendObjNode.toString().getBytes()), JSON);
 
         if (!checkStatusCode(response)) {
@@ -201,11 +199,11 @@
 
     @Override
     public void removeControllers(List<ControllerInfo> controllers) {
-        DeviceId deviceId = getHandler().data().deviceId();
-        checkNotNull(deviceId, DEVICE_ID_NULL);
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
 
         MastershipService mastershipService = getHandler().get(MastershipService.class);
-        checkNotNull(deviceId, MASTERSHIP_NULL);
+        checkNotNull(deviceId, MSG_MASTERSHIP_NULL);
 
         if (!mastershipService.isLocalMaster(deviceId)) {
             log.warn(
@@ -219,7 +217,7 @@
             log.info("Remove controller with {}:{}:{}",
                 ctrl.type(), ctrl.ip().toString(), ctrl.port());
 
-            String remCtrlUrl = CONTROLLERS_CONF_URL + SLASH + ctrl.ip().toString();
+            String remCtrlUrl = URL_CONTROLLERS_DEL + SLASH + ctrl.ip().toString();
 
             // Remove this controller
             int response = getController().delete(deviceId, remCtrlUrl, null, JSON);
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 ac6c28d..346c8ca 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
@@ -18,22 +18,27 @@
 
 import org.onosproject.drivers.server.behavior.CpuStatisticsDiscovery;
 import org.onosproject.drivers.server.behavior.MonitoringStatisticsDiscovery;
-import org.onosproject.drivers.server.devices.CpuDevice;
-import org.onosproject.drivers.server.devices.CpuVendor;
+import org.onosproject.drivers.server.devices.cpu.CpuCacheHierarchyDevice;
+import org.onosproject.drivers.server.devices.cpu.CpuDevice;
+import org.onosproject.drivers.server.devices.memory.MemoryHierarchyDevice;
 import org.onosproject.drivers.server.devices.nic.NicDevice;
-import org.onosproject.drivers.server.devices.nic.NicRxFilter;
-import org.onosproject.drivers.server.devices.nic.NicRxFilter.RxFilter;
 import org.onosproject.drivers.server.devices.ServerDeviceDescription;
 import org.onosproject.drivers.server.devices.RestServerSBDevice;
 import org.onosproject.drivers.server.stats.CpuStatistics;
+import org.onosproject.drivers.server.stats.MemoryStatistics;
 import org.onosproject.drivers.server.stats.MonitoringStatistics;
 import org.onosproject.drivers.server.stats.TimingStatistics;
 
+import org.onosproject.drivers.server.impl.devices.DefaultBasicCpuCacheDevice;
+import org.onosproject.drivers.server.impl.devices.DefaultCpuCacheHierarchyDevice;
 import org.onosproject.drivers.server.impl.devices.DefaultCpuDevice;
+import org.onosproject.drivers.server.impl.devices.DefaultMemoryHierarchyDevice;
+import org.onosproject.drivers.server.impl.devices.DefaultMemoryModuleDevice;
 import org.onosproject.drivers.server.impl.devices.DefaultNicDevice;
 import org.onosproject.drivers.server.impl.devices.DefaultRestServerSBDevice;
 import org.onosproject.drivers.server.impl.devices.DefaultServerDeviceDescription;
 import org.onosproject.drivers.server.impl.stats.DefaultCpuStatistics;
+import org.onosproject.drivers.server.impl.stats.DefaultMemoryStatistics;
 import org.onosproject.drivers.server.impl.stats.DefaultMonitoringStatistics;
 import org.onosproject.drivers.server.impl.stats.DefaultTimingStatistics;
 
@@ -43,6 +48,10 @@
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.behaviour.DevicesDiscovery;
+import org.onosproject.net.behaviour.DeviceCpuStats;
+import org.onosproject.net.behaviour.DeviceMemoryStats;
+import org.onosproject.net.behaviour.DeviceSystemStatisticsQuery;
+import org.onosproject.net.behaviour.DeviceSystemStats;
 import org.onosproject.net.device.DeviceDescription;
 import org.onosproject.net.device.DeviceDescriptionDiscovery;
 import org.onosproject.net.device.DefaultPortStatistics;
@@ -50,10 +59,8 @@
 import org.onosproject.net.device.PortDescription;
 import org.onosproject.net.device.PortStatistics;
 import org.onosproject.net.device.PortStatisticsDiscovery;
-import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.protocol.rest.RestSBDevice;
-import org.onosproject.protocol.rest.RestSBDevice.AuthenticationScheme;
 
 import org.slf4j.Logger;
 
@@ -64,6 +71,7 @@
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import com.google.common.collect.ImmutableList;
 
 import java.io.InputStream;
@@ -74,113 +82,109 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.TreeSet;
 import javax.ws.rs.ProcessingException;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.JSON;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_ID_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_NAME_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_PORT_NUMBER_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_STATS_TIMING_DEPLOY_INCONSISTENT;
+import static org.onosproject.drivers.server.Constants.PARAM_ID;
+import static org.onosproject.drivers.server.Constants.PARAM_CAPACITY;
+import static org.onosproject.drivers.server.Constants.PARAM_CHASSIS_ID;
+import static org.onosproject.drivers.server.Constants.PARAM_CPUS;
+import static org.onosproject.drivers.server.Constants.PARAM_NICS;
+import static org.onosproject.drivers.server.Constants.PARAM_NAME;
+import static org.onosproject.drivers.server.Constants.PARAM_MEMORY;
+import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_HIERARCHY;
+import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_MODULES;
+import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_STATS_FREE;
+import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_STATS_TOTAL;
+import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_STATS_USED;
+import static org.onosproject.drivers.server.Constants.PARAM_MANUFACTURER;
+import static org.onosproject.drivers.server.Constants.PARAM_HW_VENDOR;
+import static org.onosproject.drivers.server.Constants.PARAM_SW_VENDOR;
+import static org.onosproject.drivers.server.Constants.PARAM_SERIAL;
+import static org.onosproject.drivers.server.Constants.PARAM_TIMING_STATS;
+import static org.onosproject.drivers.server.Constants.PARAM_TIMING_STATS_AUTOSCALE;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_LEVEL;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_LEVELS;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_LINE_LEN;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_POLICY;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_SETS;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_SHARED;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_TYPE;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_WAYS;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHES;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_CACHE_HIERARCHY;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_CORES;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_FREQUENCY;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_ID_LOG;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_ID_PHY;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_LOAD;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_LATENCY;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_QUEUE;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_SOCKET;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_SOCKETS;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_STATUS;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_THROUGHPUT;
+import static org.onosproject.drivers.server.Constants.PARAM_CPU_VENDOR;
+import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_WIDTH_DATA;
+import static org.onosproject.drivers.server.Constants.PARAM_MEMORY_WIDTH_TOTAL;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_HW_ADDR;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_PORT_TYPE;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_RX_FILTER;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_RX_COUNT;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_RX_BYTES;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_RX_DROPS;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_RX_ERRORS;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_TX_COUNT;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_TX_BYTES;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_TX_DROPS;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_STATS_TX_ERRORS;
+import static org.onosproject.drivers.server.Constants.PARAM_MON_AVERAGE;
+import static org.onosproject.drivers.server.Constants.PARAM_MON_BUSY_CPUS;
+import static org.onosproject.drivers.server.Constants.PARAM_MON_FREE_CPUS;
+import static org.onosproject.drivers.server.Constants.PARAM_MON_MAX;
+import static org.onosproject.drivers.server.Constants.PARAM_MON_MIN;
+import static org.onosproject.drivers.server.Constants.PARAM_MON_UNIT;
+import static org.onosproject.drivers.server.Constants.PARAM_SPEED;
+import static org.onosproject.drivers.server.Constants.PARAM_SPEED_CONF;
+import static org.onosproject.drivers.server.Constants.PARAM_STATUS;
+import static org.onosproject.drivers.server.Constants.PARAM_TIMING_PARSE;
+import static org.onosproject.drivers.server.Constants.PARAM_TIMING_LAUNCH;
+import static org.onosproject.drivers.server.Constants.PARAM_TIMING_DEPLOY;
+import static org.onosproject.drivers.server.Constants.PARAM_TIMING_AUTOSCALE;
+import static org.onosproject.drivers.server.Constants.PARAM_TYPE;
+import static org.onosproject.drivers.server.Constants.SLASH;
+import static org.onosproject.drivers.server.Constants.URL_SRV_GLOBAL_STATS;
+import static org.onosproject.drivers.server.Constants.URL_SRV_RESOURCE_DISCOVERY;
+import static org.onosproject.drivers.server.Constants.URL_SERVICE_CHAINS_STATS;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Discovers the device details of
- * REST-based commodity server devices.
+ * Discovers the device details of server devices.
  */
-public class ServerDevicesDiscovery extends BasicServerDriver
+public class ServerDevicesDiscovery
+        extends BasicServerDriver
         implements  DevicesDiscovery, DeviceDescriptionDiscovery,
                     PortStatisticsDiscovery, CpuStatisticsDiscovery,
-                    MonitoringStatisticsDiscovery {
+                    MonitoringStatisticsDiscovery, DeviceSystemStatisticsQuery {
 
     private final Logger log = getLogger(getClass());
 
     /**
-     * Resource endpoints of the server agent (REST server-side).
-     */
-    private static final String RESOURCE_DISCOVERY_URL   = BASE_URL + SLASH + "resources";
-    private static final String GLOBAL_STATS_URL         = BASE_URL + SLASH + "stats";
-    private static final String SERVICE_CHAINS_STATS_URL = BASE_URL + SLASH + "chains_stats";  // + /ID
-
-    /**
-     * Parameters to be exchanged with the server's agent.
-     */
-    private static final String PARAM_MANUFACTURER     = "manufacturer";
-    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     = "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";
-    private static final String NIC_PARAM_PORT_TYPE        = "portType";
-    private static final String NIC_PARAM_PORT_TYPE_FIBER  = "fiber";
-    private static final String NIC_PARAM_PORT_TYPE_COPPER = "copper";
-    private static final String NIC_PARAM_SPEED            = "speed";
-    private static final String NIC_PARAM_STATUS           = "status";
-    private static final String NIC_PARAM_HW_ADDR          = "hwAddr";
-
-    /**
-     * NIC statistics.
-     */
-    private static final String NIC_STATS_TX_COUNT  = "txCount";
-    private static final String NIC_STATS_TX_BYTES  = "txBytes";
-    private static final String NIC_STATS_TX_DROPS  = "txDropped";
-    private static final String NIC_STATS_TX_ERRORS = "txErrors";
-    private static final String NIC_STATS_RX_COUNT  = "rxCount";
-    private static final String NIC_STATS_RX_BYTES  = "rxBytes";
-    private static final String NIC_STATS_RX_DROPS  = "rxDropped";
-    private static final String NIC_STATS_RX_ERRORS = "rxErrors";
-
-    /**
-     * 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_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_MAX        = "max";
-
-    /**
-     * Timing statistics.
-     */
-    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.
      */
-    private static final short  DISCOVERY_RETRIES  = 3;
-    private static final String CPU_VENDOR_NULL    = "Unsupported CPU vendor" +
-        " Choose one in: " + BasicServerDriver.enumTypesToString(CpuVendor.class);
-    private static final String NIC_RX_FILTER_NULL = "Unsupported NIC Rx filter" +
-        " Choose one in: " + BasicServerDriver.enumTypesToString(RxFilter.class);
-
-    /**
-     * Port types that usually appear in commodity servers.
-     */
-    public static final Map<String, Port.Type> PORT_TYPE_MAP =
-        Collections.unmodifiableMap(
-            new HashMap<String, Port.Type>() {
-                {
-                    put(NIC_PARAM_PORT_TYPE_FIBER,  Port.Type.FIBER);
-                    put(NIC_PARAM_PORT_TYPE_COPPER, Port.Type.COPPER);
-                }
-            }
-        );
+    private static final short DISCOVERY_RETRIES = 3;
 
     /**
      * Constructs server device discovery.
@@ -190,13 +194,16 @@
         log.debug("Started");
     }
 
+    /**
+     * Implements DevicesDiscovery behaviour.
+     */
     @Override
     public Set<DeviceId> deviceIds() {
         // Set of devices to return
         Set<DeviceId> devices = new HashSet<DeviceId>();
 
-        DeviceId deviceId = getHandler().data().deviceId();
-        checkNotNull(deviceId, DEVICE_ID_NULL);
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
         devices.add(deviceId);
 
         return devices;
@@ -207,6 +214,9 @@
         return getDeviceDetails(deviceId);
     }
 
+    /**
+     * Implements DeviceDescriptionDiscovery behaviour.
+     */
     @Override
     public DeviceDescription discoverDeviceDetails() {
         return getDeviceDetails(null);
@@ -221,18 +231,18 @@
     private DeviceDescription getDeviceDetails(DeviceId deviceId) {
         // Retrieve the device ID, if null given
         if (deviceId == null) {
-            deviceId = getHandler().data().deviceId();
-            checkNotNull(deviceId, DEVICE_ID_NULL);
+            deviceId = getDeviceId();
+            checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
         }
 
         // Get the device
-        RestSBDevice device = getController().getDevice(deviceId);
-        checkNotNull(device, DEVICE_NULL);
+        RestSBDevice device = getDevice(deviceId);
+        checkNotNull(device, MSG_DEVICE_NULL);
 
         // Hit the path that provides the server's resources
         InputStream response = null;
         try {
-            response = getController().get(deviceId, RESOURCE_DISCOVERY_URL, JSON);
+            response = getController().get(deviceId, URL_SRV_RESOURCE_DISCOVERY, JSON);
         } catch (ProcessingException pEx) {
             log.error("Failed to discover the device details of: {}", deviceId);
             return null;
@@ -258,62 +268,237 @@
         }
 
         // Get all the attributes
-        String id     = get(jsonNode, BasicServerDriver.PARAM_ID);
-        String vendor = get(jsonNode, PARAM_MANUFACTURER);
-        String hw     = get(jsonNode, PARAM_HW_VENDOR);
-        String sw     = get(jsonNode, PARAM_SW_VENDOR);
-        String serial = get(jsonNode, PARAM_SERIAL);
+        String id      = get(jsonNode, PARAM_ID);
+        String vendor  = get(jsonNode, PARAM_MANUFACTURER);
+        String hw      = get(jsonNode, PARAM_HW_VENDOR);
+        String sw      = get(jsonNode, PARAM_SW_VENDOR);
+        String serial  = get(jsonNode, PARAM_SERIAL);
+        long chassisId = objNode.path(PARAM_CHASSIS_ID).asLong();
 
-        // CPUs are composite attributes
-        Set<CpuDevice> cpuSet = new HashSet<CpuDevice>();
-        JsonNode cpuNode = objNode.path(BasicServerDriver.PARAM_CPUS);
+        // Pass the southbound protocol as an annotation
+        DefaultAnnotations.Builder annotations = DefaultAnnotations.builder();
+        annotations.set(AnnotationKeys.PROTOCOL, "REST");
+
+        // Parse CPU devices
+        Collection<CpuDevice> cpuSet = parseCpuDevices(objNode);
+
+        // Parse memory hierarchy device
+        MemoryHierarchyDevice memHierarchyDev = parseMemoryHierarchyDevice(objNode);
+
+        // Parse CPU cache hierachy device
+        CpuCacheHierarchyDevice cacheHierarchyDev = parseCpuCacheHierarchyDevice(objNode);
+
+        // NICs are composite attributes too
+        Collection<NicDevice> nicSet = parseNicDevices(mapper, objNode, annotations);
+
+        // Construct a server device,
+        // i.e., a RestSBDevice extended with CPU, cache, memory, and NIC information
+        RestServerSBDevice dev = new DefaultRestServerSBDevice(
+            device.ip(), device.port(), device.username(),
+            device.password(), device.protocol(), device.url(),
+            device.isActive(), device.testUrl().orElse(""),
+            vendor, hw, sw, cpuSet, cacheHierarchyDev,
+            memHierarchyDev, nicSet);
+        checkNotNull(dev, MSG_DEVICE_NULL);
+
+        // Set alive
+        raiseDeviceReconnect(dev);
+
+        // Updates the controller with the complete device information
+        getController().removeDevice(deviceId);
+        getController().addDevice((RestSBDevice) dev);
+
+        // Create a description for this server device
+        ServerDeviceDescription desc = null;
+        try {
+            desc = new DefaultServerDeviceDescription(
+                new URI(id), Device.Type.SERVER, vendor,
+                hw, sw, serial, new ChassisId(chassisId),
+                cpuSet, cacheHierarchyDev, memHierarchyDev,
+                nicSet, annotations.build());
+        } catch (URISyntaxException uEx) {
+            log.error("Failed to create a server device description for: {}",
+                deviceId);
+            return null;
+        }
+
+        log.info("Device's {} details sent to the controller", deviceId);
+
+        return desc;
+    }
+
+    /**
+     * Parse the input JSON object, looking for CPU-related
+     * information. Upon success, construct and return a list
+     * of CPU devices.
+     *
+     * @param objNode input JSON node with CPU device information
+     * @return list of CPU devices
+     */
+    private Collection<CpuDevice> parseCpuDevices(ObjectNode objNode) {
+        Collection<CpuDevice> cpuSet = Sets.newHashSet();
+        JsonNode cpuNode = objNode.path(PARAM_CPUS);
 
         // Construct CPU objects
         for (JsonNode cn : cpuNode) {
             ObjectNode cpuObjNode = (ObjectNode) cn;
 
             // All the CPU attributes
-            int           cpuId = cpuObjNode.path(CPU_PARAM_ID).asInt();
-            String cpuVendorStr = get(cn, CPU_PARAM_VENDOR);
-            long   cpuFrequency = cpuObjNode.path(CPU_PARAM_FREQUENCY).asLong();
+            int   physicalCpuId = cpuObjNode.path(PARAM_CPU_ID_PHY).asInt();
+            int    logicalCpuId = cpuObjNode.path(PARAM_CPU_ID_LOG).asInt();
+            int       cpuSocket = cpuObjNode.path(PARAM_CPU_SOCKET).asInt();
+            String cpuVendorStr = get(cn, PARAM_CPU_VENDOR);
+            long   cpuFrequency = cpuObjNode.path(PARAM_CPU_FREQUENCY).asLong();
 
-            // Verify that this is a valid vendor
-            CpuVendor cpuVendor = CpuVendor.getByName(cpuVendorStr);
-            checkNotNull(cpuVendor, CPU_VENDOR_NULL);
-
-            // Construct a CPU device
-            CpuDevice cpu = new DefaultCpuDevice(cpuId, cpuVendor, cpuFrequency);
-
-            // Add it to the set
-            cpuSet.add(cpu);
+            // Construct a CPU device and add it to the set
+            cpuSet.add(
+                DefaultCpuDevice.builder()
+                    .setCoreId(logicalCpuId, physicalCpuId)
+                    .setVendor(cpuVendorStr)
+                    .setSocket(cpuSocket)
+                    .setFrequency(cpuFrequency)
+                    .build());
         }
 
-        // NICs are composite attributes too
-        Set<NicDevice> nicSet = new HashSet<NicDevice>();
+        return cpuSet;
+    }
+
+    /**
+     * Parse the input JSON object, looking for CPU cache-related
+     * information. Upon success, construct and return a CPU cache
+     * hierarchy device.
+     *
+     * @param objNode input JSON node with CPU cache device information
+     * @return a CPU cache hierarchy devices
+     */
+    private CpuCacheHierarchyDevice parseCpuCacheHierarchyDevice(ObjectNode objNode) {
+        JsonNode cacheHierarchyNode = objNode.path(PARAM_CPU_CACHE_HIERARCHY);
+        ObjectNode cacheHierarchyObjNode = (ObjectNode) cacheHierarchyNode;
+        if (cacheHierarchyObjNode == null) {
+            return null;
+        }
+
+        int socketsNb = cacheHierarchyObjNode.path(PARAM_CPU_SOCKETS).asInt();
+        int coresNb = cacheHierarchyObjNode.path(PARAM_CPU_CORES).asInt();
+        int levels = cacheHierarchyObjNode.path(PARAM_CPU_CACHE_LEVELS).asInt();
+
+        JsonNode cacheNode = cacheHierarchyObjNode.path(PARAM_CPU_CACHES);
+
+        DefaultCpuCacheHierarchyDevice.Builder cacheBuilder =
+            DefaultCpuCacheHierarchyDevice.builder()
+                .setSocketsNumber(socketsNb)
+                .setCoresNumber(coresNb)
+                .setLevels(levels);
+
+        // Construct CPU cache objects
+        for (JsonNode cn : cacheNode) {
+            ObjectNode cacheObjNode = (ObjectNode) cn;
+
+            // CPU cache attributes
+            String cpuVendorStr = get(cn, PARAM_CPU_VENDOR);
+            String     levelStr = get(cn, PARAM_CPU_CACHE_LEVEL);
+            String      typeStr = get(cn, PARAM_CPU_CACHE_TYPE);
+            String    policyStr = get(cn, PARAM_CPU_CACHE_POLICY);
+            long       capacity = cacheObjNode.path(PARAM_CAPACITY).asLong();
+            int            sets = cacheObjNode.path(PARAM_CPU_CACHE_SETS).asInt();
+            int            ways = cacheObjNode.path(PARAM_CPU_CACHE_WAYS).asInt();
+            int         lineLen = cacheObjNode.path(PARAM_CPU_CACHE_LINE_LEN).asInt();
+            boolean      shared = cacheObjNode.path(PARAM_CPU_CACHE_SHARED).asInt() > 0;
+
+            // Construct a basic CPU cache device and add it to the hierarchy
+            cacheBuilder.addBasicCpuCacheDevice(
+                DefaultBasicCpuCacheDevice.builder()
+                    .setVendor(cpuVendorStr)
+                    .setCacheId(levelStr, typeStr)
+                    .setPolicy(policyStr)
+                    .setCapacity(capacity)
+                    .setNumberOfSets(sets)
+                    .setNumberOfWays(ways)
+                    .setLineLength(lineLen)
+                    .isShared(shared)
+                    .build());
+        }
+
+        return cacheBuilder.build();
+    }
+
+    /**
+     * Parse the input JSON object, looking for memory-related
+     * information. Upon success, construct and return a memory
+     * hierarchy device.
+     *
+     * @param objNode input JSON node with memory device information
+     * @return a memory hierarchy device
+     */
+    private MemoryHierarchyDevice parseMemoryHierarchyDevice(ObjectNode objNode) {
+        JsonNode memHierarchyNode = objNode.path(PARAM_MEMORY_HIERARCHY);
+        ObjectNode memoryHierarchyObjNode = (ObjectNode) memHierarchyNode;
+        if (memoryHierarchyObjNode == null) {
+            return null;
+        }
+
+        JsonNode memoryNode = memoryHierarchyObjNode.path(PARAM_MEMORY_MODULES);
+
+        DefaultMemoryHierarchyDevice.Builder memoryBuilder =
+            DefaultMemoryHierarchyDevice.builder();
+
+        // Construct memory modules
+        for (JsonNode mn : memoryNode) {
+            ObjectNode memoryObjNode = (ObjectNode) mn;
+
+            String typeStr = get(mn, PARAM_TYPE);
+            String manufacturerStr = get(mn, PARAM_MANUFACTURER);
+            String serialStr = get(mn, PARAM_SERIAL);
+            int dataWidth = memoryObjNode.path(PARAM_MEMORY_WIDTH_DATA).asInt();
+            int totalWidth = memoryObjNode.path(PARAM_MEMORY_WIDTH_TOTAL).asInt();
+            long capacity = memoryObjNode.path(PARAM_CAPACITY).asLong();
+            long speed = memoryObjNode.path(PARAM_SPEED).asLong();
+            long configuredSpeed = memoryObjNode.path(PARAM_SPEED_CONF).asLong();
+
+            // Construct a memory module and add it to the hierarchy
+            memoryBuilder.addMemoryModule(
+                DefaultMemoryModuleDevice.builder()
+                    .setType(typeStr)
+                    .setManufacturer(manufacturerStr)
+                    .setSerialNumber(serialStr)
+                    .setDataWidth(dataWidth)
+                    .setTotalWidth(totalWidth)
+                    .setCapacity(capacity)
+                    .setSpeed(speed)
+                    .setConfiguredSpeed(configuredSpeed)
+                    .build());
+        }
+
+        return memoryBuilder.build();
+    }
+
+    /**
+     * Parse the input JSON object, looking for NIC-related
+     * information. Upon success, construct and return a list
+     * of NIC devices.
+     *
+     * @param mapper input JSON object mapper
+     * @param objNode input JSON node with NIC device information
+     * @param annotations device annotations
+     * @return list of CPU devices
+     */
+    private Collection<NicDevice> parseNicDevices(
+            ObjectMapper mapper, ObjectNode objNode, DefaultAnnotations.Builder annotations) {
+        Collection<NicDevice> nicSet = Sets.newHashSet();
         JsonNode nicNode = objNode.path(PARAM_NICS);
 
-        DefaultAnnotations.Builder annotations = DefaultAnnotations.builder();
-
-        // Pass the southbound protocol as an annotation
-        annotations.set(AnnotationKeys.PROTOCOL, "REST");
-
         // Construct NIC objects
         for (JsonNode nn : nicNode) {
             ObjectNode nicObjNode = (ObjectNode) nn;
 
             // All the NIC attributes
-            String nicName     = get(nn, NIC_PARAM_NAME);
-            long nicIndex      = nicObjNode.path(NIC_PARAM_PORT_INDEX).asLong();
-            long speed         = nicObjNode.path(NIC_PARAM_SPEED).asLong();
-            String portTypeStr = get(nn, NIC_PARAM_PORT_TYPE);
-            Port.Type portType = PORT_TYPE_MAP.get(portTypeStr);
-            if (portType == null) {
-                throw new IllegalArgumentException(
-                    portTypeStr + " is not a valid port type for NIC " + nicName);
-            }
-            boolean status     = nicObjNode.path(NIC_PARAM_STATUS).asInt() > 0;
-            String hwAddr      = get(nn, NIC_PARAM_HW_ADDR);
-            JsonNode tagNode   = nicObjNode.path(BasicServerDriver.NIC_PARAM_RX_FILTER);
+            String nicName     = get(nn, PARAM_NAME);
+            long nicIndex      = nicObjNode.path(PARAM_ID).asLong();
+            long speed         = nicObjNode.path(PARAM_SPEED).asLong();
+            String portTypeStr = get(nn, PARAM_NIC_PORT_TYPE);
+            boolean status     = nicObjNode.path(PARAM_STATUS).asInt() > 0;
+            String hwAddr      = get(nn, PARAM_NIC_HW_ADDR);
+            JsonNode tagNode   = nicObjNode.path(PARAM_NIC_RX_FILTER);
             if (tagNode == null) {
                 throw new IllegalArgumentException(
                     "The Rx filters of NIC " + nicName + " are not reported");
@@ -328,76 +513,36 @@
                 continue;
             }
 
-            // Parse the array of strings and create an RxFilter object
-            NicRxFilter rxFilterMechanism = new NicRxFilter();
-            for (String s : rxFilters) {
-                // Verify that this is a valid Rx filter
-                RxFilter rf = RxFilter.getByName(s);
-                checkNotNull(rf, NIC_RX_FILTER_NULL);
-
-                rxFilterMechanism.addRxFilter(rf);
-            }
-
             // Store NIC name to number mapping as an annotation
             annotations.set(nicName, Long.toString(nicIndex));
 
-            // Construct a NIC device for this server
-            NicDevice nic = new DefaultNicDevice(
-                nicName, nicIndex, portType, speed, status, hwAddr, rxFilterMechanism);
-
-            // Add it to the set
-            nicSet.add(nic);
+            // Construct a NIC device and add it to the set
+            nicSet.add(
+                DefaultNicDevice.builder()
+                    .setName(nicName)
+                    .setPortNumber(nicIndex)
+                    .setPortNumber(nicIndex)
+                    .setPortType(portTypeStr)
+                    .setSpeed(speed)
+                    .setStatus(status)
+                    .setMacAddress(hwAddr)
+                    .setRxFilters(rxFilters)
+                    .build());
         }
 
-        // Construct a complete server device object.
-        // Lists of NICs and CPUs extend the information
-        // already in RestSBDevice (parent class).
-        RestServerSBDevice dev = new DefaultRestServerSBDevice(
-            device.ip(), device.port(), device.username(),
-            device.password(), device.protocol(), device.url(),
-            device.isActive(), device.testUrl().orElse(""),
-            vendor, hw, sw, AuthenticationScheme.BASIC, "",
-            cpuSet, nicSet
-        );
-        checkNotNull(dev, DEVICE_NULL);
-
-        // Set alive
-        raiseDeviceReconnect(dev);
-
-        // Updates the controller with the complete device information
-        getController().removeDevice(deviceId);
-        getController().addDevice((RestSBDevice) dev);
-
-        // Create a description for this server device
-        ServerDeviceDescription desc = null;
-
-        try {
-            desc = new DefaultServerDeviceDescription(
-                new URI(id), Device.Type.SERVER, vendor,
-                hw, sw, serial, new ChassisId(),
-                cpuSet, nicSet, annotations.build()
-            );
-        } catch (URISyntaxException uEx) {
-            log.error("Failed to create a server device description for: {}",
-                deviceId);
-            return null;
-        }
-
-        log.info("Device's {} details sent to the controller", deviceId);
-
-        return desc;
+        return nicSet;
     }
 
     @Override
     public List<PortDescription> discoverPortDetails() {
         // Retrieve the device ID
-        DeviceId deviceId = getHandler().data().deviceId();
-        checkNotNull(deviceId, DEVICE_ID_NULL);
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
 
         // .. and object
         RestServerSBDevice device = null;
         try {
-            device = (RestServerSBDevice) getController().getDevice(deviceId);
+            device = (RestServerSBDevice) getDevice(deviceId);
         } catch (ClassCastException ccEx) {
             log.error("Failed to discover ports for device {}", deviceId);
             return Collections.EMPTY_LIST;
@@ -427,7 +572,8 @@
             // Create a port description and add it to the list
             portDescriptions.add(
                     DefaultPortDescription.builder()
-                            .withPortNumber(PortNumber.portNumber(nic.portNumber(), nic.name()))
+                            // CHECK: .withPortNumber(PortNumber.portNumber(nic.portNumber(), nic.name()))
+                            .withPortNumber(PortNumber.portNumber(nic.portNumber()))
                             .isEnabled(nic.status())
                             .type(nic.portType())
                             .portSpeed(nic.speed())
@@ -442,11 +588,14 @@
         return ImmutableList.copyOf(portDescriptions);
     }
 
+    /**
+     * Implements PortStatisticsDiscovery behaviour.
+     */
     @Override
     public Collection<PortStatistics> discoverPortStatistics() {
         // Retrieve the device ID
-        DeviceId deviceId = getHandler().data().deviceId();
-        checkNotNull(deviceId, DEVICE_ID_NULL);
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
 
         // Get port statistics for this device
         return getPortStatistics(deviceId);
@@ -476,11 +625,14 @@
         return portStats;
     }
 
+    /**
+     * Implements CpuStatisticsDiscovery behaviour.
+     */
     @Override
     public Collection<CpuStatistics> discoverCpuStatistics() {
         // Retrieve the device ID
-        DeviceId deviceId = getHandler().data().deviceId();
-        checkNotNull(deviceId, DEVICE_ID_NULL);
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
 
         // Get CPU statistics for this device
         return getCpuStatistics(deviceId);
@@ -510,11 +662,14 @@
         return cpuStats;
     }
 
+    /**
+     * Implements MonitoringStatisticsDiscovery behaviour.
+     */
     @Override
     public MonitoringStatistics discoverGlobalMonitoringStatistics() {
         // Retrieve the device ID
-        DeviceId deviceId = getHandler().data().deviceId();
-        checkNotNull(deviceId, DEVICE_ID_NULL);
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
 
         // Get global monitoring statistics for this device
         return getGlobalMonitoringStatistics(deviceId);
@@ -532,7 +687,7 @@
 
         RestServerSBDevice device = null;
         try {
-            device = (RestServerSBDevice) getController().getDevice(deviceId);
+            device = (RestServerSBDevice) getDevice(deviceId);
         } catch (ClassCastException ccEx) {
             log.error("Failed to retrieve global monitoring statistics from device {}",
                 deviceId);
@@ -545,7 +700,7 @@
         // Hit the path that provides the server's global resources
         InputStream response = null;
         try {
-            response = getController().get(deviceId, GLOBAL_STATS_URL, JSON);
+            response = getController().get(deviceId, URL_SRV_GLOBAL_STATS, JSON);
         } catch (ProcessingException pEx) {
             log.error("Failed to retrieve global monitoring statistics from device {}",
                 deviceId);
@@ -576,28 +731,29 @@
         }
 
         // Get high-level CPU statistics
-        int busyCpus = objNode.path(MON_PARAM_BUSY_CPUS).asInt();
-        int freeCpus = objNode.path(MON_PARAM_FREE_CPUS).asInt();
+        int busyCpus = objNode.path(PARAM_MON_BUSY_CPUS).asInt();
+        int freeCpus = objNode.path(PARAM_MON_FREE_CPUS).asInt();
 
         // Get a list of CPU statistics per core
         Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
 
+        // Get main memory statistics
+        MemoryStatistics memStats = parseMemoryStatistics(deviceId, objNode);
+
         // Get a list of port statistics
         Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
 
         // Get zero timing statistics
         TimingStatistics timinsgStats = getZeroTimingStatistics();
 
-        // Ready to construct the grand object
-        DefaultMonitoringStatistics.Builder statsBuilder =
-            DefaultMonitoringStatistics.builder();
-
-        statsBuilder.setDeviceId(deviceId)
-                .setTimingStatistics(timinsgStats)
-                .setCpuStatistics(cpuStats)
-                .setNicStatistics(nicStats);
-
-        monStats = statsBuilder.build();
+        // Construct a global monitoring statistics object out of smaller ones
+        monStats = DefaultMonitoringStatistics.builder()
+                    .setDeviceId(deviceId)
+                    .setTimingStatistics(timinsgStats)
+                    .setCpuStatistics(cpuStats)
+                    .setMemoryStatistics(memStats)
+                    .setNicStatistics(nicStats)
+                    .build();
 
         // When a device reports monitoring data, it means it is alive
         raiseDeviceReconnect(device);
@@ -610,8 +766,8 @@
     @Override
     public MonitoringStatistics discoverMonitoringStatistics(URI tcId) {
         // Retrieve the device ID
-        DeviceId deviceId = getHandler().data().deviceId();
-        checkNotNull(deviceId, DEVICE_ID_NULL);
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
 
         // Get resource-specific monitoring statistics for this device
         return getMonitoringStatistics(deviceId, tcId);
@@ -631,7 +787,7 @@
 
         RestServerSBDevice device = null;
         try {
-            device = (RestServerSBDevice) getController().getDevice(deviceId);
+            device = (RestServerSBDevice) getDevice(deviceId);
         } catch (ClassCastException ccEx) {
             log.error("Failed to retrieve monitoring statistics from device {}",
                 deviceId);
@@ -642,7 +798,7 @@
         }
 
         // Create a resource-specific URL
-        String scUrl = SERVICE_CHAINS_STATS_URL + SLASH + tcId.toString();
+        String scUrl = URL_SERVICE_CHAINS_STATS + SLASH + tcId.toString();
 
         // Hit the path that provides the server's specific resources
         InputStream response = null;
@@ -692,22 +848,23 @@
         // Get a list of CPU statistics per core
         Collection<CpuStatistics> cpuStats = parseCpuStatistics(deviceId, objNode);
 
+        // Get main memory statistics
+        MemoryStatistics memStats = parseMemoryStatistics(deviceId, objNode);
+
         // Get a list of port statistics
         Collection<PortStatistics> nicStats = parseNicStatistics(deviceId, objNode);
 
         // Get timing statistics
         TimingStatistics timinsgStats = parseTimingStatistics(objNode);
 
-        // Ready to construct the grand object
-        DefaultMonitoringStatistics.Builder statsBuilder =
-            DefaultMonitoringStatistics.builder();
-
-        statsBuilder.setDeviceId(deviceId)
-                .setTimingStatistics(timinsgStats)
-                .setCpuStatistics(cpuStats)
-                .setNicStatistics(nicStats);
-
-        monStats = statsBuilder.build();
+        // Construct a global monitoring statistics object out of smaller ones
+        monStats = DefaultMonitoringStatistics.builder()
+                    .setDeviceId(deviceId)
+                    .setTimingStatistics(timinsgStats)
+                    .setCpuStatistics(cpuStats)
+                    .setMemoryStatistics(memStats)
+                    .setNicStatistics(nicStats)
+                    .build();
 
         // When a device reports monitoring data, it means it is alive
         raiseDeviceReconnect(device);
@@ -733,74 +890,106 @@
 
         Collection<CpuStatistics> cpuStats = Lists.newArrayList();
 
-        JsonNode cpuNode = objNode.path(BasicServerDriver.PARAM_CPUS);
+        JsonNode cpuNode = objNode.path(PARAM_CPUS);
 
         for (JsonNode cn : cpuNode) {
             ObjectNode cpuObjNode = (ObjectNode) cn;
 
             // CPU statistics builder
-            DefaultCpuStatistics.Builder cpuBuilder = DefaultCpuStatistics.builder();
+            DefaultCpuStatistics.Builder cpuStatsBuilder = DefaultCpuStatistics.builder();
 
             // Throughput statistics are optional
-            JsonNode throughputNode = cpuObjNode.get(CPU_PARAM_THROUGHPUT);
+            JsonNode throughputNode = cpuObjNode.get(PARAM_CPU_THROUGHPUT);
             if (throughputNode != null) {
-                String throughputUnit = get(throughputNode, MON_PARAM_UNIT);
+                String throughputUnit = get(throughputNode, PARAM_MON_UNIT);
                 if (!Strings.isNullOrEmpty(throughputUnit)) {
-                    cpuBuilder.setThroughputUnit(throughputUnit);
+                    cpuStatsBuilder.setThroughputUnit(throughputUnit);
                 }
                 float averageThroughput = (float) 0;
-                if (throughputNode.get(MON_PARAM_AVERAGE) != null) {
-                    averageThroughput = throughputNode.path(MON_PARAM_AVERAGE).floatValue();
+                if (throughputNode.get(PARAM_MON_AVERAGE) != null) {
+                    averageThroughput = throughputNode.path(PARAM_MON_AVERAGE).floatValue();
                 }
-                cpuBuilder.setAverageThroughput(averageThroughput);
+                cpuStatsBuilder.setAverageThroughput(averageThroughput);
             }
 
             // Latency statistics are optional
-            JsonNode latencyNode = cpuObjNode.get(CPU_PARAM_LATENCY);
+            JsonNode latencyNode = cpuObjNode.get(PARAM_CPU_LATENCY);
             if (latencyNode != null) {
-                String latencyUnit = get(latencyNode, MON_PARAM_UNIT);
+                String latencyUnit = get(latencyNode, PARAM_MON_UNIT);
                 if (!Strings.isNullOrEmpty(latencyUnit)) {
-                    cpuBuilder.setLatencyUnit(latencyUnit);
+                    cpuStatsBuilder.setLatencyUnit(latencyUnit);
                 }
                 float minLatency = (float) 0;
-                if (latencyNode.get(MON_PARAM_MIN) != null) {
-                    minLatency = latencyNode.path(MON_PARAM_MIN).floatValue();
+                if (latencyNode.get(PARAM_MON_MIN) != null) {
+                    minLatency = latencyNode.path(PARAM_MON_MIN).floatValue();
                 }
                 float averageLatency = (float) 0;
-                if (latencyNode.get(MON_PARAM_AVERAGE) != null) {
-                    averageLatency = latencyNode.path(MON_PARAM_AVERAGE).floatValue();
+                if (latencyNode.get(PARAM_MON_AVERAGE) != null) {
+                    averageLatency = latencyNode.path(PARAM_MON_AVERAGE).floatValue();
                 }
                 float maxLatency = (float) 0;
-                if (latencyNode.get(MON_PARAM_MAX) != null) {
-                    maxLatency = latencyNode.path(MON_PARAM_MAX).floatValue();
+                if (latencyNode.get(PARAM_MON_MAX) != null) {
+                    maxLatency = latencyNode.path(PARAM_MON_MAX).floatValue();
                 }
 
-                cpuBuilder.setMinLatency(minLatency)
+                cpuStatsBuilder.setMinLatency(minLatency)
                     .setAverageLatency(averageLatency)
                     .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 queueId = cpuObjNode.path(CPU_PARAM_QUEUE).asInt();
-            int busySince = cpuObjNode.path(CPU_PARAM_STATUS).asInt();
+            int cpuId = cpuObjNode.path(PARAM_ID).asInt();
+            float cpuLoad = cpuObjNode.path(PARAM_CPU_LOAD).floatValue();
+            int queueId = cpuObjNode.path(PARAM_CPU_QUEUE).asInt();
+            int busySince = cpuObjNode.path(PARAM_CPU_STATUS).asInt();
 
-            // This is mandatory information
-            cpuBuilder.setDeviceId(deviceId)
+            // We have all the statistics for this CPU core
+            cpuStats.add(
+                cpuStatsBuilder
+                    .setDeviceId(deviceId)
                     .setId(cpuId)
                     .setLoad(cpuLoad)
                     .setQueue(queueId)
-                    .setBusySince(busySince);
-
-            // We have all the statistics for this CPU core
-            cpuStats.add(cpuBuilder.build());
+                    .setBusySince(busySince)
+                    .build());
         }
 
         return cpuStats;
     }
 
     /**
+     * Parse the input JSON object, looking for memory-related
+     * statistics. Upon success, construct and return a memory
+     * statistics objects.
+     *
+     * @param deviceId the device ID that sent the JSON object
+     * @param objNode input JSON node with memory statistics information
+     * @return memory statistics object
+     */
+    private MemoryStatistics parseMemoryStatistics(DeviceId deviceId, JsonNode objNode) {
+        if ((deviceId == null) || (objNode == null)) {
+            return null;
+        }
+
+        JsonNode memoryNode = objNode.path(PARAM_MEMORY);
+        ObjectNode memoryObjNode = (ObjectNode) memoryNode;
+
+        // Fetch memory statistics
+        String unit = get(memoryNode, PARAM_MON_UNIT);
+        long used = memoryObjNode.path(PARAM_MEMORY_STATS_USED).asLong();
+        long free = memoryObjNode.path(PARAM_MEMORY_STATS_FREE).asLong();
+        long total = memoryObjNode.path(PARAM_MEMORY_STATS_TOTAL).asLong();
+
+        // Memory statistics builder
+        return DefaultMemoryStatistics.builder()
+                .setDeviceId(deviceId)
+                .setMemoryUsed(used)
+                .setMemoryFree(free)
+                .setMemoryTotal(total)
+                .build();
+    }
+
+    /**
      * Parse the input JSON object, looking for NIC-related
      * statistics. Upon success, construct and return a list
      * of NIC statistics objects.
@@ -816,7 +1005,7 @@
 
         RestServerSBDevice device = null;
         try {
-            device = (RestServerSBDevice) getController().getDevice(deviceId);
+            device = (RestServerSBDevice) getDevice(deviceId);
         } catch (ClassCastException ccEx) {
             return Collections.EMPTY_LIST;
         }
@@ -832,25 +1021,25 @@
             ObjectNode nicObjNode = (ObjectNode) nn;
 
             // All the NIC attributes
-            String nicName  = get(nn, NIC_PARAM_NAME);
-            checkArgument(!Strings.isNullOrEmpty(nicName), "NIC name is empty or NULL");
+            String nicName  = get(nn, PARAM_NAME);
+            checkArgument(!Strings.isNullOrEmpty(nicName), MSG_NIC_NAME_NULL);
 
             long portNumber = device.portNumberFromName(nicName);
-            checkArgument(portNumber >= 0, "Unknown port ID " + portNumber + " for NIC " + nicName);
+            checkArgument(portNumber >= 0, MSG_NIC_PORT_NUMBER_NEGATIVE);
 
-            long rxCount   = nicObjNode.path(NIC_STATS_RX_COUNT).asLong();
-            long rxBytes   = nicObjNode.path(NIC_STATS_RX_BYTES).asLong();
-            long rxDropped = nicObjNode.path(NIC_STATS_RX_DROPS).asLong();
-            long rxErrors  = nicObjNode.path(NIC_STATS_RX_ERRORS).asLong();
-            long txCount   = nicObjNode.path(NIC_STATS_TX_COUNT).asLong();
-            long txBytes   = nicObjNode.path(NIC_STATS_TX_BYTES).asLong();
-            long txDropped = nicObjNode.path(NIC_STATS_TX_DROPS).asLong();
-            long txErrors  = nicObjNode.path(NIC_STATS_TX_ERRORS).asLong();
+            long rxCount   = nicObjNode.path(PARAM_NIC_STATS_RX_COUNT).asLong();
+            long rxBytes   = nicObjNode.path(PARAM_NIC_STATS_RX_BYTES).asLong();
+            long rxDropped = nicObjNode.path(PARAM_NIC_STATS_RX_DROPS).asLong();
+            long rxErrors  = nicObjNode.path(PARAM_NIC_STATS_RX_ERRORS).asLong();
+            long txCount   = nicObjNode.path(PARAM_NIC_STATS_TX_COUNT).asLong();
+            long txBytes   = nicObjNode.path(PARAM_NIC_STATS_TX_BYTES).asLong();
+            long txDropped = nicObjNode.path(PARAM_NIC_STATS_TX_DROPS).asLong();
+            long txErrors  = nicObjNode.path(PARAM_NIC_STATS_TX_ERRORS).asLong();
 
-            // Incorporate these statistics into an object
-            DefaultPortStatistics.Builder nicBuilder = DefaultPortStatistics.builder();
-
-            nicBuilder.setDeviceId(deviceId)
+            // Construct a NIC statistics object and add it to the set
+            nicStats.add(
+                DefaultPortStatistics.builder()
+                    .setDeviceId(deviceId)
                     .setPort(PortNumber.portNumber(portNumber))
                     .setPacketsReceived(rxCount)
                     .setPacketsSent(txCount)
@@ -859,10 +1048,8 @@
                     .setPacketsRxDropped(rxDropped)
                     .setPacketsRxErrors(rxErrors)
                     .setPacketsTxDropped(txDropped)
-                    .setPacketsTxErrors(txErrors);
-
-            // We have statistics for this NIC
-            nicStats.add(nicBuilder.build());
+                    .setPacketsTxErrors(txErrors)
+                    .build());
         }
 
         return nicStats;
@@ -888,40 +1075,42 @@
             return getZeroTimingStatistics();
         }
 
-        DefaultTimingStatistics.Builder timingBuilder = DefaultTimingStatistics.builder();
+        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);
+        String timingStatsUnit = get(timingNode, PARAM_MON_UNIT);
         if (!Strings.isNullOrEmpty(timingStatsUnit)) {
             timingBuilder.setUnit(timingStatsUnit);
         }
 
         // Time (ns) to parse the controller's deployment instruction
         long parsingTime = 0;
-        if (timingObjNode.get(TIMING_PARAM_PARSE) != null) {
-            parsingTime = timingObjNode.path(TIMING_PARAM_PARSE).asLong();
+        if (timingObjNode.get(PARAM_TIMING_PARSE) != null) {
+            parsingTime = timingObjNode.path(PARAM_TIMING_PARSE).asLong();
         }
         // Time (ns) to do the deployment
         long launchingTime = 0;
-        if (timingObjNode.get(TIMING_PARAM_LAUNCH) != null) {
-            launchingTime = timingObjNode.path(TIMING_PARAM_LAUNCH).asLong();
+        if (timingObjNode.get(PARAM_TIMING_LAUNCH) != null) {
+            launchingTime = timingObjNode.path(PARAM_TIMING_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();
+        if (timingObjNode.get(PARAM_TIMING_DEPLOY) != null) {
+            deployTime = timingObjNode.path(PARAM_TIMING_DEPLOY).asLong();
         }
-        checkArgument(deployTime == parsingTime + launchingTime, "Inconsistent timing statistics");
+        checkArgument(deployTime == parsingTime + launchingTime,
+            MSG_STATS_TIMING_DEPLOY_INCONSISTENT);
 
         timingBuilder.setParsingTime(parsingTime)
                     .setLaunchingTime(launchingTime);
 
         // Get autoscale timing statistics
-        JsonNode autoscaleTimingNode = objNode.path(PARAM_TIMING_AUTOSCALE);
+        JsonNode autoscaleTimingNode = objNode.path(PARAM_TIMING_STATS_AUTOSCALE);
         if (autoscaleTimingNode == null) {
             return timingBuilder.build();
         }
@@ -929,8 +1118,8 @@
         ObjectNode autoScaleTimingObjNode = (ObjectNode) autoscaleTimingNode;
         // Time (ns) to autoscale a server's load
         long autoScaleTime = 0;
-        if (autoScaleTimingObjNode.get(TIMING_PARAM_AUTOSCALE) != null) {
-            autoScaleTime = autoScaleTimingObjNode.path(TIMING_PARAM_AUTOSCALE).asLong();
+        if (autoScaleTimingObjNode.get(PARAM_TIMING_AUTOSCALE) != null) {
+            autoScaleTime = autoScaleTimingObjNode.path(PARAM_TIMING_AUTOSCALE).asLong();
         }
         timingBuilder.setAutoScaleTime(autoScaleTime);
 
@@ -945,13 +1134,75 @@
      * @return TimingStatistics object
      */
     private TimingStatistics getZeroTimingStatistics() {
-        DefaultTimingStatistics.Builder zeroTimingBuilder = DefaultTimingStatistics.builder();
+        return DefaultTimingStatistics.builder()
+                    .setParsingTime(0)
+                    .setLaunchingTime(0)
+                    .setAutoScaleTime(0)
+                    .build();
+    }
 
-        zeroTimingBuilder.setParsingTime(0)
-                         .setLaunchingTime(0)
-                         .setAutoScaleTime(0);
+    /**
+     * Implements DeviceSystemStatisticsQuery behaviour.
+     */
+    @Override
+    public Optional<DeviceSystemStats> getDeviceSystemStats() {
+        // Retrieve the device ID from the handler
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
 
-        return zeroTimingBuilder.build();
+        // ....to retrieve monitoring statistics
+        MonitoringStatistics monStats = getGlobalMonitoringStatistics(deviceId);
+
+        Optional<DeviceCpuStats> cpuStats = getOverallCpuUsage(monStats);
+        Optional<DeviceMemoryStats> memoryStats = getOverallMemoryUsage(monStats);
+
+        if (cpuStats.isPresent() && memoryStats.isPresent()) {
+            return Optional.of(new DeviceSystemStats(memoryStats.get(), cpuStats.get()));
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    /**
+     * Get CPU usage of server device.
+     *
+     * @param monStats global monitoring statistics which contain CPU statistics
+     * @return cpuStats, device CPU usage stats if available
+     */
+    private Optional<DeviceCpuStats> getOverallCpuUsage(MonitoringStatistics monStats) {
+        if (monStats == null) {
+            return Optional.empty();
+        }
+
+        if (monStats.numberOfCpus() == 0) {
+            return Optional.of(new DeviceCpuStats());
+        }
+
+        float usedCpu = 0.0f;
+        for (CpuStatistics cpuCoreStats : monStats.cpuStatisticsAll()) {
+            if (cpuCoreStats.busy()) {
+                usedCpu += cpuCoreStats.load();
+            }
+        }
+
+        return Optional.of(new DeviceCpuStats(usedCpu / (float) monStats.numberOfCpus()));
+    }
+
+    /**
+     * Get memory usage of server device in KB.
+     *
+     * @param monStats global monitoring statistics which contain memory statistics
+     * @return memoryStats, device memory usage stats if available
+     */
+    private Optional<DeviceMemoryStats> getOverallMemoryUsage(MonitoringStatistics monStats) {
+        if (monStats == null) {
+            return Optional.empty();
+        }
+
+        MemoryStatistics memStats = monStats.memoryStatistics();
+
+        return Optional.of(
+            new DeviceMemoryStats(memStats.free(), memStats.used(), memStats.total()));
     }
 
 }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/ServerDriversLoader.java b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerDriversLoader.java
index 33e9e5b..45edf76 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/ServerDriversLoader.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerDriversLoader.java
@@ -24,7 +24,9 @@
  */
 @Component(immediate = true)
 public class ServerDriversLoader extends AbstractDriverLoader {
+
     public ServerDriversLoader() {
         super("/server-drivers.xml");
     }
+
 }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/ServerHandshaker.java b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerHandshaker.java
new file mode 100644
index 0000000..b141533
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerHandshaker.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2020-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;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.device.DeviceHandshaker;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TableStatisticsEntry;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.protocol.rest.RestSBDevice;
+
+import org.slf4j.Logger;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Lists;
+
+import java.io.ByteArrayInputStream;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.CompletableFuture.completedFuture;
+import static org.onosproject.drivers.server.Constants.JSON;
+import static org.onosproject.drivers.server.Constants.PARAM_CONNECTION_STATUS;
+import static org.onosproject.drivers.server.Constants.MSG_HANDLER_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_ID_NULL;
+import static org.onosproject.drivers.server.Constants.URL_SRV_PROBE_CONNECT;
+import static org.onosproject.drivers.server.Constants.URL_SRV_PROBE_DISCONNECT;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of device handshaker behaviour for server devices.
+ */
+public class ServerHandshaker
+        extends BasicServerDriver
+        implements DeviceHandshaker {
+
+    private final Logger log = getLogger(getClass());
+
+    public ServerHandshaker() {
+        super();
+        log.debug("Started");
+    }
+
+    @Override
+    public DriverHandler handler() {
+        return super.getHandler();
+    }
+
+    @Override
+    public void setHandler(DriverHandler handler) {
+        checkNotNull(handler, MSG_HANDLER_NULL);
+        this.handler = handler;
+    }
+
+    @Override
+    public boolean connect() throws IllegalStateException {
+        // Retrieve the device ID from the handler
+        DeviceId deviceId = super.getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
+
+        // Create an object node
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode sendObjNode = mapper.createObjectNode();
+        sendObjNode.put(PARAM_CONNECTION_STATUS, "connect");
+
+        // Post the connect message to the server
+        int response = getController().post(
+            deviceId, URL_SRV_PROBE_CONNECT,
+            new ByteArrayInputStream(sendObjNode.toString().getBytes()), JSON);
+
+        // Upon an error, return an empty set of rules
+        if (!checkStatusCode(response)) {
+            log.error("Failed to connect to device {}", deviceId);
+            return false;
+        }
+
+        log.info("Successfully connected to device {}", deviceId);
+
+        return true;
+    }
+
+    @Override
+    public boolean hasConnection() {
+        return isReachable();
+    }
+
+    @Override
+    public void disconnect() {
+        // Retrieve the device ID from the handler
+        DeviceId deviceId = super.getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
+
+        // Create an object node
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode sendObjNode = mapper.createObjectNode();
+        sendObjNode.put(PARAM_CONNECTION_STATUS, "disconnect");
+
+        // Post the disconnect message to the server
+        int response = getController().post(
+            deviceId, URL_SRV_PROBE_DISCONNECT,
+            new ByteArrayInputStream(sendObjNode.toString().getBytes()), JSON);
+
+        if (!checkStatusCode(response)) {
+            log.error("Failed to disconnect from device {}", deviceId);
+            return;
+        }
+
+        log.info("Successfully disconnected from device {}", deviceId);
+
+        return;
+    }
+
+    @Override
+    public boolean isReachable() {
+        // Retrieve the device ID from the handler
+        DeviceId deviceId = super.getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
+
+        // Get the device
+        RestSBDevice device = super.getDevice(deviceId);
+        checkNotNull(device, MSG_DEVICE_NULL);
+
+        return deviceIsActive(device);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return isReachable();
+    }
+
+    @Override
+    public CompletableFuture<Boolean> probeReachability() {
+        // Retrieve the device ID from the handler
+        DeviceId deviceId = super.getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
+
+        // Probe the driver to ask for flow rule service
+        FlowRuleService flowService = getHandler().get(FlowRuleService.class);
+        List<TableStatisticsEntry> tableStats = Lists.newArrayList(
+            flowService.getFlowTableStatistics(deviceId));
+
+        // If no statistics fetched, the server is not reachable
+        return completedFuture(tableStats.isEmpty() ? false : true);
+    }
+
+    @Override
+    public CompletableFuture<Boolean> probeAvailability() {
+        return probeReachability();
+    }
+
+    @Override
+    public void roleChanged(MastershipRole newRole) {
+        throw new UnsupportedOperationException("Mastership operation not supported");
+    }
+
+    @Override
+    public MastershipRole getRole() {
+        // Retrieve the device ID from the handler
+        DeviceId deviceId = super.getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
+
+        // Probe the driver to ask for mastership service
+        MastershipService mastershipService = getHandler().get(MastershipService.class);
+        return mastershipService.getLocalRole(deviceId);
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/ServerInterfaceConfig.java b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerInterfaceConfig.java
new file mode 100644
index 0000000..b7fd93f
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerInterfaceConfig.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2020-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;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.drivers.server.devices.RestServerSBDevice;
+import org.onosproject.drivers.server.devices.nic.NicDevice;
+import org.onosproject.drivers.server.devices.nic.NicRxFilter.RxFilter;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.InterfaceConfig;
+import org.onosproject.net.behaviour.PatchDescription;
+import org.onosproject.net.behaviour.TunnelDescription;
+import org.onosproject.net.device.DefaultDeviceInterfaceDescription;
+import org.onosproject.net.device.DeviceInterfaceDescription;
+
+import org.slf4j.Logger;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.ImmutableList;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_ID_NULL;
+import static org.onosproject.net.device.DeviceInterfaceDescription.Mode;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of interface config behaviour for server devices.
+ */
+public class ServerInterfaceConfig
+        extends BasicServerDriver
+        implements InterfaceConfig {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final boolean RATE_LIMIT_STATUS = false;
+    private static final short NO_LIMIT = -1;
+
+    public ServerInterfaceConfig() {
+        super();
+        log.debug("Started");
+    }
+
+    @Override
+    public boolean addTunnelMode(String ifaceName, TunnelDescription tunnelDesc) {
+        throw new UnsupportedOperationException("Interface operation not supported");
+    }
+
+    @Override
+    public boolean removeTunnelMode(String ifaceName) {
+        throw new UnsupportedOperationException("Interface operation not supported");
+    }
+
+    @Override
+    public boolean addAccessMode(String ifaceName, VlanId vlanId) {
+        throw new UnsupportedOperationException("Interface operation not supported");
+    }
+
+    @Override
+    public boolean removeAccessMode(String ifaceName) {
+        throw new UnsupportedOperationException("Interface operation not supported");
+    }
+
+    @Override
+    public boolean addPatchMode(String ifaceName, PatchDescription patchDesc) {
+        throw new UnsupportedOperationException("Interface operation not supported");
+    }
+
+    @Override
+    public boolean removePatchMode(String ifaceName) {
+        throw new UnsupportedOperationException("Interface operation not supported");
+    }
+
+    @Override
+    public boolean addTrunkMode(String ifaceName, List<VlanId> vlanIds) {
+        throw new UnsupportedOperationException("Interface operation not supported");
+    }
+
+    @Override
+    public boolean removeTrunkMode(String ifaceName) {
+        throw new UnsupportedOperationException("Interface operation not supported");
+    }
+
+    @Override
+    public boolean addRateLimit(String ifaceName, short limit) {
+        throw new UnsupportedOperationException("Interface operation not supported");
+    }
+
+    @Override
+    public boolean removeRateLimit(String ifaceName) {
+        throw new UnsupportedOperationException("Interface operation not supported");
+    }
+
+    @Override
+    public List<DeviceInterfaceDescription> getInterfaces() {
+        // Retrieve the device ID
+        DeviceId deviceId = getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
+
+        // .. and the device itself
+        RestServerSBDevice device = null;
+        try {
+            device = (RestServerSBDevice) getDevice(deviceId);
+        } catch (ClassCastException ccEx) {
+            log.error("Failed to get interfaces for device {}", deviceId);
+            return Collections.EMPTY_LIST;
+        }
+
+        if (device == null) {
+            log.error("No device with ID {} is available for interface discovery", deviceId);
+            return Collections.EMPTY_LIST;
+        }
+        if ((device.nics() == null) || (device.nics().size() == 0)) {
+            log.error("No interfaces available on {}", deviceId);
+            return Collections.EMPTY_LIST;
+        }
+
+        // List of port descriptions to return
+        List<DeviceInterfaceDescription> intfDescriptions = Lists.newArrayList();
+
+        // Sorted list of NIC ports
+        Set<NicDevice> nics = new TreeSet(device.nics());
+
+        // Iterate through the NICs of this device to populate the list
+        for (NicDevice nic : nics) {
+            List<VlanId> devVlanIds = getVlanIdListForDevice(nic);
+            Mode devMode = getDeviceMode(devVlanIds);
+
+            // Create an interface description and add it to the list
+            intfDescriptions.add(
+                new DefaultDeviceInterfaceDescription(
+                    nic.name(), devMode, devVlanIds, RATE_LIMIT_STATUS, NO_LIMIT));
+        }
+
+        return ImmutableList.copyOf(intfDescriptions);
+    }
+
+    /**
+     * Returns a list of VLAN IDs associated with a NIC device.
+     *
+     * @param nic the NIC device to be queried
+     * @return a list of VLAN IDs (if any)
+     */
+    private List<VlanId> getVlanIdListForDevice(NicDevice nic) {
+        checkNotNull(nic, MSG_DEVICE_NULL);
+        List<VlanId> vlanIds = Lists.newArrayList();
+
+        short vlanIdValue = 0;
+        for (RxFilter rxFilter : nic.rxFilterMechanisms().rxFilters()) {
+            if (rxFilter == RxFilter.VLAN) {
+                vlanIds.add(VlanId.vlanId(vlanIdValue++));
+            }
+        }
+
+        return ImmutableList.copyOf(vlanIds);
+    }
+
+    /**
+     * Returns the interface mode of a NIC device, by looking at its VLAN IDs.
+     *
+     * @param vlanIds a list of VLAN IDs associated with the device
+     * @return interface mode
+     */
+    private Mode getDeviceMode(List<VlanId> vlanIds) {
+        if (vlanIds.size() == 1) {
+            return Mode.ACCESS;
+        } else if (vlanIds.size() > 1) {
+            return Mode.TRUNK;
+        }
+
+        return Mode.NORMAL;
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/ServerPortAdmin.java b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerPortAdmin.java
new file mode 100644
index 0000000..4ab311c
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerPortAdmin.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2020-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;
+
+import org.onosproject.drivers.server.devices.RestServerSBDevice;
+import org.onosproject.drivers.server.devices.nic.NicDevice;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.PortAdmin;
+import org.onosproject.net.driver.DriverHandler;
+
+import org.slf4j.Logger;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.io.ByteArrayInputStream;
+import java.util.concurrent.CompletableFuture;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.CompletableFuture.completedFuture;
+import static org.onosproject.drivers.server.Constants.JSON;
+import static org.onosproject.drivers.server.Constants.MSG_HANDLER_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_ID_NULL;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_PORT;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_PORT_STATUS;
+import static org.onosproject.drivers.server.Constants.URL_NIC_PORT_ADMIN;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of port admin behaviour for server devices.
+ */
+public class ServerPortAdmin
+        extends BasicServerDriver
+        implements PortAdmin {
+
+    private final Logger log = getLogger(getClass());
+
+    public ServerPortAdmin() {
+        super();
+        log.debug("Started");
+    }
+
+    @Override
+    public DriverHandler handler() {
+        return super.getHandler();
+    }
+
+    @Override
+    public void setHandler(DriverHandler handler) {
+        checkNotNull(handler, MSG_HANDLER_NULL);
+        this.handler = handler;
+    }
+
+    @Override
+    public CompletableFuture<Boolean> enable(PortNumber number) {
+        return doEnable(number, true);
+    }
+
+    @Override
+    public CompletableFuture<Boolean> disable(PortNumber number) {
+        return doEnable(number, false);
+    }
+
+    @Override
+    public CompletableFuture<Boolean> isEnabled(PortNumber number) {
+        // Retrieve the device ID
+        DeviceId deviceId = super.getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
+
+        // ...and the device itself
+        RestServerSBDevice device = null;
+        try {
+            device = (RestServerSBDevice) getDevice(deviceId);
+        } catch (ClassCastException ccEx) {
+            log.error("Failed to discover ports for device {}", deviceId);
+            return completedFuture(false);
+        }
+
+        // Iterate server's NICs to find the correct port
+        for (NicDevice nic : device.nics()) {
+            if (nic.portNumber() == number.toLong()) {
+                return completedFuture(nic.status());
+            }
+        }
+
+        return completedFuture(false);
+    }
+
+    /**
+     * Perform a NIC port management command.
+     *
+     * @param portNumber port number to manage
+     * @param enabled management flag (true to enable, false to disable the port)
+     * @return boolean status
+     */
+    private CompletableFuture<Boolean> doEnable(PortNumber portNumber, boolean enabled) {
+        // Retrieve the device ID from the handler
+        DeviceId deviceId = super.getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
+
+        // Create an object node with the port status
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode sendObjNode = mapper.createObjectNode();
+        sendObjNode.put(PARAM_NIC_PORT, portNumber.toLong());
+        sendObjNode.put(PARAM_NIC_PORT_STATUS, enabled ? "enable" : "disable");
+
+        // Post the connect message to the server
+        int response = getController().post(
+            deviceId, URL_NIC_PORT_ADMIN,
+            new ByteArrayInputStream(sendObjNode.toString().getBytes()), JSON);
+
+        // Upon an error, return an empty set of rules
+        if (!checkStatusCode(response)) {
+            log.error("Failed to connect to device {}", deviceId);
+            return completedFuture(false);
+        }
+
+        log.info("Successfully sent port {} command to device {}",
+            enabled ? "enable" : "disable", deviceId);
+
+        return completedFuture(true);
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/ServerQueueConfig.java b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerQueueConfig.java
new file mode 100644
index 0000000..f50a169
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerQueueConfig.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2020-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;
+
+import org.onlab.util.Bandwidth;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.DefaultQueueDescription;
+import org.onosproject.net.behaviour.QueueConfigBehaviour;
+import org.onosproject.net.behaviour.QueueDescription;
+import org.onosproject.net.behaviour.QueueDescription.Type;
+import org.onosproject.net.behaviour.QueueId;
+import org.onosproject.protocol.rest.RestSBDevice;
+
+import org.slf4j.Logger;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Sets;
+import com.google.common.collect.ImmutableList;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import javax.ws.rs.ProcessingException;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.JSON;
+import static org.onosproject.drivers.server.Constants.PARAM_ID;
+import static org.onosproject.drivers.server.Constants.PARAM_NICS;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_MAX_RATE;
+import static org.onosproject.drivers.server.Constants.PARAM_QUEUES;
+import static org.onosproject.drivers.server.Constants.PARAM_TYPE;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_ID_NULL;
+import static org.onosproject.drivers.server.Constants.URL_NIC_QUEUE_ADMIN;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of queue config behaviour for server devices.
+ */
+public class ServerQueueConfig
+        extends BasicServerDriver
+        implements QueueConfigBehaviour {
+
+    private final Logger log = getLogger(getClass());
+
+    public ServerQueueConfig() {
+        super();
+        log.debug("Started");
+    }
+
+    @Override
+    public Collection<QueueDescription> getQueues() {
+        // Retrieve the device ID from the handler
+        DeviceId deviceId = super.getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
+
+        // Get the device
+        RestSBDevice device = super.getDevice(deviceId);
+        checkNotNull(device, MSG_DEVICE_NULL);
+
+        // Hit the path that provides queue administration
+        InputStream response = null;
+        try {
+            response = getController().get(deviceId, URL_NIC_QUEUE_ADMIN, JSON);
+        } catch (ProcessingException pEx) {
+            log.error("Failed to get NIC queues from device: {}", deviceId);
+            return Collections.EMPTY_LIST;
+        }
+
+        // Load the JSON into object
+        ObjectMapper mapper = new ObjectMapper();
+        Map<String, Object> jsonMap = null;
+        JsonNode jsonNode = null;
+        ObjectNode objNode = null;
+        try {
+            jsonMap  = mapper.readValue(response, Map.class);
+            jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
+            objNode = (ObjectNode) jsonNode;
+        } catch (IOException ioEx) {
+            log.error("Failed to get NIC queues from device: {}", deviceId);
+            return Collections.EMPTY_LIST;
+        }
+
+        if (objNode == null) {
+            log.error("Failed to get NIC queues from device: {}", deviceId);
+            return Collections.EMPTY_LIST;
+        }
+
+        Collection<QueueDescription> queueDescs = Sets.newHashSet();
+
+        // Fetch NICs' array
+        JsonNode nicsNode = objNode.path(PARAM_NICS);
+
+        for (JsonNode nn : nicsNode) {
+            ObjectNode nicObjNode = (ObjectNode) nn;
+            int nicId = nicObjNode.path(PARAM_ID).asInt();
+            JsonNode queuesNode = nicObjNode.path(PARAM_QUEUES);
+
+            // Each NIC has a set of queues
+            for (JsonNode qn : queuesNode) {
+                ObjectNode queueObjNode = (ObjectNode) qn;
+
+                // Get the attributes of a queue
+                int queueIdInt = queueObjNode.path(PARAM_ID).asInt();
+                String queueTypeStr = get(qn, PARAM_TYPE);
+                long queueRateInt = queueObjNode.path(PARAM_NIC_MAX_RATE).asLong();
+
+                QueueId queueId = QueueId.queueId("nic" + nicId + ":" + queueIdInt);
+                EnumSet<Type> queueTypes = getQueueTypesFromString(queueTypeStr);
+                Bandwidth queueRate = Bandwidth.mbps(queueRateInt);
+
+                queueDescs.add(
+                    DefaultQueueDescription.builder()
+                        .queueId(queueId)
+                        .type(queueTypes)
+                        .maxRate(queueRate)
+                        .build());
+            }
+        }
+
+        log.info("[Device {}] NIC queues: {}", deviceId, queueDescs);
+
+        return ImmutableList.copyOf(queueDescs);
+    }
+
+    @Override
+    public QueueDescription getQueue(QueueDescription queueDesc) {
+        for (QueueDescription qDesc : getQueues()) {
+            if (queueDesc.queueId().equals(qDesc.queueId())) {
+                return qDesc;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public boolean addQueue(QueueDescription queue) {
+        throw new UnsupportedOperationException("Add queue operation not supported");
+    }
+
+    @Override
+    public void deleteQueue(QueueId queueId) {
+        throw new UnsupportedOperationException("Delete queue operation not supported");
+    }
+
+    /**
+     * Convert string-based queue type into enum set.
+     *
+     * @param queueTypeStr string-based queue type
+     * @return queue types enum set
+     */
+    private EnumSet<Type> getQueueTypesFromString(String queueTypeStr) {
+        EnumSet<Type> enumSet = EnumSet.noneOf(Type.class);
+        if ((queueTypeStr == null) || (queueTypeStr.isEmpty())) {
+            return enumSet;
+        }
+
+        if (queueTypeStr.toUpperCase().equals("MIN")) {
+            enumSet.add(Type.MAX);
+        } else if (queueTypeStr.toUpperCase().equals("MAX")) {
+            enumSet.add(Type.MIN);
+        } else if (queueTypeStr.toUpperCase().equals("PRIORITY")) {
+            enumSet.add(Type.PRIORITY);
+        } else {
+            enumSet.add(Type.BURST);
+        }
+
+        return enumSet;
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/ServerTableStatisticsDiscovery.java b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerTableStatisticsDiscovery.java
new file mode 100644
index 0000000..f106d83
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/ServerTableStatisticsDiscovery.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2020-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;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.TableStatisticsDiscovery;
+import org.onosproject.net.flow.DefaultTableStatisticsEntry;
+import org.onosproject.net.flow.IndexTableId;
+import org.onosproject.net.flow.TableStatisticsEntry;
+import org.onosproject.protocol.rest.RestSBDevice;
+
+import org.slf4j.Logger;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Lists;
+import com.google.common.collect.ImmutableList;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.ProcessingException;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.JSON;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_ID_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_TABLE_SIZE_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_TABLE_INDEX_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_TABLE_COUNTER_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.PARAM_ID;
+import static org.onosproject.drivers.server.Constants.PARAM_NICS;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_TABLE;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_TABLE_ACTIVE_ENTRIES;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_TABLE_PKTS_LOOKED_UP;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_TABLE_PKTS_MATCHED;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_TABLE_MAX_SIZE;
+import static org.onosproject.drivers.server.Constants.URL_RULE_TABLE_STATS;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of table statistics discovery for server devices.
+ */
+public class ServerTableStatisticsDiscovery
+        extends BasicServerDriver
+        implements TableStatisticsDiscovery {
+
+    private final Logger log = getLogger(getClass());
+
+    public ServerTableStatisticsDiscovery() {
+        super();
+        log.debug("Started");
+    }
+
+    @Override
+    public List<TableStatisticsEntry> getTableStatistics() {
+        // Retrieve the device ID from the handler
+        DeviceId deviceId = super.getDeviceId();
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
+
+        // Get the device
+        RestSBDevice device = super.getDevice(deviceId);
+        checkNotNull(device, MSG_DEVICE_NULL);
+
+        // Hit the path that provides the server's NIC table statistics
+        InputStream response = null;
+        try {
+            response = getController().get(deviceId, URL_RULE_TABLE_STATS, JSON);
+        } catch (ProcessingException pEx) {
+            log.error("Failed to get NIC table statistics from device: {}", deviceId);
+            return Collections.EMPTY_LIST;
+        }
+
+        // Load the JSON into object
+        ObjectMapper mapper = new ObjectMapper();
+        Map<String, Object> jsonMap = null;
+        JsonNode jsonNode = null;
+        ObjectNode objNode = null;
+        try {
+            jsonMap  = mapper.readValue(response, Map.class);
+            jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
+            objNode = (ObjectNode) jsonNode;
+        } catch (IOException ioEx) {
+            log.error("Failed to get NIC table statistics from device: {}", deviceId);
+            return Collections.EMPTY_LIST;
+        }
+
+        if (jsonNode == null) {
+            log.error("Failed to get NIC table statistics from device: {}", deviceId);
+            return Collections.EMPTY_LIST;
+        }
+
+        List<TableStatisticsEntry> tableStats = Lists.newArrayList();
+
+        JsonNode nicNode = objNode.path(PARAM_NICS);
+
+        for (JsonNode nn : nicNode) {
+            ObjectNode nicObjNode = (ObjectNode) nn;
+
+            // The index of the NIC that hosts rules table(s)
+            long nicIndex = nicObjNode.path(Constants.PARAM_ID).asLong();
+
+            JsonNode tableNode = nicObjNode.path(PARAM_NIC_TABLE);
+            if (tableNode == null) {
+                throw new IllegalArgumentException("No tables reported for NIC " + nicIndex);
+            }
+
+            for (JsonNode tn : tableNode) {
+                ObjectNode tableObjNode = (ObjectNode) tn;
+
+                // NIC table attributes
+                int tableIndex = tableObjNode.path(PARAM_ID).asInt();
+                checkArgument(tableIndex >= 0, MSG_NIC_TABLE_INDEX_NEGATIVE);
+
+                long tableActiveEntries = tableObjNode.path(PARAM_NIC_TABLE_ACTIVE_ENTRIES).asLong();
+                checkArgument(tableActiveEntries >= 0, MSG_NIC_TABLE_COUNTER_NEGATIVE);
+
+                long tablePktsLookedUp = tableObjNode.path(PARAM_NIC_TABLE_PKTS_LOOKED_UP).asLong();
+                checkArgument(tablePktsLookedUp >= 0, MSG_NIC_TABLE_COUNTER_NEGATIVE);
+
+                long tablePktsMatched = tableObjNode.path(PARAM_NIC_TABLE_PKTS_MATCHED).asLong();
+                checkArgument(tablePktsMatched >= 0, MSG_NIC_TABLE_COUNTER_NEGATIVE);
+
+                long tableMaxsize = tableObjNode.path(PARAM_NIC_TABLE_MAX_SIZE).asLong();
+                checkArgument(tableMaxsize >= 0, MSG_NIC_TABLE_SIZE_NEGATIVE);
+
+                // Server's device ID and NIC ID compose a NIC device ID
+                DeviceId nicDeviceId = DeviceId.deviceId(
+                    deviceId.toString() + ":nic" + String.valueOf(nicIndex));
+
+                TableStatisticsEntry tableStat = DefaultTableStatisticsEntry.builder()
+                    .withDeviceId(nicDeviceId)
+                    .withTableId(IndexTableId.of(tableIndex))
+                    .withActiveFlowEntries(tableActiveEntries)
+                    .withPacketsLookedUpCount(tablePktsLookedUp)
+                    .withPacketsMatchedCount(tablePktsMatched)
+                    .withMaxSize(tableMaxsize > 0 ? tableMaxsize : -1)
+                    .build();
+
+                tableStats.add(tableStat);
+
+                log.debug("[Device {}] NIC {} with table statistics: {}",
+                    deviceId, nicIndex, tableStat);
+            }
+        }
+
+        return ImmutableList.copyOf(tableStats);
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/behavior/CpuStatisticsDiscovery.java b/drivers/server/src/main/java/org/onosproject/drivers/server/behavior/CpuStatisticsDiscovery.java
index 9d42f95..8a6c91c 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/behavior/CpuStatisticsDiscovery.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/behavior/CpuStatisticsDiscovery.java
@@ -33,4 +33,5 @@
      * @return CPU statistics list
      */
     Collection<CpuStatistics> discoverCpuStatistics();
+
 }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/behavior/MonitoringStatisticsDiscovery.java b/drivers/server/src/main/java/org/onosproject/drivers/server/behavior/MonitoringStatisticsDiscovery.java
index ba8ea2a..7d4aa6e 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/behavior/MonitoringStatisticsDiscovery.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/behavior/MonitoringStatisticsDiscovery.java
@@ -45,4 +45,5 @@
      * @return resource-specific monitoring statistics
      */
     MonitoringStatistics discoverMonitoringStatistics(URI tcId);
+
 }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/RestServerSBDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/RestServerSBDevice.java
index 0d64248..77873aa 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/RestServerSBDevice.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/RestServerSBDevice.java
@@ -16,15 +16,19 @@
 
 package org.onosproject.drivers.server.devices;
 
+import org.onosproject.drivers.server.devices.cpu.CpuCacheHierarchyDevice;
+import org.onosproject.drivers.server.devices.cpu.CpuDevice;
+import org.onosproject.drivers.server.devices.memory.MemoryHierarchyDevice;
 import org.onosproject.drivers.server.devices.nic.NicDevice;
 import org.onosproject.protocol.rest.RestSBDevice;
 
 import java.util.Collection;
 
 /**
- * Represents an abstraction of a REST server device in ONOS.
+ * Represents an abstraction of a REST server device.
  */
 public interface RestServerSBDevice extends RestSBDevice {
+
     /**
      * Returns the set of CPUs of the server.
      *
@@ -40,6 +44,41 @@
     int numberOfCpus();
 
     /**
+     * Returns the CPU cache hierarchy of the server.
+     *
+     * @return CPU cache hierarchy
+     */
+    CpuCacheHierarchyDevice caches();
+
+    /**
+     * Returns the number of CPU caches of the server.
+     *
+     * @return number of CPU caches
+     */
+    int numberOfCaches();
+
+    /**
+     * Returns the capacity of the CPU caches of the server.
+     *
+     * @return total CPU cache capacity
+     */
+    long cacheCapacity();
+
+    /**
+     * Returns the main memory hierarchy of the server.
+     *
+     * @return main memory hierarchy
+     */
+    MemoryHierarchyDevice memory();
+
+    /**
+     * Returns the capacity of the server's main memory.
+     *
+     * @return total main memory capacity
+     */
+    long memoryCapacity();
+
+    /**
      * Returns the set of NICs of the server.
      *
      * @return set of NICs
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/ServerDeviceDescription.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/ServerDeviceDescription.java
index 02c9a11..b2e9c73 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/ServerDeviceDescription.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/ServerDeviceDescription.java
@@ -16,6 +16,9 @@
 
 package org.onosproject.drivers.server.devices;
 
+import org.onosproject.drivers.server.devices.cpu.CpuCacheHierarchyDevice;
+import org.onosproject.drivers.server.devices.cpu.CpuDevice;
+import org.onosproject.drivers.server.devices.memory.MemoryHierarchyDevice;
 import org.onosproject.drivers.server.devices.nic.NicDevice;
 import org.onosproject.net.device.DeviceDescription;
 
@@ -34,6 +37,20 @@
     Collection<CpuDevice> cpus();
 
     /**
+     * The CPU cache hierarchy of the server device.
+     *
+     * @return CPU cache hierarchy of the server device
+     */
+    CpuCacheHierarchyDevice caches();
+
+    /**
+     * The memory hierarchy of the server device.
+     *
+     * @return memory hierarchy of the server device
+     */
+    MemoryHierarchyDevice memory();
+
+    /**
      * The set of NICs of the server device.
      *
      * @return set of NICs of the server device
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/BasicCpuCacheDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/BasicCpuCacheDevice.java
new file mode 100644
index 0000000..5f2db0f
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/BasicCpuCacheDevice.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2020-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.devices.cpu;
+
+/**
+ * Represents an abstraction of a CPU cache.
+ */
+public interface BasicCpuCacheDevice {
+
+    /**
+     * Returns the ID of this cache.
+     * A CPU cache ID is a pair of CPU cache level and type.
+     *
+     * @return cache ID
+     */
+    CpuCacheId cacheId();
+
+    /**
+     * Returns the placement policy of this cache.
+     *
+     * @return cache placement policy
+     */
+    CpuCachePlacementPolicy policy();
+
+    /**
+     * Returns the vendor of this cache.
+     * Typically, a cache is part of a CPU,
+     * therefore shares the same vendor.
+     *
+     * @return cache vendor
+     */
+    CpuVendor vendor();
+
+    /**
+     * Returns the capacity of this cache in kilo bytes.
+     *
+     * @return cache capacity in kilo bytes
+     */
+    long capacity();
+
+    /**
+     * Returns the number of sets this cache is split across.
+     *
+     * @return number of cache sets
+     */
+    int sets();
+
+    /**
+     * Returns the ways of associativity of this cache.
+     *
+     * @return ways of associativity
+     */
+    int associativityWays();
+
+    /**
+     * Returns the cache line's length in bytes.
+     *
+     * @return cache line's length in bytes
+     */
+    int lineLength();
+
+    /**
+     * Returns whether this CPU cache is shared among multiple cores
+     * or dedicated to a specific core.
+     *
+     * @return sharing status of the CPU cache
+     */
+    boolean isShared();
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCacheHierarchyDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCacheHierarchyDevice.java
new file mode 100644
index 0000000..410066c
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCacheHierarchyDevice.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2020-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.devices.cpu;
+
+import java.util.Map;
+
+/**
+ * Represents an abstraction of a CPU cache hierarchy.
+ */
+public interface CpuCacheHierarchyDevice {
+
+    /**
+     * Returns the vendor of this cache hierarchy.
+     *
+     * @return cache hierarchy vendor
+     */
+    CpuVendor vendor();
+
+    /**
+     * Returns the number of the system's CPU sockets.
+     * This number affects the number of caches present in the system.
+     *
+     * @return number of CPU sockets
+     */
+    int socketsNb();
+
+    /**
+     * Returns the number of the system's CPU cores.
+     * This number affects the number of caches present in the system.
+     *
+     * @return number of CPU cores
+     */
+    int coresNb();
+
+    /**
+     * Returns the number of cache levels of this cache.
+     *
+     * @return cache level
+     */
+    int levels();
+
+    /**
+     * Returns the capacity local to each CPU core in kilo bytes.
+     *
+     * @return per CPU core local cache capacity in kilo bytes
+     */
+    long perCoreCapacity();
+
+    /**
+     * Returns the capacity of the last-level cache in kilo bytes.
+     *
+     * @return last-level cache's capacity in kilo bytes
+     */
+    long llcCapacity();
+
+    /**
+     * Returns the capacity of the entire cache hierarchy in kilo bytes.
+     *
+     * @return entire cache hierarchy's capacity in kilo bytes
+     */
+    long totalCapacity();
+
+    /**
+     * Returns the CPU cache hierarchy.
+     *
+     * @return CPU cache hierarchy
+     */
+    Map<CpuCacheId, BasicCpuCacheDevice> cacheHierarchy();
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCacheId.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCacheId.java
new file mode 100644
index 0000000..ce9fca9
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCacheId.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2020-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.devices.cpu;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Strings;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_LEVEL_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_TYPE_NULL;
+
+/**
+ * Represents a CPU cache ID.
+ * This class is immutable.
+ */
+public final class CpuCacheId {
+
+    private final CpuCacheLevel level;
+    private final CpuCacheType type;
+
+    /**
+     * Constructor from an integer value.
+     *
+     * @param value the value to use
+     */
+    private CpuCacheId(CpuCacheLevel level, CpuCacheType type) {
+        checkNotNull(level, MSG_CPU_CACHE_LEVEL_NULL);
+        checkNotNull(type, MSG_CPU_CACHE_TYPE_NULL);
+        this.level = level;
+        this.type = type;
+    }
+
+    /**
+     * Creates a builder for CpuCacheId object.
+     *
+     * @return builder object for CpuCacheId object
+     */
+    public static CpuCacheId.Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Get the level of the CPU cache.
+     *
+     * @return CPU cache level
+     */
+    public CpuCacheLevel level() {
+        return level;
+    }
+
+    /**
+     * Get the type of the CPU cache.
+     *
+     * @return CPU cache type
+     */
+    public CpuCacheType type() {
+        return type;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .omitNullValues()
+                .add("level", level())
+                .add("type", type())
+                .toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(level, type);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof CpuCacheId)) {
+            return false;
+        }
+        CpuCacheId cacheId = (CpuCacheId) obj;
+        return  this.level() ==  cacheId.level() &&
+                this.type() == cacheId.type();
+    }
+
+    public static final class Builder {
+
+        CpuCacheLevel level;
+        CpuCacheType type;
+
+        private Builder() {
+        }
+
+        /**
+         * Sets CPU cache level.
+         *
+         * @param levelStr CPU cache level as a string
+         * @return builder object
+         */
+        public Builder setLevel(String levelStr) {
+            if (!Strings.isNullOrEmpty(levelStr)) {
+                this.level = CpuCacheLevel.getByName(levelStr);
+            }
+
+            return this;
+        }
+
+        /**
+         * Sets CPU cache type.
+         *
+         * @param typeStr CPU cache type as a string
+         * @return builder object
+         */
+        public Builder setType(String typeStr) {
+            if (!Strings.isNullOrEmpty(typeStr)) {
+                this.type = CpuCacheType.getByName(typeStr);
+            }
+
+            return this;
+        }
+
+        /**
+         * Creates a CpuCacheId object.
+         *
+         * @return CpuCacheId object
+         */
+        public CpuCacheId build() {
+            return new CpuCacheId(level, type);
+        }
+
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCacheLevel.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCacheLevel.java
new file mode 100644
index 0000000..f4cc5c1
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCacheLevel.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020-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.devices.cpu;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Representation of a CPU cache's levels.
+ */
+public enum CpuCacheLevel {
+
+    /**
+     * Depending on the hardware architecture and vendor,
+     * a CPU cache hierarchy might comprise of multiple levels
+     * of CPU caches. Some hardware architectures use 3 levels
+     * of CPU caches, while others use 4. In the former case,
+     * the last-level cache (LLC) is the L3 cache, while in the
+     * latter case LLC corresponds to the cache after L3
+     * (conceptually L4).
+     */
+    REGISTER("Register"), // CPU register
+    L1("L1"),             // L1 cache
+    L2("L2"),             // L2 cache
+    L3("L3"),             // L3 cache
+    L4("L4"),             // L4 cache
+    LLC("LLC");           // Last-level cache
+
+    private String level;
+
+    // Statically maps CPU cache levels to enum types
+    private static final Map<String, CpuCacheLevel> MAP =
+        new HashMap<String, CpuCacheLevel>();
+    static {
+        for (CpuCacheLevel cl : CpuCacheLevel.values()) {
+            MAP.put(cl.toString().toLowerCase(), cl);
+        }
+    }
+
+    private CpuCacheLevel(String level) {
+        this.level = level;
+    }
+
+    public static CpuCacheLevel getByName(String cl) {
+        return MAP.get(cl.toLowerCase());
+    }
+
+    public static int toNumber(CpuCacheLevel cacheLevel) {
+        return Integer.parseInt(cacheLevel.toString().split("L")[1]);
+    }
+
+    public static boolean isLlc(CpuCacheLevel cacheLevel) {
+        return (cacheLevel == LLC);
+    }
+
+    @Override
+    public String toString() {
+        return this.level;
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCachePlacementPolicy.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCachePlacementPolicy.java
new file mode 100644
index 0000000..75cb290
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCachePlacementPolicy.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020-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.devices.cpu;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Representation of a CPU cache's placement policies.
+ */
+public enum CpuCachePlacementPolicy {
+
+    /**
+     * Direct-mapped cache: the cache is organized into multiple
+     * sets with a single cache line per set. A memory block can
+     * only occupy a single cache line. A direct-mapped cache
+     * corresponds to an 1-way set-associative cache.
+     */
+    DIRECT_MAPPED("Direct-Mapped"),
+    /**
+     * Fully-associative cache: the cache is organized into a
+     * single cache set with multiple cache lines.
+     * A memory block can occupy any of the cache lines.
+     * A fully-associative cache with m lines corresponds to an
+     * m-way set-associative cache.
+     */
+    FULLY_ASSOCIATIVE("Fully-Associative"),
+    /**
+     * Set-associative cache: the cache is organized into ‘n’
+     * sets and each set contains ‘m’ cache lines. A memory block
+     * is first mapped onto a set and then placed into any cache
+     * line of this set.
+     */
+    SET_ASSOCIATIVE("Set-Associative");
+
+    private String cachePolicy;
+
+    // Statically maps CPU cache placement policies to enum types
+    private static final Map<String, CpuCachePlacementPolicy> MAP =
+        new HashMap<String, CpuCachePlacementPolicy>();
+    static {
+        for (CpuCachePlacementPolicy cp : CpuCachePlacementPolicy.values()) {
+            MAP.put(cp.toString().toLowerCase(), cp);
+        }
+    }
+
+    private CpuCachePlacementPolicy(String cachePolicy) {
+        this.cachePolicy = cachePolicy;
+    }
+
+    public static CpuCachePlacementPolicy getByName(String cp) {
+        return MAP.get(cp.toLowerCase());
+    }
+
+    @Override
+    public String toString() {
+        return this.cachePolicy;
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCacheType.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCacheType.java
new file mode 100644
index 0000000..553cbbf
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCacheType.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2020-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.devices.cpu;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Representation of a CPU cache's types.
+ */
+public enum CpuCacheType {
+
+    /**
+     * A CPU cache can store data, CPU instructions,
+     * or both (i.e., unified case).
+     */
+    DATA("Data"),
+    INSTRUCTION("Instruction"),
+    UNIFIED("Unified");
+
+    private String cacheType;
+
+    // Statically maps CPU cache types to enum types
+    private static final Map<String, CpuCacheType> MAP =
+        new HashMap<String, CpuCacheType>();
+    static {
+        for (CpuCacheType ct : CpuCacheType.values()) {
+            MAP.put(ct.toString().toLowerCase(), ct);
+        }
+    }
+
+    private CpuCacheType(String cacheType) {
+        this.cacheType = cacheType;
+    }
+
+    public static CpuCacheType getByName(String ct) {
+        return MAP.get(ct.toLowerCase());
+    }
+
+    public static boolean isData(CpuCacheType cacheType) {
+        return (cacheType == DATA);
+    }
+
+    public static boolean isInstruction(CpuCacheType cacheType) {
+        return (cacheType == INSTRUCTION);
+    }
+
+    public static boolean isUnified(CpuCacheType cacheType) {
+        return (cacheType == UNIFIED);
+    }
+
+    @Override
+    public String toString() {
+        return this.cacheType;
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCoreId.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCoreId.java
new file mode 100644
index 0000000..939172c
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuCoreId.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2020-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.devices.cpu;
+
+import org.onlab.util.Identifier;
+
+import com.google.common.base.MoreObjects;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Represents a CPU core ID.
+ * This class is immutable.
+ */
+public final class CpuCoreId extends Identifier<Integer> {
+
+    /**
+     * A CPU core with this ID is shared.
+     */
+    private static final int SHARED = -1;
+
+    /**
+     * Upper limits of CPU core IDs and sockets in one machine.
+     */
+    public static final int MAX_CPU_CORE_NB = 512;
+    public static final int MAX_CPU_SOCKET_NB = 4;
+
+    private final int physicalId;
+
+    /**
+     * Constructor from integer values.
+     *
+     * @param logicalId the logical ID of this CPU core
+     * @param physicalId the physical ID of this CPU core
+     */
+    public CpuCoreId(int logicalId, int physicalId) {
+        super(logicalId);
+        if (logicalId >= 0) {
+            checkArgument(logicalId < MAX_CPU_CORE_NB,
+                "Logical CPU core ID must be in [0, " +
+                String.valueOf(CpuCoreId.MAX_CPU_CORE_NB - 1) + "]");
+        }
+        this.physicalId = physicalId;
+    }
+
+    /**
+     * Constructor from a string.
+     *
+     * @param logicalIdStr the logical ID of this CPU core as a string
+     * @param physicalIdStr the physical ID of this CPU core as a string
+     */
+    public CpuCoreId(String logicalIdStr, String physicalIdStr) {
+        this(Integer.parseInt(logicalIdStr), Integer.parseInt(physicalIdStr));
+    }
+
+    /**
+     * Static constructor of a shared CPU core ID.
+     *
+     * @return a shared CPU core ID
+     */
+    public static CpuCoreId shared() {
+        return new CpuCoreId(SHARED, SHARED);
+    }
+
+    /**
+     * Get the logical ID of this CPU core.
+     *
+     * @return logical ID of this CPU core
+     */
+    public int logicalId() {
+        return identifier;
+    }
+
+    /**
+     * Get the physical ID of this CPU core.
+     *
+     * @return physical ID of this CPU core
+     */
+    public int physicalId() {
+        return physicalId;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .omitNullValues()
+                .add("logicalId",  logicalId())
+                .add("physicalId", physicalId())
+                .toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(identifier, physicalId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof CpuCoreId)) {
+            return false;
+        }
+        CpuCoreId coreId = (CpuCoreId) obj;
+        return  this.logicalId() ==  coreId.logicalId() &&
+                this.physicalId() == coreId.physicalId();
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/CpuDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuDevice.java
similarity index 76%
rename from drivers/server/src/main/java/org/onosproject/drivers/server/devices/CpuDevice.java
rename to drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuDevice.java
index 4ddd241..96d5416 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/CpuDevice.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuDevice.java
@@ -14,21 +14,26 @@
  * limitations under the License.
  */
 
-package org.onosproject.drivers.server.devices;
+package org.onosproject.drivers.server.devices.cpu;
 
 /**
- * Represents an abstraction of a CPU core in ONOS.
+ * Represents an abstraction of a CPU core.
  */
 public interface CpuDevice {
 
     /**
+     * Maximum CPU core frequency in MHz.
+     */
+    static final long MAX_FREQUENCY_MHZ = 4500;
+
+    /**
      * Returns the ID of this CPU.
      * Typically this is the order number (0...N-1)
      * of this CPU core in the socket.
      *
      * @return CPU core ID
      */
-    int id();
+    CpuCoreId id();
 
     /**
      * Returns the vendor of this CPU core.
@@ -38,6 +43,13 @@
     CpuVendor vendor();
 
     /**
+     * Returns the socket ID of this CPU core.
+     *
+     * @return CPU core socket ID
+     */
+    int socket();
+
+    /**
      * Returns the frequency of this CPU core in MHz.
      *
      * @return CPU core frequency in MHz
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/CpuVendor.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuVendor.java
similarity index 87%
rename from drivers/server/src/main/java/org/onosproject/drivers/server/devices/CpuVendor.java
rename to drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuVendor.java
index 289b3c3..37c2d56 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/CpuVendor.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/CpuVendor.java
@@ -13,7 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.drivers.server.devices;
+
+package org.onosproject.drivers.server.devices.cpu;
 
 import java.util.Map;
 import java.util.HashMap;
@@ -24,7 +25,7 @@
 public enum CpuVendor {
 
     INTEL("GenuineIntel"),
-    AMD("AMD");
+    AMD("AuthenticAMD");
 
     private String vendor;
 
@@ -41,9 +42,8 @@
         this.vendor = vendor;
     }
 
-    public static CpuVendor getByName(String pr) {
-        pr = pr.toLowerCase();
-        return MAP.get(pr);
+    public static CpuVendor getByName(String cv) {
+        return MAP.get(cv.toLowerCase());
     }
 
     @Override
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/package-info.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/package-info.java
new file mode 100644
index 0000000..4b6262a
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/cpu/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2020-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.
+ */
+
+/**
+ * API for a server's CPU devices.
+ */
+package org.onosproject.drivers.server.devices.cpu;
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/memory/MemoryHierarchyDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/memory/MemoryHierarchyDevice.java
new file mode 100644
index 0000000..cf1bf25
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/memory/MemoryHierarchyDevice.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2020-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.devices.memory;
+
+import java.util.Collection;
+
+/**
+ * Represents an abstraction of a main memory hierarchy.
+ */
+public interface MemoryHierarchyDevice {
+
+    /**
+     * Returns the number of memory modules of this main memory hierarchy.
+     *
+     * @return number of main memory modules
+     */
+    int modulesNb();
+
+    /**
+     * Returns the capacity of the entire main memory hierarchy in MBytes.
+     *
+     * @return entire main memory hierarchy's capacity in Mbytes
+     */
+    long totalCapacity();
+
+    /**
+     * Returns the main memory hierarchy.
+     *
+     * @return main memory hierarchy
+     */
+    Collection<MemoryModuleDevice> memoryHierarchy();
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/memory/MemoryModuleDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/memory/MemoryModuleDevice.java
new file mode 100644
index 0000000..bdd3fa7
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/memory/MemoryModuleDevice.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2020-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.devices.memory;
+
+/**
+ * Represents an abstraction of a main memory device.
+ */
+public interface MemoryModuleDevice {
+
+    /**
+     * Maximum main memory speed in million transfers per second (MT/s).
+     */
+    static final long MAX_SPEED_MTS = 8000;
+
+    /**
+     * Returns the type of this main memory module.
+     *
+     * @return main memory type
+     */
+    MemoryType type();
+
+    /**
+     * Returns the manufacturer of this main memory module.
+     *
+     * @return main memory manufacturer
+     */
+    String manufacturer();
+
+    /**
+     * Returns the serial number of this main memory module.
+     *
+     * @return main memory serial number
+     */
+    String serialNumber();
+
+    /**
+     * Returns the data width (in bits) of this main memory module.
+     *
+     * @return data width in bits
+     */
+    int dataWidth();
+
+    /**
+     * Returns the total width (in bits) of this main memory module.
+     *
+     * @return total width in bits
+     */
+    int totalWidth();
+
+    /**
+     * Returns the capacity (in MBytes) of this main memory module.
+     *
+     * @return capacity in MBytes
+     */
+    long capacity();
+
+    /**
+     * Returns the speed of this main memory module in MT/s.
+     *
+     * @return main memory speed in MT/s
+     */
+    long speed();
+
+    /**
+     * Returns the configuted speed of this main memory in MT/s.
+     *
+     * @return configured main memory speed in MT/s
+     */
+    long configuredSpeed();
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/memory/MemoryType.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/memory/MemoryType.java
new file mode 100644
index 0000000..bb78129
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/memory/MemoryType.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2020-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.devices.memory;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Representation of main memory types.
+ */
+public enum MemoryType {
+
+    /**
+     * Main memory types are divided into two main
+     * categories, i.e., RAM and ROM.
+     *
+     * RAM memories are further divided into:
+     * |->      SRAM: Static Random Access Memory
+     * |->      DRAM: Dynamic Random Access Memory
+     * |->     SDRAM: Synchronous DRAM
+     * |-> DDR-SDRAM: Double data-rate synchronous DRAM
+     *     |--> DDR2: 2nd Generation DDR
+     *     |--> DDR3: 3rd Generation DDR
+     *     |--> DDR4: 4th Generation DDR
+     *     |--> DDR5: 5th Generation DDR (coming soon)
+     * |->     CDRAM: Cached DRAM
+     * |->     EDRAM: Embedded DRAM
+     * |->     SGRAM: Synchronous Graphics RAM
+     * |->      VRAM: Video DRAM
+     *
+     * ROM memories are further divided into:
+     * |->   PROM: Programmable read-only memory
+     * |->  EPROM: Erasable Programmable read only memory
+     * |-> FEPROM: Flash Erasable Programmable Read Only Memory
+     * |-> EEPROM: Electrically erasable programmable read only memory
+     */
+    SRAM("SRAM"),
+    DRAM("DRAM"),
+    SDRAM("SDRAM"),
+    DDR("DDR"),
+    DDR2("DDR2"),
+    DDR3("DDR3"),
+    DDR4("DDR4"),
+    DDR5("DDR5"),
+    CDRAM("CDRAM"),
+    EDRAM("EDRAM"),
+    SGRAM("SGRAM"),
+    VRAM("VRAM"),
+    PROM("PROM"),
+    EPROM("EPROM"),
+    FEPROM("FEPROM"),
+    EEPROM("EEPROM");
+
+    private String memoryType;
+
+    // Statically maps main memory types to enum types
+    private static final Map<String, MemoryType> MAP =
+        new HashMap<String, MemoryType>();
+    static {
+        for (MemoryType mt : MemoryType.values()) {
+            MAP.put(mt.toString().toLowerCase(), mt);
+        }
+    }
+
+    private MemoryType(String memoryType) {
+        this.memoryType = memoryType;
+    }
+
+    public static MemoryType getByName(String mt) {
+        return MAP.get(mt.toLowerCase());
+    }
+
+    public static boolean isRam(MemoryType memoryType) {
+        return (memoryType == DRAM) || (memoryType == SRAM);
+    }
+
+    public static boolean isRom(MemoryType memoryType) {
+        return !isRam(memoryType);
+    }
+
+    @Override
+    public String toString() {
+        return this.memoryType;
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/memory/package-info.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/memory/package-info.java
new file mode 100644
index 0000000..fc4c577
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/memory/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2020-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.
+ */
+
+/**
+ * API for a server's memory devices.
+ */
+package org.onosproject.drivers.server.devices.memory;
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultDpdkNicFlowRule.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultDpdkNicFlowRule.java
index 4c2b7f0..34d70ee 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultDpdkNicFlowRule.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultDpdkNicFlowRule.java
@@ -26,13 +26,14 @@
 public class DefaultDpdkNicFlowRule extends DefaultNicFlowRule {
 
     public DefaultDpdkNicFlowRule(
-            FlowRule         flowRule,
-            String           trafficClassId,
-            String           interfaceName,
-            long             interfaceNumber,
-            long             cpuCoreIndex,
-            NicRuleScope     scope) {
-        super(flowRule, trafficClassId, interfaceName, interfaceNumber, cpuCoreIndex, scope);
+            FlowRule flowRule,
+            String trafficClassId,
+            String interfaceName,
+            long interfaceNumber,
+            long cpuCoreIndex,
+            NicRuleScope scope) {
+        super(flowRule, trafficClassId, interfaceName,
+            interfaceNumber, cpuCoreIndex, scope);
     }
 
     @Override
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 dd56570..0f2b2b9 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
@@ -45,6 +45,11 @@
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_RULE_CORE_ID_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_RULE_IFACE_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_RULE_IFACE_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_RULE_SCOPE_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_RULE_TC_ID_NULL;
 import static org.onosproject.net.flow.criteria.Criterion.Type.ETH_TYPE;
 import static org.onosproject.net.flow.criteria.Criterion.Type.ETH_SRC;
 import static org.onosproject.net.flow.criteria.Criterion.Type.ETH_DST;
@@ -94,29 +99,25 @@
     protected Set<NicRuleAction> actions;
 
     protected DefaultNicFlowRule(
-            FlowRule         flowRule,
-            String           trafficClassId,
-            String           interfaceName,
-            long             interfaceNumber,
-            long             cpuCoreIndex,
-            NicRuleScope     scope) {
+            FlowRule     flowRule,
+            String       trafficClassId,
+            String       interfaceName,
+            long         interfaceNumber,
+            long         cpuCoreIndex,
+            NicRuleScope scope) {
         super(flowRule);
 
-        checkArgument(!Strings.isNullOrEmpty(trafficClassId),
-            "NIC rule's traffic class ID is NULL or empty");
-        checkNotNull(interfaceName,
-            "NIC rule's interface name is NULL");
-        checkArgument(interfaceNumber >= 0,
-            "NIC rule's interface number must not be negative");
-        checkArgument(cpuCoreIndex >= 0,
-            "NIC rule's CPU core index must not be negative");
-        checkNotNull(scope, "NIC rule's scope is NULL");
+        checkArgument(!Strings.isNullOrEmpty(trafficClassId), MSG_NIC_FLOW_RULE_TC_ID_NULL);
+        checkNotNull(interfaceName, MSG_NIC_FLOW_RULE_IFACE_NULL);
+        checkArgument(interfaceNumber >= 0, MSG_NIC_FLOW_RULE_IFACE_NEGATIVE);
+        checkArgument(cpuCoreIndex >= 0, MSG_NIC_FLOW_RULE_CORE_ID_NEGATIVE);
+        checkNotNull(scope, MSG_NIC_FLOW_RULE_SCOPE_NULL);
 
-        this.trafficClassId  = trafficClassId;
-        this.interfaceName   = interfaceName;
+        this.trafficClassId = trafficClassId;
+        this.interfaceName = interfaceName;
         this.interfaceNumber = interfaceNumber;
-        this.cpuCoreIndex    = cpuCoreIndex;
-        this.scope           = scope;
+        this.cpuCoreIndex = cpuCoreIndex;
+        this.scope = scope;
 
         this.populate();
     }
@@ -124,11 +125,11 @@
     protected DefaultNicFlowRule(FlowRule flowRule) {
         super(flowRule);
 
-        this.trafficClassId  = Builder.DEFAULT_TRAFFIC_CLASS_ID;
-        this.interfaceName   = "";
+        this.trafficClassId = Builder.DEFAULT_TRAFFIC_CLASS_ID;
+        this.interfaceName = "";
         this.interfaceNumber = Builder.DEFAULT_INTERFACE_NB;
-        this.cpuCoreIndex    = Builder.DEFAULT_CPU_CORE_INDEX;
-        this.scope           = Builder.DEFAULT_RULE_SCOPE;
+        this.cpuCoreIndex = Builder.DEFAULT_CPU_CORE_INDEX;
+        this.scope = Builder.DEFAULT_RULE_SCOPE;
 
         this.populate();
     }
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 c2430c0..210a1e8 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
@@ -13,12 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.onosproject.drivers.server.devices.nic;
 
 import java.util.Objects;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_FILTER_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_RULE_NULL;
 
 /**
  * A flow rule-based Rx filter value.
@@ -92,8 +95,7 @@
      * @param value a CPU core ID for this Rx filter
      */
     private void setValue(long value) {
-        checkArgument(value >= 0,
-            "NIC flow Rx filter has invalid CPU core ID");
+        checkArgument(value >= 0, MSG_NIC_FLOW_FILTER_NEGATIVE);
         this.value = value;
     }
 
@@ -112,8 +114,7 @@
      * @param flowRule Flow Rx filter rule as a string
      */
     public void setRule(String flowRule) {
-        checkNotNull(flowRule,
-            "NIC flow Rx filter rule is NULL");
+        checkNotNull(flowRule, MSG_NIC_FLOW_RULE_NULL);
         this.flowRule = flowRule;
     }
 
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 25b28bb..772ddbd 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
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.onosproject.drivers.server.devices.nic;
 
 import org.onlab.packet.MacAddress;
@@ -20,6 +21,7 @@
 import java.util.Objects;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_FILTER_MAC_NULL;
 
 /**
  * A MAC Rx filter value.
@@ -73,7 +75,7 @@
      * @param mac MAC value
      */
     public void setValue(MacAddress mac) {
-        checkNotNull(mac, "MAC address of Rx filter is NULL");
+        checkNotNull(mac, MSG_NIC_FLOW_FILTER_MAC_NULL);
         this.mac = mac;
     }
 
@@ -122,7 +124,7 @@
 
     @Override
     public String toString() {
-        return  this.value().toString();
+        return this.value().toString();
     }
 
 }
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 ddc1930c..5c56a73 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
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.onosproject.drivers.server.devices.nic;
 
 import org.onlab.packet.MplsLabel;
@@ -20,6 +21,7 @@
 import java.util.Objects;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_FILTER_MPLS_NULL;
 
 /**
  * An MPLS Rx filter value.
@@ -74,7 +76,7 @@
      * @param mplsLabel MPLS label value
      */
     public void setValue(MplsLabel mplsLabel) {
-        checkNotNull(mplsLabel, "MPLS label of Rx filter is NULL");
+        checkNotNull(mplsLabel, MSG_NIC_FLOW_FILTER_MPLS_NULL);
         this.mplsLabel = mplsLabel;
     }
 
@@ -100,7 +102,7 @@
 
     @Override
     public String toString() {
-        return  this.value().toString();
+        return this.value().toString();
     }
 
 }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicDevice.java
index c3de14e..511a5fb 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicDevice.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicDevice.java
@@ -22,11 +22,16 @@
 
 /**
  * Represents an abstraction of a
- * network interface card (NIC) device in ONOS.
+ * network interface card (NIC) device.
  */
 public interface NicDevice extends Comparable {
 
     /**
+     * Maximum link speed in Mbps.
+     */
+    static final long MAX_SPEED = 400000;
+
+    /**
      * Returns the name of this NIC.
      *
      * @return NIC name
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleAction.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleAction.java
index ff3afeb..4f86178 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleAction.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleAction.java
@@ -23,7 +23,8 @@
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
-
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_RULE_ACTION_TYPE_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_RULE_ACTION_VAL_NULL;
 
 /**
  * Definition of network interface card (NIC) rule action.
@@ -37,6 +38,7 @@
      * Source: https://doc.dpdk.org/guides/prog_guide/rte_flow.html
      */
     public enum Action {
+
         /**
          * Leaves traffic up for additional processing
          * by subsequent flow rules.
@@ -229,16 +231,15 @@
     public static final long NO_ACTION_VALUE  = (long) -1;
 
     public NicRuleAction(Action actionType, long actionValue) {
-        checkNotNull(actionType, "NIC rule action type is NULL");
-        checkArgument(actionValue >= 0,
-            "NIC rule action " + actionType + " requires an action value");
+        checkNotNull(actionType, MSG_NIC_FLOW_RULE_ACTION_TYPE_NULL);
+        checkArgument(actionValue >= 0, MSG_NIC_FLOW_RULE_ACTION_VAL_NULL);
 
         this.actionType = actionType;
         this.actionValue = actionValue;
     }
 
     public NicRuleAction(Action actionType) {
-        checkNotNull(actionType, "NIC rule action type is NULL");
+        checkNotNull(actionType, MSG_NIC_FLOW_RULE_ACTION_TYPE_NULL);
 
         this.actionType = actionType;
         this.actionValue = NO_ACTION_VALUE;
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleScope.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleScope.java
index e9a89db..7a115ef 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleScope.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleScope.java
@@ -19,26 +19,26 @@
 import com.google.common.base.Strings;
 
 import static com.google.common.base.Preconditions.checkArgument;
-
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_RULE_SCOPE_NULL;
 
 /**
 * Definition of network interface card's (NIC) rule's scope.
 */
 public enum NicRuleScope {
+
     /**
-     * A NIC rules is applied to the ingress traffic.
+     * A NIC rules is applied to ingress traffic.
      */
     INGRESS("ingress"),
     /**
-     * A NIC rules is applied to the egress traffic.
+     * A NIC rules is applied to egress traffic.
      */
     EGRESS("egress");
 
     protected String scope;
 
     private NicRuleScope(String scope) {
-        checkArgument(!Strings.isNullOrEmpty(scope),
-            "NIC rule scope is NULL or empty");
+        checkArgument(!Strings.isNullOrEmpty(scope), MSG_NIC_FLOW_RULE_SCOPE_NULL);
         this.scope = scope.toLowerCase();
     }
 
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRxFilter.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRxFilter.java
index e2d5758..bd094ad 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRxFilter.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRxFilter.java
@@ -25,6 +25,8 @@
 import java.util.HashMap;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_FILTER_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_FILTER_MECH_NULL;
 
 /**
  * Filtering mechanisms supported by a NIC device.
@@ -35,6 +37,7 @@
      * Supported Rx filters.
      */
     public enum RxFilter {
+
         /**
          * NIC dispatches traffic according to VLAN tags.
          */
@@ -102,7 +105,7 @@
     }
 
     public NicRxFilter(RxFilter rxFilter) {
-        checkNotNull(rxFilter, "NIC Rx filter is NULL");
+        checkNotNull(rxFilter, MSG_NIC_FLOW_FILTER_NULL);
 
         if (!ArrayUtils.contains(RxFilter.values(), rxFilter)) {
             throw new IllegalArgumentException(String.valueOf(rxFilter));
@@ -113,7 +116,7 @@
     }
 
     public NicRxFilter(NicRxFilter other) {
-        checkNotNull(other, "NIC Rx filter mechanism is NULL");
+        checkNotNull(other, MSG_NIC_FLOW_FILTER_MECH_NULL);
         this.rxFilters = new HashSet<RxFilter>(other.rxFilters);
     }
 
@@ -132,7 +135,7 @@
      * @param rxFilter an Rx filter to be added
      */
     public void addRxFilter(RxFilter rxFilter) {
-        checkNotNull(rxFilter, "NIC Rx filter is NULL");
+        checkNotNull(rxFilter, MSG_NIC_FLOW_FILTER_NULL);
         this.rxFilters.add(rxFilter);
     }
 
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 2d54a81..f43a6db 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
@@ -13,11 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.onosproject.drivers.server.devices.nic;
 
 import java.util.Objects;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_FILTER_RSS_NEGATIVE;
 
 /**
  * A Receice-Side Scaling (RSS)-based Rx filter value.
@@ -72,7 +74,7 @@
      * @param rssHash the RSS hash
      */
     public void setValue(int rssHash) {
-        checkArgument(rssHash >= 0, "Invalid RSS Rx filter " + rssHash);
+        checkArgument(rssHash >= 0, MSG_NIC_FLOW_FILTER_RSS_NEGATIVE);
         this.rssHash = rssHash;
     }
 
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 dceff82..8033fb3 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
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.onosproject.drivers.server.devices.nic;
 
 import java.util.Objects;
@@ -22,7 +23,9 @@
  */
 public abstract class RxFilterValue {
 
-    /* CPU id of the server this tag will lead to */
+    /**
+     * CPU ID of the server this tag will lead to.
+     */
     protected int 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 8e16321..ba6a2eb 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
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.onosproject.drivers.server.devices.nic;
 
 import org.onlab.packet.VlanId;
@@ -20,6 +21,7 @@
 import java.util.Objects;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_FLOW_FILTER_VLAN_NULL;
 
 /**
  * A VLAN Rx filter value.
@@ -73,7 +75,7 @@
      * @param vlanId VLAN ID value
      */
     public void setValue(VlanId vlanId) {
-        checkNotNull(vlanId, "VLAN ID of Rx filter is NULL");
+        checkNotNull(vlanId, MSG_NIC_FLOW_FILTER_VLAN_NULL);
         this.vlanId = vlanId;
     }
 
@@ -99,7 +101,7 @@
 
     @Override
     public String toString() {
-        return  this.value().toString();
+        return this.value().toString();
     }
 
 }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/package-info.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/package-info.java
index a2e5cf7..f1e55eb 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/package-info.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/package-info.java
@@ -15,6 +15,6 @@
  */
 
 /**
- * API for NIC server devices.
+ * API for a server's NIC devices.
  */
 package org.onosproject.drivers.server.devices.nic;
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/gui/BaseViewMessageHandler.java b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/BaseViewMessageHandler.java
index a2a151c..753a76e 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/gui/BaseViewMessageHandler.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/BaseViewMessageHandler.java
@@ -37,8 +37,9 @@
 import java.util.Set;
 import java.util.stream.IntStream;
 
-import static org.slf4j.LoggerFactory.getLogger;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_UI_SUBMETRIC_NULL;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Base message handler for passing server data to the Web UI.
@@ -75,6 +76,13 @@
     // Device IDs
     public static final String DEVICE_IDS = "deviceIds";
 
+    protected static final Map<Integer, String> MEM_SUBMETRIC_MAP = new HashMap<Integer, String>();
+    static {
+        MEM_SUBMETRIC_MAP.put(0, "used");
+        MEM_SUBMETRIC_MAP.put(1, "free");
+        MEM_SUBMETRIC_MAP.put(2, "total");
+    }
+
     // Chart designer
     protected abstract class ControlMessageRequest extends ChartRequestHandler {
 
@@ -89,24 +97,39 @@
         protected abstract void populateChart(ChartModel cm, ObjectNode payload);
 
         /**
-         * Returns a x-axis label for a monitoring value.
+         * Returns a x-axis label for a monitoring value based on a map of submetrics.
+         *
+         * @param metric label metric
+         * @param map a map of submetrics
+         * @param index submetric index
+         * @return a data label
+         */
+        protected String getMappedLabel(MetricType metric, Map<Integer, String> map, int index) {
+            String submetric = map.get(index);
+            checkNotNull(submetric, MSG_UI_SUBMETRIC_NULL);
+            return StringUtils.lowerCase(metric.name()) + "_" + submetric;
+        }
+
+        /**
+         * Returns a x-axis label for a monitoring value based on a simple index.
          *
          * @param metric label metric
          * @param index label index
          * @return a data label
          */
-        protected String getLabel(MetricType metric, int index) {
+        protected String getIndexedLabel(MetricType metric, int index) {
             return StringUtils.lowerCase(metric.name()) + "_" + Integer.toString(index);
         }
 
         /**
-         * Fills an array of strings acting as x-axis.
+         * Fills an array of strings acting as x-axis based on a map of submetrics.
          *
          * @param metric x-axis metric
+         * @param map a map of submetrics
          * @param length the length of the array
          * @return an array of strings
          */
-        protected String[] createSeries(MetricType metric, int length) {
+        protected String[] createMappedSeries(MetricType metric, Map<Integer, String> map, int length) {
             if (length <= 0) {
                 return null;
             }
@@ -116,7 +139,30 @@
             }
 
             String[] series = IntStream.range(0, length)
-                .mapToObj(i -> getLabel(metric, i))
+                .mapToObj(i -> getMappedLabel(metric, map, i))
+                .toArray(String[]::new);
+
+            return series;
+        }
+
+        /**
+         * Fills an array of strings acting as x-axis based on a simple index.
+         *
+         * @param metric x-axis metric
+         * @param length the length of the array
+         * @return an array of strings
+         */
+        protected String[] createIndexedSeries(MetricType metric, int length) {
+            if (length <= 0) {
+                return null;
+            }
+
+            if (length > MAX_COLUMNS_NB) {
+                length = MAX_COLUMNS_NB;
+            }
+
+            String[] series = IntStream.range(0, length)
+                .mapToObj(i -> getIndexedLabel(metric, i))
                 .toArray(String[]::new);
 
             return series;
@@ -127,11 +173,11 @@
          *
          * @param deviceId the device being monitored
          * @param length the length of the array
-         * @return a map monitoring parameters to their load history buffers
+         * @return a map of monitoring parameters to their load history buffers
          */
         protected Map<Integer, LruCache<Float>> fetchCacheForDevice(DeviceId deviceId, int length) {
             if (!isValid(deviceId, length - 1)) {
-                log.error("Invalid access to data history by device {} with {} cores", deviceId, length);
+                log.error("Invalid access to data history by device {} with {} metrics", deviceId, length);
                 return null;
             }
 
@@ -161,7 +207,7 @@
                 DeviceId deviceId, int length, int index, float value) {
             if (!isValid(deviceId, length - 1) ||
                 !isValid(deviceId, index)) {
-                log.error("Invalid access to data {} history by device {} with {} cores",
+                log.error("Invalid access to data {} history by device {} with {} metrics",
                     index, deviceId, length);
                 return;
             }
@@ -172,7 +218,9 @@
                 checkNotNull(dataMap, "Failed to add measurement in the cache");
             }
 
-            dataMap.get(index).add(value);
+            if (dataMap.get(index) != null) {
+                dataMap.get(index).add(value);
+            }
         }
 
         /**
@@ -185,7 +233,7 @@
          */
         protected LruCache<Float> getDataHistory(DeviceId deviceId, int index) {
             if (!isValid(deviceId, index)) {
-                log.error("Invalid access to CPU {} load history by device {}", index, deviceId);
+                log.error("Invalid access to index {} history by device {}", index, deviceId);
                 return null;
             }
 
@@ -204,8 +252,8 @@
          * @param length the length of the array
          * @return a map of monitoring parameters to their initial values
          */
-        protected Map<Integer, Float> populateZeroData(DeviceId deviceId, int length) {
-            Map<Integer, Float> data = initializeData(length);
+        protected Map<Integer, Float> populateZeroMapData(DeviceId deviceId, int length) {
+            Map<Integer, Float> data = initializeMapData(length);
 
             for (int i = 0; i < length; i++) {
                 // Store it locally
@@ -222,8 +270,8 @@
          * @param length the length of the array
          * @return a map of monitoring parameters to their initial arrays of values
          */
-        protected Map<Integer, Float[]> populateZeroDataHistory(DeviceId deviceId, int length) {
-            Map<Integer, Float[]> data = initializeDataHistory(length);
+        protected Map<Integer, Float[]> populateZeroMapDataHistory(DeviceId deviceId, int length) {
+            Map<Integer, Float[]> data = initializeMapDataHistory(length);
 
             for (int i = 0; i < length; i++) {
                 addToCache(deviceId, length, i, 0);
@@ -254,7 +302,7 @@
          * @param metric a metric
          * @param numberOfPoints the number of data points
          */
-        protected void populateMetrics(
+        protected void populateMapMetrics(
                 ChartModel            cm,
                 Map<Integer, Float[]> data,
                 LocalDateTime         time,
@@ -264,7 +312,7 @@
                 Map<String, Object> local = Maps.newHashMap();
                 for (int j = 0; j < data.size(); j++) {
                     if (data.containsKey(j)) {
-                        local.put(getLabel(metric, j), data.get(j)[i]);
+                        local.put(getIndexedLabel(metric, j), data.get(j)[i]);
                     }
                 }
 
@@ -293,7 +341,7 @@
          * @param length the length of the array
          * @return a map of metrics to their initial values
          */
-        protected Map<Integer, Float> initializeData(int length) {
+        protected Map<Integer, Float> initializeMapData(int length) {
             Map<Integer, Float> data = Maps.newHashMap();
 
             for (int i = 0; i < length; i++) {
@@ -304,12 +352,12 @@
         }
 
         /**
-         * Create a data structure with zero-initialized arrays of data.
+         * Create a map data structure with zero-initialized arrays of data.
          *
          * @param length the length of the array
          * @return a map of metrics to their initial arrays of values
          */
-        protected Map<Integer, Float[]> initializeDataHistory(int length) {
+        protected Map<Integer, Float[]> initializeMapDataHistory(int length) {
             Map<Integer, Float[]> data = Maps.newHashMap();
 
             for (int i = 0; i < length; i++) {
@@ -372,7 +420,7 @@
                 ChartModel cm, MetricType metric, int length) {
             Map<String, Object> local = Maps.newHashMap();
             for (int i = 0; i < length; i++) {
-                local.put(getLabel(metric, i), new Float(0));
+                local.put(getIndexedLabel(metric, i), new Float(0));
             }
 
             local.put(LABEL, "No Servers");
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/gui/CpuViewMessageHandler.java b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/CpuViewMessageHandler.java
index 87be684..c0cc6b2 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/gui/CpuViewMessageHandler.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/CpuViewMessageHandler.java
@@ -43,9 +43,10 @@
 import java.util.Map;
 import java.util.Set;
 
-import static org.slf4j.LoggerFactory.getLogger;
-import static org.onosproject.drivers.server.gui.MetricType.CPU;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_UI_DATA_CPU_NULL;
+import static org.onosproject.drivers.server.gui.MetricType.CPU;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Message handler for passing CPU load data to the Web UI.
@@ -63,7 +64,8 @@
         return ImmutableSet.of(new CpuMessageRequest());
     }
 
-    private final class CpuMessageRequest extends BaseViewMessageHandler.ControlMessageRequest {
+    private final class CpuMessageRequest
+        extends BaseViewMessageHandler.ControlMessageRequest {
 
         private CpuMessageRequest() {
             super(CPU_DATA_REQ, CPU_DATA_RESP, CPUS_LABEL);
@@ -71,7 +73,7 @@
 
         @Override
         protected String[] getSeries() {
-            return createSeries(CPU, MAX_COLUMNS_NB);
+            return createIndexedSeries(CPU, MAX_COLUMNS_NB);
         }
 
         @Override
@@ -96,15 +98,15 @@
                     cpuStats = new ArrayList(serverDriver.getCpuStatistics(deviceId));
                     data = populateCpuDataHistory(deviceId, serverDev.numberOfCpus(), cpuStats);
                 } catch (Exception ex) {
-                    data = populateZeroDataHistory(deviceId, MAX_COLUMNS_NB);
+                    data = populateZeroMapDataHistory(deviceId, MAX_COLUMNS_NB);
                 }
-                checkNotNull(data, "No CPU data history to visualize");
+                checkNotNull(data, MSG_UI_DATA_CPU_NULL);
 
                 // Generate a timestamp
                 LocalDateTime ldt = new LocalDateTime(timestamp);
 
                 // Project the data
-                populateMetrics(cm, data, ldt, CPU, NUM_OF_DATA_POINTS);
+                populateMapMetrics(cm, data, ldt, CPU, NUM_OF_DATA_POINTS);
 
                 Set<DeviceId> deviceIds = Sets.newHashSet();
                 for (Device device : ds.getAvailableDevices()) {
@@ -133,14 +135,14 @@
                         cpuStats = new ArrayList(serverDriver.getCpuStatistics(deviceId));
                         data = populateCpuData(deviceId, serverDev.numberOfCpus(), cpuStats);
                     } catch (Exception ex) {
-                        data = populateZeroData(deviceId, MAX_COLUMNS_NB);
+                        data = populateZeroMapData(deviceId, MAX_COLUMNS_NB);
                     }
-                    checkNotNull(data, "No CPU data to visualize");
+                    checkNotNull(data, MSG_UI_DATA_CPU_NULL);
 
                     // Map them to the CPU cores
                     Map<String, Object> local = Maps.newHashMap();
                     for (int i = 0; i < data.size(); i++) {
-                        local.put(getLabel(CPU, i), data.get(i));
+                        local.put(getIndexedLabel(CPU, i), data.get(i));
                     }
 
                     // Last piece of data is the device ID
@@ -166,7 +168,7 @@
          */
         private Map<Integer, Float> populateCpuData(
                 DeviceId deviceId, int length, List<CpuStatistics> cpuStats) {
-            Map<Integer, Float> data = initializeData(MAX_COLUMNS_NB);
+            Map<Integer, Float> data = initializeMapData(MAX_COLUMNS_NB);
 
             for (CpuStatistics stats : cpuStats) {
                 int index = stats.id();
@@ -195,7 +197,7 @@
          */
         private Map<Integer, Float[]> populateCpuDataHistory(
                 DeviceId deviceId, int length, List<CpuStatistics> cpuStats) {
-            Map<Integer, Float[]> data = initializeDataHistory(MAX_COLUMNS_NB);
+            Map<Integer, Float[]> data = initializeMapDataHistory(MAX_COLUMNS_NB);
 
             for (CpuStatistics stats : cpuStats) {
                 int index = stats.id();
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/gui/LatencyViewMessageHandler.java b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/LatencyViewMessageHandler.java
index c76c97d..a1c29c3 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/gui/LatencyViewMessageHandler.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/LatencyViewMessageHandler.java
@@ -43,9 +43,10 @@
 import java.util.Map;
 import java.util.Set;
 
-import static org.slf4j.LoggerFactory.getLogger;
-import static org.onosproject.drivers.server.gui.MetricType.LATENCY;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_UI_DATA_LATENCY_NULL;
+import static org.onosproject.drivers.server.gui.MetricType.LATENCY;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Message handler for passing latency data to the Web UI.
@@ -63,7 +64,8 @@
         return ImmutableSet.of(new LatencyMessageRequest());
     }
 
-    private final class LatencyMessageRequest extends BaseViewMessageHandler.ControlMessageRequest {
+    private final class LatencyMessageRequest
+        extends BaseViewMessageHandler.ControlMessageRequest {
 
         private LatencyMessageRequest() {
             super(LATENCY_DATA_REQ, LATENCY_DATA_RESP, LATENCY_LABEL);
@@ -71,7 +73,7 @@
 
         @Override
         protected String[] getSeries() {
-            return createSeries(LATENCY, MAX_COLUMNS_NB);
+            return createIndexedSeries(LATENCY, MAX_COLUMNS_NB);
         }
 
         @Override
@@ -93,17 +95,17 @@
                 Map<Integer, Float[]> data = null;
                 MonitoringStatistics monStats = serverDriver.getGlobalMonitoringStatistics(deviceId);
                 if (monStats == null) {
-                    data = populateZeroDataHistory(deviceId, MAX_COLUMNS_NB);
+                    data = populateZeroMapDataHistory(deviceId, MAX_COLUMNS_NB);
                 } else {
                     data = populateLatencyDataHistory(deviceId, serverDev.numberOfCpus(), monStats);
                 }
-                checkNotNull(data, "No latency data history to visualize");
+                checkNotNull(data, MSG_UI_DATA_LATENCY_NULL);
 
                 // Generate a timestamp
                 LocalDateTime ldt = new LocalDateTime(timestamp);
 
                 // Project the data
-                populateMetrics(cm, data, ldt, LATENCY, NUM_OF_DATA_POINTS);
+                populateMapMetrics(cm, data, ldt, LATENCY, NUM_OF_DATA_POINTS);
 
                 Set<DeviceId> deviceIds = Sets.newHashSet();
                 for (Device device : ds.getAvailableDevices()) {
@@ -129,16 +131,16 @@
                     Map<Integer, Float> data = null;
                     MonitoringStatistics monStats = serverDriver.getGlobalMonitoringStatistics(deviceId);
                     if (monStats == null) {
-                        data = populateZeroData(deviceId, MAX_COLUMNS_NB);
+                        data = populateZeroMapData(deviceId, MAX_COLUMNS_NB);
                     } else {
                         data = populateLatencyData(deviceId, serverDev.numberOfCpus(), monStats);
                     }
-                    checkNotNull(data, "No latency data to visualize");
+                    checkNotNull(data, MSG_UI_DATA_LATENCY_NULL);
 
                     // Map them to the CPU cores
                     Map<String, Object> local = Maps.newHashMap();
                     for (int i = 0; i < data.size(); i++) {
-                        local.put(getLabel(LATENCY, i), data.get(i));
+                        local.put(getIndexedLabel(LATENCY, i), data.get(i));
                     }
 
                     // Last piece of data is the device ID
@@ -164,7 +166,7 @@
          */
         private Map<Integer, Float> populateLatencyData(
                 DeviceId deviceId, int length, MonitoringStatistics monStats) {
-            Map<Integer, Float> data = initializeData(MAX_COLUMNS_NB);
+            Map<Integer, Float> data = initializeMapData(MAX_COLUMNS_NB);
 
             for (CpuStatistics stats : monStats.cpuStatisticsAll()) {
                 int index = stats.id();
@@ -207,7 +209,7 @@
          */
         private Map<Integer, Float[]> populateLatencyDataHistory(
                 DeviceId deviceId, int length, MonitoringStatistics monStats) {
-            Map<Integer, Float[]> data = initializeDataHistory(MAX_COLUMNS_NB);
+            Map<Integer, Float[]> data = initializeMapDataHistory(MAX_COLUMNS_NB);
 
             for (CpuStatistics stats : monStats.cpuStatisticsAll()) {
                 int index = stats.id();
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/gui/MemoryUI.java b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/MemoryUI.java
new file mode 100644
index 0000000..7765b92
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/MemoryUI.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2020-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.gui;
+
+import org.onosproject.ui.UiExtension;
+import org.onosproject.ui.UiExtensionService;
+import org.onosproject.ui.UiMessageHandlerFactory;
+import org.onosproject.ui.UiView;
+
+import com.google.common.collect.ImmutableList;
+
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+import static org.onosproject.ui.UiView.Category.NETWORK;
+import static org.onosproject.ui.GlyphConstants.ENDSTATION;
+
+/**
+ * Mechanism to stream main memory data to the GUI.
+ */
+@Component(immediate = true, service = MemoryUI.class)
+public class MemoryUI {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    /**
+     * GUI Information.
+     */
+    private static final String MEMORY_ID = "memory";
+    private static final String MEMORY_TEXT = "Servers-Memory";
+    private static final String RES_PATH = "gui";
+    private static final ClassLoader CL = MemoryUI.class.getClassLoader();
+
+    // Factory for UI message handlers
+    private final UiMessageHandlerFactory messageHandlerFactory =
+            () -> ImmutableList.of(new MemoryViewMessageHandler());
+
+    // List of application views
+    private final List<UiView> views = ImmutableList.of(
+            new UiView(NETWORK, MEMORY_ID, MEMORY_TEXT, ENDSTATION)
+    );
+
+    // Application UI extension
+    private final UiExtension uiExtension =
+            new UiExtension.Builder(CL, views)
+                    .messageHandlerFactory(messageHandlerFactory)
+                    .resourcePath(RES_PATH)
+                    .build();
+
+    /**
+     * Interact with ONOS.
+     */
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected UiExtensionService uiExtensionService;
+
+    @Activate
+    protected void activate() {
+        uiExtensionService.register(uiExtension);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        uiExtensionService.unregister(uiExtension);
+        log.info("Stopped");
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/gui/MemoryViewMessageHandler.java b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/MemoryViewMessageHandler.java
new file mode 100644
index 0000000..6bd8b2c
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/MemoryViewMessageHandler.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2020-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.gui;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.primitives.Floats;
+
+import org.onosproject.drivers.server.behavior.MonitoringStatisticsDiscovery;
+import org.onosproject.drivers.server.devices.RestServerSBDevice;
+import org.onosproject.drivers.server.stats.MemoryStatistics;
+import org.onosproject.drivers.server.stats.MonitoringStatistics;
+import org.onosproject.drivers.server.stats.MonitoringUnit.CapacityUnit;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.ui.RequestHandler;
+import org.onosproject.ui.chart.ChartModel;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.joda.time.LocalDateTime;
+import org.slf4j.Logger;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_UI_DATA_MEMORY_NULL;
+import static org.onosproject.drivers.server.gui.MetricType.MEMORY;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Message handler for passing memory data to the Web UI.
+ */
+public class MemoryViewMessageHandler extends BaseViewMessageHandler {
+
+    private static final Logger log = getLogger(MemoryViewMessageHandler.class);
+
+    private static final String MEMORY_DATA_REQ = "memoryDataRequest";
+    private static final String MEMORY_DATA_RESP = "memoryDataResponse";
+    private static final String MEMORY_LABEL = "memorys";
+
+    @Override
+    protected Collection<RequestHandler> createRequestHandlers() {
+        return ImmutableSet.of(new MemoryMessageRequest());
+    }
+
+    private final class MemoryMessageRequest
+        extends BaseViewMessageHandler.ControlMessageRequest {
+
+        // Memory UI has 3 columns (used, free, and total memory)
+        public static final int MEM_COLUMNS_NB = 3;
+
+        private MemoryMessageRequest() {
+            super(MEMORY_DATA_REQ, MEMORY_DATA_RESP, MEMORY_LABEL);
+        }
+
+        @Override
+        protected String[] getSeries() {
+            return createMappedSeries(MEMORY, MEM_SUBMETRIC_MAP, MEM_COLUMNS_NB);
+        }
+
+        @Override
+        protected void populateChart(ChartModel cm, ObjectNode payload) {
+            DeviceService ds = get(DeviceService.class);
+            if ((ds == null) || (ds.getAvailableDeviceCount() == 0)) {
+                fillDataWhenNoDevicePresent(cm, MEMORY, MEM_COLUMNS_NB);
+                return;
+            }
+
+            String uri = string(payload, "devId");
+
+            // Project only one device over time
+            if (!Strings.isNullOrEmpty(uri)) {
+                DeviceId deviceId = DeviceId.deviceId(uri);
+                RestServerSBDevice serverDev =
+                    (RestServerSBDevice) basicDriver.getController().getDevice(deviceId);
+
+                Map<Integer, Float[]> data = null;
+                MonitoringStatistics monStats = serverDriver.getGlobalMonitoringStatistics(deviceId);
+                if (monStats == null) {
+                    data = populateZeroMapDataHistory(deviceId, MEM_COLUMNS_NB);
+                } else {
+                    data = populateMemoryDataHistory(deviceId, MEM_COLUMNS_NB, monStats);
+                }
+                checkNotNull(data, MSG_UI_DATA_MEMORY_NULL);
+
+                // Generate a timestamp
+                LocalDateTime ldt = new LocalDateTime(timestamp);
+
+                // Project the data
+                populateMapMetrics(cm, data, ldt, MEMORY, NUM_OF_DATA_POINTS);
+
+                Set<DeviceId> deviceIds = Sets.newHashSet();
+                for (Device device : ds.getAvailableDevices()) {
+                    // Only devices that support this type of monitoring behaviour are considered
+                    if (device.is(MonitoringStatisticsDiscovery.class) && serverDev.isActive()) {
+                        deviceIds.add(device.id());
+                    }
+                }
+
+                // Drop down list to select devices
+                attachDeviceList(cm, deviceIds);
+            } else {
+                for (Device device : ds.getAvailableDevices()) {
+                    // Only devices that support this type of monitoring behaviour are considered
+                    if (!device.is(MonitoringStatisticsDiscovery.class)) {
+                        continue;
+                    }
+
+                    DeviceId deviceId = device.id();
+                    RestServerSBDevice serverDev =
+                        (RestServerSBDevice) basicDriver.getController().getDevice(deviceId);
+
+                    Map<Integer, Float> data = null;
+                    MonitoringStatistics monStats = serverDriver.getGlobalMonitoringStatistics(deviceId);
+                    if (monStats == null) {
+                        data = populateZeroMapData(deviceId, MEM_COLUMNS_NB);
+                    } else {
+                        data = populateMemoryData(deviceId, MEM_COLUMNS_NB, monStats);
+                    }
+                    checkNotNull(data, MSG_UI_DATA_MEMORY_NULL);
+
+                    // Map them to the memory submetrics
+                    Map<String, Object> local = Maps.newHashMap();
+                    for (int i = 0; i < data.size(); i++) {
+                        local.put(getMappedLabel(MEMORY, MEM_SUBMETRIC_MAP, i), data.get(i));
+                    }
+
+                    // Last piece of data is the device ID
+                    if (serverDev.isActive()) {
+                        local.put(LABEL, deviceId);
+                        populateMetric(cm.addDataPoint(deviceId), local);
+                    } else {
+                        local.put(LABEL, "");
+                        populateMetric(cm.addDataPoint(""), local);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Turn the current monitoring data into a data
+         * structure that can feed the Memory UI memory.
+         *
+         * @param deviceId the device ID being monitored
+         * @param length the length of the array
+         * @param monStats a MonitoringStatistics object
+         * @return a map of memory metrics to their values
+         */
+        private Map<Integer, Float> populateMemoryData(
+                DeviceId deviceId, int length, MonitoringStatistics monStats) {
+            Map<Integer, Float> data = initializeMapData(MEM_COLUMNS_NB);
+
+            MemoryStatistics memStats = monStats.memoryStatistics();
+
+            CapacityUnit capacityUnit = (CapacityUnit) memStats.unit();
+            Float used = new Float(memStats.used());
+            Float free = new Float(memStats.free());
+            Float total = new Float(memStats.total());
+
+            // Unit conversions
+            used = CapacityUnit.toGigaBytes(used, capacityUnit);
+            free = CapacityUnit.toGigaBytes(free, capacityUnit);
+            total = CapacityUnit.toGigaBytes(total, capacityUnit);
+
+            // Store them locally
+            addToCache(deviceId, length, 0, used);
+            addToCache(deviceId, length, 1, free);
+            addToCache(deviceId, length, 2, total);
+
+            // And into the map
+            data.put(0, used);
+            data.put(1, free);
+            data.put(2, total);
+
+            return data;
+        }
+
+        /**
+         * Turn the monitoring data history into a
+         * data structure that can feed the Memory UI memory.
+         *
+         * @param deviceId the device ID being monitored
+         * @param length the length of the array
+         * @param monStats a MonitoringStatistics object
+         * @return a map of memory metrics to their arrays of values
+         */
+        private Map<Integer, Float[]> populateMemoryDataHistory(
+                DeviceId deviceId, int length, MonitoringStatistics monStats) {
+            Map<Integer, Float[]> data = initializeMapDataHistory(MEM_COLUMNS_NB);
+
+            MemoryStatistics memStats = monStats.memoryStatistics();
+
+            CapacityUnit capacityUnit = (CapacityUnit) memStats.unit();
+            Float used = new Float(memStats.used());
+            Float free = new Float(memStats.free());
+            Float total = new Float(memStats.total());
+
+            // Unit conversions
+            used = CapacityUnit.toGigaBytes(used, capacityUnit);
+            free = CapacityUnit.toGigaBytes(free, capacityUnit);
+            total = CapacityUnit.toGigaBytes(total, capacityUnit);
+
+            // Store them locally
+            addToCache(deviceId, length, 0, used);
+            addToCache(deviceId, length, 1, free);
+            addToCache(deviceId, length, 2, total);
+
+            for (int i = 0; i < length; i++) {
+                LruCache<Float> loadCache = getDataHistory(deviceId, i);
+                if (loadCache == null) {
+                    continue;
+                }
+                float[] floatArray = Floats.toArray(Arrays.asList(loadCache.values().toArray(new Float[0])));
+
+                // Fill the missing points
+                float[] filledLoadArray = fillData(floatArray, NUM_OF_DATA_POINTS);
+
+                // Set the data
+                data.put(i, ArrayUtils.toObject(filledLoadArray));
+            }
+
+            // Keep a timestamp
+            timestamp = System.currentTimeMillis();
+
+            return data;
+        }
+
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/gui/MetricType.java b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/MetricType.java
index 841b83e..69a3cd8 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/gui/MetricType.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/MetricType.java
@@ -22,11 +22,16 @@
 public enum MetricType {
 
     /**
-     * CPU cores of a commodity server.
+     * A commodity server's CPU cores.
      */
     CPU,
 
     /**
+     * A commodity server's main memory.
+     */
+    MEMORY,
+
+    /**
      * Per core latency.
      */
     LATENCY,
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/gui/ThroughputViewMessageHandler.java b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/ThroughputViewMessageHandler.java
index 91105e9..512aa95 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/gui/ThroughputViewMessageHandler.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/gui/ThroughputViewMessageHandler.java
@@ -43,9 +43,10 @@
 import java.util.Map;
 import java.util.Set;
 
-import static org.slf4j.LoggerFactory.getLogger;
-import static org.onosproject.drivers.server.gui.MetricType.THROUGHPUT;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_UI_DATA_THROUGHPUT_NULL;
+import static org.onosproject.drivers.server.gui.MetricType.THROUGHPUT;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Message handler for passing throughput data to the Web UI.
@@ -63,7 +64,8 @@
         return ImmutableSet.of(new ThroughputMessageRequest());
     }
 
-    private final class ThroughputMessageRequest extends BaseViewMessageHandler.ControlMessageRequest {
+    private final class ThroughputMessageRequest
+        extends BaseViewMessageHandler.ControlMessageRequest {
 
         private ThroughputMessageRequest() {
             super(THROUGHPUT_DATA_REQ, THROUGHPUT_DATA_RESP, THROUGHPUT_LABEL);
@@ -71,7 +73,7 @@
 
         @Override
         protected String[] getSeries() {
-            return createSeries(THROUGHPUT, MAX_COLUMNS_NB);
+            return createIndexedSeries(THROUGHPUT, MAX_COLUMNS_NB);
         }
 
         @Override
@@ -93,17 +95,17 @@
                 Map<Integer, Float[]> data = null;
                 MonitoringStatistics monStats = serverDriver.getGlobalMonitoringStatistics(deviceId);
                 if (monStats == null) {
-                    data = populateZeroDataHistory(deviceId, MAX_COLUMNS_NB);
+                    data = populateZeroMapDataHistory(deviceId, MAX_COLUMNS_NB);
                 } else {
                     data = populateThroughputDataHistory(deviceId, serverDev.numberOfCpus(), monStats);
                 }
-                checkNotNull(data, "No throughput data history to visualize");
+                checkNotNull(data, MSG_UI_DATA_THROUGHPUT_NULL);
 
                 // Generate a timestamp
                 LocalDateTime ldt = new LocalDateTime(timestamp);
 
                 // Project the data
-                populateMetrics(cm, data, ldt, THROUGHPUT, NUM_OF_DATA_POINTS);
+                populateMapMetrics(cm, data, ldt, THROUGHPUT, NUM_OF_DATA_POINTS);
 
                 Set<DeviceId> deviceIds = Sets.newHashSet();
                 for (Device device : ds.getAvailableDevices()) {
@@ -129,16 +131,16 @@
                     Map<Integer, Float> data = null;
                     MonitoringStatistics monStats = serverDriver.getGlobalMonitoringStatistics(deviceId);
                     if (monStats == null) {
-                        data = populateZeroData(deviceId, MAX_COLUMNS_NB);
+                        data = populateZeroMapData(deviceId, MAX_COLUMNS_NB);
                     } else {
                         data = populateThroughputData(deviceId, serverDev.numberOfCpus(), monStats);
                     }
-                    checkNotNull(data, "No throughput data to visualize");
+                    checkNotNull(data, MSG_UI_DATA_THROUGHPUT_NULL);
 
                     // Map them to the CPU cores
                     Map<String, Object> local = Maps.newHashMap();
                     for (int i = 0; i < data.size(); i++) {
-                        local.put(getLabel(THROUGHPUT, i), data.get(i));
+                        local.put(getIndexedLabel(THROUGHPUT, i), data.get(i));
                     }
 
                     // Last piece of data is the device ID
@@ -164,7 +166,7 @@
          */
         private Map<Integer, Float> populateThroughputData(
                 DeviceId deviceId, int length, MonitoringStatistics monStats) {
-            Map<Integer, Float> data = initializeData(MAX_COLUMNS_NB);
+            Map<Integer, Float> data = initializeMapData(MAX_COLUMNS_NB);
 
             for (CpuStatistics stats : monStats.cpuStatisticsAll()) {
                 int index = stats.id();
@@ -206,7 +208,7 @@
          */
         private Map<Integer, Float[]> populateThroughputDataHistory(
                 DeviceId deviceId, int length, MonitoringStatistics monStats) {
-            Map<Integer, Float[]> data = initializeDataHistory(MAX_COLUMNS_NB);
+            Map<Integer, Float[]> data = initializeMapDataHistory(MAX_COLUMNS_NB);
 
             for (CpuStatistics stats : monStats.cpuStatisticsAll()) {
                 int index = stats.id();
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultBasicCpuCacheDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultBasicCpuCacheDevice.java
new file mode 100644
index 0000000..b6dcb70
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultBasicCpuCacheDevice.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2020-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.impl.devices;
+
+import org.onosproject.drivers.server.devices.cpu.BasicCpuCacheDevice;
+import org.onosproject.drivers.server.devices.cpu.CpuCacheId;
+import org.onosproject.drivers.server.devices.cpu.CpuCachePlacementPolicy;
+import org.onosproject.drivers.server.devices.cpu.CpuVendor;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Strings;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_CAPACITY_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_ID_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_LINE_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_POLICY_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_SETS_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_WAYS_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_VENDOR_NULL;
+
+/**
+ * Default implementation for basic CPU cache devices.
+ */
+public final class DefaultBasicCpuCacheDevice implements BasicCpuCacheDevice {
+
+    private final CpuCacheId cacheId;
+    private final CpuCachePlacementPolicy policy;
+    private final CpuVendor vendor;
+    private final long capacity;
+    private final int sets;
+    private final int lineLength;
+    private final int ways;
+    private final boolean isShared;
+
+    private DefaultBasicCpuCacheDevice(CpuCacheId cacheId,
+                                       CpuCachePlacementPolicy policy,
+                                       CpuVendor vendor,
+                                       long capacity,
+                                       int sets,
+                                       int ways,
+                                       int lineLength,
+                                       boolean isShared) {
+        checkNotNull(cacheId, MSG_CPU_CACHE_ID_NULL);
+        checkNotNull(policy, MSG_CPU_CACHE_POLICY_NULL);
+        checkNotNull(vendor, MSG_CPU_VENDOR_NULL);
+        checkArgument(capacity > 0, MSG_CPU_CACHE_CAPACITY_NEGATIVE);
+        checkArgument(sets > 0, MSG_CPU_CACHE_SETS_NEGATIVE);
+        checkArgument(lineLength > 0, MSG_CPU_CACHE_LINE_NEGATIVE);
+        checkArgument(ways > 0, MSG_CPU_CACHE_WAYS_NEGATIVE);
+
+        this.cacheId = cacheId;
+        this.policy = policy;
+        this.vendor = vendor;
+        this.capacity = capacity;
+        this.sets = sets;
+        this.ways = ways;
+        this.lineLength = lineLength;
+        this.isShared = isShared;
+    }
+
+    /**
+     * Creates a builder for DefaultBasicCpuCacheDevice object.
+     *
+     * @return builder object for DefaultBasicCpuCacheDevice object
+     */
+    public static DefaultBasicCpuCacheDevice.Builder builder() {
+        return new Builder();
+    }
+
+    @Override
+    public CpuCacheId cacheId() {
+        return cacheId;
+    }
+
+    @Override
+    public CpuCachePlacementPolicy policy() {
+        return policy;
+    }
+
+    @Override
+    public CpuVendor vendor() {
+        return vendor;
+    }
+
+    @Override
+    public long capacity() {
+        return capacity;
+    }
+
+    @Override
+    public int sets() {
+        return sets;
+    }
+
+    @Override
+    public int associativityWays() {
+        return ways;
+    }
+
+    @Override
+    public int lineLength() {
+        return lineLength;
+    }
+
+    @Override
+    public boolean isShared() {
+        return isShared;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .omitNullValues()
+                .add("cacheId",    cacheId())
+                .add("policy",     policy())
+                .add("vendor",     vendor())
+                .add("capacity",   capacity())
+                .add("sets",       sets())
+                .add("ways",       associativityWays())
+                .add("lineLength", lineLength())
+                .add("isShared",   isShared())
+                .toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof BasicCpuCacheDevice)) {
+            return false;
+        }
+        BasicCpuCacheDevice device = (BasicCpuCacheDevice) obj;
+        return  this.cacheId() ==  device.cacheId() &&
+                this.policy() == device.policy() &&
+                this.vendor() == device.vendor() &&
+                this.capacity() == device.capacity() &&
+                this.sets() == device.sets() &&
+                this.associativityWays() == device.associativityWays() &&
+                this.lineLength() == device.lineLength() &&
+                this.isShared() == device.isShared();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(cacheId, policy, vendor, capacity, sets, ways, lineLength, isShared);
+    }
+
+    public static final class Builder {
+
+        CpuCacheId cacheId = null;
+        CpuCachePlacementPolicy policy = null;
+        CpuVendor vendor = null;
+        long capacity = -1;
+        int sets = -1;
+        int ways = -1;
+        int lineLength = -1;
+        boolean isShared = false;
+
+        private Builder() {
+
+        }
+
+        /**
+         * Sets CPU cache vendor.
+         *
+         * @param vendorStr CPU cache vendor as a string
+         * @return builder object
+         */
+        public Builder setVendor(String vendorStr) {
+            if (!Strings.isNullOrEmpty(vendorStr)) {
+                this.vendor = CpuVendor.getByName(vendorStr);
+            }
+
+            return this;
+        }
+
+        /**
+         * Sets CPU cache ID.
+         *
+         * @param levelStr CPU cache level as a string
+         * @param typeStr CPU cache type as a string
+         * @return builder object
+         */
+        public Builder setCacheId(String levelStr, String typeStr) {
+            if (!Strings.isNullOrEmpty(levelStr) && !Strings.isNullOrEmpty(typeStr)) {
+                this.cacheId = CpuCacheId.builder()
+                    .setLevel(levelStr)
+                    .setType(typeStr)
+                    .build();
+            }
+
+            return this;
+        }
+
+        /**
+         * Sets CPU cache policy.
+         *
+         * @param policyStr CPU cache policy as a string
+         * @return builder object
+         */
+        public Builder setPolicy(String policyStr) {
+            if (!Strings.isNullOrEmpty(policyStr)) {
+                this.policy = CpuCachePlacementPolicy.getByName(policyStr);
+            }
+
+            return this;
+        }
+
+        /**
+         * Sets the CPU cache capacity.
+         *
+         * @param capacity CPU cache capacity
+         * @return builder object
+         */
+        public Builder setCapacity(long capacity) {
+            this.capacity = capacity;
+            return this;
+        }
+
+        /**
+         * Sets the CPU cache sets.
+         *
+         * @param sets CPU cache sets
+         * @return builder object
+         */
+        public Builder setNumberOfSets(int sets) {
+            this.sets = sets;
+            return this;
+        }
+
+        /**
+         * Sets the associativity ways of the CPU cache.
+         *
+         * @param ways CPU cache ways
+         * @return builder object
+         */
+        public Builder setNumberOfWays(int ways) {
+            this.ways = ways;
+            return this;
+        }
+
+        /**
+         * Sets the length of the CPU cache line.
+         *
+         * @param lineLength CPU cache line length
+         * @return builder object
+         */
+        public Builder setLineLength(int lineLength) {
+            this.lineLength = lineLength;
+            return this;
+        }
+
+        /**
+         * Sets whether this CPU cache is shared or not.
+         *
+         * @param isShared CPU cache sharing status
+         * @return builder object
+         */
+        public Builder isShared(boolean isShared) {
+            this.isShared = isShared;
+            return this;
+        }
+
+        /**
+         * Creates a DefaultBasicCpuCacheDevice object.
+         *
+         * @return DefaultBasicCpuCacheDevice object
+         */
+        public DefaultBasicCpuCacheDevice build() {
+            return new DefaultBasicCpuCacheDevice(
+                cacheId, policy, vendor, capacity,
+                sets, ways, lineLength, isShared);
+        }
+
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultCpuCacheHierarchyDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultCpuCacheHierarchyDevice.java
new file mode 100644
index 0000000..7782dfa
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultCpuCacheHierarchyDevice.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2020-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.impl.devices;
+
+import org.onosproject.drivers.server.devices.cpu.BasicCpuCacheDevice;
+import org.onosproject.drivers.server.devices.cpu.CpuCacheHierarchyDevice;
+import org.onosproject.drivers.server.devices.cpu.CpuCacheId;
+import org.onosproject.drivers.server.devices.cpu.CpuCoreId;
+import org.onosproject.drivers.server.devices.cpu.CpuVendor;
+
+import com.google.common.base.MoreObjects;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_CAPACITY_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_CAPACITY_CORE_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_CAPACITY_LLC_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_CAPACITY_TOTAL_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_HIERARCHY_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_INSERTION_FAILED;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_LEVELS_EXCEEDED;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_LEVELS_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CORES_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_SOCKETS_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_VENDOR_NULL;
+
+/**
+ * Default implementation for a CPU cache hierarchy.
+ */
+public final class DefaultCpuCacheHierarchyDevice implements CpuCacheHierarchyDevice {
+
+    private final CpuVendor vendor;
+    private final int socketsNb;
+    private final int coresNb;
+    private final int levels;
+    private final long perCoreCapacity;
+    private final long llcCapacity;
+    private final long totalCapacity;
+    private Map<CpuCacheId, BasicCpuCacheDevice> cacheHierarchy;
+
+    /**
+     * Maximum number of CPU cache levels.
+     */
+    public static final int MAX_CPU_CACHE_LEVELS = 4;
+
+    private DefaultCpuCacheHierarchyDevice(
+                CpuVendor vendor,
+                int socketsNb,
+                int coresNb,
+                int levels,
+                long perCoreCapacity,
+                long llcCapacity,
+                Map<CpuCacheId, BasicCpuCacheDevice> cacheHierarchy) {
+        checkNotNull(vendor, MSG_CPU_VENDOR_NULL);
+        checkArgument(socketsNb > 0, MSG_CPU_SOCKETS_NEGATIVE);
+        checkArgument((coresNb > 0) && (coresNb < CpuCoreId.MAX_CPU_CORE_NB),
+            MSG_CPU_CORES_NEGATIVE);
+        checkArgument((levels > 0) && (levels <= MAX_CPU_CACHE_LEVELS),
+            MSG_CPU_CACHE_LEVELS_NEGATIVE);
+        checkArgument(perCoreCapacity > 0, MSG_CPU_CACHE_CAPACITY_CORE_NEGATIVE);
+        checkArgument(llcCapacity > 0, MSG_CPU_CACHE_CAPACITY_LLC_NEGATIVE);
+        checkNotNull(cacheHierarchy, MSG_CPU_CACHE_HIERARCHY_NULL);
+
+        this.vendor = vendor;
+        this.socketsNb = socketsNb;
+        this.coresNb = coresNb;
+        this.levels = levels;
+        this.perCoreCapacity = perCoreCapacity;
+        this.llcCapacity = llcCapacity;
+
+        // The total capacity is the sum of the (shared) socket-level and the per-core capacities
+        this.totalCapacity = (socketsNb * llcCapacity) + (coresNb * perCoreCapacity);
+        checkArgument((this.totalCapacity > this.perCoreCapacity) &&
+                      (this.totalCapacity > this.llcCapacity),
+                      MSG_CPU_CACHE_CAPACITY_TOTAL_NEGATIVE);
+        this.cacheHierarchy = cacheHierarchy;
+    }
+
+    /**
+     * Creates a builder for DefaultCpuCacheHierarchyDevice object.
+     *
+     * @return builder object for DefaultCpuCacheHierarchyDevice object
+     */
+    public static DefaultCpuCacheHierarchyDevice.Builder builder() {
+        return new Builder();
+    }
+
+    @Override
+    public CpuVendor vendor() {
+        return this.vendor;
+    }
+
+    @Override
+    public int socketsNb() {
+        return this.socketsNb;
+    }
+
+    @Override
+    public int coresNb() {
+        return this.coresNb;
+    }
+
+    @Override
+    public int levels() {
+        return this.levels;
+    }
+
+    @Override
+    public long perCoreCapacity() {
+        return this.perCoreCapacity;
+    }
+
+    @Override
+    public long llcCapacity() {
+        return this.llcCapacity;
+    }
+
+    @Override
+    public long totalCapacity() {
+        return this.totalCapacity;
+    }
+
+    @Override
+    public Map<CpuCacheId, BasicCpuCacheDevice> cacheHierarchy() {
+        return cacheHierarchy;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .omitNullValues()
+                .add("vendor", vendor())
+                .add("socketsNb", socketsNb())
+                .add("coresNb", coresNb())
+                .add("levels", levels())
+                .add("perCoreCapacity", perCoreCapacity())
+                .add("llcCapacity", llcCapacity())
+                .add("totalCapacity", totalCapacity())
+                .add("cacheHierarchy", cacheHierarchy())
+                .toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof CpuCacheHierarchyDevice)) {
+            return false;
+        }
+        CpuCacheHierarchyDevice device = (CpuCacheHierarchyDevice) obj;
+        return  this.vendor() == device.vendor() &&
+                this.socketsNb() ==  device.socketsNb() &&
+                this.coresNb() ==  device.coresNb() &&
+                this.levels() ==  device.levels() &&
+                this.perCoreCapacity() == device.perCoreCapacity() &&
+                this.llcCapacity() == device.llcCapacity() &&
+                this.totalCapacity() == device.totalCapacity() &&
+                this.cacheHierarchy() == device.cacheHierarchy();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(vendor, socketsNb, coresNb, levels, perCoreCapacity,
+            llcCapacity, totalCapacity, cacheHierarchy);
+    }
+
+    public static final class Builder {
+
+        CpuVendor vendor = null;
+        int socketsNb = -1;
+        int coresNb = -1;
+        int levels = -1;
+        long perCoreCapacity = 0;
+        long llcCapacity = 0;
+        Map<CpuCacheId, BasicCpuCacheDevice> cacheHierarchy =
+            new HashMap<CpuCacheId, BasicCpuCacheDevice>();
+
+        private Builder() {
+
+        }
+
+        /**
+         * Sets the number of CPU sockets.
+         *
+         * @param socketsNb number of CPU sockets
+         * @return builder object
+         */
+        public Builder setSocketsNumber(int socketsNb) {
+            this.socketsNb = socketsNb;
+            return this;
+        }
+
+        /**
+         * Sets the number of CPU cores.
+         *
+         * @param coresNb number of CPU cores
+         * @return builder object
+         */
+        public Builder setCoresNumber(int coresNb) {
+            this.coresNb = coresNb;
+            return this;
+        }
+
+        /**
+         * Sets the number of CPU cache levels.
+         *
+         * @param levels number of CPU cache levels
+         * @return builder object
+         */
+        public Builder setLevels(int levels) {
+            this.levels = levels;
+            return this;
+        }
+
+        /**
+         * Add a basic CPU cache device into this hierarchy.
+         *
+         * @param cacheDev a new basic CPU cache device
+         * @return builder object
+         */
+        public Builder addBasicCpuCacheDevice(BasicCpuCacheDevice cacheDev) {
+            checkNotNull(cacheDev, "Basic CPU cache device is null");
+            int currentSize = this.cacheHierarchy.size();
+            this.cacheHierarchy.put(cacheDev.cacheId(), cacheDev);
+            checkArgument(this.cacheHierarchy.size() == currentSize + 1,
+                MSG_CPU_CACHE_INSERTION_FAILED);
+            checkArgument(this.cacheHierarchy.size() <= MAX_CPU_CACHE_LEVELS,
+                MSG_CPU_CACHE_LEVELS_EXCEEDED);
+
+            if (this.vendor == null) {
+                this.vendor = cacheDev.vendor();
+            }
+
+            long capacity = cacheDev.capacity();
+            checkArgument(capacity > 0, MSG_CPU_CACHE_CAPACITY_NEGATIVE);
+            if (cacheDev.isShared()) {
+                this.llcCapacity = capacity;
+            } else {
+                this.perCoreCapacity += capacity;
+            }
+
+            return this;
+        }
+
+        /**
+         * Creates a DefaultCpuCacheHierarchyDevice object.
+         *
+         * @return DefaultCpuCacheHierarchyDevice object
+         */
+        public DefaultCpuCacheHierarchyDevice build() {
+            return new DefaultCpuCacheHierarchyDevice(
+                vendor, socketsNb, coresNb, levels,
+                perCoreCapacity, llcCapacity, cacheHierarchy);
+        }
+
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultCpuDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultCpuDevice.java
index 79712fc..9b1caaa 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultCpuDevice.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultCpuDevice.java
@@ -16,53 +16,57 @@
 
 package org.onosproject.drivers.server.impl.devices;
 
-import org.onosproject.drivers.server.devices.CpuDevice;
-import org.onosproject.drivers.server.devices.CpuVendor;
-
-import org.onosproject.drivers.server.impl.stats.DefaultCpuStatistics;
+import org.onosproject.drivers.server.devices.cpu.CpuCoreId;
+import org.onosproject.drivers.server.devices.cpu.CpuDevice;
+import org.onosproject.drivers.server.devices.cpu.CpuVendor;
 
 import com.google.common.base.MoreObjects;
+import com.google.common.base.Strings;
+
+import java.util.Objects;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkArgument;
-
-import java.util.Objects;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CORE_ID_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_FREQUENCY_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_SOCKET_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_VENDOR_NULL;
 
 /**
  * Default implementation for CPU core devices.
  */
-public class DefaultCpuDevice implements CpuDevice {
+public final class DefaultCpuDevice implements CpuDevice {
 
-    private final int       id;
+    private final CpuCoreId id;
     private final CpuVendor vendor;
-    private final long      frequency;
+    private final int socket;
+    private final long frequency;
 
-    // Maximum CPU core frequency in MHz
-    public static final long MAX_FREQUENCY_MHZ = 4500;
+    private DefaultCpuDevice(CpuCoreId id, CpuVendor vendor, int socket, long frequency) {
+        checkNotNull(id, MSG_CPU_CORE_ID_NULL);
+        checkNotNull(vendor, MSG_CPU_VENDOR_NULL);
+        checkArgument((socket >= 0) && (socket < CpuCoreId.MAX_CPU_SOCKET_NB),
+            MSG_CPU_SOCKET_NEGATIVE);
+        checkArgument((frequency > 0) && (frequency <= CpuDevice.MAX_FREQUENCY_MHZ),
+            MSG_CPU_FREQUENCY_NEGATIVE);
 
-    public DefaultCpuDevice(int id, CpuVendor vendor, long frequency) {
-        checkArgument(
-            (id >= 0) && (id < DefaultCpuStatistics.MAX_CPU_NB),
-            "CPU core ID must be in [0, " +
-            String.valueOf(DefaultCpuStatistics.MAX_CPU_NB - 1) + "]"
-        );
-        checkNotNull(
-            vendor,
-            "CPU core vendor cannot be null"
-        );
-        checkArgument(
-            (frequency > 0) && (frequency <= MAX_FREQUENCY_MHZ),
-            "CPU core frequency (MHz) must be positive and less or equal than " +
-            MAX_FREQUENCY_MHZ + " MHz"
-        );
-
-        this.id        = id;
-        this.vendor    = vendor;
+        this.id = id;
+        this.vendor = vendor;
+        this.socket = socket;
         this.frequency = frequency;
     }
 
+    /**
+     * Creates a builder for DefaultCpuDevice object.
+     *
+     * @return builder object for DefaultCpuDevice object
+     */
+    public static DefaultCpuDevice.Builder builder() {
+        return new Builder();
+    }
+
     @Override
-    public int id() {
+    public CpuCoreId id() {
         return this.id;
     }
 
@@ -72,6 +76,11 @@
     }
 
     @Override
+    public int socket() {
+        return this.socket;
+    }
+
+    @Override
     public long frequency() {
         return this.frequency;
     }
@@ -82,6 +91,7 @@
                 .omitNullValues()
                 .add("id",        id())
                 .add("vendor",    vendor())
+                .add("socket",    socket())
                 .add("frequency", frequency())
                 .toString();
     }
@@ -97,12 +107,85 @@
         CpuDevice device = (CpuDevice) obj;
         return  this.id() ==  device.id() &&
                 this.vendor() == device.vendor() &&
+                this.socket() == device.socket() &&
                 this.frequency() == device.frequency();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(id, vendor, frequency);
+        return Objects.hash(id, vendor, socket, frequency);
+    }
+
+    public static final class Builder {
+        CpuCoreId id = null;
+        CpuVendor vendor = null;
+        int socket = -1;
+        long frequency = -1;
+
+        private Builder() {
+
+        }
+
+        /**
+         * Sets the CPU core ID of this CPU.
+         *
+         * @param logicalCoreId logical CPU core ID
+         * @param physicalCoreId physical CPU core ID
+         * @return builder object
+         */
+        public Builder setCoreId(int logicalCoreId, int physicalCoreId) {
+            this.id = new CpuCoreId(logicalCoreId, physicalCoreId);
+
+            return this;
+        }
+
+        /**
+         * Sets the CPU vendor of this CPU.
+         *
+         * @param vendorStr CPU vendor as a string
+         * @return builder object
+         */
+        public Builder setVendor(String vendorStr) {
+            if (!Strings.isNullOrEmpty(vendorStr)) {
+                this.vendor = CpuVendor.getByName(vendorStr);
+            }
+
+            return this;
+        }
+
+        /**
+         * Sets the CPU socket of this CPU.
+         *
+         * @param socket CPU socket
+         * @return builder object
+         */
+        public Builder setSocket(int socket) {
+            this.socket = socket;
+
+            return this;
+        }
+
+        /**
+         * Sets the frequency of this CPU.
+         *
+         * @param frequency CPU frequency
+         * @return builder object
+         */
+        public Builder setFrequency(long frequency) {
+            this.frequency = frequency;
+
+            return this;
+        }
+
+        /**
+         * Creates a DefaultCpuDevice object.
+         *
+         * @return DefaultCpuDevice object
+         */
+        public DefaultCpuDevice build() {
+            return new DefaultCpuDevice(id, vendor, socket, frequency);
+        }
+
     }
 
 }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultMemoryHierarchyDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultMemoryHierarchyDevice.java
new file mode 100644
index 0000000..e467856
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultMemoryHierarchyDevice.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2020-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.impl.devices;
+
+import org.onosproject.drivers.server.devices.memory.MemoryHierarchyDevice;
+import org.onosproject.drivers.server.devices.memory.MemoryModuleDevice;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Sets;
+
+import java.util.Collection;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.drivers.server.Constants.MSG_MEM_HIERARCHY_EMPTY;
+import static org.onosproject.drivers.server.Constants.MSG_MEM_MODULE_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_MEM_CAPACITY_NEGATIVE;
+
+/**
+ * Default implementation for main memory hierarchy devices.
+ */
+public final class DefaultMemoryHierarchyDevice implements MemoryHierarchyDevice {
+
+    private final long totalCapacity;
+    private Collection<MemoryModuleDevice> memoryHierarchy = null;
+
+    private DefaultMemoryHierarchyDevice(long totalCapacity,
+                                         Collection<MemoryModuleDevice> memoryHierarchy) {
+        checkArgument(totalCapacity > 0, MSG_MEM_CAPACITY_NEGATIVE);
+        checkArgument(!memoryHierarchy.isEmpty(), MSG_MEM_HIERARCHY_EMPTY);
+
+        this.totalCapacity = totalCapacity;
+        this.memoryHierarchy = memoryHierarchy;
+    }
+
+    /**
+     * Creates a builder for DefaultMemoryHierarchyDevice object.
+     *
+     * @return builder object for DefaultMemoryHierarchyDevice object
+     */
+    public static DefaultMemoryHierarchyDevice.Builder builder() {
+        return new Builder();
+    }
+
+    @Override
+    public int modulesNb() {
+        return this.memoryHierarchy.size();
+    }
+
+    @Override
+    public long totalCapacity() {
+        return this.totalCapacity;
+    }
+
+    @Override
+    public Collection<MemoryModuleDevice> memoryHierarchy() {
+        return this.memoryHierarchy;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .omitNullValues()
+                .add("modulesNb", modulesNb())
+                .add("totalCapacity", totalCapacity())
+                .add("memoryHierarchy", memoryHierarchy())
+                .toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof MemoryHierarchyDevice)) {
+            return false;
+        }
+        MemoryHierarchyDevice device = (MemoryHierarchyDevice) obj;
+        return  this.modulesNb() ==  device.modulesNb() &&
+                this.totalCapacity() == device.totalCapacity() &&
+                this.memoryHierarchy() == device.memoryHierarchy();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(totalCapacity, memoryHierarchy);
+    }
+
+    public static final class Builder {
+        long totalCapacity = 0;
+        Collection<MemoryModuleDevice> memoryHierarchy = Sets.newHashSet();
+
+        private Builder() {
+
+        }
+
+        /**
+         * Adds a new memory module into the hierarchy.
+         *
+         * @param moduleDev a memory module to add into the hierarchy
+         * @return builder object
+         */
+        public Builder addMemoryModule(MemoryModuleDevice moduleDev) {
+            checkNotNull(moduleDev, MSG_MEM_MODULE_NULL);
+            this.memoryHierarchy.add(moduleDev);
+            this.totalCapacity += moduleDev.capacity();
+
+            return this;
+        }
+
+        /**
+         * Creates a DefaultMemoryHierarchyDevice object.
+         *
+         * @return DefaultMemoryHierarchyDevice object
+         */
+        public DefaultMemoryHierarchyDevice build() {
+            return new DefaultMemoryHierarchyDevice(totalCapacity, memoryHierarchy);
+        }
+
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultMemoryModuleDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultMemoryModuleDevice.java
new file mode 100644
index 0000000..2ab5fde
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultMemoryModuleDevice.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2020-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.impl.devices;
+
+import org.onosproject.drivers.server.devices.memory.MemoryModuleDevice;
+import org.onosproject.drivers.server.devices.memory.MemoryType;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Strings;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.drivers.server.Constants.MSG_MEM_MANUFACTURER_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_MEM_SERIAL_NB_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_MEM_SIZE_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_MEM_SPEED_CONF_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_MEM_SPEED_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_MEM_TYPE_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_MEM_WIDTH_DATA_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_MEM_WIDTH_TOTAL_NEGATIVE;
+import static org.onosproject.drivers.server.devices.memory.MemoryModuleDevice.MAX_SPEED_MTS;
+
+/**
+ * Default implementation for main memory module devices.
+ */
+public final class DefaultMemoryModuleDevice implements MemoryModuleDevice {
+
+    private final MemoryType type;
+    private final String manufacturer;
+    private final String serialNumber;
+    private final int dataWidth;
+    private final int totalWidth;
+    private final long capacity;
+    private final long speed;
+    private final long configuredSpeed;
+
+    private DefaultMemoryModuleDevice(MemoryType type,
+                                      String manufacturer,
+                                      String serialNumber,
+                                      int dataWidth,
+                                      int totalWidth,
+                                      long capacity,
+                                      long speed,
+                                      long configuredSpeed) {
+        checkNotNull(type, MSG_MEM_TYPE_NULL);
+        checkNotNull(manufacturer, MSG_MEM_MANUFACTURER_NULL);
+        checkNotNull(serialNumber, MSG_MEM_SERIAL_NB_NULL);
+        checkArgument(dataWidth > 0, MSG_MEM_WIDTH_DATA_NEGATIVE);
+        checkArgument(totalWidth > 0, MSG_MEM_WIDTH_TOTAL_NEGATIVE);
+        checkArgument(capacity > 0, MSG_MEM_SIZE_NEGATIVE);
+        checkArgument((speed > 0) && (speed <= MAX_SPEED_MTS), MSG_MEM_SPEED_NEGATIVE);
+        checkArgument((configuredSpeed > 0) && (configuredSpeed <= speed), MSG_MEM_SPEED_CONF_NEGATIVE);
+
+        this.type = type;
+        this.manufacturer = manufacturer;
+        this.serialNumber = serialNumber;
+        this.dataWidth = dataWidth;
+        this.totalWidth = totalWidth;
+        this.capacity = capacity;
+        this.speed = speed;
+        this.configuredSpeed = configuredSpeed;
+    }
+
+    /**
+     * Creates a builder for DefaultMemoryModuleDevice object.
+     *
+     * @return builder object for DefaultMemoryModuleDevice object
+     */
+    public static DefaultMemoryModuleDevice.Builder builder() {
+        return new Builder();
+    }
+
+    @Override
+    public MemoryType type() {
+        return this.type;
+    }
+
+    @Override
+    public String manufacturer() {
+        return this.manufacturer;
+    }
+
+    @Override
+    public String serialNumber() {
+        return this.serialNumber;
+    }
+
+    @Override
+    public int dataWidth() {
+        return this.dataWidth;
+    }
+
+    @Override
+    public int totalWidth() {
+        return this.totalWidth;
+    }
+
+    @Override
+    public long capacity() {
+        return this.capacity;
+    }
+
+    @Override
+    public long speed() {
+        return this.speed;
+    }
+
+    @Override
+    public long configuredSpeed() {
+        return this.configuredSpeed;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .omitNullValues()
+                .add("type", type())
+                .add("manufacturer", manufacturer())
+                .add("serialNumber", serialNumber())
+                .add("dataWidth", dataWidth())
+                .add("totalWidth", totalWidth())
+                .add("capacity", capacity())
+                .add("speed", speed())
+                .add("configuredSpeed", configuredSpeed())
+                .toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof MemoryModuleDevice)) {
+            return false;
+        }
+        MemoryModuleDevice device = (MemoryModuleDevice) obj;
+        return  this.type() ==  device.type() &&
+                this.manufacturer() == device.manufacturer() &&
+                this.serialNumber() == device.serialNumber() &&
+                this.dataWidth() == device.dataWidth() &&
+                this.totalWidth() == device.totalWidth() &&
+                this.capacity() == device.capacity() &&
+                this.speed() == device.speed() &&
+                this.configuredSpeed() == device.configuredSpeed();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(type, manufacturer, serialNumber,
+            dataWidth, totalWidth, capacity, speed, configuredSpeed);
+    }
+
+    public static final class Builder {
+        MemoryType type = null;
+        String manufacturer = null;
+        String serialNumber = null;
+        int dataWidth = -1;
+        int totalWidth = -1;
+        long capacity = -1;
+        long speed = -1;
+        long configuredSpeed = -1;
+
+        private Builder() {
+
+        }
+
+        /**
+         * Sets the type of this memory module.
+         *
+         * @param typeStr memory module's type
+         * @return builder object
+         */
+        public Builder setType(String typeStr) {
+            if (!Strings.isNullOrEmpty(typeStr)) {
+                this.type = MemoryType.getByName(typeStr);
+            }
+
+            return this;
+        }
+
+        /**
+         * Sets the manufacturer of this memory module.
+         *
+         * @param manufacturer memory module's manufacturer
+         * @return builder object
+         */
+        public Builder setManufacturer(String manufacturer) {
+            if (!Strings.isNullOrEmpty(manufacturer)) {
+                this.manufacturer = manufacturer;
+            }
+
+            return this;
+        }
+
+        /**
+         * Sets the serial number of this memory module.
+         *
+         * @param serialNumber memory module's serial number
+         * @return builder object
+         */
+        public Builder setSerialNumber(String serialNumber) {
+            if (!Strings.isNullOrEmpty(serialNumber)) {
+                this.serialNumber = serialNumber;
+            }
+
+            return this;
+        }
+
+        /**
+         * Sets this memory module's data width.
+         *
+         * @param dataWidth memory's data width
+         * @return builder object
+         */
+        public Builder setDataWidth(int dataWidth) {
+            this.dataWidth = dataWidth;
+
+            return this;
+        }
+
+        /**
+         * Sets this memory module's total width.
+         *
+         * @param totalWidth memory's total width
+         * @return builder object
+         */
+        public Builder setTotalWidth(int totalWidth) {
+            this.totalWidth = totalWidth;
+
+            return this;
+        }
+
+        /**
+         * Sets this memory module's capacity in MBytes.
+         *
+         * @param capacity memory's capacity
+         * @return builder object
+         */
+        public Builder setCapacity(long capacity) {
+            this.capacity = capacity;
+
+            return this;
+        }
+
+        /**
+         * Sets the speed of this memory module.
+         *
+         * @param speed memory speed
+         * @return builder object
+         */
+        public Builder setSpeed(long speed) {
+            this.speed = speed;
+
+            return this;
+        }
+
+        /**
+         * Sets the configured speed of this memory module.
+         *
+         * @param configuredSpeed memory's configured speed
+         * @return builder object
+         */
+        public Builder setConfiguredSpeed(long configuredSpeed) {
+            this.configuredSpeed = configuredSpeed;
+
+            return this;
+        }
+
+        /**
+         * Creates a DefaultMemoryModuleDevice object.
+         *
+         * @return DefaultMemoryModuleDevice object
+         */
+        public DefaultMemoryModuleDevice build() {
+            return new DefaultMemoryModuleDevice(
+                type, manufacturer, serialNumber,
+                dataWidth, totalWidth, capacity,
+                speed, configuredSpeed);
+        }
+
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultNicDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultNicDevice.java
index 9f782d8..a8270f5 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultNicDevice.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultNicDevice.java
@@ -18,49 +18,61 @@
 
 import org.onosproject.drivers.server.devices.nic.NicDevice;
 import org.onosproject.drivers.server.devices.nic.NicRxFilter;
+import org.onosproject.drivers.server.devices.nic.NicRxFilter.RxFilter;
+import org.onosproject.net.Port;
 
 import org.onlab.packet.MacAddress;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
 
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 import static org.onosproject.net.Port.Type;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_NAME_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_MAC_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_PORT_NUMBER_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_PORT_TYPE_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_RX_FILTER_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_RX_FILTERS_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_SPEED_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_PORT_TYPE_COPPER;
+import static org.onosproject.drivers.server.Constants.PARAM_NIC_PORT_TYPE_FIBER;
 
 /**
  * Default implementation for NIC devices.
  */
-public class DefaultNicDevice implements NicDevice, Comparable {
+public final class DefaultNicDevice implements NicDevice, Comparable {
 
     private final String     name;
     private final long       portNumber;
-    private final long       speed;
     private final Type       portType;
+    private final long       speed;
     private boolean          status;
     private final MacAddress macAddress;
     private NicRxFilter      rxFilterMechanisms;
 
-    // 200 Gbps or 200.000 Mbps
-    public static final long MAX_SPEED = 200000;
-
-    public DefaultNicDevice(
+    private DefaultNicDevice(
             String      name,
             long        portNumber,
             Type        portType,
             long        speed,
             boolean     status,
-            String      macStr,
+            MacAddress  mac,
             NicRxFilter rxFilterMechanisms) {
-        checkArgument(!Strings.isNullOrEmpty(name), "NIC name cannot be empty or NULL");
-        checkArgument(portNumber >= 0, "NIC port number must be non-negative");
-        checkNotNull(portType, "NIC port type cannot be null");
-        checkArgument((speed >= 0) && (speed <= MAX_SPEED),
-            "NIC speed must be positive and less or equal than " + MAX_SPEED + " Mbps");
-        checkNotNull(macStr, "NIC MAC address cannot be null");
-        checkNotNull(rxFilterMechanisms, "NIC Rx filter mechanisms cannot be null");
+        checkArgument(!Strings.isNullOrEmpty(name), MSG_NIC_NAME_NULL);
+        checkArgument(portNumber >= 0, MSG_NIC_PORT_NUMBER_NEGATIVE);
+        checkNotNull(portType, MSG_NIC_PORT_TYPE_NULL);
+        checkArgument((speed >= 0) && (speed <= NicDevice.MAX_SPEED),
+            MSG_NIC_SPEED_NEGATIVE);
+        checkNotNull(mac, MSG_NIC_MAC_NULL);
+        checkNotNull(rxFilterMechanisms, MSG_NIC_RX_FILTERS_NULL);
 
         // Implies a problem
         if (speed == 0) {
@@ -72,8 +84,17 @@
         this.speed      = speed;
         this.portType   = portType;
         this.status     = status;
-        this.macAddress = MacAddress.valueOf(macStr);
-        this.rxFilterMechanisms  = rxFilterMechanisms;
+        this.macAddress = mac;
+        this.rxFilterMechanisms = rxFilterMechanisms;
+    }
+
+    /**
+     * Creates a builder for DefaultNicDevice object.
+     *
+     * @return builder object for DefaultNicDevice object
+     */
+    public static DefaultNicDevice.Builder builder() {
+        return new Builder();
     }
 
     @Override
@@ -123,7 +144,7 @@
     }
 
     @Override
-    public void addRxFilterMechanism(NicRxFilter.RxFilter rxFilter) {
+    public void addRxFilterMechanism(RxFilter rxFilter) {
         this.rxFilterMechanisms.addRxFilter(rxFilter);
     }
 
@@ -186,4 +207,137 @@
         return -1;
     }
 
+    public static final class Builder {
+        String      name;
+        long        portNumber = -1;
+        Type        portType = Type.FIBER;
+        long        speed = -1;
+        boolean     status = false;
+        MacAddress  macAddress = MacAddress.ZERO;
+        NicRxFilter rxFilterMechanisms = new NicRxFilter();
+
+        /**
+         * Port types that usually appear in commodity servers.
+         */
+        static final Map<String, Port.Type> PORT_TYPE_MAP =
+            Collections.unmodifiableMap(
+                new HashMap<String, Port.Type>() {
+                    {
+                        put(PARAM_NIC_PORT_TYPE_COPPER, Port.Type.COPPER);
+                        put(PARAM_NIC_PORT_TYPE_FIBER,  Port.Type.FIBER);
+                    }
+                }
+        );
+
+        private Builder() {
+
+        }
+
+        /**
+         * Sets the name of this NIC.
+         *
+         * @param name NIC name
+         * @return builder object
+         */
+        public Builder setName(String name) {
+            this.name = name;
+
+            return this;
+        }
+
+        /**
+         * Sets the NIC's port number.
+         *
+         * @param portNumber NIC's port number
+         * @return builder object
+         */
+        public Builder setPortNumber(long portNumber) {
+            this.portNumber = portNumber;
+
+            return this;
+        }
+
+        /**
+         * Sets the NIC's port type as a string.
+         *
+         * @param portTypeStr NIC's port type
+         * @return builder object
+         */
+        public Builder setPortType(String portTypeStr) {
+            portType = PORT_TYPE_MAP.get(portTypeStr);
+            if (portType == null) {
+                throw new IllegalArgumentException(
+                    portTypeStr + " is not a valid NIC port type");
+            }
+
+            return this;
+        }
+
+        /**
+         * Sets the NIC's speed.
+         *
+         * @param speed NIC's speed
+         * @return builder object
+         */
+        public Builder setSpeed(long speed) {
+            this.speed = speed;
+
+            return this;
+        }
+
+        /**
+         * Sets the NIC's status.
+         *
+         * @param status NIC's status
+         * @return builder object
+         */
+        public Builder setStatus(boolean status) {
+            this.status = status;
+
+            return this;
+        }
+
+        /**
+         * Sets the NIC's MAC address.
+         *
+         * @param macAddressStr NIC's MAC address
+         * @return builder object
+         */
+        public Builder setMacAddress(String macAddressStr) {
+            this.macAddress = MacAddress.valueOf(macAddressStr);
+
+            return this;
+        }
+
+        /**
+         * Sets the NIC's list of Rx filters as strings.
+         *
+         * @param rxFilters NIC's list of Rx filters
+         * @return builder object
+         */
+        public Builder setRxFilters(List<String> rxFilters) {
+            checkNotNull(rxFilters, MSG_NIC_RX_FILTERS_NULL);
+            for (String s : rxFilters) {
+                // Verify that this is a valid Rx filter
+                RxFilter rf = RxFilter.getByName(s);
+                checkNotNull(rf, MSG_NIC_RX_FILTER_NULL);
+                this.rxFilterMechanisms.addRxFilter(rf);
+            }
+
+            return this;
+        }
+
+        /**
+         * Creates a DefaultNicDevice object.
+         *
+         * @return DefaultNicDevice object
+         */
+        public DefaultNicDevice build() {
+            return new DefaultNicDevice(
+                name, portNumber, portType, speed,
+                status, macAddress, rxFilterMechanisms);
+        }
+
+    }
+
 }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultRestServerSBDevice.java b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultRestServerSBDevice.java
index b43c2ec..781afc5 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultRestServerSBDevice.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultRestServerSBDevice.java
@@ -16,7 +16,9 @@
 
 package org.onosproject.drivers.server.impl.devices;
 
-import org.onosproject.drivers.server.devices.CpuDevice;
+import org.onosproject.drivers.server.devices.cpu.CpuCacheHierarchyDevice;
+import org.onosproject.drivers.server.devices.cpu.CpuDevice;
+import org.onosproject.drivers.server.devices.memory.MemoryHierarchyDevice;
 import org.onosproject.drivers.server.devices.nic.NicDevice;
 import org.onosproject.drivers.server.devices.RestServerSBDevice;
 
@@ -26,12 +28,15 @@
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
 
-import java.util.Objects;
 import java.util.Collection;
+import java.util.Objects;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_HIERARCHY_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_LIST_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_MEM_HIERARCHY_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_LIST_NULL;
 
 /**
  * Default implementation for REST server devices.
@@ -39,35 +44,46 @@
 public class DefaultRestServerSBDevice
         extends DefaultRestSBDevice implements RestServerSBDevice {
 
-    private Collection<CpuDevice> cpus = Lists.newArrayList();
-    private Collection<NicDevice> nics = Lists.newArrayList();
+    private Collection<CpuDevice> cpus = null;
+    private CpuCacheHierarchyDevice caches = null;
+    private MemoryHierarchyDevice memory = null;
+    private Collection<NicDevice> nics = null;
+
+    /**
+     * Indicates an invalid port.
+     */
+    private static final long INVALID_PORT = (long) -1;
+    private static final String INVALID_PORT_STR = "";
 
     public DefaultRestServerSBDevice(
             IpAddress ip, int port, String name, String password,
             String protocol, String url, boolean isActive,
-            Collection<CpuDevice> cpus, Collection<NicDevice> nics) {
-        this(
-            ip, port, name, password, protocol, url, isActive,
-            "", "", "", "", AuthenticationScheme.BASIC, "", cpus, nics
-        );
+            Collection<CpuDevice> cpus,
+            CpuCacheHierarchyDevice caches,
+            MemoryHierarchyDevice memory,
+            Collection<NicDevice> nics) {
+        this(ip, port, name, password, protocol, url, isActive,
+            "", "", "", "", cpus, caches, memory, nics);
     }
 
     public DefaultRestServerSBDevice(
             IpAddress ip, int port, String name, String password,
             String protocol, String url, boolean isActive, String testUrl,
             String manufacturer, String hwVersion, String swVersion,
-            AuthenticationScheme authenticationScheme, String token,
-            Collection<CpuDevice> cpus, Collection<NicDevice> nics) {
-        super(
-            ip, port, name, password, protocol, url, isActive,
-            testUrl, manufacturer, hwVersion, swVersion,
-            authenticationScheme, token
-        );
+            Collection<CpuDevice> cpus, CpuCacheHierarchyDevice caches,
+            MemoryHierarchyDevice memory, Collection<NicDevice> nics) {
+        super(ip, port, name, password, protocol, url, isActive,
+              testUrl, manufacturer, hwVersion, swVersion,
+              AuthenticationScheme.BASIC, "");
 
-        checkNotNull(cpus, "Device's set of CPUs cannot be null");
-        checkNotNull(nics, "Device's set of NICs cannot be null");
+        checkNotNull(cpus, MSG_CPU_LIST_NULL);
+        checkNotNull(caches, MSG_CPU_CACHE_HIERARCHY_NULL);
+        checkNotNull(memory, MSG_MEM_HIERARCHY_NULL);
+        checkNotNull(nics, MSG_NIC_LIST_NULL);
 
         this.cpus = cpus;
+        this.caches = caches;
+        this.memory = memory;
         this.nics = nics;
     }
 
@@ -82,6 +98,31 @@
     }
 
     @Override
+    public CpuCacheHierarchyDevice caches() {
+        return this.caches;
+    }
+
+    @Override
+    public int numberOfCaches() {
+        return this.caches.levels();
+    }
+
+    @Override
+    public long cacheCapacity() {
+        return this.caches.totalCapacity();
+    }
+
+    @Override
+    public MemoryHierarchyDevice memory() {
+        return this.memory;
+    }
+
+    @Override
+    public long memoryCapacity() {
+        return this.memory.totalCapacity();
+    }
+
+    @Override
     public Collection<NicDevice> nics() {
         return this.nics;
     }
@@ -94,7 +135,7 @@
     @Override
     public long portNumberFromName(String portName) {
         if (Strings.isNullOrEmpty(portName)) {
-            return -1;
+            return INVALID_PORT;
         }
 
         for (NicDevice nic : this.nics) {
@@ -103,13 +144,13 @@
             }
         }
 
-        return -1;
+        return INVALID_PORT;
     }
 
     @Override
     public String portNameFromNumber(long portNumber) {
         if (portNumber < 0) {
-            return "";
+            return INVALID_PORT_STR;
         }
 
         for (NicDevice nic : this.nics) {
@@ -118,7 +159,7 @@
             }
         }
 
-        return "";
+        return INVALID_PORT_STR;
     }
 
     @Override
@@ -135,9 +176,10 @@
                 .add("hwVersion", hwVersion().orElse(null))
                 .add("swVersion", swVersion().orElse(null))
                 .add("cpus", cpus())
+                .add("cpuCaches", caches())
+                .add("memory", memory())
                 .add("nics", nics())
                 .toString();
-
     }
 
     @Override
@@ -148,16 +190,21 @@
         if (!(obj instanceof RestServerSBDevice)) {
             return false;
         }
-        RestServerSBDevice device = (RestServerSBDevice) obj;
 
-        return  this.username().equals(device.username()) &&
+        RestServerSBDevice device = (RestServerSBDevice) obj;
+        return  this.protocol().equals(device.protocol()) &&
+                this.username().equals(device.username()) &&
                 this.ip().equals(device.ip()) &&
-                this.port() == device.port();
+                this.port() == device.port() &&
+                this.cpus() == device.cpus() &&
+                this.caches() == device.caches() &&
+                this.memory() == device.memory() &&
+                this.nics() == device.nics();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(ip(), port(), cpus(), nics());
+        return Objects.hash(ip(), port(), cpus(), caches(), memory(), nics());
     }
 
 }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultServerDeviceDescription.java b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultServerDeviceDescription.java
index c5ac09a..eff32eb 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultServerDeviceDescription.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/devices/DefaultServerDeviceDescription.java
@@ -16,12 +16,15 @@
 
 package org.onosproject.drivers.server.impl.devices;
 
-import org.onosproject.drivers.server.devices.CpuDevice;
+import org.onosproject.drivers.server.devices.cpu.CpuCacheHierarchyDevice;
+import org.onosproject.drivers.server.devices.cpu.CpuDevice;
+import org.onosproject.drivers.server.devices.memory.MemoryHierarchyDevice;
 import org.onosproject.drivers.server.devices.nic.NicDevice;
 import org.onosproject.drivers.server.devices.ServerDeviceDescription;
 
 import org.onosproject.net.device.DefaultDeviceDescription;
 import org.onosproject.net.SparseAnnotations;
+
 import org.onlab.packet.ChassisId;
 
 import com.google.common.base.Objects;
@@ -31,6 +34,10 @@
 
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CACHE_HIERARCHY_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_LIST_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_MEM_HIERARCHY_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_NIC_LIST_NULL;
 import static org.onosproject.net.Device.Type;
 
 /**
@@ -40,6 +47,8 @@
         implements ServerDeviceDescription {
 
     private final Collection<CpuDevice> cpus;
+    private final CpuCacheHierarchyDevice caches;
+    private final MemoryHierarchyDevice memory;
     private final Collection<NicDevice> nics;
 
     /**
@@ -53,6 +62,8 @@
      * @param serialNumber device serial number
      * @param chassis      chassis id
      * @param cpus         set of CPUs
+     * @param caches       CPU cache hierarchy
+     * @param memory       memory hierarchy
      * @param nics         set of network interface cards (NICs)
      * @param annotations  optional key/value annotations map
      */
@@ -60,10 +71,11 @@
             URI uri, Type type, String manufacturer,
             String hwVersion, String swVersion,
             String serialNumber, ChassisId chassis,
-            Collection<CpuDevice> cpus, Collection<NicDevice> nics,
+            Collection<CpuDevice> cpus, CpuCacheHierarchyDevice caches,
+            MemoryHierarchyDevice memory, Collection<NicDevice> nics,
             SparseAnnotations... annotations) {
         this(uri, type, manufacturer, hwVersion, swVersion, serialNumber,
-             chassis, true, cpus, nics, annotations);
+             chassis, true, cpus, caches, memory, nics, annotations);
     }
 
     /**
@@ -77,6 +89,8 @@
      * @param serialNumber     device serial number
      * @param chassis          chassis id
      * @param cpus             set of CPUs
+     * @param caches           CPU cache hierarchy
+     * @param memory           memory hierarchy
      * @param nics             set of network interface cards (NICs)
      * @param defaultAvailable optional whether device is by default available
      * @param annotations      optional key/value annotations map
@@ -86,31 +100,36 @@
             String hwVersion, String swVersion,
             String serialNumber, ChassisId chassis,
             boolean defaultAvailable,
-            Collection<CpuDevice> cpus, Collection<NicDevice> nics,
+            Collection<CpuDevice> cpus, CpuCacheHierarchyDevice caches,
+            MemoryHierarchyDevice memory, Collection<NicDevice> nics,
             SparseAnnotations... annotations) {
         super(
             uri, type, manufacturer, hwVersion, swVersion,
             serialNumber, chassis, defaultAvailable, annotations
         );
 
-        checkNotNull(cpus, "Device's set of CPUs cannot be null");
-        checkNotNull(nics, "Device's set of NICs cannot be null");
+        checkNotNull(cpus, MSG_CPU_LIST_NULL);
+        checkNotNull(caches, MSG_CPU_CACHE_HIERARCHY_NULL);
+        checkNotNull(memory, MSG_MEM_HIERARCHY_NULL);
+        checkNotNull(nics, MSG_NIC_LIST_NULL);
 
         this.cpus = cpus;
+        this.caches = caches;
+        this.memory = memory;
         this.nics = nics;
     }
 
     /**
      * Creates a server device description using the supplied information.
      * @param base ServerDeviceDescription to basic information
-     * @param annotations Annotations to use.
+     * @param annotations Annotations to use
      */
     public DefaultServerDeviceDescription(ServerDeviceDescription base,
                                     SparseAnnotations... annotations) {
         this(base.deviceUri(), base.type(), base.manufacturer(),
              base.hwVersion(), base.swVersion(), base.serialNumber(),
              base.chassisId(), base.isDefaultAvailable(),
-             base.cpus(), base.nics(),
+             base.cpus(), base.caches(), base.memory(), base.nics(),
              annotations);
     }
 
@@ -118,7 +137,7 @@
      * Creates a device description using the supplied information.
      * @param base ServerDeviceDescription to basic information (except for type)
      * @param type device type
-     * @param annotations Annotations to use.
+     * @param annotations Annotations to use
      */
     public DefaultServerDeviceDescription(
             ServerDeviceDescription base, Type type,
@@ -126,7 +145,7 @@
         this(base.deviceUri(), type, base.manufacturer(),
              base.hwVersion(), base.swVersion(), base.serialNumber(),
              base.chassisId(), base.isDefaultAvailable(),
-             base.cpus(), base.nics(),
+             base.cpus(), base.caches(), base.memory(), base.nics(),
              annotations);
     }
 
@@ -135,7 +154,7 @@
      *
      * @param base ServerDeviceDescription to basic information (except for defaultAvailable)
      * @param defaultAvailable whether device should be made available by default
-     * @param annotations Annotations to use.
+     * @param annotations Annotations to use
      */
     public DefaultServerDeviceDescription(
             ServerDeviceDescription base,
@@ -144,7 +163,7 @@
         this(base.deviceUri(), base.type(), base.manufacturer(),
              base.hwVersion(), base.swVersion(), base.serialNumber(),
              base.chassisId(), defaultAvailable,
-             base.cpus(), base.nics(),
+             base.cpus(), base.caches(), base.memory(), base.nics(),
              annotations);
     }
 
@@ -154,6 +173,16 @@
     }
 
     @Override
+    public CpuCacheHierarchyDevice caches() {
+        return this.caches;
+    }
+
+    @Override
+    public MemoryHierarchyDevice memory() {
+        return this.memory;
+    }
+
+    @Override
     public Collection<NicDevice> nics() {
         return this.nics;
     }
@@ -168,6 +197,8 @@
                 .add("swVersion",    swVersion())
                 .add("serial",       serialNumber())
                 .add("cpus",         cpus)
+                .add("cpuCaches",    caches)
+                .add("memory",       memory)
                 .add("nics",         nics)
                 .add("annotations",  annotations())
                 .toString();
@@ -175,7 +206,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(super.hashCode(), cpus, nics);
+        return Objects.hashCode(super.hashCode(), cpus, caches, memory, nics);
     }
 
     @Override
@@ -193,6 +224,8 @@
                 && Objects.equal(this.serialNumber(),       that.serialNumber())
                 && Objects.equal(this.chassisId(),          that.chassisId())
                 && Objects.equal(this.cpus(),               that.cpus())
+                && Objects.equal(this.caches(),             that.caches())
+                && Objects.equal(this.memory(),             that.memory())
                 && Objects.equal(this.nics(),               that.nics())
                 && Objects.equal(this.isDefaultAvailable(), that.isDefaultAvailable());
         }
@@ -203,6 +236,9 @@
     DefaultServerDeviceDescription() {
         super(null, null, null, null, null, null, null, (SparseAnnotations[]) null);
         this.cpus = null;
+        this.caches = null;
+        this.memory = null;
         this.nics = null;
     }
+
 }
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 f2b6968..6296cc1 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
@@ -16,10 +16,12 @@
 
 package org.onosproject.drivers.server.impl.stats;
 
+import org.onosproject.drivers.server.devices.cpu.CpuCoreId;
 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;
@@ -28,21 +30,18 @@
 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;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CORE_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_LOAD_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_ID_NULL;
 
 /**
  * Default implementation for CPU statistics.
  */
 public final class DefaultCpuStatistics implements CpuStatistics {
 
-    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;
-
     private final DeviceId deviceId;
 
     private final int id;
@@ -63,16 +62,15 @@
     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");
-        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 + "]");
+        checkNotNull(deviceId, MSG_DEVICE_ID_NULL);
+        checkArgument((id >= 0) && (id < CpuCoreId.MAX_CPU_CORE_NB), MSG_CPU_CORE_NEGATIVE);
+        checkArgument((load >= CpuStatistics.MIN_CPU_LOAD) &&
+                      (load <= CpuStatistics.MAX_CPU_LOAD), MSG_CPU_LOAD_NEGATIVE);
 
-        this.deviceId  = deviceId;
-        this.id        = id;
-        this.load      = load;
-        this.queue     = queue;
+        this.deviceId = deviceId;
+        this.id = id;
+        this.load = load;
+        this.queue = queue;
         this.busySince = busySince;
 
         this.throughputUnit = (throughputUnit == null) ?
@@ -91,10 +89,10 @@
 
     // Constructor for serializer
     private DefaultCpuStatistics() {
-        this.deviceId  = null;
-        this.id        = 0;
-        this.load      = 0;
-        this.queue     = 0;
+        this.deviceId = null;
+        this.id = 0;
+        this.load = 0;
+        this.queue = 0;
         this.busySince = -1;
 
         this.throughputUnit = null;
@@ -349,6 +347,7 @@
                 throughputUnit, averageThroughput,
                 latencyUnit, minLatency, averageLatency, maxLatency);
         }
+
     }
 
 }
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultMemoryStatistics.java b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultMemoryStatistics.java
new file mode 100644
index 0000000..fda4942
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultMemoryStatistics.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2020-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.impl.stats;
+
+import org.onosproject.drivers.server.stats.MemoryStatistics;
+import org.onosproject.drivers.server.stats.MonitoringUnit;
+
+import org.onosproject.net.DeviceId;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Strings;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_STATS_MEMORY_FREE_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_STATS_MEMORY_TOTAL_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_STATS_MEMORY_USED_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_STATS_UNIT_NULL;
+import static org.onosproject.drivers.server.stats.MonitoringUnit.CapacityUnit;
+
+/**
+ * Default implementation for main memory statistics.
+ */
+public final class DefaultMemoryStatistics implements MemoryStatistics {
+
+    private static final CapacityUnit DEF_MEM_UNIT = CapacityUnit.KILOBYTES;
+
+    private final DeviceId deviceId;
+    private final MonitoringUnit unit;
+    private long used;
+    private long free;
+    private long total;
+
+    private DefaultMemoryStatistics(DeviceId deviceId, MonitoringUnit unit,
+                                    long used, long free, long total) {
+        checkNotNull(unit, MSG_STATS_UNIT_NULL);
+        checkArgument(used >= 0, MSG_STATS_MEMORY_USED_NEGATIVE);
+        checkArgument(free >= 0, MSG_STATS_MEMORY_FREE_NEGATIVE);
+        checkArgument((total >= 0) &&
+                      (used + free == total), MSG_STATS_MEMORY_TOTAL_NEGATIVE);
+
+        this.deviceId = deviceId;
+        this.unit = unit;
+        this.used = used;
+        this.free = free;
+        this.total = total;
+    }
+
+    // Constructor for serializer
+    private DefaultMemoryStatistics() {
+        this.deviceId = null;
+        this.unit = null;
+        this.used = 0;
+        this.free = 0;
+        this.total = 0;
+    }
+
+    /**
+     * Creates a builder for DefaultMemoryStatistics object.
+     *
+     * @return builder object for DefaultMemoryStatistics object
+     */
+    public static DefaultMemoryStatistics.Builder builder() {
+        return new Builder();
+    }
+
+    @Override
+    public MonitoringUnit unit() {
+        return this.unit;
+    }
+
+    @Override
+    public long used() {
+        return this.used;
+    }
+
+    @Override
+    public long free() {
+        return this.free;
+    }
+
+    @Override
+    public long total() {
+        return this.total;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .omitNullValues()
+                .add("device", deviceId)
+                .add("unit", this.unit())
+                .add("used", this.used())
+                .add("free", this.free())
+                .add("total", this.total())
+                .toString();
+    }
+
+    public static final class Builder {
+
+        DeviceId deviceId;
+        MonitoringUnit unit = DEF_MEM_UNIT;
+        long used;
+        long free;
+        long total;
+
+        private Builder() {
+
+        }
+
+        /**
+         * Sets the device identifier.
+         *
+         * @param deviceId device identifier
+         * @return builder object
+         */
+        public Builder setDeviceId(DeviceId deviceId) {
+            this.deviceId = deviceId;
+
+            return this;
+        }
+
+        /**
+         * Sets memory statistics unit.
+         *
+         * @param unitStr memory statistics unit as a string
+         * @return builder object
+         */
+        public Builder setUnit(String unitStr) {
+            if (!Strings.isNullOrEmpty(unitStr)) {
+                this.unit = CapacityUnit.getByName(unitStr);
+            }
+
+            return this;
+        }
+
+        /**
+         * Sets the amount of used main memory.
+         *
+         * @param used used main memory
+         * @return builder object
+         */
+        public Builder setMemoryUsed(long used) {
+            this.used = used;
+            return this;
+        }
+
+        /**
+         * Sets the amount of free main memory.
+         *
+         * @param free free main memory
+         * @return builder object
+         */
+        public Builder setMemoryFree(long free) {
+            this.free = free;
+            return this;
+        }
+
+        /**
+         * Sets the total amount of main memory.
+         *
+         * @param total total main memory
+         * @return builder object
+         */
+        public Builder setMemoryTotal(long total) {
+            this.total = total;
+            return this;
+        }
+
+        /**
+         * Creates a DefaultMemoryStatistics object.
+         *
+         * @return DefaultMemoryStatistics object
+         */
+        public DefaultMemoryStatistics build() {
+            return new DefaultMemoryStatistics(
+                deviceId, unit, used, free, total);
+        }
+
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultMonitoringStatistics.java b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultMonitoringStatistics.java
index c05687f..6a4e0de 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultMonitoringStatistics.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/impl/stats/DefaultMonitoringStatistics.java
@@ -16,7 +16,9 @@
 
 package org.onosproject.drivers.server.impl.stats;
 
+import org.onosproject.drivers.server.devices.cpu.CpuCoreId;
 import org.onosproject.drivers.server.stats.CpuStatistics;
+import org.onosproject.drivers.server.stats.MemoryStatistics;
 import org.onosproject.drivers.server.stats.MonitoringStatistics;
 import org.onosproject.drivers.server.stats.TimingStatistics;
 
@@ -29,10 +31,16 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.drivers.server.Constants.MSG_CPU_CORE_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_DEVICE_ID_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_STATS_CPU_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_STATS_MEMORY_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_STATS_NIC_NULL;
+import static org.onosproject.drivers.server.Constants.MSG_STATS_TIMING_NULL;
 
 /**
  * Default monitoring statistics for server devices.
- * Includes CPU, NIC, and timing statistics.
+ * Includes CPU, main memory, NIC, and timing statistics.
  */
 public final class DefaultMonitoringStatistics implements MonitoringStatistics {
 
@@ -40,30 +48,35 @@
 
     private final TimingStatistics           timingStatistics;
     private final Collection<CpuStatistics>  cpuStatistics;
+    private final MemoryStatistics           memoryStatistics;
     private final Collection<PortStatistics> nicStatistics;
 
     private DefaultMonitoringStatistics(
             DeviceId                   deviceId,
             TimingStatistics           timingStatistics,
             Collection<CpuStatistics>  cpuStatistics,
+            MemoryStatistics           memoryStatistics,
             Collection<PortStatistics> nicStatistics) {
-        checkNotNull(deviceId,         "Device ID is NULL");
-        checkNotNull(timingStatistics, "Timing statistics are NULL");
-        checkNotNull(cpuStatistics,    "CPU statistics are NULL");
-        checkNotNull(nicStatistics,    "NIC statistics are NULL");
+        checkNotNull(deviceId,         MSG_DEVICE_ID_NULL);
+        checkNotNull(timingStatistics, MSG_STATS_TIMING_NULL);
+        checkNotNull(cpuStatistics,    MSG_STATS_CPU_NULL);
+        checkNotNull(memoryStatistics, MSG_STATS_MEMORY_NULL);
+        checkNotNull(nicStatistics,    MSG_STATS_NIC_NULL);
 
-        this.deviceId         = deviceId;
+        this.deviceId = deviceId;
         this.timingStatistics = timingStatistics;
-        this.cpuStatistics    = cpuStatistics;
-        this.nicStatistics    = nicStatistics;
+        this.cpuStatistics = cpuStatistics;
+        this.memoryStatistics = memoryStatistics;
+        this.nicStatistics = nicStatistics;
     }
 
     // Constructor for serializer
     private DefaultMonitoringStatistics() {
-        this.deviceId         = null;
+        this.deviceId = null;
         this.timingStatistics = null;
-        this.cpuStatistics    = null;
-        this.nicStatistics    = null;
+        this.cpuStatistics = null;
+        this.memoryStatistics = null;
+        this.nicStatistics = null;
     }
 
     /**
@@ -87,11 +100,8 @@
 
     @Override
     public CpuStatistics cpuStatistics(int cpuId) {
-        checkArgument(
-            (cpuId >= 0) && (cpuId < DefaultCpuStatistics.MAX_CPU_NB),
-            "CPU core ID must be in [0, " +
-            String.valueOf(DefaultCpuStatistics.MAX_CPU_NB - 1) + "]"
-        );
+        checkArgument((cpuId >= 0) && (cpuId < CpuCoreId.MAX_CPU_CORE_NB),
+            MSG_CPU_CORE_NEGATIVE);
         for (CpuStatistics cs : this.cpuStatistics) {
             if (cs.id() == cpuId) {
                 return cs;
@@ -101,6 +111,11 @@
     }
 
     @Override
+    public MemoryStatistics memoryStatistics() {
+        return this.memoryStatistics;
+    }
+
+    @Override
     public Collection<PortStatistics> nicStatisticsAll() {
         return this.nicStatistics;
     }
@@ -132,6 +147,7 @@
                 .omitNullValues()
                 .add("timingStatistics", timingStatistics())
                 .add("cpuStatistics",    cpuStatisticsAll())
+                .add("memoryStatistics", memoryStatistics())
                 .add("nicStatistics",    nicStatisticsAll())
                 .toString();
     }
@@ -141,6 +157,7 @@
         DeviceId                   deviceId;
         TimingStatistics           timingStatistics;
         Collection<CpuStatistics>  cpuStatistics;
+        MemoryStatistics           memoryStatistics;
         Collection<PortStatistics> nicStatistics;
 
         private Builder() {
@@ -184,6 +201,18 @@
         }
 
         /**
+         * Sets memory statistics.
+         *
+         * @param memoryStatistics memory statistics
+         * @return builder object
+         */
+        public Builder setMemoryStatistics(MemoryStatistics memoryStatistics) {
+            this.memoryStatistics = memoryStatistics;
+
+            return this;
+        }
+
+        /**
          * Sets NIC statistics.
          *
          * @param nicStatistics NIC statistics
@@ -202,11 +231,8 @@
          */
         public DefaultMonitoringStatistics build() {
             return new DefaultMonitoringStatistics(
-                deviceId,
-                timingStatistics,
-                cpuStatistics,
-                nicStatistics
-            );
+                deviceId, timingStatistics, cpuStatistics,
+                memoryStatistics, nicStatistics);
         }
 
     }
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 6c4f86f..3e72eb6 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
@@ -24,6 +24,10 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.server.Constants.MSG_STATS_TIMING_AUTO_SCALE_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_STATS_TIMING_LAUNCH_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_STATS_TIMING_PARSE_NEGATIVE;
+import static org.onosproject.drivers.server.Constants.MSG_STATS_UNIT_NULL;
 import static org.onosproject.drivers.server.stats.MonitoringUnit.LatencyUnit;
 
 /**
@@ -31,25 +35,25 @@
  */
 public final class DefaultTimingStatistics implements TimingStatistics {
 
-    private static final LatencyUnit DEF_UNIT = LatencyUnit.NANO_SECOND;
+    private static final LatencyUnit DEF_TIME_UNIT = LatencyUnit.NANO_SECOND;
 
     private final MonitoringUnit unit;
     private final long deployCommandParsingTime;
     private final long deployCommandLaunchingTime;
-    private long autoScaleTime;
+    private final 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, "Auto-scale time is negative");
+        checkNotNull(unit, MSG_STATS_UNIT_NULL);
+        checkArgument(parsingTime >= 0, MSG_STATS_TIMING_PARSE_NEGATIVE);
+        checkArgument(launchingTime >= 0, MSG_STATS_TIMING_LAUNCH_NEGATIVE);
+        checkArgument(autoScaleTime >= 0, MSG_STATS_TIMING_AUTO_SCALE_NEGATIVE);
 
         this.unit = unit;
-        this.deployCommandParsingTime   = parsingTime;
+        this.deployCommandParsingTime = parsingTime;
         this.deployCommandLaunchingTime = launchingTime;
         this.autoScaleTime = autoScaleTime;
     }
@@ -57,7 +61,7 @@
     // Constructor for serializer
     private DefaultTimingStatistics() {
         this.unit = null;
-        this.deployCommandParsingTime   = 0;
+        this.deployCommandParsingTime = 0;
         this.deployCommandLaunchingTime = 0;
         this.autoScaleTime = 0;
     }
@@ -100,7 +104,7 @@
     public String toString() {
         return MoreObjects.toStringHelper(this)
                 .omitNullValues()
-                .add("unit", this.unit().toString())
+                .add("unit", this.unit())
                 .add("parsingTime", this.deployCommandParsingTime())
                 .add("launchingTime", this.deployCommandLaunchingTime())
                 .add("deploymentTime", this.totalDeploymentTime())
@@ -110,7 +114,7 @@
 
     public static final class Builder {
 
-        MonitoringUnit unit = DEF_UNIT;
+        MonitoringUnit unit = DEF_TIME_UNIT;
         long deployCommandParsingTime;
         long deployCommandLaunchingTime;
         long autoScaleTime;
@@ -179,6 +183,7 @@
                 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 5296d57..fcd0aab 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
@@ -24,6 +24,12 @@
 public interface CpuStatistics {
 
     /**
+     * Minimum and maximum CPU load values.
+     */
+    static final float MIN_CPU_LOAD = (float) 0.0;
+    static final float MAX_CPU_LOAD = (float) 1.0;
+
+    /**
      * Returns the ID of a CPU core.
      *
      * @return CPU core identifier
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/stats/MemoryStatistics.java b/drivers/server/src/main/java/org/onosproject/drivers/server/stats/MemoryStatistics.java
new file mode 100644
index 0000000..4ff34e2
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/stats/MemoryStatistics.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020-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;
+
+/**
+ * Main memory statistics API.
+ */
+public interface MemoryStatistics {
+
+    /**
+     * Returns the unit of main memory statistics.
+     *
+     * @return main memory statistics' unit
+     */
+    MonitoringUnit unit();
+
+    /**
+     * Returns the amount of main memory being used.
+     *
+     * @return used main memory
+     */
+    long used();
+
+    /**
+     * Returns the amount of main memory being free.
+     *
+     * @return free main memory
+     */
+    long free();
+
+    /**
+     * Returns the total amount of main memory.
+     *
+     * @return total main memory
+     */
+    long total();
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/stats/MonitoringStatistics.java b/drivers/server/src/main/java/org/onosproject/drivers/server/stats/MonitoringStatistics.java
index c713d01..e41fe11 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/stats/MonitoringStatistics.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/stats/MonitoringStatistics.java
@@ -51,6 +51,13 @@
     CpuStatistics cpuStatistics(int cpuId);
 
     /**
+     * Returns main memory statistics of a server device.
+     *
+     * @return main memory statistics
+     */
+    MemoryStatistics memoryStatistics();
+
+    /**
      * Returns the NIC statistics of a server device.
      * Includes the statistics of all NICs.
      *
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
index 33d8763..b55ce70 100644
--- 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
@@ -13,11 +13,15 @@
  * 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;
 
+import static org.onosproject.drivers.server.Constants.MSG_CONVERSION_TO_BITS;
+import static org.onosproject.drivers.server.Constants.MSG_CONVERSION_TO_BYTES;
+
 /**
  * Representation of a monitoring unit.
  */
@@ -129,6 +133,155 @@
 
     };
 
+    /**
+     * Capacity-related monitoring units.
+     */
+    public enum CapacityUnit implements MonitoringUnit {
+
+        BITS("Bits"),
+        KILOBITS("kBits"),
+        MEGABITS("MBits"),
+        GIGABITS("GBits"),
+        BYTES("Bytes"),
+        KILOBYTES("kBytes"),
+        MEGABYTES("MBytes"),
+        GIGABYTES("GBytes");
+
+        private String capacityUnit;
+
+        // Statically maps capacity monitoring units to enum types
+        private static final Map<String, MonitoringUnit> MAP =
+            new HashMap<String, MonitoringUnit>();
+        static {
+            for (CapacityUnit cu : CapacityUnit.values()) {
+                MAP.put(cu.toString().toLowerCase(), (MonitoringUnit) cu);
+            }
+        }
+
+        private CapacityUnit(String capacityUnit) {
+            this.capacityUnit = capacityUnit;
+        }
+
+        public static MonitoringUnit getByName(String cu) {
+            cu = cu.toLowerCase();
+            return MAP.get(cu);
+        }
+
+        public static float toBits(float value, CapacityUnit fromUnit) {
+            if (value == 0) {
+                return value;
+            }
+
+            if (fromUnit == BITS) {
+                return value;
+            } else if (fromUnit == KILOBITS) {
+                return (value * 1000);
+            } else if (fromUnit == MEGABITS) {
+                return (value * 1000000);
+            } else if (fromUnit == GIGABITS) {
+                return (value * 1000000000);
+            } else if (fromUnit == BYTES) {
+                return value * 8;
+            } else if (fromUnit == KILOBYTES) {
+                return (value * 1000) * 8;
+            } else if (fromUnit == MEGABYTES) {
+                return (value * 1000000) * 8;
+            } else if (fromUnit == GIGABYTES) {
+                return (value * 1000000000) * 8;
+            }
+
+            throw new IllegalArgumentException(MSG_CONVERSION_TO_BITS);
+        }
+
+        public static float toKiloBits(float value, CapacityUnit fromUnit) {
+            return toBits(value, fromUnit) / 1000;
+        }
+
+        public static float toMegaBits(float value, CapacityUnit fromUnit) {
+            return toBits(value, fromUnit) / 1000000;
+        }
+
+        public static float toGigaBits(float value, CapacityUnit fromUnit) {
+            return toBits(value, fromUnit) / 1000000000;
+        }
+
+        public static float toBytes(float value, CapacityUnit fromUnit) {
+            if (value == 0) {
+                return value;
+            }
+
+            if (fromUnit == BITS) {
+                return value / 8;
+            } else if (fromUnit == KILOBITS) {
+                return (value * 1000) / 8;
+            } else if (fromUnit == MEGABITS) {
+                return (value * 1000000) / 8;
+            } else if (fromUnit == GIGABITS) {
+                return (value * 1000000000) / 8;
+            } else if (fromUnit == BYTES) {
+                return value;
+            } else if (fromUnit == KILOBYTES) {
+                return value * 1000;
+            } else if (fromUnit == MEGABYTES) {
+                return value * 1000000;
+            } else if (fromUnit == GIGABYTES) {
+                return value * 1000000000;
+            }
+
+            throw new IllegalArgumentException(MSG_CONVERSION_TO_BYTES);
+        }
+
+        public static float toKiloBytes(float value, CapacityUnit fromUnit) {
+            return toBytes(value, fromUnit) / 1000;
+        }
+
+        public static float toMegaBytes(float value, CapacityUnit fromUnit) {
+            return toBytes(value, fromUnit) / 1000000;
+        }
+
+        public static float toGigaBytes(float value, CapacityUnit fromUnit) {
+            return toBytes(value, fromUnit) / 1000000000;
+        }
+
+        @Override
+        public String toString() {
+            return this.capacityUnit;
+        }
+
+    };
+
+    /**
+     * Percentage-related monitoring unit.
+     */
+    public enum PercentageUnit implements MonitoringUnit {
+
+        PERCENTAGE("percentage");
+
+        private String percentageUnit;
+
+        // Statically maps percentage monitoring units to enum types
+        private static final Map<String, MonitoringUnit> MAP =
+            new HashMap<String, MonitoringUnit>();
+        static {
+            MAP.put(PERCENTAGE.toString().toLowerCase(), (MonitoringUnit) PERCENTAGE);
+        }
+
+        private PercentageUnit(String percentageUnit) {
+            this.percentageUnit = percentageUnit;
+        }
+
+        public static MonitoringUnit getByName(String pu) {
+            pu = pu.toLowerCase();
+            return MAP.get(pu);
+        }
+
+        @Override
+        public String toString() {
+            return this.percentageUnit;
+        }
+
+    };
+
     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 23215ce..d45d065 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
@@ -29,32 +29,32 @@
     MonitoringUnit unit();
 
     /**
-     * Time (ns) to parse the controller's deployment instruction.
+     * Time (default is ns) to parse the controller's deployment instruction.
      *
-     * @return time in nanoseconds to parse a 'deploy' command
+     * @return time to parse a 'deploy' command
      */
     long deployCommandParsingTime();
 
     /**
-     * Time (ns) to launch a slave process in the dataplane.
+     * Time (default is ns) to launch a slave process in the dataplane.
      *
-     * @return time in nanoseconds to launch a 'deploy' command
+     * @return time to launch a 'deploy' command
      */
     long deployCommandLaunchingTime();
 
     /**
-     * Time (ns) to parse + launch the controller's deployment instruction.
+     * Time (default is ns) to parse + launch the controller's deployment instruction.
      * This is the sum of the above two timers.
      *
-     * @return time in nanoseconds to parse + launch a 'deploy' command
+     * @return time to parse + launch a 'deploy' command
      */
     long totalDeploymentTime();
 
     /**
-     * Time (ns) to perform a local reconfiguration.
+     * Time (default is ns) to perform a local reconfiguration.
      * (i.e., the agent auto-scales the number of CPUs).
      *
-     * @return time in nanoseconds to auto scale
+     * @return time to auto scale
      */
     long autoScaleTime();
 
diff --git a/drivers/server/src/main/resources/app/view/memory/memory.css b/drivers/server/src/main/resources/app/view/memory/memory.css
new file mode 100644
index 0000000..243f3e5
--- /dev/null
+++ b/drivers/server/src/main/resources/app/view/memory/memory.css
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020-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.
+ */
+
+/*
+ ONOS GUI -- Memory UI -- CSS file
+ */
+
+#ov-memory {
+    padding: 20px;
+    position: relative;
+}
+
+.light #ov-memory {
+    color: navy;
+}
+.dark #ov-memory {
+    color: #88f;
+}
+
+#ov-memory .button-panel {
+    margin: 10px;
+    width: 200px;
+}
+
+.light #ov-memory .button-panel {
+    background-color: #ccf;
+}
+.dark #ov-memory .button-panel {
+    background-color: #444;
+}
+
+#ov-memory #chart-loader {
+    position: absolute;
+    width: 200px;
+    height: 50px;
+    margin-left: -100px;
+    margin-top: -25px;
+    z-index: 900;
+    top: 50%;
+    text-align: center;
+    left: 50%;
+    font-size: 25px;
+    font-weight: bold;
+    color: #ccc;
+}
\ No newline at end of file
diff --git a/drivers/server/src/main/resources/app/view/memory/memory.html b/drivers/server/src/main/resources/app/view/memory/memory.html
new file mode 100644
index 0000000..398a9fa
--- /dev/null
+++ b/drivers/server/src/main/resources/app/view/memory/memory.html
@@ -0,0 +1,27 @@
+<!-- partial HTML -->
+<div id="ov-memory">
+    <div id="chart-loader" ng-show="!devId && showLoader">
+        No Servers
+    </div>
+    <div ng-show="!devId">
+        <canvas id="bar" class="chart chart-bar" chart-data="data"
+                chart-labels="labels" chart-legend="true" chart-click="onClick"
+                chart-series="series" chart-options="options" height="100%">
+        </canvas>
+    </div>
+    <div ng-show="devId">
+        <h2>
+            Chart for Device {{devId || "(No device selected)"}}
+        </h2>
+        <div class="ctrl-btns">
+            <select ng-options="deviceId as deviceId for deviceId in deviceIds"
+                    ng-model="selectedItem" ng-change="onChange(selectedItem)">
+                <option value="">-- select a device --</option>
+            </select>
+        </div>
+        <canvas id="line" class="chart chart-line" chart-data="data"
+                chart-labels="labels" chart-legend="true"
+                chart-series="series" chart-options="options" height="100%">
+        </canvas>
+    </div>
+</div>
diff --git a/drivers/server/src/main/resources/app/view/memory/memory.js b/drivers/server/src/main/resources/app/view/memory/memory.js
new file mode 100644
index 0000000..37c2eee
--- /dev/null
+++ b/drivers/server/src/main/resources/app/view/memory/memory.js
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2020-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.
+ */
+
+/*
+ ONOS GUI -- Memory View Module
+ */
+(function () {
+    'use strict';
+
+    // injected references
+    var $log, $scope, $location, ks, fs, cbs, ns;
+
+    var hasDeviceId;
+    var barsNb = 3;
+
+    var labels = new Array(1);
+    var data = new Array(barsNb);
+    for (var i = 0; i < barsNb; i++) {
+        data[i] = new Array(1);
+        data[i][0] = 0;
+    }
+
+    angular.module('ovMemory', ["chart.js"])
+        .controller('OvMemoryCtrl',
+        ['$log', '$scope', '$location', 'FnService', 'ChartBuilderService', 'NavService',
+
+        function (_$log_, _$scope_, _$location_, _fs_, _cbs_, _ns_) {
+            var params;
+            $log = _$log_;
+            $scope = _$scope_;
+            $location = _$location_;
+            fs = _fs_;
+            cbs = _cbs_;
+            ns = _ns_;
+
+            params = $location.search();
+
+            if (params.hasOwnProperty('devId')) {
+                $scope.devId = params['devId'];
+                hasDeviceId = true;
+            } else {
+                hasDeviceId = false;
+            }
+
+            cbs.buildChart({
+                scope: $scope,
+                tag: 'memory',
+                query: params
+            });
+
+            $scope.$watch('chartData', function () {
+                if (!fs.isEmptyObject($scope.chartData)) {
+                    $scope.showLoader = false;
+                    var length = $scope.chartData.length;
+                    labels = new Array(length);
+                    for (var i = 0; i < barsNb; i++) {
+                        data[i] = new Array(length);
+                    }
+
+                    $scope.chartData.forEach(
+                        function (cm, idx) {
+                            data[0][idx]  = cm.memory_used;
+                            data[1][idx]  = cm.memory_free;
+                            data[2][idx]  = cm.memory_total;
+
+                            labels[idx] = cm.label;
+                        }
+                    );
+                }
+
+                $scope.labels = labels;
+                $scope.data = data;
+
+                $scope.options = {
+                    scales: {
+                        yAxes: [{
+                            type: 'linear',
+                            position: 'left',
+                            id: 'y-axis-memory',
+                            ticks: {
+                                beginAtZero: true,
+                                fontSize: 28,
+                            },
+                            scaleLabel: {
+                                display: true,
+                                labelString: 'Memory Utilization (GBytes)',
+                                fontSize: 28,
+                            }
+                        }],
+                        xAxes: [{
+                            id: 'x-axis-servers',
+                            ticks: {
+                                fontSize: 28,
+                            },
+                            scaleLabel: {
+                                display: false,
+                                fontSize: 28,
+                            }
+                        }]
+                    }
+                };
+
+                $scope.onClick = function (points, evt) {
+                    var label = labels[points[0]._index];
+                    if (label) {
+                        ns.navTo('memory', { devId: label });
+                        $log.log(label);
+                    }
+                };
+
+                if (!fs.isEmptyObject($scope.annots)) {
+                    $scope.deviceIds = JSON.parse($scope.annots.deviceIds);
+                }
+
+                $scope.onChange = function (deviceId) {
+                    ns.navTo('memory', { devId: deviceId });
+                };
+            });
+
+            $scope.series = new Array(barsNb);
+            $scope.series[0] = 'Memory - Used';
+            $scope.series[1] = 'Memory - Free';
+            $scope.series[2] = 'Memory - Total';
+
+            $scope.labels = labels;
+            $scope.data = data;
+
+            $scope.showLoader = true;
+
+            $log.log('OvMemoryCtrl has been created');
+        }]);
+
+}());
diff --git a/drivers/server/src/main/resources/gui/css.html b/drivers/server/src/main/resources/gui/css.html
index 8eca7d4..b1cf4e5 100644
--- a/drivers/server/src/main/resources/gui/css.html
+++ b/drivers/server/src/main/resources/gui/css.html
@@ -1,3 +1,4 @@
 <link rel="stylesheet" href="app/view/cpu/cpu.css">
+<link rel="stylesheet" href="app/view/memory/memory.css">
 <link rel="stylesheet" href="app/view/latency/latency.css">
 <link rel="stylesheet" href="app/view/throughput/throughput.css">
diff --git a/drivers/server/src/main/resources/gui/js.html b/drivers/server/src/main/resources/gui/js.html
index 99a84e8..94ec76b 100644
--- a/drivers/server/src/main/resources/gui/js.html
+++ b/drivers/server/src/main/resources/gui/js.html
@@ -1,3 +1,4 @@
 <script src="app/view/cpu/cpu.js"></script>
+<script src="app/view/memory/memory.js"></script>
 <script src="app/view/latency/latency.js"></script>
 <script src="app/view/throughput/throughput.js"></script>
diff --git a/drivers/server/src/main/resources/server-drivers.xml b/drivers/server/src/main/resources/server-drivers.xml
index 11c75d1..a8a07cd 100644
--- a/drivers/server/src/main/resources/server-drivers.xml
+++ b/drivers/server/src/main/resources/server-drivers.xml
@@ -15,7 +15,13 @@
   ~ limitations under the License.
   -->
 <drivers>
-    <driver name="restServer" extends="default" manufacturer="GenuineIntel" hwVersion="Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz" swVersion="Click 2.1">
+    <driver name="rest-server" manufacturer="Unknown" hwVersion="Unknown" swVersion="Unknown" extends="default">
+        <behaviour api="org.onosproject.net.behaviour.BasicSystemOperations"
+                   impl="org.onosproject.drivers.server.ServerBasicSystemOperations"/>
+
+        <behaviour api="org.onosproject.net.device.DeviceHandshaker"
+                   impl="org.onosproject.drivers.server.ServerHandshaker"/>
+
         <behaviour api="org.onosproject.net.behaviour.DevicesDiscovery"
                    impl="org.onosproject.drivers.server.ServerDevicesDiscovery"/>
 
@@ -28,16 +34,38 @@
         <behaviour api="org.onosproject.net.device.PortStatisticsDiscovery"
                    impl="org.onosproject.drivers.server.ServerDevicesDiscovery"/>
 
+        <behaviour api="org.onosproject.net.behaviour.PortAdmin"
+                   impl="org.onosproject.drivers.server.ServerPortAdmin"/>
+
+        <behaviour api="org.onosproject.net.behaviour.InterfaceConfig"
+                   impl="org.onosproject.drivers.server.ServerInterfaceConfig"/>
+
         <behaviour api="org.onosproject.drivers.server.behavior.CpuStatisticsDiscovery"
                    impl="org.onosproject.drivers.server.ServerDevicesDiscovery"/>
 
         <behaviour api="org.onosproject.drivers.server.behavior.MonitoringStatisticsDiscovery"
                    impl="org.onosproject.drivers.server.ServerDevicesDiscovery"/>
 
+        <behaviour api="org.onosproject.net.behaviour.DeviceSystemStatisticsQuery"
+                   impl="org.onosproject.drivers.server.ServerDevicesDiscovery"/>
+
         <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
                    impl="org.onosproject.drivers.server.FlowRuleProgrammableServerImpl"/>
+
+        <behaviour api="org.onosproject.net.behaviour.TableStatisticsDiscovery"
+                   impl="org.onosproject.drivers.server.ServerTableStatisticsDiscovery"/>
+
+        <behaviour api="org.onosproject.net.behaviour.QueueConfigBehaviour"
+                   impl="org.onosproject.drivers.server.ServerQueueConfig"/>
+
         <property name="ruleDeleteBatchSize">500</property>
     </driver>
+
+    <driver name="rest-server-intel" manufacturer="GenuineIntel" hwVersion="Unknown" swVersion="Click 2.1" extends="rest-server">
+    </driver>
+
+    <driver name="rest-server-amd" manufacturer="AuthenticAMD" hwVersion="Unknown" swVersion="Click 2.1" extends="rest-server">
+    </driver>
 </drivers>
 
 
diff --git a/drivers/server/src/test/java/org/onosproject/drivers/server/RestSBControllerMock.java b/drivers/server/src/test/java/org/onosproject/drivers/server/RestSBControllerMock.java
index 265432b..6e8df34 100644
--- a/drivers/server/src/test/java/org/onosproject/drivers/server/RestSBControllerMock.java
+++ b/drivers/server/src/test/java/org/onosproject/drivers/server/RestSBControllerMock.java
@@ -40,6 +40,10 @@
 
 import static org.hamcrest.Matchers.notNullValue;
 import static org.junit.Assert.assertThat;
+import static org.onosproject.drivers.server.Constants.PARAM_CTRL;
+import static org.onosproject.drivers.server.Constants.PARAM_CTRL_IP;
+import static org.onosproject.drivers.server.Constants.PARAM_CTRL_PORT;
+import static org.onosproject.drivers.server.Constants.PARAM_CTRL_TYPE;
 
 /**
  * Test class for REST SB controller.
@@ -57,14 +61,6 @@
     private RestSBDevice restDevice1;
     private static List<ControllerInfo> controllers;
 
-    /**
-     * Parameters to be exchanged with the server's agent.
-     */
-    private static final String PARAM_CTRL      = "controllers";
-    private static final String PARAM_CTRL_IP   = "ip";
-    private static final String PARAM_CTRL_PORT = "port";
-    private static final String PARAM_CTRL_TYPE = "type";
-
     public RestSBControllerMock() {
         restDeviceId1 = TestConfig.REST_DEV_ID1;
         assertThat(restDeviceId1, notNullValue());
@@ -190,4 +186,5 @@
     public int cancelServerSentEvents(DeviceId deviceId) {
         return 200;
     }
+
 }
\ No newline at end of file
diff --git a/drivers/server/src/test/java/org/onosproject/drivers/server/ServerControllerConfigTest.java b/drivers/server/src/test/java/org/onosproject/drivers/server/ServerControllerConfigTest.java
index add6e03..54e6196 100644
--- a/drivers/server/src/test/java/org/onosproject/drivers/server/ServerControllerConfigTest.java
+++ b/drivers/server/src/test/java/org/onosproject/drivers/server/ServerControllerConfigTest.java
@@ -44,15 +44,7 @@
  */
 public class ServerControllerConfigTest {
 
-    // Device information used during the tests
-    private static final String REST_SCHEME = "rest";
-    private static final String REST_DEV_TEST_IP_1 = "10.0.0.1";
-    private static final int    REST_TEST_PORT = 80;
-
     // Controller information used during the tests
-    private static final String REST_CTRL_TEST_IP_1 = "10.0.0.253";
-    private static final String REST_CTRL_TEST_IP_2 = "10.0.0.254";
-    private static final String REST_CTRL_TEST_TYPE = "tcp";
     private static List<ControllerInfo> controllers;
 
     // Device used during the tests