1 /**
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
18 #include "conditions.hpp"
19 #include "json_config.hpp"
20 #include "nonzero_speed_trust.hpp"
21 #include "power_interface.hpp"
22 #include "power_off_rule.hpp"
23 #include "tach_sensor.hpp"
24 #include "types.hpp"
25
26 #include <nlohmann/json.hpp>
27 #include <phosphor-logging/log.hpp>
28
29 #include <algorithm>
30 #include <format>
31 #include <map>
32 #include <memory>
33 #include <optional>
34 #include <vector>
35
36 namespace phosphor::fan::monitor
37 {
38
39 using json = nlohmann::json;
40 using namespace phosphor::logging;
41
42 namespace tClass
43 {
44
45 // Get a constructed trust group class for a non-zero speed group
46 CreateGroupFunction
getNonZeroSpeed(const std::vector<trust::GroupDefinition> & group)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
56 const std::map<std::string, trustHandler> trusts = {
57 {"nonzerospeed", tClass::getNonZeroSpeed}};
58 const std::map<std::string, condHandler> conditions = {
59 {"propertiesmatch", condition::getPropertiesMatch}};
60 const std::map<std::string, size_t> methods = {
61 {"timebased", MethodMode::timebased}, {"count", MethodMode::count}};
62
getTrustGrps(const json & obj)63 const 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
getSensorDefs(const json & sensors)126 const 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 }
148 // Target path is optional
149 std::string targetPath;
150 if (sensor.contains("target_path"))
151 {
152 targetPath = sensor["target_path"].get<std::string>();
153 }
154 // Factor is optional and defaults to 1
155 double factor = 1.0;
156 if (sensor.contains("factor"))
157 {
158 factor = sensor["factor"].get<double>();
159 }
160 // Offset is optional and defaults to 0
161 int64_t offset = 0;
162 if (sensor.contains("offset"))
163 {
164 offset = sensor["offset"].get<int64_t>();
165 }
166 // Threshold is optional and defaults to 1
167 size_t threshold = 1;
168 if (sensor.contains("threshold"))
169 {
170 threshold = sensor["threshold"].get<size_t>();
171 }
172 // 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 }
178
179 SensorDefinition def{
180 .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));
190 }
191
192 return sensorDefs;
193 }
194
getFanDefs(const json & obj)195 const std::vector<FanDefinition> getFanDefs(const json& obj)
196 {
197 std::vector<FanDefinition> fanDefs;
198
199 for (const auto& fan : obj["fans"])
200 {
201 if (!fan.contains("inventory") || !fan.contains("deviation") ||
202 !fan.contains("sensors"))
203 {
204 // Log error on missing required parameters
205 log<level::ERR>(
206 "Missing required fan monitor definition parameters",
207 entry("REQUIRED_PARAMETERS=%s",
208 "{inventory, deviation, sensors}"));
209 throw std::runtime_error(
210 "Missing required fan monitor definition parameters");
211 }
212 // Valid deviation range is 0 - 100%
213 auto deviation = fan["deviation"].get<size_t>();
214 if (100 < deviation)
215 {
216 auto msg = std::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());
222 }
223
224 // Upper deviation defaults to the deviation value and
225 // can also be separately specified.
226 size_t upperDeviation = deviation;
227 if (fan.contains("upper_deviation"))
228 {
229 upperDeviation = fan["upper_deviation"].get<size_t>();
230 if (100 < upperDeviation)
231 {
232 auto msg =
233 std::format("Invalid upper_deviation of {} found, must "
234 "be between 0 and 100",
235 upperDeviation);
236
237 log<level::ERR>(msg.c_str());
238 throw std::runtime_error(msg.c_str());
239 }
240 }
241
242 // Construct the sensor definitions for this fan
243 auto sensorDefs = getSensorDefs(fan["sensors"]);
244
245 // Functional delay is optional and defaults to 0
246 size_t funcDelay = 0;
247 if (fan.contains("functional_delay"))
248 {
249 funcDelay = fan["functional_delay"].get<size_t>();
250 }
251
252 // Method is optional and defaults to time based functional
253 // determination
254 size_t method = MethodMode::timebased;
255 size_t countInterval = 1;
256 if (fan.contains("method"))
257 {
258 auto methodConf = fan["method"].get<std::string>();
259 auto methodFunc = methods.find(methodConf);
260 if (methodFunc != methods.end())
261 {
262 method = methodFunc->second;
263 }
264 else
265 {
266 // Log error on unsupported method parameter
267 log<level::ERR>("Invalid fan method");
268 throw std::runtime_error("Invalid fan method");
269 }
270
271 // Read the count interval value used with the count method.
272 if (method == MethodMode::count)
273 {
274 if (fan.contains("count_interval"))
275 {
276 countInterval = fan["count_interval"].get<size_t>();
277 }
278 }
279 }
280
281 // Timeout defaults to 0
282 size_t timeout = 0;
283 if (method == MethodMode::timebased)
284 {
285 if (!fan.contains("allowed_out_of_range_time"))
286 {
287 // Log error on missing required parameter
288 log<level::ERR>(
289 "Missing required fan monitor definition parameters",
290 entry("REQUIRED_PARAMETER=%s",
291 "{allowed_out_of_range_time}"));
292 throw std::runtime_error(
293 "Missing required fan monitor definition parameters");
294 }
295 else
296 {
297 timeout = fan["allowed_out_of_range_time"].get<size_t>();
298 }
299 }
300
301 // Monitor start delay is optional and defaults to 0
302 size_t monitorDelay = 0;
303 if (fan.contains("monitor_start_delay"))
304 {
305 monitorDelay = fan["monitor_start_delay"].get<size_t>();
306 }
307
308 // num_sensors_nonfunc_for_fan_nonfunc is optional and defaults
309 // to zero if not present, meaning the code will not set the
310 // parent fan to nonfunctional based on sensors.
311 size_t nonfuncSensorsCount = 0;
312 if (fan.contains("num_sensors_nonfunc_for_fan_nonfunc"))
313 {
314 nonfuncSensorsCount =
315 fan["num_sensors_nonfunc_for_fan_nonfunc"].get<size_t>();
316 }
317
318 // nonfunc_rotor_error_delay is optional, though it will
319 // default to zero if 'fault_handling' is present.
320 std::optional<size_t> nonfuncRotorErrorDelay;
321 if (fan.contains("nonfunc_rotor_error_delay"))
322 {
323 nonfuncRotorErrorDelay =
324 fan["nonfunc_rotor_error_delay"].get<size_t>();
325 }
326 else if (obj.contains("fault_handling"))
327 {
328 nonfuncRotorErrorDelay = 0;
329 }
330
331 // fan_missing_error_delay is optional.
332 std::optional<size_t> fanMissingErrorDelay;
333 if (fan.contains("fan_missing_error_delay"))
334 {
335 fanMissingErrorDelay =
336 fan.at("fan_missing_error_delay").get<size_t>();
337 }
338
339 // Handle optional conditions
340 auto cond = std::optional<Condition>();
341 if (fan.contains("condition"))
342 {
343 if (!fan["condition"].contains("name"))
344 {
345 // Log error on missing required parameter
346 log<level::ERR>(
347 "Missing required fan monitor condition parameter",
348 entry("REQUIRED_PARAMETER=%s", "{name}"));
349 throw std::runtime_error(
350 "Missing required fan monitor condition parameter");
351 }
352 auto name = fan["condition"]["name"].get<std::string>();
353 // The function for fan monitoring condition
354 // (Must have a supported function within the condition namespace)
355 std::transform(name.begin(), name.end(), name.begin(), tolower);
356 auto handler = conditions.find(name);
357 if (handler != conditions.end())
358 {
359 cond = handler->second(fan["condition"]);
360 }
361 else
362 {
363 log<level::INFO>(
364 "No handler found for configured condition",
365 entry("CONDITION_NAME=%s", name.c_str()),
366 entry("JSON_DUMP=%s", fan["condition"].dump().c_str()));
367 }
368 }
369
370 // if the fan should be set to functional when plugged in
371 bool setFuncOnPresent = false;
372 if (fan.contains("set_func_on_present"))
373 {
374 setFuncOnPresent = fan["set_func_on_present"].get<bool>();
375 }
376
377 FanDefinition def{
378 .name = fan["inventory"].get<std::string>(),
379 .method = method,
380 .funcDelay = funcDelay,
381 .timeout = timeout,
382 .deviation = deviation,
383 .upperDeviation = upperDeviation,
384 .numSensorFailsForNonfunc = nonfuncSensorsCount,
385 .monitorStartDelay = monitorDelay,
386 .countInterval = countInterval,
387 .nonfuncRotorErrDelay = nonfuncRotorErrorDelay,
388 .fanMissingErrDelay = fanMissingErrorDelay,
389 .sensorList = std::move(sensorDefs),
390 .condition = cond,
391 .funcOnPresent = setFuncOnPresent};
392
393 fanDefs.push_back(std::move(def));
394 }
395
396 return fanDefs;
397 }
398
getPowerOffPowerRuleState(const json & powerOffConfig)399 PowerRuleState getPowerOffPowerRuleState(const json& powerOffConfig)
400 {
401 // The state is optional and defaults to runtime
402 PowerRuleState ruleState{PowerRuleState::runtime};
403
404 if (powerOffConfig.contains("state"))
405 {
406 auto state = powerOffConfig.at("state").get<std::string>();
407 if (state == "at_pgood")
408 {
409 ruleState = PowerRuleState::atPgood;
410 }
411 else if (state != "runtime")
412 {
413 auto msg = std::format("Invalid power off state entry {}", state);
414 log<level::ERR>(msg.c_str());
415 throw std::runtime_error(msg.c_str());
416 }
417 }
418
419 return ruleState;
420 }
421
getPowerOffCause(const json & powerOffConfig)422 std::unique_ptr<PowerOffCause> getPowerOffCause(const json& powerOffConfig)
423 {
424 std::unique_ptr<PowerOffCause> cause;
425
426 if (!powerOffConfig.contains("count") || !powerOffConfig.contains("cause"))
427 {
428 const auto msg =
429 "Missing 'count' or 'cause' entries in power off config";
430 log<level::ERR>(msg);
431 throw std::runtime_error(msg);
432 }
433
434 auto count = powerOffConfig.at("count").get<size_t>();
435 auto powerOffCause = powerOffConfig.at("cause").get<std::string>();
436
437 const std::map<std::string, std::function<std::unique_ptr<PowerOffCause>()>>
438 causes{
439 {"missing_fan_frus",
440 [count]() { return std::make_unique<MissingFanFRUCause>(count); }},
441 {"nonfunc_fan_rotors",
442 [count]() {
443 return std::make_unique<NonfuncFanRotorCause>(count);
444 }},
445 {"fan_frus_with_nonfunc_rotors", [count]() {
446 return std::make_unique<FanFRUsWithNonfuncRotorsCause>(count);
447 }}};
448
449 auto it = causes.find(powerOffCause);
450 if (it != causes.end())
451 {
452 cause = it->second();
453 }
454 else
455 {
456 auto msg =
457 std::format("Invalid power off cause {} in power off config JSON",
458 powerOffCause);
459 log<level::ERR>(msg.c_str());
460 throw std::runtime_error(msg.c_str());
461 }
462
463 return cause;
464 }
465
466 std::unique_ptr<PowerOffAction>
getPowerOffAction(const json & powerOffConfig,std::shared_ptr<PowerInterfaceBase> & powerInterface,PowerOffAction::PrePowerOffFunc & func)467 getPowerOffAction(const json& powerOffConfig,
468 std::shared_ptr<PowerInterfaceBase>& powerInterface,
469 PowerOffAction::PrePowerOffFunc& func)
470 {
471 std::unique_ptr<PowerOffAction> action;
472 if (!powerOffConfig.contains("type"))
473 {
474 const auto msg = "Missing 'type' entry in power off config";
475 log<level::ERR>(msg);
476 throw std::runtime_error(msg);
477 }
478
479 auto type = powerOffConfig.at("type").get<std::string>();
480
481 if (((type == "hard") || (type == "soft")) &&
482 !powerOffConfig.contains("delay"))
483 {
484 const auto msg = "Missing 'delay' entry in power off config";
485 log<level::ERR>(msg);
486 throw std::runtime_error(msg);
487 }
488 else if ((type == "epow") &&
489 (!powerOffConfig.contains("service_mode_delay") ||
490 !powerOffConfig.contains("meltdown_delay")))
491 {
492 const auto msg = "Missing 'service_mode_delay' or 'meltdown_delay' "
493 "entry in power off config";
494 log<level::ERR>(msg);
495 throw std::runtime_error(msg);
496 }
497
498 if (type == "hard")
499 {
500 action = std::make_unique<HardPowerOff>(
501 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
502 }
503 else if (type == "soft")
504 {
505 action = std::make_unique<SoftPowerOff>(
506 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
507 }
508 else if (type == "epow")
509 {
510 action = std::make_unique<EpowPowerOff>(
511 powerOffConfig.at("service_mode_delay").get<uint32_t>(),
512 powerOffConfig.at("meltdown_delay").get<uint32_t>(), powerInterface,
513 func);
514 }
515 else
516 {
517 auto msg =
518 std::format("Invalid 'type' entry {} in power off config", type);
519 log<level::ERR>(msg.c_str());
520 throw std::runtime_error(msg.c_str());
521 }
522
523 return action;
524 }
525
getPowerOffRules(const json & obj,std::shared_ptr<PowerInterfaceBase> & powerInterface,PowerOffAction::PrePowerOffFunc & func)526 std::vector<std::unique_ptr<PowerOffRule>> getPowerOffRules(
527 const json& obj, std::shared_ptr<PowerInterfaceBase>& powerInterface,
528 PowerOffAction::PrePowerOffFunc& func)
529 {
530 std::vector<std::unique_ptr<PowerOffRule>> rules;
531
532 if (!(obj.contains("fault_handling") &&
533 obj.at("fault_handling").contains("power_off_config")))
534 {
535 return rules;
536 }
537
538 for (const auto& config : obj.at("fault_handling").at("power_off_config"))
539 {
540 auto state = getPowerOffPowerRuleState(config);
541 auto cause = getPowerOffCause(config);
542 auto action = getPowerOffAction(config, powerInterface, func);
543
544 auto rule = std::make_unique<PowerOffRule>(
545 std::move(state), std::move(cause), std::move(action));
546 rules.push_back(std::move(rule));
547 }
548
549 return rules;
550 }
551
getNumNonfuncRotorsBeforeError(const json & obj)552 std::optional<size_t> getNumNonfuncRotorsBeforeError(const json& obj)
553 {
554 std::optional<size_t> num;
555
556 if (obj.contains("fault_handling"))
557 {
558 // Defaults to 1 if not present inside of 'fault_handling'.
559 num = obj.at("fault_handling")
560 .value("num_nonfunc_rotors_before_error", 1);
561 }
562
563 return num;
564 }
565
566 } // namespace phosphor::fan::monitor
567