xref: /openbmc/phosphor-fan-presence/monitor/json_parser.cpp (revision 18fb12b87edfb09f5307e95b129f00aa3b6fcccd)
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 <fmt/format.h>
27 
28 #include <nlohmann/json.hpp>
29 #include <phosphor-logging/log.hpp>
30 
31 #include <algorithm>
32 #include <map>
33 #include <memory>
34 #include <optional>
35 #include <vector>
36 
37 namespace phosphor::fan::monitor
38 {
39 
40 using json = nlohmann::json;
41 using namespace phosphor::logging;
42 
43 namespace tClass
44 {
45 
46 // Get a constructed trust group class for a non-zero speed group
47 CreateGroupFunction
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 
57 const std::map<std::string, trustHandler> trusts = {
58     {"nonzerospeed", tClass::getNonZeroSpeed}};
59 const std::map<std::string, condHandler> conditions = {
60     {"propertiesmatch", condition::getPropertiesMatch}};
61 const std::map<std::string, size_t> methods = {
62     {"timebased", MethodMode::timebased}, {"count", MethodMode::count}};
63 
64 const 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 
127 const 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         }
149         // Target path is optional
150         std::string targetPath;
151         if (sensor.contains("target_path"))
152         {
153             targetPath = sensor["target_path"].get<std::string>();
154         }
155         // Factor is optional and defaults to 1
156         double factor = 1.0;
157         if (sensor.contains("factor"))
158         {
159             factor = sensor["factor"].get<double>();
160         }
161         // Offset is optional and defaults to 0
162         int64_t offset = 0;
163         if (sensor.contains("offset"))
164         {
165             offset = sensor["offset"].get<int64_t>();
166         }
167         // Threshold is optional and defaults to 1
168         size_t threshold = 1;
169         if (sensor.contains("threshold"))
170         {
171             threshold = sensor["threshold"].get<size_t>();
172         }
173         // 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         }
179 
180         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));
190     }
191 
192     return sensorDefs;
193 }
194 
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 = 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());
222         }
223 
224         // 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 
234         // Method is optional and defaults to time based functional
235         // determination
236         size_t method = MethodMode::timebased;
237         size_t countInterval = 1;
238         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             }
252 
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             }
261         }
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 
283         // 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 
290         // 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 
300         // 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 
313         // 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 
321         // Handle optional conditions
322         auto cond = std::optional<Condition>();
323         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         }
351 
352         // 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 
359         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));
374     }
375 
376     return fanDefs;
377 }
378 
379 PowerRuleState 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 
402 std::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 
442 std::unique_ptr<PowerOffAction>
443     getPowerOffAction(const json& powerOffConfig,
444                       std::shared_ptr<PowerInterfaceBase>& powerInterface,
445                       PowerOffAction::PrePowerOffFunc& func)
446 {
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>(
477             powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
478     }
479     else if (type == "soft")
480     {
481         action = std::make_unique<SoftPowerOff>(
482             powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
483     }
484     else if (type == "epow")
485     {
486         action = std::make_unique<EpowPowerOff>(
487             powerOffConfig.at("service_mode_delay").get<uint32_t>(),
488             powerOffConfig.at("meltdown_delay").get<uint32_t>(), powerInterface,
489             func);
490     }
491     else
492     {
493         auto msg = fmt::format("Invalid 'type' entry {} in power off config",
494                                type);
495         log<level::ERR>(msg.c_str());
496         throw std::runtime_error(msg.c_str());
497     }
498 
499     return action;
500 }
501 
502 std::vector<std::unique_ptr<PowerOffRule>>
503     getPowerOffRules(const json& obj,
504                      std::shared_ptr<PowerInterfaceBase>& powerInterface,
505                      PowerOffAction::PrePowerOffFunc& func)
506 {
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);
519         auto action = getPowerOffAction(config, powerInterface, func);
520 
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 
529 std::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 
543 } // namespace phosphor::fan::monitor
544