xref: /openbmc/phosphor-fan-presence/monitor/json_parser.cpp (revision fbf4703f3de7fbdbd8388e946bd71c3b760b174c)
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
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 
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 
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{.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));
189     }
190 
191     return sensorDefs;
192 }
193 
194 const std::vector<FanDefinition> getFanDefs(const json& obj)
195 {
196     std::vector<FanDefinition> fanDefs;
197 
198     for (const auto& fan : obj["fans"])
199     {
200         if (!fan.contains("inventory") || !fan.contains("deviation") ||
201             !fan.contains("sensors"))
202         {
203             // Log error on missing required parameters
204             log<level::ERR>(
205                 "Missing required fan monitor definition parameters",
206                 entry("REQUIRED_PARAMETERS=%s",
207                       "{inventory, deviation, sensors}"));
208             throw std::runtime_error(
209                 "Missing required fan monitor definition parameters");
210         }
211         // Valid deviation range is 0 - 100%
212         auto deviation = fan["deviation"].get<size_t>();
213         if (100 < deviation)
214         {
215             auto msg = std::format(
216                 "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());
221         }
222 
223         // 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 =
232                     std::format("Invalid upper_deviation of {} found, must "
233                                 "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 
241         // 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 
251         // Method is optional and defaults to time based functional
252         // determination
253         size_t method = MethodMode::timebased;
254         size_t countInterval = 1;
255         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             }
269 
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             }
278         }
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 
300         // 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 
307         // 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 
317         // 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 
330         // 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 
338         // Handle optional conditions
339         auto cond = std::optional<Condition>();
340         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         }
368 
369         // 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 
376         FanDefinition def{.name = fan["inventory"].get<std::string>(),
377                           .method = method,
378                           .funcDelay = funcDelay,
379                           .timeout = timeout,
380                           .deviation = deviation,
381                           .upperDeviation = upperDeviation,
382                           .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));
392     }
393 
394     return fanDefs;
395 }
396 
397 PowerRuleState 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         {
411             auto msg = std::format("Invalid power off state entry {}", state);
412             log<level::ERR>(msg.c_str());
413             throw std::runtime_error(msg.c_str());
414         }
415     }
416 
417     return ruleState;
418 }
419 
420 std::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); }},
439             {"nonfunc_fan_rotors", [count]() {
440                  return std::make_unique<NonfuncFanRotorCause>(count);
441              }}};
442 
443     auto it = causes.find(powerOffCause);
444     if (it != causes.end())
445     {
446         cause = it->second();
447     }
448     else
449     {
450         auto msg =
451             std::format("Invalid power off cause {} in power off config JSON",
452                         powerOffCause);
453         log<level::ERR>(msg.c_str());
454         throw std::runtime_error(msg.c_str());
455     }
456 
457     return cause;
458 }
459 
460 std::unique_ptr<PowerOffAction>
461     getPowerOffAction(const json& powerOffConfig,
462                       std::shared_ptr<PowerInterfaceBase>& powerInterface,
463                       PowerOffAction::PrePowerOffFunc& func)
464 {
465     std::unique_ptr<PowerOffAction> action;
466     if (!powerOffConfig.contains("type"))
467     {
468         const auto msg = "Missing 'type' entry in power off config";
469         log<level::ERR>(msg);
470         throw std::runtime_error(msg);
471     }
472 
473     auto type = powerOffConfig.at("type").get<std::string>();
474 
475     if (((type == "hard") || (type == "soft")) &&
476         !powerOffConfig.contains("delay"))
477     {
478         const auto msg = "Missing 'delay' entry in power off config";
479         log<level::ERR>(msg);
480         throw std::runtime_error(msg);
481     }
482     else if ((type == "epow") &&
483              (!powerOffConfig.contains("service_mode_delay") ||
484               !powerOffConfig.contains("meltdown_delay")))
485     {
486         const auto msg = "Missing 'service_mode_delay' or 'meltdown_delay' "
487                          "entry in power off config";
488         log<level::ERR>(msg);
489         throw std::runtime_error(msg);
490     }
491 
492     if (type == "hard")
493     {
494         action = std::make_unique<HardPowerOff>(
495             powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
496     }
497     else if (type == "soft")
498     {
499         action = std::make_unique<SoftPowerOff>(
500             powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
501     }
502     else if (type == "epow")
503     {
504         action = std::make_unique<EpowPowerOff>(
505             powerOffConfig.at("service_mode_delay").get<uint32_t>(),
506             powerOffConfig.at("meltdown_delay").get<uint32_t>(), powerInterface,
507             func);
508     }
509     else
510     {
511         auto msg = std::format("Invalid 'type' entry {} in power off config",
512                                type);
513         log<level::ERR>(msg.c_str());
514         throw std::runtime_error(msg.c_str());
515     }
516 
517     return action;
518 }
519 
520 std::vector<std::unique_ptr<PowerOffRule>>
521     getPowerOffRules(const json& obj,
522                      std::shared_ptr<PowerInterfaceBase>& powerInterface,
523                      PowerOffAction::PrePowerOffFunc& func)
524 {
525     std::vector<std::unique_ptr<PowerOffRule>> rules;
526 
527     if (!(obj.contains("fault_handling") &&
528           obj.at("fault_handling").contains("power_off_config")))
529     {
530         return rules;
531     }
532 
533     for (const auto& config : obj.at("fault_handling").at("power_off_config"))
534     {
535         auto state = getPowerOffPowerRuleState(config);
536         auto cause = getPowerOffCause(config);
537         auto action = getPowerOffAction(config, powerInterface, func);
538 
539         auto rule = std::make_unique<PowerOffRule>(
540             std::move(state), std::move(cause), std::move(action));
541         rules.push_back(std::move(rule));
542     }
543 
544     return rules;
545 }
546 
547 std::optional<size_t> getNumNonfuncRotorsBeforeError(const json& obj)
548 {
549     std::optional<size_t> num;
550 
551     if (obj.contains("fault_handling"))
552     {
553         // Defaults to 1 if not present inside of 'fault_handling'.
554         num = obj.at("fault_handling")
555                   .value("num_nonfunc_rotors_before_error", 1);
556     }
557 
558     return num;
559 }
560 
561 } // namespace phosphor::fan::monitor
562