blob: 20a3bcbf81171e99371fcdd475330e92e947ad74 [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"
Jolie Ku69f2f482020-10-21 09:59:43 +080023#include "tach_sensor.hpp"
Matthew Barth9ea8bee2020-06-04 14:27:19 -050024#include "types.hpp"
25
26#include <nlohmann/json.hpp>
27#include <phosphor-logging/log.hpp>
28
29#include <algorithm>
Patrick Williamsfbf47032023-07-17 12:27:34 -050030#include <format>
Matthew Barth9ea8bee2020-06-04 14:27:19 -050031#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}};
Jolie Ku69f2f482020-10-21 09:59:43 +080060const std::map<std::string, size_t> methods = {
61 {"timebased", MethodMode::timebased}, {"count", MethodMode::count}};
Matthew Barth9ea8bee2020-06-04 14:27:19 -050062
63const std::vector<CreateGroupFunction> getTrustGrps(const json& obj)
64{
65 std::vector<CreateGroupFunction> grpFuncs;
66
67 if (obj.contains("sensor_trust_groups"))
68 {
69 for (auto& stg : obj["sensor_trust_groups"])
70 {
71 if (!stg.contains("class") || !stg.contains("group"))
72 {
73 // Log error on missing required parameters
74 log<level::ERR>(
75 "Missing required fan monitor trust group parameters",
76 entry("REQUIRED_PARAMETERS=%s", "{class, group}"));
77 throw std::runtime_error(
78 "Missing required fan trust group parameters");
79 }
80 auto tgClass = stg["class"].get<std::string>();
81 std::vector<trust::GroupDefinition> group;
82 for (auto& member : stg["group"])
83 {
84 // Construct list of group members
85 if (!member.contains("name"))
86 {
87 // Log error on missing required parameter
88 log<level::ERR>(
89 "Missing required fan monitor trust group member name",
90 entry("CLASS=%s", tgClass.c_str()));
91 throw std::runtime_error(
92 "Missing required fan monitor trust group member name");
93 }
94 auto in_trust = true;
95 if (member.contains("in_trust"))
96 {
97 in_trust = member["in_trust"].get<bool>();
98 }
99 group.emplace_back(trust::GroupDefinition{
100 member["name"].get<std::string>(), in_trust});
101 }
102 // The class for fan sensor trust groups
103 // (Must have a supported function within the tClass namespace)
104 std::transform(tgClass.begin(), tgClass.end(), tgClass.begin(),
105 tolower);
106 auto handler = trusts.find(tgClass);
107 if (handler != trusts.end())
108 {
109 // Call function for trust group class
110 grpFuncs.emplace_back(handler->second(group));
111 }
112 else
113 {
114 // Log error on unsupported trust group class
115 log<level::ERR>("Invalid fan monitor trust group class",
116 entry("CLASS=%s", tgClass.c_str()));
117 throw std::runtime_error(
118 "Invalid fan monitor trust group class");
119 }
120 }
121 }
122
123 return grpFuncs;
124}
125
Matthew Barth22ab93b2020-06-08 10:47:56 -0500126const std::vector<SensorDefinition> getSensorDefs(const json& sensors)
127{
128 std::vector<SensorDefinition> sensorDefs;
129
130 for (const auto& sensor : sensors)
131 {
132 if (!sensor.contains("name") || !sensor.contains("has_target"))
133 {
134 // Log error on missing required parameters
135 log<level::ERR>(
136 "Missing required fan sensor definition parameters",
137 entry("REQUIRED_PARAMETERS=%s", "{name, has_target}"));
138 throw std::runtime_error(
139 "Missing required fan sensor definition parameters");
140 }
141 // Target interface is optional and defaults to
142 // 'xyz.openbmc_project.Control.FanSpeed'
143 std::string targetIntf = "xyz.openbmc_project.Control.FanSpeed";
144 if (sensor.contains("target_interface"))
145 {
146 targetIntf = sensor["target_interface"].get<std::string>();
147 }
Chau Ly27cc39f2022-09-20 08:16:56 +0000148 // Target path is optional
149 std::string targetPath;
150 if (sensor.contains("target_path"))
151 {
152 targetPath = sensor["target_path"].get<std::string>();
153 }
Matthew Barth22ab93b2020-06-08 10:47:56 -0500154 // Factor is optional and defaults to 1
Matt Spinler18fb12b2023-05-09 11:17:42 -0500155 double factor = 1.0;
Matthew Barth22ab93b2020-06-08 10:47:56 -0500156 if (sensor.contains("factor"))
157 {
158 factor = sensor["factor"].get<double>();
159 }
160 // Offset is optional and defaults to 0
Matt Spinler18fb12b2023-05-09 11:17:42 -0500161 int64_t offset = 0;
Matthew Barth22ab93b2020-06-08 10:47:56 -0500162 if (sensor.contains("offset"))
163 {
164 offset = sensor["offset"].get<int64_t>();
165 }
Jolie Ku69f2f482020-10-21 09:59:43 +0800166 // Threshold is optional and defaults to 1
Matt Spinler18fb12b2023-05-09 11:17:42 -0500167 size_t threshold = 1;
Jolie Ku69f2f482020-10-21 09:59:43 +0800168 if (sensor.contains("threshold"))
169 {
170 threshold = sensor["threshold"].get<size_t>();
171 }
Matthew Barth8a8aa442021-11-19 14:13:13 -0600172 // Ignore being above the allowed max is optional, defaults to not
173 bool ignoreAboveMax = false;
174 if (sensor.contains("ignore_above_max"))
175 {
176 ignoreAboveMax = sensor["ignore_above_max"].get<bool>();
177 }
Matthew Barth22ab93b2020-06-08 10:47:56 -0500178
Matt Spinler18fb12b2023-05-09 11:17:42 -0500179 SensorDefinition def{.name = sensor["name"].get<std::string>(),
180 .hasTarget = sensor["has_target"].get<bool>(),
181 .targetInterface = targetIntf,
182 .targetPath = targetPath,
183 .factor = factor,
184 .offset = offset,
185 .threshold = threshold,
186 .ignoreAboveMax = ignoreAboveMax};
187
188 sensorDefs.push_back(std::move(def));
Matthew Barth22ab93b2020-06-08 10:47:56 -0500189 }
190
191 return sensorDefs;
192}
193
194const std::vector<FanDefinition> getFanDefs(const json& obj)
195{
196 std::vector<FanDefinition> fanDefs;
197
198 for (const auto& fan : obj["fans"])
199 {
Jolie Ku69f2f482020-10-21 09:59:43 +0800200 if (!fan.contains("inventory") || !fan.contains("deviation") ||
201 !fan.contains("sensors"))
Matthew Barth22ab93b2020-06-08 10:47:56 -0500202 {
203 // Log error on missing required parameters
204 log<level::ERR>(
205 "Missing required fan monitor definition parameters",
206 entry("REQUIRED_PARAMETERS=%s",
Jolie Ku69f2f482020-10-21 09:59:43 +0800207 "{inventory, deviation, sensors}"));
Matthew Barth22ab93b2020-06-08 10:47:56 -0500208 throw std::runtime_error(
209 "Missing required fan monitor definition parameters");
210 }
Matthew Barth4c4de262021-02-17 13:03:02 -0600211 // Valid deviation range is 0 - 100%
212 auto deviation = fan["deviation"].get<size_t>();
Mike Capps808d7fe2022-06-13 10:12:16 -0400213 if (100 < deviation)
Matthew Barth4c4de262021-02-17 13:03:02 -0600214 {
Patrick Williamsfbf47032023-07-17 12:27:34 -0500215 auto msg = std::format(
Mike Capps78182482022-06-23 11:28:26 -0400216 "Invalid deviation of {} found, must be between 0 and 100",
217 deviation);
218
219 log<level::ERR>(msg.c_str());
220 throw std::runtime_error(msg.c_str());
Matthew Barth4c4de262021-02-17 13:03:02 -0600221 }
222
Matt Spinlerf724c162023-05-10 11:14:37 -0500223 // Upper deviation defaults to the deviation value and
224 // can also be separately specified.
225 size_t upperDeviation = deviation;
226 if (fan.contains("upper_deviation"))
227 {
228 upperDeviation = fan["upper_deviation"].get<size_t>();
229 if (100 < upperDeviation)
230 {
231 auto msg =
Patrick Williamsfbf47032023-07-17 12:27:34 -0500232 std::format("Invalid upper_deviation of {} found, must "
Matt Spinlerf724c162023-05-10 11:14:37 -0500233 "be between 0 and 100",
234 upperDeviation);
235
236 log<level::ERR>(msg.c_str());
237 throw std::runtime_error(msg.c_str());
238 }
239 }
240
Matthew Barth22ab93b2020-06-08 10:47:56 -0500241 // Construct the sensor definitions for this fan
242 auto sensorDefs = getSensorDefs(fan["sensors"]);
243
244 // Functional delay is optional and defaults to 0
245 size_t funcDelay = 0;
246 if (fan.contains("functional_delay"))
247 {
248 funcDelay = fan["functional_delay"].get<size_t>();
249 }
250
Jolie Ku69f2f482020-10-21 09:59:43 +0800251 // Method is optional and defaults to time based functional
252 // determination
253 size_t method = MethodMode::timebased;
Matt Spinler623635c2021-03-29 13:13:59 -0500254 size_t countInterval = 1;
Jolie Ku69f2f482020-10-21 09:59:43 +0800255 if (fan.contains("method"))
256 {
257 auto methodConf = fan["method"].get<std::string>();
258 auto methodFunc = methods.find(methodConf);
259 if (methodFunc != methods.end())
260 {
261 method = methodFunc->second;
262 }
263 else
264 {
265 // Log error on unsupported method parameter
266 log<level::ERR>("Invalid fan method");
267 throw std::runtime_error("Invalid fan method");
268 }
Matt Spinler623635c2021-03-29 13:13:59 -0500269
270 // Read the count interval value used with the count method.
271 if (method == MethodMode::count)
272 {
273 if (fan.contains("count_interval"))
274 {
275 countInterval = fan["count_interval"].get<size_t>();
276 }
277 }
Jolie Ku69f2f482020-10-21 09:59:43 +0800278 }
279
280 // Timeout defaults to 0
281 size_t timeout = 0;
282 if (method == MethodMode::timebased)
283 {
284 if (!fan.contains("allowed_out_of_range_time"))
285 {
286 // Log error on missing required parameter
287 log<level::ERR>(
288 "Missing required fan monitor definition parameters",
289 entry("REQUIRED_PARAMETER=%s",
290 "{allowed_out_of_range_time}"));
291 throw std::runtime_error(
292 "Missing required fan monitor definition parameters");
293 }
294 else
295 {
296 timeout = fan["allowed_out_of_range_time"].get<size_t>();
297 }
298 }
299
Matt Spinlerb0412d02020-10-12 16:53:52 -0500300 // Monitor start delay is optional and defaults to 0
301 size_t monitorDelay = 0;
302 if (fan.contains("monitor_start_delay"))
303 {
304 monitorDelay = fan["monitor_start_delay"].get<size_t>();
305 }
306
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500307 // num_sensors_nonfunc_for_fan_nonfunc is optional and defaults
308 // to zero if not present, meaning the code will not set the
309 // parent fan to nonfunctional based on sensors.
310 size_t nonfuncSensorsCount = 0;
311 if (fan.contains("num_sensors_nonfunc_for_fan_nonfunc"))
312 {
313 nonfuncSensorsCount =
314 fan["num_sensors_nonfunc_for_fan_nonfunc"].get<size_t>();
315 }
316
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500317 // nonfunc_rotor_error_delay is optional, though it will
318 // default to zero if 'fault_handling' is present.
319 std::optional<size_t> nonfuncRotorErrorDelay;
320 if (fan.contains("nonfunc_rotor_error_delay"))
321 {
322 nonfuncRotorErrorDelay =
323 fan["nonfunc_rotor_error_delay"].get<size_t>();
324 }
325 else if (obj.contains("fault_handling"))
326 {
327 nonfuncRotorErrorDelay = 0;
328 }
329
Matt Spinler27f6b682020-10-27 08:43:37 -0500330 // fan_missing_error_delay is optional.
331 std::optional<size_t> fanMissingErrorDelay;
332 if (fan.contains("fan_missing_error_delay"))
333 {
334 fanMissingErrorDelay =
335 fan.at("fan_missing_error_delay").get<size_t>();
336 }
337
Matthew Barth3ad14342020-06-08 16:17:42 -0500338 // Handle optional conditions
Matthew Barth8a0c2322020-06-17 09:53:10 -0500339 auto cond = std::optional<Condition>();
Matthew Barth3ad14342020-06-08 16:17:42 -0500340 if (fan.contains("condition"))
341 {
342 if (!fan["condition"].contains("name"))
343 {
344 // Log error on missing required parameter
345 log<level::ERR>(
346 "Missing required fan monitor condition parameter",
347 entry("REQUIRED_PARAMETER=%s", "{name}"));
348 throw std::runtime_error(
349 "Missing required fan monitor condition parameter");
350 }
351 auto name = fan["condition"]["name"].get<std::string>();
352 // The function for fan monitoring condition
353 // (Must have a supported function within the condition namespace)
354 std::transform(name.begin(), name.end(), name.begin(), tolower);
355 auto handler = conditions.find(name);
356 if (handler != conditions.end())
357 {
358 cond = handler->second(fan["condition"]);
359 }
360 else
361 {
362 log<level::INFO>(
363 "No handler found for configured condition",
364 entry("CONDITION_NAME=%s", name.c_str()),
365 entry("JSON_DUMP=%s", fan["condition"].dump().c_str()));
366 }
367 }
Jolie Ku69f2f482020-10-21 09:59:43 +0800368
Matt Spinlera3584bd2021-03-29 15:48:30 -0500369 // if the fan should be set to functional when plugged in
370 bool setFuncOnPresent = false;
371 if (fan.contains("set_func_on_present"))
372 {
373 setFuncOnPresent = fan["set_func_on_present"].get<bool>();
374 }
375
Matt Spinler18fb12b2023-05-09 11:17:42 -0500376 FanDefinition def{.name = fan["inventory"].get<std::string>(),
377 .method = method,
378 .funcDelay = funcDelay,
379 .timeout = timeout,
380 .deviation = deviation,
Matt Spinlerf724c162023-05-10 11:14:37 -0500381 .upperDeviation = upperDeviation,
Matt Spinler18fb12b2023-05-09 11:17:42 -0500382 .numSensorFailsForNonfunc = nonfuncSensorsCount,
383 .monitorStartDelay = monitorDelay,
384 .countInterval = countInterval,
385 .nonfuncRotorErrDelay = nonfuncRotorErrorDelay,
386 .fanMissingErrDelay = fanMissingErrorDelay,
387 .sensorList = std::move(sensorDefs),
388 .condition = cond,
389 .funcOnPresent = setFuncOnPresent};
390
391 fanDefs.push_back(std::move(def));
Matthew Barth22ab93b2020-06-08 10:47:56 -0500392 }
393
394 return fanDefs;
395}
396
Matt Spinlerf06ab072020-10-14 12:58:22 -0500397PowerRuleState getPowerOffPowerRuleState(const json& powerOffConfig)
398{
399 // The state is optional and defaults to runtime
400 PowerRuleState ruleState{PowerRuleState::runtime};
401
402 if (powerOffConfig.contains("state"))
403 {
404 auto state = powerOffConfig.at("state").get<std::string>();
405 if (state == "at_pgood")
406 {
407 ruleState = PowerRuleState::atPgood;
408 }
409 else if (state != "runtime")
410 {
Patrick Williamsfbf47032023-07-17 12:27:34 -0500411 auto msg = std::format("Invalid power off state entry {}", state);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500412 log<level::ERR>(msg.c_str());
413 throw std::runtime_error(msg.c_str());
414 }
415 }
416
417 return ruleState;
418}
419
420std::unique_ptr<PowerOffCause> getPowerOffCause(const json& powerOffConfig)
421{
422 std::unique_ptr<PowerOffCause> cause;
423
424 if (!powerOffConfig.contains("count") || !powerOffConfig.contains("cause"))
425 {
426 const auto msg =
427 "Missing 'count' or 'cause' entries in power off config";
428 log<level::ERR>(msg);
429 throw std::runtime_error(msg);
430 }
431
432 auto count = powerOffConfig.at("count").get<size_t>();
433 auto powerOffCause = powerOffConfig.at("cause").get<std::string>();
434
435 const std::map<std::string, std::function<std::unique_ptr<PowerOffCause>()>>
436 causes{
437 {"missing_fan_frus",
438 [count]() { return std::make_unique<MissingFanFRUCause>(count); }},
Matt Spinler4c62fc72024-02-14 16:10:19 -0600439 {"nonfunc_fan_rotors",
440 [count]() {
Patrick Williams5e15c3b2023-10-20 11:18:11 -0500441 return std::make_unique<NonfuncFanRotorCause>(count);
Matt Spinler4c62fc72024-02-14 16:10:19 -0600442 }},
443 {"fan_frus_with_nonfunc_rotors", [count]() {
444 return std::make_unique<FanFRUsWithNonfuncRotorsCause>(count);
Patrick Williams5e15c3b2023-10-20 11:18:11 -0500445 }}};
Matt Spinlerf06ab072020-10-14 12:58:22 -0500446
447 auto it = causes.find(powerOffCause);
448 if (it != causes.end())
449 {
450 cause = it->second();
451 }
452 else
453 {
454 auto msg =
Patrick Williamsfbf47032023-07-17 12:27:34 -0500455 std::format("Invalid power off cause {} in power off config JSON",
Matt Spinlerf06ab072020-10-14 12:58:22 -0500456 powerOffCause);
457 log<level::ERR>(msg.c_str());
458 throw std::runtime_error(msg.c_str());
459 }
460
461 return cause;
462}
463
464std::unique_ptr<PowerOffAction>
465 getPowerOffAction(const json& powerOffConfig,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500466 std::shared_ptr<PowerInterfaceBase>& powerInterface,
467 PowerOffAction::PrePowerOffFunc& func)
Matt Spinlerf06ab072020-10-14 12:58:22 -0500468{
469 std::unique_ptr<PowerOffAction> action;
470 if (!powerOffConfig.contains("type"))
471 {
472 const auto msg = "Missing 'type' entry in power off config";
473 log<level::ERR>(msg);
474 throw std::runtime_error(msg);
475 }
476
477 auto type = powerOffConfig.at("type").get<std::string>();
478
479 if (((type == "hard") || (type == "soft")) &&
480 !powerOffConfig.contains("delay"))
481 {
482 const auto msg = "Missing 'delay' entry in power off config";
483 log<level::ERR>(msg);
484 throw std::runtime_error(msg);
485 }
486 else if ((type == "epow") &&
487 (!powerOffConfig.contains("service_mode_delay") ||
488 !powerOffConfig.contains("meltdown_delay")))
489 {
490 const auto msg = "Missing 'service_mode_delay' or 'meltdown_delay' "
491 "entry in power off config";
492 log<level::ERR>(msg);
493 throw std::runtime_error(msg);
494 }
495
496 if (type == "hard")
497 {
498 action = std::make_unique<HardPowerOff>(
Matt Spinlerac1efc12020-10-27 10:20:11 -0500499 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500500 }
501 else if (type == "soft")
502 {
503 action = std::make_unique<SoftPowerOff>(
Matt Spinlerac1efc12020-10-27 10:20:11 -0500504 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500505 }
506 else if (type == "epow")
507 {
508 action = std::make_unique<EpowPowerOff>(
509 powerOffConfig.at("service_mode_delay").get<uint32_t>(),
Matt Spinlerac1efc12020-10-27 10:20:11 -0500510 powerOffConfig.at("meltdown_delay").get<uint32_t>(), powerInterface,
511 func);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500512 }
513 else
514 {
Patrick Williamsfbf47032023-07-17 12:27:34 -0500515 auto msg = std::format("Invalid 'type' entry {} in power off config",
Patrick Williams61b73292023-05-10 07:50:12 -0500516 type);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500517 log<level::ERR>(msg.c_str());
518 throw std::runtime_error(msg.c_str());
519 }
520
521 return action;
522}
523
524std::vector<std::unique_ptr<PowerOffRule>>
525 getPowerOffRules(const json& obj,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500526 std::shared_ptr<PowerInterfaceBase>& powerInterface,
527 PowerOffAction::PrePowerOffFunc& func)
Matt Spinlerf06ab072020-10-14 12:58:22 -0500528{
529 std::vector<std::unique_ptr<PowerOffRule>> rules;
530
531 if (!(obj.contains("fault_handling") &&
532 obj.at("fault_handling").contains("power_off_config")))
533 {
534 return rules;
535 }
536
537 for (const auto& config : obj.at("fault_handling").at("power_off_config"))
538 {
539 auto state = getPowerOffPowerRuleState(config);
540 auto cause = getPowerOffCause(config);
Matt Spinlerac1efc12020-10-27 10:20:11 -0500541 auto action = getPowerOffAction(config, powerInterface, func);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500542
543 auto rule = std::make_unique<PowerOffRule>(
544 std::move(state), std::move(cause), std::move(action));
545 rules.push_back(std::move(rule));
546 }
547
548 return rules;
549}
550
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500551std::optional<size_t> getNumNonfuncRotorsBeforeError(const json& obj)
552{
553 std::optional<size_t> num;
554
555 if (obj.contains("fault_handling"))
556 {
557 // Defaults to 1 if not present inside of 'fault_handling'.
558 num = obj.at("fault_handling")
559 .value("num_nonfunc_rotors_before_error", 1);
560 }
561
562 return num;
563}
564
Matthew Barth9ea8bee2020-06-04 14:27:19 -0500565} // namespace phosphor::fan::monitor