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 "types.hpp"
24 
25 #include <fmt/format.h>
26 
27 #include <nlohmann/json.hpp>
28 #include <phosphor-logging/log.hpp>
29 
30 #include <algorithm>
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 
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                 log<level::ERR>(
73                     "Missing required fan monitor trust group parameters",
74                     entry("REQUIRED_PARAMETERS=%s", "{class, group}"));
75                 throw std::runtime_error(
76                     "Missing required fan trust group parameters");
77             }
78             auto tgClass = stg["class"].get<std::string>();
79             std::vector<trust::GroupDefinition> group;
80             for (auto& member : stg["group"])
81             {
82                 // Construct list of group members
83                 if (!member.contains("name"))
84                 {
85                     // Log error on missing required parameter
86                     log<level::ERR>(
87                         "Missing required fan monitor trust group member name",
88                         entry("CLASS=%s", tgClass.c_str()));
89                     throw std::runtime_error(
90                         "Missing required fan monitor trust group member name");
91                 }
92                 auto in_trust = true;
93                 if (member.contains("in_trust"))
94                 {
95                     in_trust = member["in_trust"].get<bool>();
96                 }
97                 group.emplace_back(trust::GroupDefinition{
98                     member["name"].get<std::string>(), in_trust});
99             }
100             // The class for fan sensor trust groups
101             // (Must have a supported function within the tClass namespace)
102             std::transform(tgClass.begin(), tgClass.end(), tgClass.begin(),
103                            tolower);
104             auto handler = trusts.find(tgClass);
105             if (handler != trusts.end())
106             {
107                 // Call function for trust group class
108                 grpFuncs.emplace_back(handler->second(group));
109             }
110             else
111             {
112                 // Log error on unsupported trust group class
113                 log<level::ERR>("Invalid fan monitor trust group class",
114                                 entry("CLASS=%s", tgClass.c_str()));
115                 throw std::runtime_error(
116                     "Invalid fan monitor trust group class");
117             }
118         }
119     }
120 
121     return grpFuncs;
122 }
123 
124 const std::vector<SensorDefinition> getSensorDefs(const json& sensors)
125 {
126     std::vector<SensorDefinition> sensorDefs;
127 
128     for (const auto& sensor : sensors)
129     {
130         if (!sensor.contains("name") || !sensor.contains("has_target"))
131         {
132             // Log error on missing required parameters
133             log<level::ERR>(
134                 "Missing required fan sensor definition parameters",
135                 entry("REQUIRED_PARAMETERS=%s", "{name, has_target}"));
136             throw std::runtime_error(
137                 "Missing required fan sensor definition parameters");
138         }
139         // Target interface is optional and defaults to
140         // 'xyz.openbmc_project.Control.FanSpeed'
141         std::string targetIntf = "xyz.openbmc_project.Control.FanSpeed";
142         if (sensor.contains("target_interface"))
143         {
144             targetIntf = sensor["target_interface"].get<std::string>();
145         }
146         // Factor is optional and defaults to 1
147         auto factor = 1.0;
148         if (sensor.contains("factor"))
149         {
150             factor = sensor["factor"].get<double>();
151         }
152         // Offset is optional and defaults to 0
153         auto offset = 0;
154         if (sensor.contains("offset"))
155         {
156             offset = sensor["offset"].get<int64_t>();
157         }
158 
159         sensorDefs.emplace_back(std::tuple(sensor["name"].get<std::string>(),
160                                            sensor["has_target"].get<bool>(),
161                                            targetIntf, factor, offset));
162     }
163 
164     return sensorDefs;
165 }
166 
167 const std::vector<FanDefinition> getFanDefs(const json& obj)
168 {
169     std::vector<FanDefinition> fanDefs;
170 
171     for (const auto& fan : obj["fans"])
172     {
173         if (!fan.contains("inventory") ||
174             !fan.contains("allowed_out_of_range_time") ||
175             !fan.contains("deviation") || !fan.contains("sensors"))
176         {
177             // Log error on missing required parameters
178             log<level::ERR>(
179                 "Missing required fan monitor definition parameters",
180                 entry("REQUIRED_PARAMETERS=%s",
181                       "{inventory, allowed_out_of_range_time, deviation, "
182                       "sensors}"));
183             throw std::runtime_error(
184                 "Missing required fan monitor definition parameters");
185         }
186         // Construct the sensor definitions for this fan
187         auto sensorDefs = getSensorDefs(fan["sensors"]);
188 
189         // Functional delay is optional and defaults to 0
190         size_t funcDelay = 0;
191         if (fan.contains("functional_delay"))
192         {
193             funcDelay = fan["functional_delay"].get<size_t>();
194         }
195 
196         // Monitor start delay is optional and defaults to 0
197         size_t monitorDelay = 0;
198         if (fan.contains("monitor_start_delay"))
199         {
200             monitorDelay = fan["monitor_start_delay"].get<size_t>();
201         }
202 
203         // num_sensors_nonfunc_for_fan_nonfunc is optional and defaults
204         // to zero if not present, meaning the code will not set the
205         // parent fan to nonfunctional based on sensors.
206         size_t nonfuncSensorsCount = 0;
207         if (fan.contains("num_sensors_nonfunc_for_fan_nonfunc"))
208         {
209             nonfuncSensorsCount =
210                 fan["num_sensors_nonfunc_for_fan_nonfunc"].get<size_t>();
211         }
212 
213         // nonfunc_rotor_error_delay is optional, though it will
214         // default to zero if 'fault_handling' is present.
215         std::optional<size_t> nonfuncRotorErrorDelay;
216         if (fan.contains("nonfunc_rotor_error_delay"))
217         {
218             nonfuncRotorErrorDelay =
219                 fan["nonfunc_rotor_error_delay"].get<size_t>();
220         }
221         else if (obj.contains("fault_handling"))
222         {
223             nonfuncRotorErrorDelay = 0;
224         }
225 
226         // fan_missing_error_delay is optional.
227         std::optional<size_t> fanMissingErrorDelay;
228         if (fan.contains("fan_missing_error_delay"))
229         {
230             fanMissingErrorDelay =
231                 fan.at("fan_missing_error_delay").get<size_t>();
232         }
233 
234         // Handle optional conditions
235         auto cond = std::optional<Condition>();
236         if (fan.contains("condition"))
237         {
238             if (!fan["condition"].contains("name"))
239             {
240                 // Log error on missing required parameter
241                 log<level::ERR>(
242                     "Missing required fan monitor condition parameter",
243                     entry("REQUIRED_PARAMETER=%s", "{name}"));
244                 throw std::runtime_error(
245                     "Missing required fan monitor condition parameter");
246             }
247             auto name = fan["condition"]["name"].get<std::string>();
248             // The function for fan monitoring condition
249             // (Must have a supported function within the condition namespace)
250             std::transform(name.begin(), name.end(), name.begin(), tolower);
251             auto handler = conditions.find(name);
252             if (handler != conditions.end())
253             {
254                 cond = handler->second(fan["condition"]);
255             }
256             else
257             {
258                 log<level::INFO>(
259                     "No handler found for configured condition",
260                     entry("CONDITION_NAME=%s", name.c_str()),
261                     entry("JSON_DUMP=%s", fan["condition"].dump().c_str()));
262             }
263         }
264         fanDefs.emplace_back(std::tuple(
265             fan["inventory"].get<std::string>(), funcDelay,
266             fan["allowed_out_of_range_time"].get<size_t>(),
267             fan["deviation"].get<size_t>(), nonfuncSensorsCount, monitorDelay,
268             nonfuncRotorErrorDelay, fanMissingErrorDelay, sensorDefs, cond));
269     }
270 
271     return fanDefs;
272 }
273 
274 PowerRuleState getPowerOffPowerRuleState(const json& powerOffConfig)
275 {
276     // The state is optional and defaults to runtime
277     PowerRuleState ruleState{PowerRuleState::runtime};
278 
279     if (powerOffConfig.contains("state"))
280     {
281         auto state = powerOffConfig.at("state").get<std::string>();
282         if (state == "at_pgood")
283         {
284             ruleState = PowerRuleState::atPgood;
285         }
286         else if (state != "runtime")
287         {
288             auto msg = fmt::format("Invalid power off state entry {}", state);
289             log<level::ERR>(msg.c_str());
290             throw std::runtime_error(msg.c_str());
291         }
292     }
293 
294     return ruleState;
295 }
296 
297 std::unique_ptr<PowerOffCause> getPowerOffCause(const json& powerOffConfig)
298 {
299     std::unique_ptr<PowerOffCause> cause;
300 
301     if (!powerOffConfig.contains("count") || !powerOffConfig.contains("cause"))
302     {
303         const auto msg =
304             "Missing 'count' or 'cause' entries in power off config";
305         log<level::ERR>(msg);
306         throw std::runtime_error(msg);
307     }
308 
309     auto count = powerOffConfig.at("count").get<size_t>();
310     auto powerOffCause = powerOffConfig.at("cause").get<std::string>();
311 
312     const std::map<std::string, std::function<std::unique_ptr<PowerOffCause>()>>
313         causes{
314             {"missing_fan_frus",
315              [count]() { return std::make_unique<MissingFanFRUCause>(count); }},
316             {"nonfunc_fan_rotors", [count]() {
317                  return std::make_unique<NonfuncFanRotorCause>(count);
318              }}};
319 
320     auto it = causes.find(powerOffCause);
321     if (it != causes.end())
322     {
323         cause = it->second();
324     }
325     else
326     {
327         auto msg =
328             fmt::format("Invalid power off cause {} in power off config JSON",
329                         powerOffCause);
330         log<level::ERR>(msg.c_str());
331         throw std::runtime_error(msg.c_str());
332     }
333 
334     return cause;
335 }
336 
337 std::unique_ptr<PowerOffAction>
338     getPowerOffAction(const json& powerOffConfig,
339                       std::shared_ptr<PowerInterfaceBase>& powerInterface,
340                       PowerOffAction::PrePowerOffFunc& func)
341 {
342     std::unique_ptr<PowerOffAction> action;
343     if (!powerOffConfig.contains("type"))
344     {
345         const auto msg = "Missing 'type' entry in power off config";
346         log<level::ERR>(msg);
347         throw std::runtime_error(msg);
348     }
349 
350     auto type = powerOffConfig.at("type").get<std::string>();
351 
352     if (((type == "hard") || (type == "soft")) &&
353         !powerOffConfig.contains("delay"))
354     {
355         const auto msg = "Missing 'delay' entry in power off config";
356         log<level::ERR>(msg);
357         throw std::runtime_error(msg);
358     }
359     else if ((type == "epow") &&
360              (!powerOffConfig.contains("service_mode_delay") ||
361               !powerOffConfig.contains("meltdown_delay")))
362     {
363         const auto msg = "Missing 'service_mode_delay' or 'meltdown_delay' "
364                          "entry in power off config";
365         log<level::ERR>(msg);
366         throw std::runtime_error(msg);
367     }
368 
369     if (type == "hard")
370     {
371         action = std::make_unique<HardPowerOff>(
372             powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
373     }
374     else if (type == "soft")
375     {
376         action = std::make_unique<SoftPowerOff>(
377             powerOffConfig.at("delay").get<uint32_t>(), powerInterface, func);
378     }
379     else if (type == "epow")
380     {
381         action = std::make_unique<EpowPowerOff>(
382             powerOffConfig.at("service_mode_delay").get<uint32_t>(),
383             powerOffConfig.at("meltdown_delay").get<uint32_t>(), powerInterface,
384             func);
385     }
386     else
387     {
388         auto msg =
389             fmt::format("Invalid 'type' entry {} in power off config", type);
390         log<level::ERR>(msg.c_str());
391         throw std::runtime_error(msg.c_str());
392     }
393 
394     return action;
395 }
396 
397 std::vector<std::unique_ptr<PowerOffRule>>
398     getPowerOffRules(const json& obj,
399                      std::shared_ptr<PowerInterfaceBase>& powerInterface,
400                      PowerOffAction::PrePowerOffFunc& func)
401 {
402     std::vector<std::unique_ptr<PowerOffRule>> rules;
403 
404     if (!(obj.contains("fault_handling") &&
405           obj.at("fault_handling").contains("power_off_config")))
406     {
407         return rules;
408     }
409 
410     for (const auto& config : obj.at("fault_handling").at("power_off_config"))
411     {
412         auto state = getPowerOffPowerRuleState(config);
413         auto cause = getPowerOffCause(config);
414         auto action = getPowerOffAction(config, powerInterface, func);
415 
416         auto rule = std::make_unique<PowerOffRule>(
417             std::move(state), std::move(cause), std::move(action));
418         rules.push_back(std::move(rule));
419     }
420 
421     return rules;
422 }
423 
424 std::optional<size_t> getNumNonfuncRotorsBeforeError(const json& obj)
425 {
426     std::optional<size_t> num;
427 
428     if (obj.contains("fault_handling"))
429     {
430         // Defaults to 1 if not present inside of 'fault_handling'.
431         num = obj.at("fault_handling")
432                   .value("num_nonfunc_rotors_before_error", 1);
433     }
434 
435     return num;
436 }
437 
438 } // namespace phosphor::fan::monitor
439