blob: d81d76729ac6fcb7729d82288ecba365304b6df3 [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
Matt Spinlerf06ab072020-10-14 12:58:22 -050026#include <fmt/format.h>
27
Matthew Barth9ea8bee2020-06-04 14:27:19 -050028#include <nlohmann/json.hpp>
29#include <phosphor-logging/log.hpp>
30
31#include <algorithm>
32#include <map>
33#include <memory>
Matthew Barth22ab93b2020-06-08 10:47:56 -050034#include <optional>
Matthew Barth9ea8bee2020-06-04 14:27:19 -050035#include <vector>
36
37namespace phosphor::fan::monitor
38{
39
40using json = nlohmann::json;
41using namespace phosphor::logging;
42
43namespace tClass
44{
45
46// Get a constructed trust group class for a non-zero speed group
47CreateGroupFunction
48 getNonZeroSpeed(const std::vector<trust::GroupDefinition>& group)
49{
50 return [group]() {
51 return std::make_unique<trust::NonzeroSpeed>(std::move(group));
52 };
53}
54
55} // namespace tClass
56
57const std::map<std::string, trustHandler> trusts = {
58 {"nonzerospeed", tClass::getNonZeroSpeed}};
Matthew Barth3ad14342020-06-08 16:17:42 -050059const std::map<std::string, condHandler> conditions = {
60 {"propertiesmatch", condition::getPropertiesMatch}};
Jolie Ku69f2f482020-10-21 09:59:43 +080061const std::map<std::string, size_t> methods = {
62 {"timebased", MethodMode::timebased}, {"count", MethodMode::count}};
Matthew Barth9ea8bee2020-06-04 14:27:19 -050063
64const std::vector<CreateGroupFunction> getTrustGrps(const json& obj)
65{
66 std::vector<CreateGroupFunction> grpFuncs;
67
68 if (obj.contains("sensor_trust_groups"))
69 {
70 for (auto& stg : obj["sensor_trust_groups"])
71 {
72 if (!stg.contains("class") || !stg.contains("group"))
73 {
74 // Log error on missing required parameters
75 log<level::ERR>(
76 "Missing required fan monitor trust group parameters",
77 entry("REQUIRED_PARAMETERS=%s", "{class, group}"));
78 throw std::runtime_error(
79 "Missing required fan trust group parameters");
80 }
81 auto tgClass = stg["class"].get<std::string>();
82 std::vector<trust::GroupDefinition> group;
83 for (auto& member : stg["group"])
84 {
85 // Construct list of group members
86 if (!member.contains("name"))
87 {
88 // Log error on missing required parameter
89 log<level::ERR>(
90 "Missing required fan monitor trust group member name",
91 entry("CLASS=%s", tgClass.c_str()));
92 throw std::runtime_error(
93 "Missing required fan monitor trust group member name");
94 }
95 auto in_trust = true;
96 if (member.contains("in_trust"))
97 {
98 in_trust = member["in_trust"].get<bool>();
99 }
100 group.emplace_back(trust::GroupDefinition{
101 member["name"].get<std::string>(), in_trust});
102 }
103 // The class for fan sensor trust groups
104 // (Must have a supported function within the tClass namespace)
105 std::transform(tgClass.begin(), tgClass.end(), tgClass.begin(),
106 tolower);
107 auto handler = trusts.find(tgClass);
108 if (handler != trusts.end())
109 {
110 // Call function for trust group class
111 grpFuncs.emplace_back(handler->second(group));
112 }
113 else
114 {
115 // Log error on unsupported trust group class
116 log<level::ERR>("Invalid fan monitor trust group class",
117 entry("CLASS=%s", tgClass.c_str()));
118 throw std::runtime_error(
119 "Invalid fan monitor trust group class");
120 }
121 }
122 }
123
124 return grpFuncs;
125}
126
Matthew Barth22ab93b2020-06-08 10:47:56 -0500127const std::vector<SensorDefinition> getSensorDefs(const json& sensors)
128{
129 std::vector<SensorDefinition> sensorDefs;
130
131 for (const auto& sensor : sensors)
132 {
133 if (!sensor.contains("name") || !sensor.contains("has_target"))
134 {
135 // Log error on missing required parameters
136 log<level::ERR>(
137 "Missing required fan sensor definition parameters",
138 entry("REQUIRED_PARAMETERS=%s", "{name, has_target}"));
139 throw std::runtime_error(
140 "Missing required fan sensor definition parameters");
141 }
142 // Target interface is optional and defaults to
143 // 'xyz.openbmc_project.Control.FanSpeed'
144 std::string targetIntf = "xyz.openbmc_project.Control.FanSpeed";
145 if (sensor.contains("target_interface"))
146 {
147 targetIntf = sensor["target_interface"].get<std::string>();
148 }
Chau Ly27cc39f2022-09-20 08:16:56 +0000149 // Target path is optional
150 std::string targetPath;
151 if (sensor.contains("target_path"))
152 {
153 targetPath = sensor["target_path"].get<std::string>();
154 }
Matthew Barth22ab93b2020-06-08 10:47:56 -0500155 // Factor is optional and defaults to 1
Matt Spinler18fb12b2023-05-09 11:17:42 -0500156 double factor = 1.0;
Matthew Barth22ab93b2020-06-08 10:47:56 -0500157 if (sensor.contains("factor"))
158 {
159 factor = sensor["factor"].get<double>();
160 }
161 // Offset is optional and defaults to 0
Matt Spinler18fb12b2023-05-09 11:17:42 -0500162 int64_t offset = 0;
Matthew Barth22ab93b2020-06-08 10:47:56 -0500163 if (sensor.contains("offset"))
164 {
165 offset = sensor["offset"].get<int64_t>();
166 }
Jolie Ku69f2f482020-10-21 09:59:43 +0800167 // Threshold is optional and defaults to 1
Matt Spinler18fb12b2023-05-09 11:17:42 -0500168 size_t threshold = 1;
Jolie Ku69f2f482020-10-21 09:59:43 +0800169 if (sensor.contains("threshold"))
170 {
171 threshold = sensor["threshold"].get<size_t>();
172 }
Matthew Barth8a8aa442021-11-19 14:13:13 -0600173 // Ignore being above the allowed max is optional, defaults to not
174 bool ignoreAboveMax = false;
175 if (sensor.contains("ignore_above_max"))
176 {
177 ignoreAboveMax = sensor["ignore_above_max"].get<bool>();
178 }
Matthew Barth22ab93b2020-06-08 10:47:56 -0500179
Matt Spinler18fb12b2023-05-09 11:17:42 -0500180 SensorDefinition def{.name = sensor["name"].get<std::string>(),
181 .hasTarget = sensor["has_target"].get<bool>(),
182 .targetInterface = targetIntf,
183 .targetPath = targetPath,
184 .factor = factor,
185 .offset = offset,
186 .threshold = threshold,
187 .ignoreAboveMax = ignoreAboveMax};
188
189 sensorDefs.push_back(std::move(def));
Matthew Barth22ab93b2020-06-08 10:47:56 -0500190 }
191
192 return sensorDefs;
193}
194
195const std::vector<FanDefinition> getFanDefs(const json& obj)
196{
197 std::vector<FanDefinition> fanDefs;
198
199 for (const auto& fan : obj["fans"])
200 {
Jolie Ku69f2f482020-10-21 09:59:43 +0800201 if (!fan.contains("inventory") || !fan.contains("deviation") ||
202 !fan.contains("sensors"))
Matthew Barth22ab93b2020-06-08 10:47:56 -0500203 {
204 // Log error on missing required parameters
205 log<level::ERR>(
206 "Missing required fan monitor definition parameters",
207 entry("REQUIRED_PARAMETERS=%s",
Jolie Ku69f2f482020-10-21 09:59:43 +0800208 "{inventory, deviation, sensors}"));
Matthew Barth22ab93b2020-06-08 10:47:56 -0500209 throw std::runtime_error(
210 "Missing required fan monitor definition parameters");
211 }
Matthew Barth4c4de262021-02-17 13:03:02 -0600212 // Valid deviation range is 0 - 100%
213 auto deviation = fan["deviation"].get<size_t>();
Mike Capps808d7fe2022-06-13 10:12:16 -0400214 if (100 < deviation)
Matthew Barth4c4de262021-02-17 13:03:02 -0600215 {
Mike Capps78182482022-06-23 11:28:26 -0400216 auto msg = fmt::format(
217 "Invalid deviation of {} found, must be between 0 and 100",
218 deviation);
219
220 log<level::ERR>(msg.c_str());
221 throw std::runtime_error(msg.c_str());
Matthew Barth4c4de262021-02-17 13:03:02 -0600222 }
223
Matthew Barth22ab93b2020-06-08 10:47:56 -0500224 // Construct the sensor definitions for this fan
225 auto sensorDefs = getSensorDefs(fan["sensors"]);
226
227 // Functional delay is optional and defaults to 0
228 size_t funcDelay = 0;
229 if (fan.contains("functional_delay"))
230 {
231 funcDelay = fan["functional_delay"].get<size_t>();
232 }
233
Jolie Ku69f2f482020-10-21 09:59:43 +0800234 // Method is optional and defaults to time based functional
235 // determination
236 size_t method = MethodMode::timebased;
Matt Spinler623635c2021-03-29 13:13:59 -0500237 size_t countInterval = 1;
Jolie Ku69f2f482020-10-21 09:59:43 +0800238 if (fan.contains("method"))
239 {
240 auto methodConf = fan["method"].get<std::string>();
241 auto methodFunc = methods.find(methodConf);
242 if (methodFunc != methods.end())
243 {
244 method = methodFunc->second;
245 }
246 else
247 {
248 // Log error on unsupported method parameter
249 log<level::ERR>("Invalid fan method");
250 throw std::runtime_error("Invalid fan method");
251 }
Matt Spinler623635c2021-03-29 13:13:59 -0500252
253 // Read the count interval value used with the count method.
254 if (method == MethodMode::count)
255 {
256 if (fan.contains("count_interval"))
257 {
258 countInterval = fan["count_interval"].get<size_t>();
259 }
260 }
Jolie Ku69f2f482020-10-21 09:59:43 +0800261 }
262
263 // Timeout defaults to 0
264 size_t timeout = 0;
265 if (method == MethodMode::timebased)
266 {
267 if (!fan.contains("allowed_out_of_range_time"))
268 {
269 // Log error on missing required parameter
270 log<level::ERR>(
271 "Missing required fan monitor definition parameters",
272 entry("REQUIRED_PARAMETER=%s",
273 "{allowed_out_of_range_time}"));
274 throw std::runtime_error(
275 "Missing required fan monitor definition parameters");
276 }
277 else
278 {
279 timeout = fan["allowed_out_of_range_time"].get<size_t>();
280 }
281 }
282
Matt Spinlerb0412d02020-10-12 16:53:52 -0500283 // Monitor start delay is optional and defaults to 0
284 size_t monitorDelay = 0;
285 if (fan.contains("monitor_start_delay"))
286 {
287 monitorDelay = fan["monitor_start_delay"].get<size_t>();
288 }
289
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500290 // num_sensors_nonfunc_for_fan_nonfunc is optional and defaults
291 // to zero if not present, meaning the code will not set the
292 // parent fan to nonfunctional based on sensors.
293 size_t nonfuncSensorsCount = 0;
294 if (fan.contains("num_sensors_nonfunc_for_fan_nonfunc"))
295 {
296 nonfuncSensorsCount =
297 fan["num_sensors_nonfunc_for_fan_nonfunc"].get<size_t>();
298 }
299
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500300 // nonfunc_rotor_error_delay is optional, though it will
301 // default to zero if 'fault_handling' is present.
302 std::optional<size_t> nonfuncRotorErrorDelay;
303 if (fan.contains("nonfunc_rotor_error_delay"))
304 {
305 nonfuncRotorErrorDelay =
306 fan["nonfunc_rotor_error_delay"].get<size_t>();
307 }
308 else if (obj.contains("fault_handling"))
309 {
310 nonfuncRotorErrorDelay = 0;
311 }
312
Matt Spinler27f6b682020-10-27 08:43:37 -0500313 // fan_missing_error_delay is optional.
314 std::optional<size_t> fanMissingErrorDelay;
315 if (fan.contains("fan_missing_error_delay"))
316 {
317 fanMissingErrorDelay =
318 fan.at("fan_missing_error_delay").get<size_t>();
319 }
320
Matthew Barth3ad14342020-06-08 16:17:42 -0500321 // Handle optional conditions
Matthew Barth8a0c2322020-06-17 09:53:10 -0500322 auto cond = std::optional<Condition>();
Matthew Barth3ad14342020-06-08 16:17:42 -0500323 if (fan.contains("condition"))
324 {
325 if (!fan["condition"].contains("name"))
326 {
327 // Log error on missing required parameter
328 log<level::ERR>(
329 "Missing required fan monitor condition parameter",
330 entry("REQUIRED_PARAMETER=%s", "{name}"));
331 throw std::runtime_error(
332 "Missing required fan monitor condition parameter");
333 }
334 auto name = fan["condition"]["name"].get<std::string>();
335 // The function for fan monitoring condition
336 // (Must have a supported function within the condition namespace)
337 std::transform(name.begin(), name.end(), name.begin(), tolower);
338 auto handler = conditions.find(name);
339 if (handler != conditions.end())
340 {
341 cond = handler->second(fan["condition"]);
342 }
343 else
344 {
345 log<level::INFO>(
346 "No handler found for configured condition",
347 entry("CONDITION_NAME=%s", name.c_str()),
348 entry("JSON_DUMP=%s", fan["condition"].dump().c_str()));
349 }
350 }
Jolie Ku69f2f482020-10-21 09:59:43 +0800351
Matt Spinlera3584bd2021-03-29 15:48:30 -0500352 // if the fan should be set to functional when plugged in
353 bool setFuncOnPresent = false;
354 if (fan.contains("set_func_on_present"))
355 {
356 setFuncOnPresent = fan["set_func_on_present"].get<bool>();
357 }
358
Matt Spinler18fb12b2023-05-09 11:17:42 -0500359 FanDefinition def{.name = fan["inventory"].get<std::string>(),
360 .method = method,
361 .funcDelay = funcDelay,
362 .timeout = timeout,
363 .deviation = deviation,
364 .numSensorFailsForNonfunc = nonfuncSensorsCount,
365 .monitorStartDelay = monitorDelay,
366 .countInterval = countInterval,
367 .nonfuncRotorErrDelay = nonfuncRotorErrorDelay,
368 .fanMissingErrDelay = fanMissingErrorDelay,
369 .sensorList = std::move(sensorDefs),
370 .condition = cond,
371 .funcOnPresent = setFuncOnPresent};
372
373 fanDefs.push_back(std::move(def));
Matthew Barth22ab93b2020-06-08 10:47:56 -0500374 }
375
376 return fanDefs;
377}
378
Matt Spinlerf06ab072020-10-14 12:58:22 -0500379PowerRuleState getPowerOffPowerRuleState(const json& powerOffConfig)
380{
381 // The state is optional and defaults to runtime
382 PowerRuleState ruleState{PowerRuleState::runtime};
383
384 if (powerOffConfig.contains("state"))
385 {
386 auto state = powerOffConfig.at("state").get<std::string>();
387 if (state == "at_pgood")
388 {
389 ruleState = PowerRuleState::atPgood;
390 }
391 else if (state != "runtime")
392 {
393 auto msg = fmt::format("Invalid power off state entry {}", state);
394 log<level::ERR>(msg.c_str());
395 throw std::runtime_error(msg.c_str());
396 }
397 }
398
399 return ruleState;
400}
401
402std::unique_ptr<PowerOffCause> getPowerOffCause(const json& powerOffConfig)
403{
404 std::unique_ptr<PowerOffCause> cause;
405
406 if (!powerOffConfig.contains("count") || !powerOffConfig.contains("cause"))
407 {
408 const auto msg =
409 "Missing 'count' or 'cause' entries in power off config";
410 log<level::ERR>(msg);
411 throw std::runtime_error(msg);
412 }
413
414 auto count = powerOffConfig.at("count").get<size_t>();
415 auto powerOffCause = powerOffConfig.at("cause").get<std::string>();
416
417 const std::map<std::string, std::function<std::unique_ptr<PowerOffCause>()>>
418 causes{
419 {"missing_fan_frus",
420 [count]() { return std::make_unique<MissingFanFRUCause>(count); }},
421 {"nonfunc_fan_rotors", [count]() {
422 return std::make_unique<NonfuncFanRotorCause>(count);
423 }}};
424
425 auto it = causes.find(powerOffCause);
426 if (it != causes.end())
427 {
428 cause = it->second();
429 }
430 else
431 {
432 auto msg =
433 fmt::format("Invalid power off cause {} in power off config JSON",
434 powerOffCause);
435 log<level::ERR>(msg.c_str());
436 throw std::runtime_error(msg.c_str());
437 }
438
439 return cause;
440}
441
442std::unique_ptr<PowerOffAction>
443 getPowerOffAction(const json& powerOffConfig,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500444 std::shared_ptr<PowerInterfaceBase>& powerInterface,
445 PowerOffAction::PrePowerOffFunc& func)
Matt Spinlerf06ab072020-10-14 12:58:22 -0500446{
447 std::unique_ptr<PowerOffAction> action;
448 if (!powerOffConfig.contains("type"))
449 {
450 const auto msg = "Missing 'type' entry in power off config";
451 log<level::ERR>(msg);
452 throw std::runtime_error(msg);
453 }
454
455 auto type = powerOffConfig.at("type").get<std::string>();
456
457 if (((type == "hard") || (type == "soft")) &&
458 !powerOffConfig.contains("delay"))
459 {
460 const auto msg = "Missing 'delay' entry in power off config";
461 log<level::ERR>(msg);
462 throw std::runtime_error(msg);
463 }
464 else if ((type == "epow") &&
465 (!powerOffConfig.contains("service_mode_delay") ||
466 !powerOffConfig.contains("meltdown_delay")))
467 {
468 const auto msg = "Missing 'service_mode_delay' or 'meltdown_delay' "
469 "entry in power off config";
470 log<level::ERR>(msg);
471 throw std::runtime_error(msg);
472 }
473
474 if (type == "hard")
475 {
476 action = std::make_unique<HardPowerOff>(
Matt Spinlerac1efc12020-10-27 10:20:11 -0500477 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500478 }
479 else if (type == "soft")
480 {
481 action = std::make_unique<SoftPowerOff>(
Matt Spinlerac1efc12020-10-27 10:20:11 -0500482 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500483 }
484 else if (type == "epow")
485 {
486 action = std::make_unique<EpowPowerOff>(
487 powerOffConfig.at("service_mode_delay").get<uint32_t>(),
Matt Spinlerac1efc12020-10-27 10:20:11 -0500488 powerOffConfig.at("meltdown_delay").get<uint32_t>(), powerInterface,
489 func);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500490 }
491 else
492 {
Patrick Williams61b73292023-05-10 07:50:12 -0500493 auto msg = fmt::format("Invalid 'type' entry {} in power off config",
494 type);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500495 log<level::ERR>(msg.c_str());
496 throw std::runtime_error(msg.c_str());
497 }
498
499 return action;
500}
501
502std::vector<std::unique_ptr<PowerOffRule>>
503 getPowerOffRules(const json& obj,
Matt Spinlerac1efc12020-10-27 10:20:11 -0500504 std::shared_ptr<PowerInterfaceBase>& powerInterface,
505 PowerOffAction::PrePowerOffFunc& func)
Matt Spinlerf06ab072020-10-14 12:58:22 -0500506{
507 std::vector<std::unique_ptr<PowerOffRule>> rules;
508
509 if (!(obj.contains("fault_handling") &&
510 obj.at("fault_handling").contains("power_off_config")))
511 {
512 return rules;
513 }
514
515 for (const auto& config : obj.at("fault_handling").at("power_off_config"))
516 {
517 auto state = getPowerOffPowerRuleState(config);
518 auto cause = getPowerOffCause(config);
Matt Spinlerac1efc12020-10-27 10:20:11 -0500519 auto action = getPowerOffAction(config, powerInterface, func);
Matt Spinlerf06ab072020-10-14 12:58:22 -0500520
521 auto rule = std::make_unique<PowerOffRule>(
522 std::move(state), std::move(cause), std::move(action));
523 rules.push_back(std::move(rule));
524 }
525
526 return rules;
527}
528
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500529std::optional<size_t> getNumNonfuncRotorsBeforeError(const json& obj)
530{
531 std::optional<size_t> num;
532
533 if (obj.contains("fault_handling"))
534 {
535 // Defaults to 1 if not present inside of 'fault_handling'.
536 num = obj.at("fault_handling")
537 .value("num_nonfunc_rotors_before_error", 1);
538 }
539
540 return num;
541}
542
Matthew Barth9ea8bee2020-06-04 14:27:19 -0500543} // namespace phosphor::fan::monitor