blob: b2b0c068248f1eee83e03100eb26b2ac85369a5e [file] [log] [blame]
Matthew Barth9ea8bee2020-06-04 14:27:19 -05001/**
2 * Copyright © 2020 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include "json_parser.hpp"
17
Matthew Barth22ab93b2020-06-08 10:47:56 -050018#include "conditions.hpp"
Matthew Barth9ea8bee2020-06-04 14:27:19 -050019#include "json_config.hpp"
20#include "nonzero_speed_trust.hpp"
Matt Spinlerf06ab072020-10-14 12:58:22 -050021#include "power_interface.hpp"
22#include "power_off_rule.hpp"
Matthew Barth9ea8bee2020-06-04 14:27:19 -050023#include "types.hpp"
24
Matt Spinlerf06ab072020-10-14 12:58:22 -050025#include <fmt/format.h>
26
Matthew Barth9ea8bee2020-06-04 14:27:19 -050027#include <nlohmann/json.hpp>
28#include <phosphor-logging/log.hpp>
29
30#include <algorithm>
31#include <map>
32#include <memory>
Matthew Barth22ab93b2020-06-08 10:47:56 -050033#include <optional>
Matthew Barth9ea8bee2020-06-04 14:27:19 -050034#include <vector>
35
36namespace phosphor::fan::monitor
37{
38
39using json = nlohmann::json;
40using namespace phosphor::logging;
41
42namespace tClass
43{
44
45// Get a constructed trust group class for a non-zero speed group
46CreateGroupFunction
47 getNonZeroSpeed(const std::vector<trust::GroupDefinition>& group)
48{
49 return [group]() {
50 return std::make_unique<trust::NonzeroSpeed>(std::move(group));
51 };
52}
53
54} // namespace tClass
55
56const std::map<std::string, trustHandler> trusts = {
57 {"nonzerospeed", tClass::getNonZeroSpeed}};
Matthew Barth3ad14342020-06-08 16:17:42 -050058const std::map<std::string, condHandler> conditions = {
59 {"propertiesmatch", condition::getPropertiesMatch}};
Matthew Barth9ea8bee2020-06-04 14:27:19 -050060
61const std::vector<CreateGroupFunction> getTrustGrps(const json& obj)
62{
63 std::vector<CreateGroupFunction> grpFuncs;
64
65 if (obj.contains("sensor_trust_groups"))
66 {
67 for (auto& stg : obj["sensor_trust_groups"])
68 {
69 if (!stg.contains("class") || !stg.contains("group"))
70 {
71 // Log error on missing required parameters
72 log<level::ERR>(
73 "Missing required fan monitor trust group parameters",
74 entry("REQUIRED_PARAMETERS=%s", "{class, group}"));
75 throw std::runtime_error(
76 "Missing required fan trust group parameters");
77 }
78 auto tgClass = stg["class"].get<std::string>();
79 std::vector<trust::GroupDefinition> group;
80 for (auto& member : stg["group"])
81 {
82 // Construct list of group members
83 if (!member.contains("name"))
84 {
85 // Log error on missing required parameter
86 log<level::ERR>(
87 "Missing required fan monitor trust group member name",
88 entry("CLASS=%s", tgClass.c_str()));
89 throw std::runtime_error(
90 "Missing required fan monitor trust group member name");
91 }
92 auto in_trust = true;
93 if (member.contains("in_trust"))
94 {
95 in_trust = member["in_trust"].get<bool>();
96 }
97 group.emplace_back(trust::GroupDefinition{
98 member["name"].get<std::string>(), in_trust});
99 }
100 // The class for fan sensor trust groups
101 // (Must have a supported function within the tClass namespace)
102 std::transform(tgClass.begin(), tgClass.end(), tgClass.begin(),
103 tolower);
104 auto handler = trusts.find(tgClass);
105 if (handler != trusts.end())
106 {
107 // Call function for trust group class
108 grpFuncs.emplace_back(handler->second(group));
109 }
110 else
111 {
112 // Log error on unsupported trust group class
113 log<level::ERR>("Invalid fan monitor trust group class",
114 entry("CLASS=%s", tgClass.c_str()));
115 throw std::runtime_error(
116 "Invalid fan monitor trust group class");
117 }
118 }
119 }
120
121 return grpFuncs;
122}
123
Matthew Barth22ab93b2020-06-08 10:47:56 -0500124const std::vector<SensorDefinition> getSensorDefs(const json& sensors)
125{
126 std::vector<SensorDefinition> sensorDefs;
127
128 for (const auto& sensor : sensors)
129 {
130 if (!sensor.contains("name") || !sensor.contains("has_target"))
131 {
132 // Log error on missing required parameters
133 log<level::ERR>(
134 "Missing required fan sensor definition parameters",
135 entry("REQUIRED_PARAMETERS=%s", "{name, has_target}"));
136 throw std::runtime_error(
137 "Missing required fan sensor definition parameters");
138 }
139 // Target interface is optional and defaults to
140 // 'xyz.openbmc_project.Control.FanSpeed'
141 std::string targetIntf = "xyz.openbmc_project.Control.FanSpeed";
142 if (sensor.contains("target_interface"))
143 {
144 targetIntf = sensor["target_interface"].get<std::string>();
145 }
146 // Factor is optional and defaults to 1
147 auto factor = 1.0;
148 if (sensor.contains("factor"))
149 {
150 factor = sensor["factor"].get<double>();
151 }
152 // Offset is optional and defaults to 0
153 auto offset = 0;
154 if (sensor.contains("offset"))
155 {
156 offset = sensor["offset"].get<int64_t>();
157 }
158
159 sensorDefs.emplace_back(std::tuple(sensor["name"].get<std::string>(),
160 sensor["has_target"].get<bool>(),
161 targetIntf, factor, offset));
162 }
163
164 return sensorDefs;
165}
166
167const std::vector<FanDefinition> getFanDefs(const json& obj)
168{
169 std::vector<FanDefinition> fanDefs;
170
171 for (const auto& fan : obj["fans"])
172 {
173 if (!fan.contains("inventory") ||
174 !fan.contains("allowed_out_of_range_time") ||
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500175 !fan.contains("deviation") || !fan.contains("sensors"))
Matthew Barth22ab93b2020-06-08 10:47:56 -0500176 {
177 // Log error on missing required parameters
178 log<level::ERR>(
179 "Missing required fan monitor definition parameters",
180 entry("REQUIRED_PARAMETERS=%s",
181 "{inventory, allowed_out_of_range_time, deviation, "
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500182 "sensors}"));
Matthew Barth22ab93b2020-06-08 10:47:56 -0500183 throw std::runtime_error(
184 "Missing required fan monitor definition parameters");
185 }
186 // Construct the sensor definitions for this fan
187 auto sensorDefs = getSensorDefs(fan["sensors"]);
188
189 // Functional delay is optional and defaults to 0
190 size_t funcDelay = 0;
191 if (fan.contains("functional_delay"))
192 {
193 funcDelay = fan["functional_delay"].get<size_t>();
194 }
195
Matt Spinlerb0412d02020-10-12 16:53:52 -0500196 // Monitor start delay is optional and defaults to 0
197 size_t monitorDelay = 0;
198 if (fan.contains("monitor_start_delay"))
199 {
200 monitorDelay = fan["monitor_start_delay"].get<size_t>();
201 }
202
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500203 // num_sensors_nonfunc_for_fan_nonfunc is optional and defaults
204 // to zero if not present, meaning the code will not set the
205 // parent fan to nonfunctional based on sensors.
206 size_t nonfuncSensorsCount = 0;
207 if (fan.contains("num_sensors_nonfunc_for_fan_nonfunc"))
208 {
209 nonfuncSensorsCount =
210 fan["num_sensors_nonfunc_for_fan_nonfunc"].get<size_t>();
211 }
212
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500213 // nonfunc_rotor_error_delay is optional, though it will
214 // default to zero if 'fault_handling' is present.
215 std::optional<size_t> nonfuncRotorErrorDelay;
216 if (fan.contains("nonfunc_rotor_error_delay"))
217 {
218 nonfuncRotorErrorDelay =
219 fan["nonfunc_rotor_error_delay"].get<size_t>();
220 }
221 else if (obj.contains("fault_handling"))
222 {
223 nonfuncRotorErrorDelay = 0;
224 }
225
Matt Spinler27f6b682020-10-27 08:43:37 -0500226 // fan_missing_error_delay is optional.
227 std::optional<size_t> fanMissingErrorDelay;
228 if (fan.contains("fan_missing_error_delay"))
229 {
230 fanMissingErrorDelay =
231 fan.at("fan_missing_error_delay").get<size_t>();
232 }
233
Matthew Barth3ad14342020-06-08 16:17:42 -0500234 // Handle optional conditions
Matthew Barth8a0c2322020-06-17 09:53:10 -0500235 auto cond = std::optional<Condition>();
Matthew Barth3ad14342020-06-08 16:17:42 -0500236 if (fan.contains("condition"))
237 {
238 if (!fan["condition"].contains("name"))
239 {
240 // Log error on missing required parameter
241 log<level::ERR>(
242 "Missing required fan monitor condition parameter",
243 entry("REQUIRED_PARAMETER=%s", "{name}"));
244 throw std::runtime_error(
245 "Missing required fan monitor condition parameter");
246 }
247 auto name = fan["condition"]["name"].get<std::string>();
248 // The function for fan monitoring condition
249 // (Must have a supported function within the condition namespace)
250 std::transform(name.begin(), name.end(), name.begin(), tolower);
251 auto handler = conditions.find(name);
252 if (handler != conditions.end())
253 {
254 cond = handler->second(fan["condition"]);
255 }
256 else
257 {
258 log<level::INFO>(
259 "No handler found for configured condition",
260 entry("CONDITION_NAME=%s", name.c_str()),
261 entry("JSON_DUMP=%s", fan["condition"].dump().c_str()));
262 }
263 }
Matt Spinler27f6b682020-10-27 08:43:37 -0500264 fanDefs.emplace_back(std::tuple(
265 fan["inventory"].get<std::string>(), funcDelay,
266 fan["allowed_out_of_range_time"].get<size_t>(),
267 fan["deviation"].get<size_t>(), nonfuncSensorsCount, monitorDelay,
268 nonfuncRotorErrorDelay, fanMissingErrorDelay, sensorDefs, cond));
Matthew Barth22ab93b2020-06-08 10:47:56 -0500269 }
270
271 return fanDefs;
272}
273
Matt Spinlerf06ab072020-10-14 12:58:22 -0500274PowerRuleState getPowerOffPowerRuleState(const json& powerOffConfig)
275{
276 // The state is optional and defaults to runtime
277 PowerRuleState ruleState{PowerRuleState::runtime};
278
279 if (powerOffConfig.contains("state"))
280 {
281 auto state = powerOffConfig.at("state").get<std::string>();
282 if (state == "at_pgood")
283 {
284 ruleState = PowerRuleState::atPgood;
285 }
286 else if (state != "runtime")
287 {
288 auto msg = fmt::format("Invalid power off state entry {}", state);
289 log<level::ERR>(msg.c_str());
290 throw std::runtime_error(msg.c_str());
291 }
292 }
293
294 return ruleState;
295}
296
297std::unique_ptr<PowerOffCause> getPowerOffCause(const json& powerOffConfig)
298{
299 std::unique_ptr<PowerOffCause> cause;
300
301 if (!powerOffConfig.contains("count") || !powerOffConfig.contains("cause"))
302 {
303 const auto msg =
304 "Missing 'count' or 'cause' entries in power off config";
305 log<level::ERR>(msg);
306 throw std::runtime_error(msg);
307 }
308
309 auto count = powerOffConfig.at("count").get<size_t>();
310 auto powerOffCause = powerOffConfig.at("cause").get<std::string>();
311
312 const std::map<std::string, std::function<std::unique_ptr<PowerOffCause>()>>
313 causes{
314 {"missing_fan_frus",
315 [count]() { return std::make_unique<MissingFanFRUCause>(count); }},
316 {"nonfunc_fan_rotors", [count]() {
317 return std::make_unique<NonfuncFanRotorCause>(count);
318 }}};
319
320 auto it = causes.find(powerOffCause);
321 if (it != causes.end())
322 {
323 cause = it->second();
324 }
325 else
326 {
327 auto msg =
328 fmt::format("Invalid power off cause {} in power off config JSON",
329 powerOffCause);
330 log<level::ERR>(msg.c_str());
331 throw std::runtime_error(msg.c_str());
332 }
333
334 return cause;
335}
336
337std::unique_ptr<PowerOffAction>
338 getPowerOffAction(const json& powerOffConfig,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500339 std::shared_ptr<PowerInterfaceBase>& powerInterface,
340 PowerOffAction::PrePowerOffFunc& func)
Matt Spinlerf06ab072020-10-14 12:58:22 -0500341{
342 std::unique_ptr<PowerOffAction> action;
343 if (!powerOffConfig.contains("type"))
344 {
345 const auto msg = "Missing 'type' entry in power off config";
346 log<level::ERR>(msg);
347 throw std::runtime_error(msg);
348 }
349
350 auto type = powerOffConfig.at("type").get<std::string>();
351
352 if (((type == "hard") || (type == "soft")) &&
353 !powerOffConfig.contains("delay"))
354 {
355 const auto msg = "Missing 'delay' entry in power off config";
356 log<level::ERR>(msg);
357 throw std::runtime_error(msg);
358 }
359 else if ((type == "epow") &&
360 (!powerOffConfig.contains("service_mode_delay") ||
361 !powerOffConfig.contains("meltdown_delay")))
362 {
363 const auto msg = "Missing 'service_mode_delay' or 'meltdown_delay' "
364 "entry in power off config";
365 log<level::ERR>(msg);
366 throw std::runtime_error(msg);
367 }
368
369 if (type == "hard")
370 {
371 action = std::make_unique<HardPowerOff>(
Matt Spinlerac1efc12020-10-27 10:20:11 -0500372 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500373 }
374 else if (type == "soft")
375 {
376 action = std::make_unique<SoftPowerOff>(
Matt Spinlerac1efc12020-10-27 10:20:11 -0500377 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500378 }
379 else if (type == "epow")
380 {
381 action = std::make_unique<EpowPowerOff>(
382 powerOffConfig.at("service_mode_delay").get<uint32_t>(),
Matt Spinlerac1efc12020-10-27 10:20:11 -0500383 powerOffConfig.at("meltdown_delay").get<uint32_t>(), powerInterface,
384 func);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500385 }
386 else
387 {
388 auto msg =
389 fmt::format("Invalid 'type' entry {} in power off config", type);
390 log<level::ERR>(msg.c_str());
391 throw std::runtime_error(msg.c_str());
392 }
393
394 return action;
395}
396
397std::vector<std::unique_ptr<PowerOffRule>>
398 getPowerOffRules(const json& obj,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500399 std::shared_ptr<PowerInterfaceBase>& powerInterface,
400 PowerOffAction::PrePowerOffFunc& func)
Matt Spinlerf06ab072020-10-14 12:58:22 -0500401{
402 std::vector<std::unique_ptr<PowerOffRule>> rules;
403
404 if (!(obj.contains("fault_handling") &&
405 obj.at("fault_handling").contains("power_off_config")))
406 {
407 return rules;
408 }
409
410 for (const auto& config : obj.at("fault_handling").at("power_off_config"))
411 {
412 auto state = getPowerOffPowerRuleState(config);
413 auto cause = getPowerOffCause(config);
Matt Spinlerac1efc12020-10-27 10:20:11 -0500414 auto action = getPowerOffAction(config, powerInterface, func);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500415
416 auto rule = std::make_unique<PowerOffRule>(
417 std::move(state), std::move(cause), std::move(action));
418 rules.push_back(std::move(rule));
419 }
420
421 return rules;
422}
423
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500424std::optional<size_t> getNumNonfuncRotorsBeforeError(const json& obj)
425{
426 std::optional<size_t> num;
427
428 if (obj.contains("fault_handling"))
429 {
430 // Defaults to 1 if not present inside of 'fault_handling'.
431 num = obj.at("fault_handling")
432 .value("num_nonfunc_rotors_before_error", 1);
433 }
434
435 return num;
436}
437
Matthew Barth9ea8bee2020-06-04 14:27:19 -0500438} // namespace phosphor::fan::monitor