blob: e5312a009a3f7505777e34408335edaf8a216110 [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>
Patrick Williams67b8ebe2024-02-23 20:40:52 -060010#include <ranges>
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080011#include <unordered_map>
Jagpal Singh Gillafbac902024-02-22 18:09:56 -080012#include <unordered_set>
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080013#include <utility>
14
15PHOSPHOR_LOG2_USING;
16
Patrick Williams67b8ebe2024-02-23 20:40:52 -060017namespace phosphor::health::metric
18{
19namespace config
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080020{
21
22using json = nlohmann::json;
23
24// Default health metric config
25extern json defaultHealthMetricConfig;
26
27// Valid thresholds from config
Jagpal Singh Gillafbac902024-02-22 18:09:56 -080028static const auto validThresholdTypesWithBound =
29 std::unordered_set<std::string>{"Critical_Lower", "Critical_Upper",
30 "Warning_Lower", "Warning_Upper"};
31
32static const auto validThresholdBounds =
33 std::unordered_map<std::string, ThresholdIntf::Bound>{
34 {"Lower", ThresholdIntf::Bound::Lower},
35 {"Upper", ThresholdIntf::Bound::Upper}};
36
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080037static const auto validThresholdTypes =
38 std::unordered_map<std::string, ThresholdIntf::Type>{
39 {"Critical", ThresholdIntf::Type::Critical},
40 {"Warning", ThresholdIntf::Type::Warning}};
41
42// Valid metrics from config
43static const auto validTypes =
44 std::unordered_map<std::string, Type>{{"CPU", Type::cpu},
45 {"Memory", Type::memory},
46 {"Storage", Type::storage},
47 {"Inode", Type::inode}};
48
49// Valid submetrics from config
50static const auto validSubTypes = std::unordered_map<std::string, SubType>{
51 {"CPU", SubType::cpuTotal},
52 {"CPU_User", SubType::cpuUser},
53 {"CPU_Kernel", SubType::cpuKernel},
54 {"Memory", SubType::memoryTotal},
55 {"Memory_Free", SubType::memoryFree},
56 {"Memory_Available", SubType::memoryAvailable},
57 {"Memory_Shared", SubType::memoryShared},
58 {"Memory_Buffered_And_Cached", SubType::memoryBufferedAndCached},
Jagpal Singh Gill7f3fd6e2024-02-12 16:20:00 -080059 {"Storage_RW", SubType::storageReadWrite},
Jagpal Singh Gilldfe839f2024-02-16 09:54:02 -080060 {"Storage_TMP", SubType::storageTmp}};
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080061
62/** Deserialize a Threshold from JSON. */
63void from_json(const json& j, Threshold& self)
64{
65 self.value = j.value("Value", 100.0);
66 self.log = j.value("Log", false);
67 self.target = j.value("Target", Threshold::defaults::target);
68}
69
70/** Deserialize a HealthMetric from JSON. */
71void from_json(const json& j, HealthMetric& self)
72{
73 self.collectionFreq = std::chrono::seconds(j.value(
74 "Frequency",
75 std::chrono::seconds(HealthMetric::defaults::frequency).count()));
76
77 self.windowSize = j.value("Window_size",
78 HealthMetric::defaults::windowSize);
79 // Path is only valid for storage
80 self.path = j.value("Path", "");
81
82 auto thresholds = j.find("Threshold");
83 if (thresholds == j.end())
84 {
85 return;
86 }
87
88 for (auto& [key, value] : thresholds->items())
89 {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -080090 if (!validThresholdTypesWithBound.contains(key))
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080091 {
92 warning("Invalid ThresholdType: {TYPE}", "TYPE", key);
93 continue;
94 }
95
96 auto config = value.template get<Threshold>();
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080097 if (!std::isfinite(config.value))
98 {
99 throw std::invalid_argument("Invalid threshold value");
100 }
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800101
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800102 static constexpr auto keyDelimiter = "_";
103 std::string typeStr = key.substr(0, key.find_first_of(keyDelimiter));
104 std::string boundStr = key.substr(key.find_last_of(keyDelimiter) + 1,
105 key.length());
106
107 self.thresholds.emplace(
108 std::make_tuple(validThresholdTypes.at(typeStr),
109 validThresholdBounds.at(boundStr)),
110 config);
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800111 }
112}
113
114json parseConfigFile(std::string configFile)
115{
116 std::ifstream jsonFile(configFile);
117 if (!jsonFile.is_open())
118 {
119 info("config JSON file not found: {PATH}", "PATH", configFile);
120 return {};
121 }
122
123 try
124 {
125 return json::parse(jsonFile, nullptr, true);
126 }
127 catch (const json::parse_error& e)
128 {
129 error("Failed to parse JSON config file {PATH}: {ERROR}", "PATH",
130 configFile, "ERROR", e);
131 }
132
133 return {};
134}
135
136void printConfig(HealthMetric::map_t& configs)
137{
138 for (auto& [type, configList] : configs)
139 {
140 for (auto& config : configList)
141 {
142 debug(
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600143 "TYPE={TYPE}, NAME={NAME} SUBTYPE={SUBTYPE} PATH={PATH}, FREQ={FREQ}, WSIZE={WSIZE}",
144 "TYPE", type, "NAME", config.name, "SUBTYPE", config.subType,
145 "PATH", config.path, "FREQ", config.collectionFreq.count(),
146 "WSIZE", config.windowSize);
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800147
148 for (auto& [key, threshold] : config.thresholds)
149 {
150 debug(
151 "THRESHOLD TYPE={TYPE} THRESHOLD BOUND={BOUND} VALUE={VALUE} LOG={LOG} TARGET={TARGET}",
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600152 "TYPE", get<ThresholdIntf::Type>(key), "BOUND",
153 get<ThresholdIntf::Bound>(key), "VALUE", threshold.value,
154 "LOG", threshold.log, "TARGET", threshold.target);
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800155 }
156 }
157 }
158}
159
160auto getHealthMetricConfigs() -> HealthMetric::map_t
161{
162 json mergedConfig(defaultHealthMetricConfig);
163
164 if (auto platformConfig = parseConfigFile(HEALTH_CONFIG_FILE);
165 !platformConfig.empty())
166 {
167 mergedConfig.merge_patch(platformConfig);
168 }
169
170 HealthMetric::map_t configs = {};
171 for (auto& [name, metric] : mergedConfig.items())
172 {
173 static constexpr auto nameDelimiter = "_";
174 std::string typeStr = name.substr(0, name.find_first_of(nameDelimiter));
175
176 auto type = validTypes.find(typeStr);
177 if (type == validTypes.end())
178 {
179 warning("Invalid metric type: {TYPE}", "TYPE", typeStr);
180 continue;
181 }
182
183 auto config = metric.template get<HealthMetric>();
Jagpal Singh Gill1f920052024-02-16 09:57:18 -0800184 config.name = name;
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800185
186 auto subType = validSubTypes.find(name);
187 config.subType = (subType != validSubTypes.end() ? subType->second
188 : SubType::NA);
189
190 configs[type->second].emplace_back(std::move(config));
191 }
192 printConfig(configs);
193 return configs;
194}
195
196json defaultHealthMetricConfig = R"({
197 "CPU": {
198 "Frequency": 1,
199 "Window_size": 120,
200 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800201 "Critical_Upper": {
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800202 "Value": 90.0,
203 "Log": true,
204 "Target": ""
205 },
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800206 "Warning_Upper": {
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800207 "Value": 80.0,
208 "Log": false,
209 "Target": ""
210 }
211 }
212 },
213 "CPU_User": {
214 "Frequency": 1,
Patrick Williamsc00c19e2024-02-23 19:07:32 -0600215 "Window_size": 120
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800216 },
217 "CPU_Kernel": {
218 "Frequency": 1,
Patrick Williamsc00c19e2024-02-23 19:07:32 -0600219 "Window_size": 120
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800220 },
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800221 "Memory": {
222 "Frequency": 1,
Patrick Williamsb3a8df22024-02-23 19:08:58 -0600223 "Window_size": 120
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800224 },
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800225 "Memory_Available": {
226 "Frequency": 1,
227 "Window_size": 120,
228 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800229 "Critical_Lower": {
230 "Value": 15.0,
231 "Log": true,
232 "Target": ""
233 }
234 }
235 },
236 "Memory_Free": {
237 "Frequency": 1,
Patrick Williams78a224b2024-02-23 19:10:19 -0600238 "Window_size": 120
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800239 },
Jagpal Singh Gillc6897812024-02-16 09:59:47 -0800240 "Memory_Shared": {
241 "Frequency": 1,
242 "Window_size": 120,
243 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800244 "Critical_Upper": {
Jagpal Singh Gillc6897812024-02-16 09:59:47 -0800245 "Value": 85.0,
246 "Log": true,
247 "Target": ""
248 }
249 }
250 },
251 "Memory_Buffered_And_Cached": {
252 "Frequency": 1,
Patrick Williamse7b17de2024-02-23 19:12:14 -0600253 "Window_size": 120
Jagpal Singh Gillc6897812024-02-16 09:59:47 -0800254 },
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800255 "Storage_RW": {
256 "Path": "/run/initramfs/rw",
257 "Frequency": 1,
258 "Window_size": 120,
259 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800260 "Critical_Lower": {
261 "Value": 15.0,
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800262 "Log": true,
263 "Target": ""
264 }
265 }
Jagpal Singh Gill7f3fd6e2024-02-12 16:20:00 -0800266 },
267 "Storage_TMP": {
268 "Path": "/tmp",
269 "Frequency": 1,
270 "Window_size": 120,
271 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800272 "Critical_Lower": {
273 "Value": 15.0,
Jagpal Singh Gill7f3fd6e2024-02-12 16:20:00 -0800274 "Log": true,
275 "Target": ""
276 }
277 }
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800278 }
279})"_json;
280
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600281} // namespace config
282
283namespace details
284{
285auto reverse_map_search(const auto& m, auto v)
286{
287 if (auto match = std::ranges::find_if(
288 m, [=](const auto& p) { return p.second == v; });
289 match != std::end(m))
290 {
291 return match->first;
292 }
293 return std::format("Enum({})", std::to_underlying(v));
294}
295} // namespace details
296
297// to_string specialization for Type.
298auto to_string(Type t) -> std::string
299{
300 return details::reverse_map_search(config::validTypes, t);
301}
302
303// to_string specializaiton for SubType.
304auto to_string(SubType t) -> std::string
305{
306 return details::reverse_map_search(config::validSubTypes, t);
307}
308
309} // namespace phosphor::health::metric