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/lg2.hpp>
28
29 #include <algorithm>
30 #include <map>
31 #include <memory>
32 #include <optional>
33 #include <vector>
34
35 namespace phosphor::fan::monitor
36 {
37
38 using json = nlohmann::json;
39
40 namespace tClass
41 {
42
43 // Get a constructed trust group class for a non-zero speed group
getNonZeroSpeed(const std::vector<trust::GroupDefinition> & group)44 CreateGroupFunction getNonZeroSpeed(
45 const std::vector<trust::GroupDefinition>& group)
46 {
47 return [group]() {
48 return std::make_unique<trust::NonzeroSpeed>(std::move(group));
49 };
50 }
51
52 } // namespace tClass
53
54 const std::map<std::string, trustHandler> trusts = {
55 {"nonzerospeed", tClass::getNonZeroSpeed}};
56 const std::map<std::string, condHandler> conditions = {
57 {"propertiesmatch", condition::getPropertiesMatch}};
58 const std::map<std::string, size_t> methods = {
59 {"timebased", MethodMode::timebased}, {"count", MethodMode::count}};
60
getTrustGrps(const json & obj)61 const 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 lg2::error(
73 "Missing required fan monitor trust group parameters 'class, group'");
74 throw std::runtime_error(
75 "Missing required fan trust group parameters");
76 }
77 auto tgClass = stg["class"].get<std::string>();
78 std::vector<trust::GroupDefinition> group;
79 for (auto& member : stg["group"])
80 {
81 // Construct list of group members
82 if (!member.contains("name"))
83 {
84 // Log error on missing required parameter
85 lg2::error(
86 "Missing required fan monitor trust group member name for class {CLASS}",
87 "CLASS", tgClass);
88 throw std::runtime_error(
89 "Missing required fan monitor trust group member name");
90 }
91 auto in_trust = true;
92 if (member.contains("in_trust"))
93 {
94 in_trust = member["in_trust"].get<bool>();
95 }
96 group.emplace_back(trust::GroupDefinition{
97 member["name"].get<std::string>(), in_trust});
98 }
99 // The class for fan sensor trust groups
100 // (Must have a supported function within the tClass namespace)
101 std::transform(tgClass.begin(), tgClass.end(), tgClass.begin(),
102 tolower);
103 auto handler = trusts.find(tgClass);
104 if (handler != trusts.end())
105 {
106 // Call function for trust group class
107 grpFuncs.emplace_back(handler->second(group));
108 }
109 else
110 {
111 // Log error on unsupported trust group class
112 lg2::error("Invalid fan monitor trust group class {CLASS}",
113 "CLASS", tgClass);
114 throw std::runtime_error(
115 "Invalid fan monitor trust group class");
116 }
117 }
118 }
119
120 return grpFuncs;
121 }
122
getSensorDefs(const json & sensors)123 const std::vector<SensorDefinition> getSensorDefs(const json& sensors)
124 {
125 std::vector<SensorDefinition> sensorDefs;
126
127 for (const auto& sensor : sensors)
128 {
129 if (!sensor.contains("name") || !sensor.contains("has_target"))
130 {
131 // Log error on missing required parameters
132 lg2::error(
133 "Missing required fan sensor definition parameters 'name, has_target'");
134 throw std::runtime_error(
135 "Missing required fan sensor definition parameters");
136 }
137 // Target interface is optional and defaults to
138 // 'xyz.openbmc_project.Control.FanSpeed'
139 std::string targetIntf = "xyz.openbmc_project.Control.FanSpeed";
140 if (sensor.contains("target_interface"))
141 {
142 targetIntf = sensor["target_interface"].get<std::string>();
143 }
144 // Target path is optional
145 std::string targetPath;
146 if (sensor.contains("target_path"))
147 {
148 targetPath = sensor["target_path"].get<std::string>();
149 }
150 // Factor is optional and defaults to 1
151 double factor = 1.0;
152 if (sensor.contains("factor"))
153 {
154 factor = sensor["factor"].get<double>();
155 }
156 // Offset is optional and defaults to 0
157 int64_t offset = 0;
158 if (sensor.contains("offset"))
159 {
160 offset = sensor["offset"].get<int64_t>();
161 }
162 // Threshold is optional and defaults to 1
163 size_t threshold = 1;
164 if (sensor.contains("threshold"))
165 {
166 threshold = sensor["threshold"].get<size_t>();
167 }
168 // Ignore being above the allowed max is optional, defaults to not
169 bool ignoreAboveMax = false;
170 if (sensor.contains("ignore_above_max"))
171 {
172 ignoreAboveMax = sensor["ignore_above_max"].get<bool>();
173 }
174
175 SensorDefinition def{
176 .name = sensor["name"].get<std::string>(),
177 .hasTarget = sensor["has_target"].get<bool>(),
178 .targetInterface = targetIntf,
179 .targetPath = targetPath,
180 .factor = factor,
181 .offset = offset,
182 .threshold = threshold,
183 .ignoreAboveMax = ignoreAboveMax};
184
185 sensorDefs.push_back(std::move(def));
186 }
187
188 return sensorDefs;
189 }
190
getFanDefs(const json & obj)191 const std::vector<FanDefinition> getFanDefs(const json& obj)
192 {
193 std::vector<FanDefinition> fanDefs;
194
195 for (const auto& fan : obj["fans"])
196 {
197 if (!fan.contains("inventory") || !fan.contains("deviation") ||
198 !fan.contains("sensors"))
199 {
200 // Log error on missing required parameters
201 lg2::error(
202 "Missing required fan monitor definition parameters 'inventory, deviation, sensors'");
203 throw std::runtime_error(
204 "Missing required fan monitor definition parameters");
205 }
206 // Valid deviation range is 0 - 100%
207 auto deviation = fan["deviation"].get<size_t>();
208 if (100 < deviation)
209 {
210 lg2::error(
211 "Invalid deviation of {DEVIATION} found, must be between 0 and 100",
212 "DEVIATION", deviation);
213 throw std::runtime_error(
214 "Invalid deviation found, must be between 0 and 100");
215 }
216
217 // Upper deviation defaults to the deviation value and
218 // can also be separately specified.
219 size_t upperDeviation = deviation;
220 if (fan.contains("upper_deviation"))
221 {
222 upperDeviation = fan["upper_deviation"].get<size_t>();
223 if (100 < upperDeviation)
224 {
225 lg2::error(
226 "Invalid upper_deviation of {UPPER_DEVIATION} found, must be between 0 and 100",
227 "UPPER_DEVIATION", upperDeviation);
228 throw std::runtime_error(
229 "Invalid upper_deviation found, must be between 0 and 100");
230 }
231 }
232
233 // Construct the sensor definitions for this fan
234 auto sensorDefs = getSensorDefs(fan["sensors"]);
235
236 // Functional delay is optional and defaults to 0
237 size_t funcDelay = 0;
238 if (fan.contains("functional_delay"))
239 {
240 funcDelay = fan["functional_delay"].get<size_t>();
241 }
242
243 // Method is optional and defaults to time based functional
244 // determination
245 size_t method = MethodMode::timebased;
246 size_t countInterval = 1;
247 if (fan.contains("method"))
248 {
249 auto methodConf = fan["method"].get<std::string>();
250 auto methodFunc = methods.find(methodConf);
251 if (methodFunc != methods.end())
252 {
253 method = methodFunc->second;
254 }
255 else
256 {
257 // Log error on unsupported method parameter
258 lg2::error("Invalid fan method");
259 throw std::runtime_error("Invalid fan method");
260 }
261
262 // Read the count interval value used with the count method.
263 if (method == MethodMode::count)
264 {
265 if (fan.contains("count_interval"))
266 {
267 countInterval = fan["count_interval"].get<size_t>();
268 }
269 }
270 }
271
272 // Timeout defaults to 0
273 size_t timeout = 0;
274 if (method == MethodMode::timebased)
275 {
276 if (!fan.contains("allowed_out_of_range_time"))
277 {
278 // Log error on missing required parameter
279 lg2::error(
280 "Missing required fan monitor definition parameters 'allowed_out_of_range_time'");
281 throw std::runtime_error(
282 "Missing required fan monitor definition parameters");
283 }
284 else
285 {
286 timeout = fan["allowed_out_of_range_time"].get<size_t>();
287 }
288 }
289
290 // Monitor start delay is optional and defaults to 0
291 size_t monitorDelay = 0;
292 if (fan.contains("monitor_start_delay"))
293 {
294 monitorDelay = fan["monitor_start_delay"].get<size_t>();
295 }
296
297 // num_sensors_nonfunc_for_fan_nonfunc is optional and defaults
298 // to zero if not present, meaning the code will not set the
299 // parent fan to nonfunctional based on sensors.
300 size_t nonfuncSensorsCount = 0;
301 if (fan.contains("num_sensors_nonfunc_for_fan_nonfunc"))
302 {
303 nonfuncSensorsCount =
304 fan["num_sensors_nonfunc_for_fan_nonfunc"].get<size_t>();
305 }
306
307 // nonfunc_rotor_error_delay is optional, though it will
308 // default to zero if 'fault_handling' is present.
309 std::optional<size_t> nonfuncRotorErrorDelay;
310 if (fan.contains("nonfunc_rotor_error_delay"))
311 {
312 nonfuncRotorErrorDelay =
313 fan["nonfunc_rotor_error_delay"].get<size_t>();
314 }
315 else if (obj.contains("fault_handling"))
316 {
317 nonfuncRotorErrorDelay = 0;
318 }
319
320 // fan_missing_error_delay is optional.
321 std::optional<size_t> fanMissingErrorDelay;
322 if (fan.contains("fan_missing_error_delay"))
323 {
324 fanMissingErrorDelay =
325 fan.at("fan_missing_error_delay").get<size_t>();
326 }
327
328 // Handle optional conditions
329 auto cond = std::optional<Condition>();
330 if (fan.contains("condition"))
331 {
332 if (!fan["condition"].contains("name"))
333 {
334 // Log error on missing required parameter
335 lg2::error(
336 "Missing required fan monitor condition parameter 'name'");
337 throw std::runtime_error(
338 "Missing required fan monitor condition parameter");
339 }
340 auto name = fan["condition"]["name"].get<std::string>();
341 // The function for fan monitoring condition
342 // (Must have a supported function within the condition namespace)
343 std::transform(name.begin(), name.end(), name.begin(), tolower);
344 auto handler = conditions.find(name);
345 if (handler != conditions.end())
346 {
347 cond = handler->second(fan["condition"]);
348 }
349 else
350 {
351 lg2::info(
352 "No handler found for configured condition {CONDITION_NAME}",
353 "CONDITION_NAME", name, "JSON_DUMP",
354 fan["condition"].dump());
355 }
356 }
357
358 // if the fan should be set to functional when plugged in
359 bool setFuncOnPresent = false;
360 if (fan.contains("set_func_on_present"))
361 {
362 setFuncOnPresent = fan["set_func_on_present"].get<bool>();
363 }
364
365 FanDefinition def{
366 .name = fan["inventory"].get<std::string>(),
367 .method = method,
368 .funcDelay = funcDelay,
369 .timeout = timeout,
370 .deviation = deviation,
371 .upperDeviation = upperDeviation,
372 .numSensorFailsForNonfunc = nonfuncSensorsCount,
373 .monitorStartDelay = monitorDelay,
374 .countInterval = countInterval,
375 .nonfuncRotorErrDelay = nonfuncRotorErrorDelay,
376 .fanMissingErrDelay = fanMissingErrorDelay,
377 .sensorList = std::move(sensorDefs),
378 .condition = cond,
379 .funcOnPresent = setFuncOnPresent};
380
381 fanDefs.push_back(std::move(def));
382 }
383
384 return fanDefs;
385 }
386
getPowerOffPowerRuleState(const json & powerOffConfig)387 PowerRuleState getPowerOffPowerRuleState(const json& powerOffConfig)
388 {
389 // The state is optional and defaults to runtime
390 PowerRuleState ruleState{PowerRuleState::runtime};
391
392 if (powerOffConfig.contains("state"))
393 {
394 auto state = powerOffConfig.at("state").get<std::string>();
395 if (state == "at_pgood")
396 {
397 ruleState = PowerRuleState::atPgood;
398 }
399 else if (state != "runtime")
400 {
401 lg2::error("Invalid power off state entry {STATE}", "STATE", state);
402 throw std::runtime_error("Invalid power off state entry");
403 }
404 }
405
406 return ruleState;
407 }
408
getPowerOffCause(const json & powerOffConfig)409 std::unique_ptr<PowerOffCause> getPowerOffCause(const json& powerOffConfig)
410 {
411 std::unique_ptr<PowerOffCause> cause;
412
413 if (!powerOffConfig.contains("count") || !powerOffConfig.contains("cause"))
414 {
415 lg2::error("Missing 'count' or 'cause' entries in power off config");
416 throw std::runtime_error(
417 "Missing 'count' or 'cause' entries in power off config");
418 }
419
420 auto count = powerOffConfig.at("count").get<size_t>();
421 auto powerOffCause = powerOffConfig.at("cause").get<std::string>();
422
423 const std::map<std::string, std::function<std::unique_ptr<PowerOffCause>()>>
424 causes{
425 {"missing_fan_frus",
426 [count]() { return std::make_unique<MissingFanFRUCause>(count); }},
427 {"nonfunc_fan_rotors",
428 [count]() {
429 return std::make_unique<NonfuncFanRotorCause>(count);
430 }},
431 {"fan_frus_with_nonfunc_rotors", [count]() {
432 return std::make_unique<FanFRUsWithNonfuncRotorsCause>(count);
433 }}};
434
435 auto it = causes.find(powerOffCause);
436 if (it != causes.end())
437 {
438 cause = it->second();
439 }
440 else
441 {
442 lg2::error(
443 "Invalid power off cause {POWER_OFF_CAUSE} in power off config JSON",
444 "POWER_OFF_CAUSE", powerOffCause);
445 throw std::runtime_error(
446 "Invalid power off cause in power off config JSON");
447 }
448
449 return cause;
450 }
451
getPowerOffAction(const json & powerOffConfig,std::shared_ptr<PowerInterfaceBase> & powerInterface,PowerOffAction::PrePowerOffFunc & func)452 std::unique_ptr<PowerOffAction> getPowerOffAction(
453 const json& powerOffConfig,
454 std::shared_ptr<PowerInterfaceBase>& powerInterface,
455 PowerOffAction::PrePowerOffFunc& func)
456 {
457 std::unique_ptr<PowerOffAction> action;
458 if (!powerOffConfig.contains("type"))
459 {
460 lg2::error("Missing 'type' entry in power off config");
461 throw std::runtime_error("Missing 'type' entry in power off config");
462 }
463
464 auto type = powerOffConfig.at("type").get<std::string>();
465
466 if (((type == "hard") || (type == "soft")) &&
467 !powerOffConfig.contains("delay"))
468 {
469 lg2::error("Missing 'delay' entry in power off config");
470 throw std::runtime_error("Missing 'delay' entry in power off config");
471 }
472 else if ((type == "epow") &&
473 (!powerOffConfig.contains("service_mode_delay") ||
474 !powerOffConfig.contains("meltdown_delay")))
475 {
476 lg2::error(
477 "Missing 'service_mode_delay' or 'meltdown_delay' entry in power off config");
478 throw std::runtime_error(
479 "Missing 'service_mode_delay' or 'meltdown_delay' entry in power off config");
480 }
481
482 if (type == "hard")
483 {
484 action = std::make_unique<HardPowerOff>(
485 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
486 }
487 else if (type == "soft")
488 {
489 action = std::make_unique<SoftPowerOff>(
490 powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
491 }
492 else if (type == "epow")
493 {
494 action = std::make_unique<EpowPowerOff>(
495 powerOffConfig.at("service_mode_delay").get<uint32_t>(),
496 powerOffConfig.at("meltdown_delay").get<uint32_t>(), powerInterface,
497 func);
498 }
499 else
500 {
501 lg2::error("Invalid 'type' entry {TYPE} in power off config", "TYPE",
502 type);
503 throw std::runtime_error("Invalid 'type' entry in power off config");
504 }
505
506 return action;
507 }
508
getPowerOffRules(const json & obj,std::shared_ptr<PowerInterfaceBase> & powerInterface,PowerOffAction::PrePowerOffFunc & func)509 std::vector<std::unique_ptr<PowerOffRule>> getPowerOffRules(
510 const json& obj, std::shared_ptr<PowerInterfaceBase>& powerInterface,
511 PowerOffAction::PrePowerOffFunc& func)
512 {
513 std::vector<std::unique_ptr<PowerOffRule>> rules;
514
515 if (!(obj.contains("fault_handling") &&
516 obj.at("fault_handling").contains("power_off_config")))
517 {
518 return rules;
519 }
520
521 for (const auto& config : obj.at("fault_handling").at("power_off_config"))
522 {
523 auto state = getPowerOffPowerRuleState(config);
524 auto cause = getPowerOffCause(config);
525 auto action = getPowerOffAction(config, powerInterface, func);
526
527 auto rule = std::make_unique<PowerOffRule>(
528 std::move(state), std::move(cause), std::move(action));
529 rules.push_back(std::move(rule));
530 }
531
532 return rules;
533 }
534
getNumNonfuncRotorsBeforeError(const json & obj)535 std::optional<size_t> getNumNonfuncRotorsBeforeError(const json& obj)
536 {
537 std::optional<size_t> num;
538
539 if (obj.contains("fault_handling"))
540 {
541 // Defaults to 1 if not present inside of 'fault_handling'.
542 num = obj.at("fault_handling")
543 .value("num_nonfunc_rotors_before_error", 1);
544 }
545
546 return num;
547 }
548
549 } // namespace phosphor::fan::monitor
550