blob: e506f17537671f4428716df976b59afe2be12ab3 [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
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -08008#include <cmath>
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -08009#include <fstream>
10#include <unordered_map>
Jagpal Singh Gillafbac902024-02-22 18:09:56 -080011#include <unordered_set>
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080012#include <utility>
13
14PHOSPHOR_LOG2_USING;
15
16namespace phosphor::health::metric::config
17{
18
19using json = nlohmann::json;
20
21// Default health metric config
22extern json defaultHealthMetricConfig;
23
24// Valid thresholds from config
Jagpal Singh Gillafbac902024-02-22 18:09:56 -080025static const auto validThresholdTypesWithBound =
26 std::unordered_set<std::string>{"Critical_Lower", "Critical_Upper",
27 "Warning_Lower", "Warning_Upper"};
28
29static const auto validThresholdBounds =
30 std::unordered_map<std::string, ThresholdIntf::Bound>{
31 {"Lower", ThresholdIntf::Bound::Lower},
32 {"Upper", ThresholdIntf::Bound::Upper}};
33
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080034static const auto validThresholdTypes =
35 std::unordered_map<std::string, ThresholdIntf::Type>{
36 {"Critical", ThresholdIntf::Type::Critical},
37 {"Warning", ThresholdIntf::Type::Warning}};
38
39// Valid metrics from config
40static const auto validTypes =
41 std::unordered_map<std::string, Type>{{"CPU", Type::cpu},
42 {"Memory", Type::memory},
43 {"Storage", Type::storage},
44 {"Inode", Type::inode}};
45
46// Valid submetrics from config
47static const auto validSubTypes = std::unordered_map<std::string, SubType>{
48 {"CPU", SubType::cpuTotal},
49 {"CPU_User", SubType::cpuUser},
50 {"CPU_Kernel", SubType::cpuKernel},
51 {"Memory", SubType::memoryTotal},
52 {"Memory_Free", SubType::memoryFree},
53 {"Memory_Available", SubType::memoryAvailable},
54 {"Memory_Shared", SubType::memoryShared},
55 {"Memory_Buffered_And_Cached", SubType::memoryBufferedAndCached},
Jagpal Singh Gill7f3fd6e2024-02-12 16:20:00 -080056 {"Storage_RW", SubType::storageReadWrite},
Jagpal Singh Gilldfe839f2024-02-16 09:54:02 -080057 {"Storage_TMP", SubType::storageTmp}};
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080058
59/** Deserialize a Threshold from JSON. */
60void from_json(const json& j, Threshold& self)
61{
62 self.value = j.value("Value", 100.0);
63 self.log = j.value("Log", false);
64 self.target = j.value("Target", Threshold::defaults::target);
65}
66
67/** Deserialize a HealthMetric from JSON. */
68void from_json(const json& j, HealthMetric& self)
69{
70 self.collectionFreq = std::chrono::seconds(j.value(
71 "Frequency",
72 std::chrono::seconds(HealthMetric::defaults::frequency).count()));
73
74 self.windowSize = j.value("Window_size",
75 HealthMetric::defaults::windowSize);
76 // Path is only valid for storage
77 self.path = j.value("Path", "");
78
79 auto thresholds = j.find("Threshold");
80 if (thresholds == j.end())
81 {
82 return;
83 }
84
85 for (auto& [key, value] : thresholds->items())
86 {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -080087 if (!validThresholdTypesWithBound.contains(key))
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080088 {
89 warning("Invalid ThresholdType: {TYPE}", "TYPE", key);
90 continue;
91 }
92
93 auto config = value.template get<Threshold>();
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080094 if (!std::isfinite(config.value))
95 {
96 throw std::invalid_argument("Invalid threshold value");
97 }
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080098
Jagpal Singh Gillafbac902024-02-22 18:09:56 -080099 static constexpr auto keyDelimiter = "_";
100 std::string typeStr = key.substr(0, key.find_first_of(keyDelimiter));
101 std::string boundStr = key.substr(key.find_last_of(keyDelimiter) + 1,
102 key.length());
103
104 self.thresholds.emplace(
105 std::make_tuple(validThresholdTypes.at(typeStr),
106 validThresholdBounds.at(boundStr)),
107 config);
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800108 }
109}
110
111json parseConfigFile(std::string configFile)
112{
113 std::ifstream jsonFile(configFile);
114 if (!jsonFile.is_open())
115 {
116 info("config JSON file not found: {PATH}", "PATH", configFile);
117 return {};
118 }
119
120 try
121 {
122 return json::parse(jsonFile, nullptr, true);
123 }
124 catch (const json::parse_error& e)
125 {
126 error("Failed to parse JSON config file {PATH}: {ERROR}", "PATH",
127 configFile, "ERROR", e);
128 }
129
130 return {};
131}
132
133void printConfig(HealthMetric::map_t& configs)
134{
135 for (auto& [type, configList] : configs)
136 {
137 for (auto& config : configList)
138 {
139 debug(
140 "MTYPE={MTYPE}, MNAME={MNAME} MSTYPE={MSTYPE} PATH={PATH}, FREQ={FREQ}, WSIZE={WSIZE}",
141 "MTYPE", std::to_underlying(type), "MNAME", config.name,
142 "MSTYPE", std::to_underlying(config.subType), "PATH",
143 config.path, "FREQ", config.collectionFreq.count(), "WSIZE",
144 config.windowSize);
145
146 for (auto& [key, threshold] : config.thresholds)
147 {
148 debug(
149 "THRESHOLD TYPE={TYPE} THRESHOLD BOUND={BOUND} VALUE={VALUE} LOG={LOG} TARGET={TARGET}",
150 "TYPE", std::to_underlying(get<ThresholdIntf::Type>(key)),
151 "BOUND", std::to_underlying(get<ThresholdIntf::Bound>(key)),
152 "VALUE", threshold.value, "LOG", threshold.log, "TARGET",
153 threshold.target);
154 }
155 }
156 }
157}
158
159auto getHealthMetricConfigs() -> HealthMetric::map_t
160{
161 json mergedConfig(defaultHealthMetricConfig);
162
163 if (auto platformConfig = parseConfigFile(HEALTH_CONFIG_FILE);
164 !platformConfig.empty())
165 {
166 mergedConfig.merge_patch(platformConfig);
167 }
168
169 HealthMetric::map_t configs = {};
170 for (auto& [name, metric] : mergedConfig.items())
171 {
172 static constexpr auto nameDelimiter = "_";
173 std::string typeStr = name.substr(0, name.find_first_of(nameDelimiter));
174
175 auto type = validTypes.find(typeStr);
176 if (type == validTypes.end())
177 {
178 warning("Invalid metric type: {TYPE}", "TYPE", typeStr);
179 continue;
180 }
181
182 auto config = metric.template get<HealthMetric>();
Jagpal Singh Gill1f920052024-02-16 09:57:18 -0800183 config.name = name;
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800184
185 auto subType = validSubTypes.find(name);
186 config.subType = (subType != validSubTypes.end() ? subType->second
187 : SubType::NA);
188
189 configs[type->second].emplace_back(std::move(config));
190 }
191 printConfig(configs);
192 return configs;
193}
194
195json defaultHealthMetricConfig = R"({
196 "CPU": {
197 "Frequency": 1,
198 "Window_size": 120,
199 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800200 "Critical_Upper": {
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800201 "Value": 90.0,
202 "Log": true,
203 "Target": ""
204 },
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800205 "Warning_Upper": {
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800206 "Value": 80.0,
207 "Log": false,
208 "Target": ""
209 }
210 }
211 },
212 "CPU_User": {
213 "Frequency": 1,
214 "Window_size": 120,
215 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800216 "Critical_Upper": {
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800217 "Value": 90.0,
218 "Log": true,
219 "Target": ""
220 },
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800221 "Warning_Upper": {
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800222 "Value": 80.0,
223 "Log": false,
224 "Target": ""
225 }
226 }
227 },
228 "CPU_Kernel": {
229 "Frequency": 1,
230 "Window_size": 120,
231 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800232 "Critical_Upper": {
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800233 "Value": 90.0,
234 "Log": true,
235 "Target": ""
236 },
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800237 "Warning_Upper": {
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800238 "Value": 80.0,
239 "Log": false,
240 "Target": ""
241 }
242 }
243 },
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800244 "Memory": {
245 "Frequency": 1,
246 "Window_size": 120,
247 "Threshold": {
248 "Critical_Upper": {
249 "Value": 85.0,
250 "Log": true,
251 "Target": ""
252 }
253 }
254 },
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800255 "Memory_Available": {
256 "Frequency": 1,
257 "Window_size": 120,
258 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800259 "Critical_Lower": {
260 "Value": 15.0,
261 "Log": true,
262 "Target": ""
263 }
264 }
265 },
266 "Memory_Free": {
267 "Frequency": 1,
268 "Window_size": 120,
269 "Threshold": {
270 "Critical_Lower": {
271 "Value": 15.0,
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800272 "Log": true,
273 "Target": ""
274 }
275 }
276 },
Jagpal Singh Gillc6897812024-02-16 09:59:47 -0800277 "Memory_Shared": {
278 "Frequency": 1,
279 "Window_size": 120,
280 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800281 "Critical_Upper": {
Jagpal Singh Gillc6897812024-02-16 09:59:47 -0800282 "Value": 85.0,
283 "Log": true,
284 "Target": ""
285 }
286 }
287 },
288 "Memory_Buffered_And_Cached": {
289 "Frequency": 1,
290 "Window_size": 120,
291 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800292 "Critical_Upper": {
Jagpal Singh Gillc6897812024-02-16 09:59:47 -0800293 "Value": 85.0,
294 "Log": true,
295 "Target": ""
296 }
297 }
298 },
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800299 "Storage_RW": {
300 "Path": "/run/initramfs/rw",
301 "Frequency": 1,
302 "Window_size": 120,
303 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800304 "Critical_Lower": {
305 "Value": 15.0,
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800306 "Log": true,
307 "Target": ""
308 }
309 }
Jagpal Singh Gill7f3fd6e2024-02-12 16:20:00 -0800310 },
311 "Storage_TMP": {
312 "Path": "/tmp",
313 "Frequency": 1,
314 "Window_size": 120,
315 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800316 "Critical_Lower": {
317 "Value": 15.0,
Jagpal Singh Gill7f3fd6e2024-02-12 16:20:00 -0800318 "Log": true,
319 "Target": ""
320 }
321 }
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800322 }
323})"_json;
324
325} // namespace phosphor::health::metric::config