blob: 8320f2505cbe11e2f93ff68ee6f7b282bff33c2c [file] [log] [blame]
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -08001#include "config.h"
2
3#include "health_metric_config.hpp"
4
5#include <nlohmann/json.hpp>
6#include <phosphor-logging/lg2.hpp>
7
8#include <fstream>
9#include <unordered_map>
10#include <utility>
11
12PHOSPHOR_LOG2_USING;
13
14namespace phosphor::health::metric::config
15{
16
17using json = nlohmann::json;
18
19// Default health metric config
20extern json defaultHealthMetricConfig;
21
22// Valid thresholds from config
23static const auto validThresholdTypes =
24 std::unordered_map<std::string, ThresholdIntf::Type>{
25 {"Critical", ThresholdIntf::Type::Critical},
26 {"Warning", ThresholdIntf::Type::Warning}};
27
28// Valid metrics from config
29static const auto validTypes =
30 std::unordered_map<std::string, Type>{{"CPU", Type::cpu},
31 {"Memory", Type::memory},
32 {"Storage", Type::storage},
33 {"Inode", Type::inode}};
34
35// Valid submetrics from config
36static const auto validSubTypes = std::unordered_map<std::string, SubType>{
37 {"CPU", SubType::cpuTotal},
38 {"CPU_User", SubType::cpuUser},
39 {"CPU_Kernel", SubType::cpuKernel},
40 {"Memory", SubType::memoryTotal},
41 {"Memory_Free", SubType::memoryFree},
42 {"Memory_Available", SubType::memoryAvailable},
43 {"Memory_Shared", SubType::memoryShared},
44 {"Memory_Buffered_And_Cached", SubType::memoryBufferedAndCached},
45 {"Storage_RW", SubType::storageReadWrite}};
46
47/** Deserialize a Threshold from JSON. */
48void from_json(const json& j, Threshold& self)
49{
50 self.value = j.value("Value", 100.0);
51 self.log = j.value("Log", false);
52 self.target = j.value("Target", Threshold::defaults::target);
53}
54
55/** Deserialize a HealthMetric from JSON. */
56void from_json(const json& j, HealthMetric& self)
57{
58 self.collectionFreq = std::chrono::seconds(j.value(
59 "Frequency",
60 std::chrono::seconds(HealthMetric::defaults::frequency).count()));
61
62 self.windowSize = j.value("Window_size",
63 HealthMetric::defaults::windowSize);
64 // Path is only valid for storage
65 self.path = j.value("Path", "");
66
67 auto thresholds = j.find("Threshold");
68 if (thresholds == j.end())
69 {
70 return;
71 }
72
73 for (auto& [key, value] : thresholds->items())
74 {
75 if (!validThresholdTypes.contains(key))
76 {
77 warning("Invalid ThresholdType: {TYPE}", "TYPE", key);
78 continue;
79 }
80
81 auto config = value.template get<Threshold>();
82
83 // ThresholdIntf::Bound::Upper is the only use case for
84 // ThresholdIntf::Bound
85 self.thresholds.emplace(std::make_tuple(validThresholdTypes.at(key),
86 ThresholdIntf::Bound::Upper),
87 config);
88 }
89}
90
91json parseConfigFile(std::string configFile)
92{
93 std::ifstream jsonFile(configFile);
94 if (!jsonFile.is_open())
95 {
96 info("config JSON file not found: {PATH}", "PATH", configFile);
97 return {};
98 }
99
100 try
101 {
102 return json::parse(jsonFile, nullptr, true);
103 }
104 catch (const json::parse_error& e)
105 {
106 error("Failed to parse JSON config file {PATH}: {ERROR}", "PATH",
107 configFile, "ERROR", e);
108 }
109
110 return {};
111}
112
113void printConfig(HealthMetric::map_t& configs)
114{
115 for (auto& [type, configList] : configs)
116 {
117 for (auto& config : configList)
118 {
119 debug(
120 "MTYPE={MTYPE}, MNAME={MNAME} MSTYPE={MSTYPE} PATH={PATH}, FREQ={FREQ}, WSIZE={WSIZE}",
121 "MTYPE", std::to_underlying(type), "MNAME", config.name,
122 "MSTYPE", std::to_underlying(config.subType), "PATH",
123 config.path, "FREQ", config.collectionFreq.count(), "WSIZE",
124 config.windowSize);
125
126 for (auto& [key, threshold] : config.thresholds)
127 {
128 debug(
129 "THRESHOLD TYPE={TYPE} THRESHOLD BOUND={BOUND} VALUE={VALUE} LOG={LOG} TARGET={TARGET}",
130 "TYPE", std::to_underlying(get<ThresholdIntf::Type>(key)),
131 "BOUND", std::to_underlying(get<ThresholdIntf::Bound>(key)),
132 "VALUE", threshold.value, "LOG", threshold.log, "TARGET",
133 threshold.target);
134 }
135 }
136 }
137}
138
139auto getHealthMetricConfigs() -> HealthMetric::map_t
140{
141 json mergedConfig(defaultHealthMetricConfig);
142
143 if (auto platformConfig = parseConfigFile(HEALTH_CONFIG_FILE);
144 !platformConfig.empty())
145 {
146 mergedConfig.merge_patch(platformConfig);
147 }
148
149 HealthMetric::map_t configs = {};
150 for (auto& [name, metric] : mergedConfig.items())
151 {
152 static constexpr auto nameDelimiter = "_";
153 std::string typeStr = name.substr(0, name.find_first_of(nameDelimiter));
154
155 auto type = validTypes.find(typeStr);
156 if (type == validTypes.end())
157 {
158 warning("Invalid metric type: {TYPE}", "TYPE", typeStr);
159 continue;
160 }
161
162 auto config = metric.template get<HealthMetric>();
163
164 auto subType = validSubTypes.find(name);
165 config.subType = (subType != validSubTypes.end() ? subType->second
166 : SubType::NA);
167
168 configs[type->second].emplace_back(std::move(config));
169 }
170 printConfig(configs);
171 return configs;
172}
173
174json defaultHealthMetricConfig = R"({
175 "CPU": {
176 "Frequency": 1,
177 "Window_size": 120,
178 "Threshold": {
179 "Critical": {
180 "Value": 90.0,
181 "Log": true,
182 "Target": ""
183 },
184 "Warning": {
185 "Value": 80.0,
186 "Log": false,
187 "Target": ""
188 }
189 }
190 },
191 "CPU_User": {
192 "Frequency": 1,
193 "Window_size": 120,
194 "Threshold": {
195 "Critical": {
196 "Value": 90.0,
197 "Log": true,
198 "Target": ""
199 },
200 "Warning": {
201 "Value": 80.0,
202 "Log": false,
203 "Target": ""
204 }
205 }
206 },
207 "CPU_Kernel": {
208 "Frequency": 1,
209 "Window_size": 120,
210 "Threshold": {
211 "Critical": {
212 "Value": 90.0,
213 "Log": true,
214 "Target": ""
215 },
216 "Warning": {
217 "Value": 80.0,
218 "Log": false,
219 "Target": ""
220 }
221 }
222 },
223 "Memory_Available": {
224 "Frequency": 1,
225 "Window_size": 120,
226 "Threshold": {
227 "Critical": {
228 "Value": 85.0,
229 "Log": true,
230 "Target": ""
231 }
232 }
233 },
234 "Storage_RW": {
235 "Path": "/run/initramfs/rw",
236 "Frequency": 1,
237 "Window_size": 120,
238 "Threshold": {
239 "Critical": {
240 "Value": 85.0,
241 "Log": true,
242 "Target": ""
243 }
244 }
245 }
246})"_json;
247
248} // namespace phosphor::health::metric::config