xref: /openbmc/phosphor-fan-presence/monitor/json_parser.cpp (revision 9d533806250cea56406bdd39e025f0d820c4ed90)
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