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