metric_config: add lower bound support

Add the lower bound support to the metric config and fix the parsing
logic to update threshold configs accordingly. Update the collection
test for lower bound.

Change-Id: I6fd3bd8d85974f95ba877eb8d19a77c8acffef7c
Signed-off-by: Jagpal Singh Gill <paligill@gmail.com>
diff --git a/health_metric_config.cpp b/health_metric_config.cpp
index 04ee169..e506f17 100644
--- a/health_metric_config.cpp
+++ b/health_metric_config.cpp
@@ -8,6 +8,7 @@
 #include <cmath>
 #include <fstream>
 #include <unordered_map>
+#include <unordered_set>
 #include <utility>
 
 PHOSPHOR_LOG2_USING;
@@ -21,6 +22,15 @@
 extern json defaultHealthMetricConfig;
 
 // Valid thresholds from config
+static const auto validThresholdTypesWithBound =
+    std::unordered_set<std::string>{"Critical_Lower", "Critical_Upper",
+                                    "Warning_Lower", "Warning_Upper"};
+
+static const auto validThresholdBounds =
+    std::unordered_map<std::string, ThresholdIntf::Bound>{
+        {"Lower", ThresholdIntf::Bound::Lower},
+        {"Upper", ThresholdIntf::Bound::Upper}};
+
 static const auto validThresholdTypes =
     std::unordered_map<std::string, ThresholdIntf::Type>{
         {"Critical", ThresholdIntf::Type::Critical},
@@ -74,7 +84,7 @@
 
     for (auto& [key, value] : thresholds->items())
     {
-        if (!validThresholdTypes.contains(key))
+        if (!validThresholdTypesWithBound.contains(key))
         {
             warning("Invalid ThresholdType: {TYPE}", "TYPE", key);
             continue;
@@ -86,11 +96,15 @@
             throw std::invalid_argument("Invalid threshold value");
         }
 
-        // ThresholdIntf::Bound::Upper is the only use case for
-        // ThresholdIntf::Bound
-        self.thresholds.emplace(std::make_tuple(validThresholdTypes.at(key),
-                                                ThresholdIntf::Bound::Upper),
-                                config);
+        static constexpr auto keyDelimiter = "_";
+        std::string typeStr = key.substr(0, key.find_first_of(keyDelimiter));
+        std::string boundStr = key.substr(key.find_last_of(keyDelimiter) + 1,
+                                          key.length());
+
+        self.thresholds.emplace(
+            std::make_tuple(validThresholdTypes.at(typeStr),
+                            validThresholdBounds.at(boundStr)),
+            config);
     }
 }
 
@@ -183,12 +197,12 @@
         "Frequency": 1,
         "Window_size": 120,
         "Threshold": {
-            "Critical": {
+            "Critical_Upper": {
                 "Value": 90.0,
                 "Log": true,
                 "Target": ""
             },
-            "Warning": {
+            "Warning_Upper": {
                 "Value": 80.0,
                 "Log": false,
                 "Target": ""
@@ -199,12 +213,12 @@
         "Frequency": 1,
         "Window_size": 120,
         "Threshold": {
-            "Critical": {
+            "Critical_Upper": {
                 "Value": 90.0,
                 "Log": true,
                 "Target": ""
             },
-            "Warning": {
+            "Warning_Upper": {
                 "Value": 80.0,
                 "Log": false,
                 "Target": ""
@@ -215,24 +229,46 @@
         "Frequency": 1,
         "Window_size": 120,
         "Threshold": {
-            "Critical": {
+            "Critical_Upper": {
                 "Value": 90.0,
                 "Log": true,
                 "Target": ""
             },
-            "Warning": {
+            "Warning_Upper": {
                 "Value": 80.0,
                 "Log": false,
                 "Target": ""
             }
         }
     },
+    "Memory": {
+        "Frequency": 1,
+        "Window_size": 120,
+        "Threshold": {
+            "Critical_Upper": {
+                "Value": 85.0,
+                "Log": true,
+                "Target": ""
+            }
+        }
+    },
     "Memory_Available": {
         "Frequency": 1,
         "Window_size": 120,
         "Threshold": {
-            "Critical": {
-                "Value": 85.0,
+            "Critical_Lower": {
+                "Value": 15.0,
+                "Log": true,
+                "Target": ""
+            }
+        }
+    },
+    "Memory_Free": {
+        "Frequency": 1,
+        "Window_size": 120,
+        "Threshold": {
+            "Critical_Lower": {
+                "Value": 15.0,
                 "Log": true,
                 "Target": ""
             }
@@ -242,7 +278,7 @@
         "Frequency": 1,
         "Window_size": 120,
         "Threshold": {
-            "Critical": {
+            "Critical_Upper": {
                 "Value": 85.0,
                 "Log": true,
                 "Target": ""
@@ -253,7 +289,7 @@
         "Frequency": 1,
         "Window_size": 120,
         "Threshold": {
-            "Critical": {
+            "Critical_Upper": {
                 "Value": 85.0,
                 "Log": true,
                 "Target": ""
@@ -265,8 +301,8 @@
         "Frequency": 1,
         "Window_size": 120,
         "Threshold": {
-            "Critical": {
-                "Value": 85.0,
+            "Critical_Lower": {
+                "Value": 15.0,
                 "Log": true,
                 "Target": ""
             }
@@ -277,8 +313,8 @@
         "Frequency": 1,
         "Window_size": 120,
         "Threshold": {
-            "Critical": {
-                "Value": 85.0,
+            "Critical_Lower": {
+                "Value": 15.0,
                 "Log": true,
                 "Target": ""
             }