Add resource name into MetricsDatabase, handle exception gracefully

Change-Id: Icf19965a0fcbfd9696c42b12c32441fd3b161734
diff --git a/apps/cpman/api/src/main/java/org/onosproject/cpman/MetricsDatabase.java b/apps/cpman/api/src/main/java/org/onosproject/cpman/MetricsDatabase.java
index 54167b8..ccdc6b8 100644
--- a/apps/cpman/api/src/main/java/org/onosproject/cpman/MetricsDatabase.java
+++ b/apps/cpman/api/src/main/java/org/onosproject/cpman/MetricsDatabase.java
@@ -30,6 +30,13 @@
     String metricName();
 
     /**
+     * Returns the resource name of this database.
+     *
+     * @return resource name
+     */
+    String resourceName();
+
+    /**
      * Update metric value by specifying metric type.
      *
      * @param metricType    metric type (e.g., load, usage, etc.)
@@ -138,6 +145,14 @@
         Builder withMetricName(String metricName);
 
         /**
+         * Sets the resource name.
+         *
+         * @param resourceName resource name
+         * @return builder object
+         */
+        Builder withResourceName(String resourceName);
+
+        /**
          * Add a new metric to be monitored.
          *
          * @param metricType control metric type
diff --git a/apps/cpman/app/src/main/java/org/onosproject/cpman/impl/ControlPlaneMonitor.java b/apps/cpman/app/src/main/java/org/onosproject/cpman/impl/ControlPlaneMonitor.java
index 3452255..fa971b6 100644
--- a/apps/cpman/app/src/main/java/org/onosproject/cpman/impl/ControlPlaneMonitor.java
+++ b/apps/cpman/app/src/main/java/org/onosproject/cpman/impl/ControlPlaneMonitor.java
@@ -77,6 +77,8 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ClusterCommunicationService communicationService;
 
+    private static final String DEFAULT_RESOURCE = "default";
+
     private static final Set RESOURCE_TYPE_SET =
             ImmutableSet.of(Type.CONTROL_MESSAGE, Type.DISK, Type.NETWORK);
 
@@ -96,6 +98,8 @@
 
     private static final String METRIC_TYPE_NULL = "Control metric type cannot be null";
 
+    Set<Map<ControlMetricType, Double>> debugSets = Sets.newHashSet();
+
     private static final Serializer SERIALIZER = Serializer
             .using(new KryoNamespace.Builder()
                     .register(KryoNamespaces.API)
@@ -107,8 +111,8 @@
 
     @Activate
     public void activate() {
-        cpuMetrics = genMDbBuilder(Type.CPU, CPU_METRICS);
-        memoryMetrics = genMDbBuilder(Type.MEMORY, MEMORY_METRICS);
+        cpuMetrics = genMDbBuilder(DEFAULT_RESOURCE, Type.CPU, CPU_METRICS);
+        memoryMetrics = genMDbBuilder(DEFAULT_RESOURCE, Type.MEMORY, MEMORY_METRICS);
         controlMessageMap = Maps.newConcurrentMap();
         diskMetricsMap = Maps.newConcurrentMap();
         networkMetricsMap = Maps.newConcurrentMap();
@@ -170,7 +174,7 @@
                 if (ctrlMsgBuf.get(deviceId.get()).keySet()
                         .containsAll(CONTROL_MESSAGE_METRICS)) {
                     updateControlMessages(ctrlMsgBuf.get(deviceId.get()), deviceId.get());
-                    ctrlMsgBuf.get(deviceId.get()).clear();
+                    ctrlMsgBuf.get(deviceId.get());
                 }
             }
         } else {
@@ -304,31 +308,34 @@
         return ImmutableSet.of();
     }
 
-    private MetricsDatabase genMDbBuilder(Type resourceType,
+    private MetricsDatabase genMDbBuilder(String resourceName,
+                                          Type resourceType,
                                           Set<ControlMetricType> metricTypes) {
         MetricsDatabase.Builder builder = new DefaultMetricsDatabase.Builder();
         builder.withMetricName(resourceType.toString());
+        builder.withResourceName(resourceName);
         metricTypes.forEach(type -> builder.addMetricType(type.toString()));
         return builder.build();
     }
 
     private void updateNetworkMetrics(Map<ControlMetricType, Double> metricMap,
                                       String resName) {
-        networkMetricsMap.putIfAbsent(resName,
-                genMDbBuilder(Type.NETWORK, NETWORK_METRICS));
+        networkMetricsMap.putIfAbsent(resName, genMDbBuilder(resName,
+                Type.NETWORK, NETWORK_METRICS));
         networkMetricsMap.get(resName).updateMetrics(convertMap(metricMap));
     }
 
     private void updateDiskMetrics(Map<ControlMetricType, Double> metricMap,
                                    String resName) {
-        diskMetricsMap.putIfAbsent(resName, genMDbBuilder(Type.DISK, DISK_METRICS));
+        diskMetricsMap.putIfAbsent(resName, genMDbBuilder(resName,
+                Type.DISK, DISK_METRICS));
         diskMetricsMap.get(resName).updateMetrics(convertMap(metricMap));
     }
 
     private void updateControlMessages(Map<ControlMetricType, Double> metricMap,
                                        DeviceId devId) {
-        controlMessageMap.putIfAbsent(devId,
-                genMDbBuilder(Type.CONTROL_MESSAGE, CONTROL_MESSAGE_METRICS));
+        controlMessageMap.putIfAbsent(devId, genMDbBuilder(devId.toString(),
+                Type.CONTROL_MESSAGE, CONTROL_MESSAGE_METRICS));
         controlMessageMap.get(devId).updateMetrics(convertMap(metricMap));
     }
 
diff --git a/apps/cpman/app/src/main/java/org/onosproject/cpman/impl/DefaultMetricsDatabase.java b/apps/cpman/app/src/main/java/org/onosproject/cpman/impl/DefaultMetricsDatabase.java
index 31a19da..fd88072 100644
--- a/apps/cpman/app/src/main/java/org/onosproject/cpman/impl/DefaultMetricsDatabase.java
+++ b/apps/cpman/app/src/main/java/org/onosproject/cpman/impl/DefaultMetricsDatabase.java
@@ -26,6 +26,8 @@
 import org.rrd4j.core.RrdDb;
 import org.rrd4j.core.RrdDef;
 import org.rrd4j.core.Sample;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -43,7 +45,10 @@
  * An implementation of control plane metrics back-end database.
  */
 public final class DefaultMetricsDatabase implements MetricsDatabase {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
     private String metricName;
+    private String resourceName;
     private RrdDb rrdDb;
     private Sample sample;
     private static final long SECONDS_OF_DAY = 60L * 60L * 24L;
@@ -60,8 +65,9 @@
      * @param metricName  metric name
      * @param rrdDb       round robin database
      */
-    private DefaultMetricsDatabase(String metricName, RrdDb rrdDb) {
+    private DefaultMetricsDatabase(String metricName, String resourceName, RrdDb rrdDb) {
         this.metricName = metricName;
+        this.resourceName = resourceName;
         this.rrdDb = rrdDb;
     }
 
@@ -71,6 +77,11 @@
     }
 
     @Override
+    public String resourceName() {
+        return this.resourceName;
+    }
+
+    @Override
     public void updateMetric(String metricType, double value) {
         updateMetric(metricType, value, System.currentTimeMillis() / 1000L);
     }
@@ -83,7 +94,7 @@
             sample.setValue(metricType, value);
             sample.update();
         } catch (IOException e) {
-            e.printStackTrace();
+            log.error("Failed to update metric value due to {}", e.getMessage());
         }
     }
 
@@ -106,7 +117,7 @@
             });
             sample.update();
         } catch (IOException e) {
-            e.printStackTrace();
+            log.error("Failed to update metric values due to {}", e.getMessage());
         }
     }
 
@@ -116,9 +127,9 @@
             checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
             return rrdDb.getDatasource(metricType).getLastValue();
         } catch (IOException e) {
-            e.printStackTrace();
+            log.error("Failed to obtain metric value due to {}", e.getMessage());
+            return 0D;
         }
-        return 0D;
     }
 
     @Override
@@ -130,11 +141,14 @@
             if (checkTimeRange(startTime, endTime)) {
                 FetchRequest fr = rrdDb.createFetchRequest(CONSOL_FUNCTION, startTime, endTime);
                 return arrangeDataPoints(fr.fetchData().getValues(metricType));
+            } else {
+                log.warn("Data projection is out-of-range");
+                return new double[0];
             }
         } catch (IOException e) {
-            e.printStackTrace();
+            log.error("Failed to obtain metric values due to {}", e.getMessage());
+            return new double[0];
         }
-        return new double[0];
     }
 
     @Override
@@ -145,9 +159,9 @@
             long startTime = endTime - SECONDS_OF_DAY + 1;
             return minMetric(metricType, startTime, endTime);
         } catch (IOException e) {
-            e.printStackTrace();
+            log.error("Failed to obtain metric value due to {}", e.getMessage());
+            return 0D;
         }
-        return 0D;
     }
 
     @Override
@@ -158,9 +172,9 @@
             long startTime = endTime - SECONDS_OF_DAY;
             return maxMetric(metricType, startTime, endTime);
         } catch (IOException e) {
-            e.printStackTrace();
+            log.error("Failed to obtain metric value due to {}", e.getMessage());
+            return 0D;
         }
-        return 0D;
     }
 
     @Override
@@ -171,9 +185,9 @@
             long startTime = endTime - SECONDS_OF_DAY;
             return metrics(metricType, startTime, endTime);
         } catch (IOException e) {
-            e.printStackTrace();
+            log.error("Failed to obtain metric values due to {}", e.getMessage());
+            return new double[0];
         }
-        return new double[0];
     }
 
     @Override
@@ -183,22 +197,25 @@
             if (checkTimeRange(startTime, endTime)) {
                 FetchRequest fr = rrdDb.createFetchRequest(CONSOL_FUNCTION, startTime, endTime);
                 return arrangeDataPoints(fr.fetchData().getValues(metricType));
+            } else {
+                log.warn("Data projection is out-of-range");
+                return new double[0];
             }
         } catch (IOException e) {
-            e.printStackTrace();
+            log.error("Failed to obtain metric values due to {}", e.getMessage());
+            return new double[0];
         }
-        return new double[0];
     }
 
     @Override
     public long lastUpdate(String metricType) {
         try {
             checkArgument(rrdDb.containsDs(metricType), NON_EXIST_METRIC);
-            rrdDb.getLastUpdateTime();
+            return rrdDb.getLastUpdateTime();
         } catch (IOException e) {
-            e.printStackTrace();
+            log.error("Failed to obtain last update time due to {}", e.getMessage());
+            return 0L;
         }
-        return 0L;
     }
 
     // try to check whether projected time range is within a day
@@ -242,12 +259,15 @@
         private static final int STEP_VALUE = 1;
         private static final int ROW_VALUE = 60 * 24;
         private static final String METRIC_NAME_MSG = "Must specify a metric name.";
+        private static final String RESOURCE_NAME_MSG = "Must specify a resource name.";
         private static final String METRIC_TYPE_MSG = "Must supply at least a metric type.";
+        private static final String SPLITTER = "_";
 
         private RrdDb rrdDb;
         private RrdDef rrdDef;
         private List<DsDef> dsDefs;
         private String metricName;
+        private String resourceName;
 
         public Builder() {
             // initialize data source definition list
@@ -255,11 +275,14 @@
         }
 
         @Override
-        public Builder withMetricName(String metricName) {
-            this.metricName = metricName;
+        public Builder withMetricName(String metric) {
+            this.metricName = metric;
+            return this;
+        }
 
-            // define the resolution of monitored metrics
-            rrdDef = new RrdDef(DB_PATH + "_" + metricName, RESOLUTION_IN_SECOND);
+        @Override
+        public MetricsDatabase.Builder withResourceName(String resource) {
+            this.resourceName = resource;
             return this;
         }
 
@@ -272,8 +295,13 @@
         @Override
         public MetricsDatabase build() {
             checkNotNull(metricName, METRIC_NAME_MSG);
+            checkNotNull(resourceName, RESOURCE_NAME_MSG);
             checkArgument(dsDefs.size() != 0, METRIC_TYPE_MSG);
 
+            // define the resolution of monitored metrics
+            rrdDef = new RrdDef(DB_PATH + SPLITTER + metricName +
+                     SPLITTER + resourceName, RESOLUTION_IN_SECOND);
+
             try {
                 DsDef[] dsDefArray = new DsDef[dsDefs.size()];
                 IntStream.range(0, dsDefs.size()).forEach(i -> dsDefArray[i] = dsDefs.get(i));
@@ -292,7 +320,7 @@
                 e.printStackTrace();
             }
 
-            return new DefaultMetricsDatabase(metricName, rrdDb);
+            return new DefaultMetricsDatabase(metricName, resourceName, rrdDb);
         }
 
         private DsDef defineSchema(String metricType) {
diff --git a/apps/cpman/app/src/test/java/org/onosproject/cpman/impl/MetricsDatabaseTest.java b/apps/cpman/app/src/test/java/org/onosproject/cpman/impl/MetricsDatabaseTest.java
index 713c7d8..75a2482 100644
--- a/apps/cpman/app/src/test/java/org/onosproject/cpman/impl/MetricsDatabaseTest.java
+++ b/apps/cpman/app/src/test/java/org/onosproject/cpman/impl/MetricsDatabaseTest.java
@@ -15,12 +15,18 @@
  */
 package org.onosproject.cpman.impl;
 
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
 import org.junit.Before;
 import org.junit.Test;
+import org.onosproject.cpman.ControlMetricType;
+import org.onosproject.cpman.ControlResource;
 import org.onosproject.cpman.MetricsDatabase;
+import org.onosproject.net.DeviceId;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 import static org.hamcrest.Matchers.is;
@@ -34,9 +40,11 @@
     private MetricsDatabase mdb;
     private static final String CPU_METRIC = "cpu";
     private static final String CPU_LOAD = "load";
+    private static final String DEFAULT_RES = "resource";
     private static final String MEMORY_METRIC = "memory";
     private static final String MEMORY_FREE_PERC = "freePerc";
     private static final String MEMORY_USED_PERC = "usedPerc";
+    private Map<DeviceId, MetricsDatabase> devMetricsMap;
 
     /**
      * Initializes metrics database instance.
@@ -45,6 +53,7 @@
     public void setUp() {
         mdb = new DefaultMetricsDatabase.Builder()
                 .withMetricName(CPU_METRIC)
+                .withResourceName(DEFAULT_RES)
                 .addMetricType(CPU_LOAD)
                 .build();
     }
@@ -114,6 +123,7 @@
     public void testMultipleMetrics() {
         MetricsDatabase multiMdb = new DefaultMetricsDatabase.Builder()
                         .withMetricName(MEMORY_METRIC)
+                        .withResourceName(DEFAULT_RES)
                         .addMetricType(MEMORY_FREE_PERC)
                         .addMetricType(MEMORY_USED_PERC)
                         .build();
@@ -126,4 +136,51 @@
         assertThat(30D, is(multiMdb.recentMetric(MEMORY_FREE_PERC)));
         assertThat(70D, is(multiMdb.recentMetric(MEMORY_USED_PERC)));
     }
+
+    /**
+     * Tests device metrics map update and query.
+     */
+    @Test
+    public void testDeviceMetricsMap() {
+        ControlResource.Type type = ControlResource.Type.CONTROL_MESSAGE;
+        DeviceId devId1 = DeviceId.deviceId("of:0000000000000101");
+        DeviceId devId2 = DeviceId.deviceId("of:0000000000000102");
+
+        devMetricsMap = Maps.newHashMap();
+
+        Set<DeviceId> devices = ImmutableSet.of(devId1, devId2);
+        devices.forEach(dev ->
+            devMetricsMap.putIfAbsent(dev,
+                    genMDbBuilder(type, ControlResource.CONTROL_MESSAGE_METRICS)
+                            .withResourceName(dev.toString())
+                            .build()));
+
+        Map<String, Double> metrics1 = new HashMap<>();
+        ControlResource.CONTROL_MESSAGE_METRICS.forEach(msgType ->
+                metrics1.putIfAbsent(msgType.toString(), 10D));
+
+        Map<String, Double> metrics2 = new HashMap<>();
+        ControlResource.CONTROL_MESSAGE_METRICS.forEach(msgType ->
+                metrics2.putIfAbsent(msgType.toString(), 20D));
+
+
+        devMetricsMap.get(devId1).updateMetrics(metrics1);
+        devMetricsMap.get(devId2).updateMetrics(metrics2);
+
+        ControlResource.CONTROL_MESSAGE_METRICS.forEach(msgType ->
+                assertThat(10D, is(devMetricsMap.get(devId1).recentMetric(msgType.toString())))
+        );
+
+        ControlResource.CONTROL_MESSAGE_METRICS.forEach(msgType ->
+                assertThat(20D, is(devMetricsMap.get(devId2).recentMetric(msgType.toString())))
+        );
+    }
+
+    private MetricsDatabase.Builder genMDbBuilder(ControlResource.Type resourceType,
+                                          Set<ControlMetricType> metricTypes) {
+        MetricsDatabase.Builder builder = new DefaultMetricsDatabase.Builder();
+        builder.withMetricName(resourceType.toString());
+        metricTypes.forEach(type -> builder.addMetricType(type.toString()));
+        return builder;
+    }
 }
diff --git a/providers/openflow/message/src/main/java/org/onosproject/provider/of/message/impl/OpenFlowControlMessageAggregator.java b/providers/openflow/message/src/main/java/org/onosproject/provider/of/message/impl/OpenFlowControlMessageAggregator.java
index cce9e0d..a0cacc8 100644
--- a/providers/openflow/message/src/main/java/org/onosproject/provider/of/message/impl/OpenFlowControlMessageAggregator.java
+++ b/providers/openflow/message/src/main/java/org/onosproject/provider/of/message/impl/OpenFlowControlMessageAggregator.java
@@ -141,7 +141,7 @@
      * @return count value
      */
     private long getCount(OFType type) {
-        return (long) countMeterMap.get(type).getOneMinuteRate() *
-                EXECUTE_PERIOD_IN_SECOND;
+        return (long) (countMeterMap.get(type).getOneMinuteRate()
+                * EXECUTE_PERIOD_IN_SECOND);
     }
 }