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