blob: 88f09727a2270260a926da48509074d2020d1384 [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>{
Jagpal Singh Gill28ce2ef2024-02-28 15:57:10 -080039 {"HardShutdown", ThresholdIntf::Type::HardShutdown},
40 {"SoftShutdown", ThresholdIntf::Type::SoftShutdown},
41 {"PerformanceLoss", ThresholdIntf::Type::PerformanceLoss},
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080042 {"Critical", ThresholdIntf::Type::Critical},
43 {"Warning", ThresholdIntf::Type::Warning}};
44
45// Valid metrics from config
46static const auto validTypes =
47 std::unordered_map<std::string, Type>{{"CPU", Type::cpu},
48 {"Memory", Type::memory},
49 {"Storage", Type::storage},
50 {"Inode", Type::inode}};
51
52// Valid submetrics from config
53static const auto validSubTypes = std::unordered_map<std::string, SubType>{
54 {"CPU", SubType::cpuTotal},
55 {"CPU_User", SubType::cpuUser},
56 {"CPU_Kernel", SubType::cpuKernel},
57 {"Memory", SubType::memoryTotal},
58 {"Memory_Free", SubType::memoryFree},
59 {"Memory_Available", SubType::memoryAvailable},
60 {"Memory_Shared", SubType::memoryShared},
61 {"Memory_Buffered_And_Cached", SubType::memoryBufferedAndCached},
Jagpal Singh Gill97582802024-02-27 13:59:11 -080062 {"Storage_RW", SubType::NA},
63 {"Storage_TMP", SubType::NA}};
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080064
65/** Deserialize a Threshold from JSON. */
66void from_json(const json& j, Threshold& self)
67{
68 self.value = j.value("Value", 100.0);
69 self.log = j.value("Log", false);
70 self.target = j.value("Target", Threshold::defaults::target);
71}
72
73/** Deserialize a HealthMetric from JSON. */
74void from_json(const json& j, HealthMetric& self)
75{
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080076 self.windowSize = j.value("Window_size",
77 HealthMetric::defaults::windowSize);
78 // Path is only valid for storage
79 self.path = j.value("Path", "");
80
81 auto thresholds = j.find("Threshold");
82 if (thresholds == j.end())
83 {
84 return;
85 }
86
87 for (auto& [key, value] : thresholds->items())
88 {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -080089 if (!validThresholdTypesWithBound.contains(key))
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -080090 {
91 warning("Invalid ThresholdType: {TYPE}", "TYPE", key);
92 continue;
93 }
94
95 auto config = value.template get<Threshold>();
Jagpal Singh Gill23f091e2023-12-10 15:23:19 -080096 if (!std::isfinite(config.value))
97 {
98 throw std::invalid_argument("Invalid threshold value");
99 }
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800100
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800101 static constexpr auto keyDelimiter = "_";
102 std::string typeStr = key.substr(0, key.find_first_of(keyDelimiter));
103 std::string boundStr = key.substr(key.find_last_of(keyDelimiter) + 1,
104 key.length());
105
106 self.thresholds.emplace(
107 std::make_tuple(validThresholdTypes.at(typeStr),
108 validThresholdBounds.at(boundStr)),
109 config);
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800110 }
111}
112
113json parseConfigFile(std::string configFile)
114{
115 std::ifstream jsonFile(configFile);
116 if (!jsonFile.is_open())
117 {
118 info("config JSON file not found: {PATH}", "PATH", configFile);
119 return {};
120 }
121
122 try
123 {
124 return json::parse(jsonFile, nullptr, true);
125 }
126 catch (const json::parse_error& e)
127 {
128 error("Failed to parse JSON config file {PATH}: {ERROR}", "PATH",
129 configFile, "ERROR", e);
130 }
131
132 return {};
133}
134
135void printConfig(HealthMetric::map_t& configs)
136{
137 for (auto& [type, configList] : configs)
138 {
139 for (auto& config : configList)
140 {
141 debug(
Jagpal Singh Gillb2df8172024-03-03 18:23:21 -0800142 "TYPE={TYPE}, NAME={NAME} SUBTYPE={SUBTYPE} PATH={PATH}, WSIZE={WSIZE}",
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600143 "TYPE", type, "NAME", config.name, "SUBTYPE", config.subType,
Jagpal Singh Gillb2df8172024-03-03 18:23:21 -0800144 "PATH", config.path, "WSIZE", config.windowSize);
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800145
146 for (auto& [key, threshold] : config.thresholds)
147 {
148 debug(
149 "THRESHOLD TYPE={TYPE} THRESHOLD BOUND={BOUND} VALUE={VALUE} LOG={LOG} TARGET={TARGET}",
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600150 "TYPE", get<ThresholdIntf::Type>(key), "BOUND",
151 get<ThresholdIntf::Bound>(key), "VALUE", threshold.value,
152 "LOG", threshold.log, "TARGET", threshold.target);
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800153 }
154 }
155 }
156}
157
158auto getHealthMetricConfigs() -> HealthMetric::map_t
159{
160 json mergedConfig(defaultHealthMetricConfig);
161
162 if (auto platformConfig = parseConfigFile(HEALTH_CONFIG_FILE);
163 !platformConfig.empty())
164 {
165 mergedConfig.merge_patch(platformConfig);
166 }
167
168 HealthMetric::map_t configs = {};
169 for (auto& [name, metric] : mergedConfig.items())
170 {
171 static constexpr auto nameDelimiter = "_";
172 std::string typeStr = name.substr(0, name.find_first_of(nameDelimiter));
173
174 auto type = validTypes.find(typeStr);
175 if (type == validTypes.end())
176 {
177 warning("Invalid metric type: {TYPE}", "TYPE", typeStr);
178 continue;
179 }
180
181 auto config = metric.template get<HealthMetric>();
Jagpal Singh Gill1f920052024-02-16 09:57:18 -0800182 config.name = name;
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800183
184 auto subType = validSubTypes.find(name);
185 config.subType = (subType != validSubTypes.end() ? subType->second
186 : SubType::NA);
187
188 configs[type->second].emplace_back(std::move(config));
189 }
190 printConfig(configs);
191 return configs;
192}
193
194json defaultHealthMetricConfig = R"({
195 "CPU": {
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800196 "Window_size": 120,
197 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800198 "Critical_Upper": {
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800199 "Value": 90.0,
200 "Log": true,
201 "Target": ""
202 },
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800203 "Warning_Upper": {
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800204 "Value": 80.0,
205 "Log": false,
206 "Target": ""
207 }
208 }
209 },
210 "CPU_User": {
Patrick Williamsc00c19e2024-02-23 19:07:32 -0600211 "Window_size": 120
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800212 },
213 "CPU_Kernel": {
Patrick Williamsc00c19e2024-02-23 19:07:32 -0600214 "Window_size": 120
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800215 },
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800216 "Memory": {
Patrick Williamsb3a8df22024-02-23 19:08:58 -0600217 "Window_size": 120
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800218 },
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800219 "Memory_Available": {
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800220 "Window_size": 120,
221 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800222 "Critical_Lower": {
223 "Value": 15.0,
224 "Log": true,
225 "Target": ""
226 }
227 }
228 },
229 "Memory_Free": {
Patrick Williams78a224b2024-02-23 19:10:19 -0600230 "Window_size": 120
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800231 },
Jagpal Singh Gillc6897812024-02-16 09:59:47 -0800232 "Memory_Shared": {
Jagpal Singh Gillc6897812024-02-16 09:59:47 -0800233 "Window_size": 120,
234 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800235 "Critical_Upper": {
Jagpal Singh Gillc6897812024-02-16 09:59:47 -0800236 "Value": 85.0,
237 "Log": true,
238 "Target": ""
239 }
240 }
241 },
242 "Memory_Buffered_And_Cached": {
Patrick Williamse7b17de2024-02-23 19:12:14 -0600243 "Window_size": 120
Jagpal Singh Gillc6897812024-02-16 09:59:47 -0800244 },
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800245 "Storage_RW": {
246 "Path": "/run/initramfs/rw",
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800247 "Window_size": 120,
248 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800249 "Critical_Lower": {
250 "Value": 15.0,
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800251 "Log": true,
252 "Target": ""
253 }
254 }
Jagpal Singh Gill7f3fd6e2024-02-12 16:20:00 -0800255 },
256 "Storage_TMP": {
257 "Path": "/tmp",
Jagpal Singh Gill7f3fd6e2024-02-12 16:20:00 -0800258 "Window_size": 120,
259 "Threshold": {
Jagpal Singh Gillafbac902024-02-22 18:09:56 -0800260 "Critical_Lower": {
261 "Value": 15.0,
Jagpal Singh Gill7f3fd6e2024-02-12 16:20:00 -0800262 "Log": true,
263 "Target": ""
264 }
265 }
Jagpal Singh Gill7e11ab02023-12-08 11:41:41 -0800266 }
267})"_json;
268
Patrick Williams67b8ebe2024-02-23 20:40:52 -0600269} // namespace config
270
271namespace details
272{
273auto reverse_map_search(const auto& m, auto v)
274{
275 if (auto match = std::ranges::find_if(
276 m, [=](const auto& p) { return p.second == v; });
277 match != std::end(m))
278 {
279 return match->first;
280 }
281 return std::format("Enum({})", std::to_underlying(v));
282}
283} // namespace details
284
285// to_string specialization for Type.
286auto to_string(Type t) -> std::string
287{
288 return details::reverse_map_search(config::validTypes, t);
289}
290
291// to_string specializaiton for SubType.
292auto to_string(SubType t) -> std::string
293{
294 return details::reverse_map_search(config::validSubTypes, t);
295}
296
297} // namespace phosphor::health::metric