xref: /openbmc/phosphor-fan-presence/presence/json_parser.cpp (revision ebead9a8a1831b69ac044f3b5e24ff2b05b5a515)
1 /**
2  * Copyright © 2019 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 "anyof.hpp"
19 #include "fallback.hpp"
20 #include "gpio.hpp"
21 #include "json_config.hpp"
22 #include "sdbusplus.hpp"
23 #include "tach.hpp"
24 
25 #include <nlohmann/json.hpp>
26 #include <phosphor-logging/lg2.hpp>
27 #include <sdbusplus/bus.hpp>
28 #include <xyz/openbmc_project/Logging/Create/server.hpp>
29 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
30 
31 #include <filesystem>
32 #include <fstream>
33 #include <string>
34 
35 namespace phosphor
36 {
37 namespace fan
38 {
39 namespace presence
40 {
41 
42 using json = nlohmann::json;
43 namespace fs = std::filesystem;
44 using namespace phosphor::logging;
45 
46 policies JsonConfig::_policies;
47 const std::map<std::string, methodHandler> JsonConfig::_methods = {
48     {"tach", method::getTach}, {"gpio", method::getGpio}};
49 const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies = {
50     {"anyof", rpolicy::getAnyof}, {"fallback", rpolicy::getFallback}};
51 
52 const auto loggingPath = "/xyz/openbmc_project/logging";
53 const auto loggingCreateIface = "xyz.openbmc_project.Logging.Create";
54 
JsonConfig(sdbusplus::bus_t & bus)55 JsonConfig::JsonConfig(sdbusplus::bus_t& bus) : _bus(bus) {}
56 
start()57 void JsonConfig::start()
58 {
59     using config = fan::JsonConfig;
60 
61     if (!_loaded)
62     {
63         process(config::load(config::getConfFile(confAppName, confFileName)));
64 
65         _loaded = true;
66 
67         for (auto& p : _policies)
68         {
69             p->monitor();
70         }
71     }
72 }
73 
get()74 const policies& JsonConfig::get()
75 {
76     return _policies;
77 }
78 
sighupHandler(sdeventplus::source::Signal &,const struct signalfd_siginfo *)79 void JsonConfig::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/,
80                                const struct signalfd_siginfo* /*sigInfo*/)
81 {
82     try
83     {
84         using config = fan::JsonConfig;
85 
86         _reporter.reset();
87 
88         // Load and process the json configuration
89         process(config::load(config::getConfFile(confAppName, confFileName)));
90 
91         for (auto& p : _policies)
92         {
93             p->monitor();
94         }
95         lg2::info("Configuration loaded successfully");
96     }
97     catch (const std::runtime_error& re)
98     {
99         lg2::error("Error loading config, no config changes made: {ERROR}",
100                    "ERROR", re);
101     }
102 }
103 
process(const json & jsonConf)104 void JsonConfig::process(const json& jsonConf)
105 {
106     policies policies;
107     std::vector<fanPolicy> fans;
108     // Set the expected number of fan entries
109     // to be size of the list of fan json config entries
110     // (Must be done to eliminate vector reallocation of fan references)
111     fans.reserve(jsonConf.size());
112     for (auto& member : jsonConf)
113     {
114         if (!member.contains("name") || !member.contains("path") ||
115             !member.contains("methods") || !member.contains("rpolicy"))
116         {
117             lg2::error(
118                 "Missing one of the required fan presence properties, which are: 'name, path, methods, rpolicy'");
119             throw std::runtime_error(
120                 "Missing required fan presence properties");
121         }
122 
123         // Loop thru the configured methods of presence detection
124         std::vector<std::unique_ptr<PresenceSensor>> sensors;
125         for (auto& method : member["methods"].items())
126         {
127             if (!method.value().contains("type"))
128             {
129                 lg2::error(
130                     "Missing required fan presence method type for fan {FAN_NAME}",
131                     "FAN_NAME", member["name"].get<std::string>());
132                 throw std::runtime_error(
133                     "Missing required fan presence method type");
134             }
135             // The method type of fan presence detection
136             // (Must have a supported function within the method namespace)
137             auto type = method.value()["type"].get<std::string>();
138             std::transform(type.begin(), type.end(), type.begin(), tolower);
139             auto func = _methods.find(type);
140             if (func != _methods.end())
141             {
142                 // Call function for method type
143                 auto sensor = func->second(fans.size(), method.value());
144                 if (sensor)
145                 {
146                     sensors.emplace_back(std::move(sensor));
147                 }
148             }
149             else
150             {
151                 lg2::error(
152                     "Invalid fan presence method type {METHOD_TYPE} for fan {FAN_NAME}",
153                     "FAN_NAME", member["name"].get<std::string>(),
154                     "METHOD_TYPE", type);
155                 throw std::runtime_error("Invalid fan presence method type");
156             }
157         }
158 
159         // Get the amount of time a fan must be not present before
160         // creating an error.
161         std::optional<size_t> timeUntilError;
162         if (member.contains("fan_missing_error_time"))
163         {
164             timeUntilError = member["fan_missing_error_time"].get<size_t>();
165         }
166 
167         std::unique_ptr<EEPROMDevice> eepromDevice;
168         if (member.contains("eeprom"))
169         {
170             const auto& eeprom = member.at("eeprom");
171             if (!eeprom.contains("bus_address") ||
172                 !eeprom.contains("driver_name") ||
173                 !eeprom.contains("bind_delay_ms"))
174             {
175                 lg2::error(
176                     "Missing address, driver_name, or bind_delay_ms in eeprom section for fan {FAN_NAME}",
177                     "FAN_NAME", member["name"].get<std::string>());
178 
179                 throw std::runtime_error("Missing address, driver_name, or "
180                                          "bind_delay_ms in eeprom section");
181             }
182             eepromDevice = std::make_unique<EEPROMDevice>(
183                 eeprom["bus_address"].get<std::string>(),
184                 eeprom["driver_name"].get<std::string>(),
185                 eeprom["bind_delay_ms"].get<size_t>());
186         }
187 
188         auto fan =
189             std::make_tuple(member["name"], member["path"], timeUntilError);
190         // Create a fan object
191         fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
192 
193         // Add fan presence policy
194         auto policy =
195             getPolicy(member["rpolicy"], fans.back(), std::move(eepromDevice));
196         if (policy)
197         {
198             policies.emplace_back(std::move(policy));
199         }
200     }
201 
202     // Success, refresh fans and policies lists
203     _fans.clear();
204     _fans.swap(fans);
205 
206     _policies.clear();
207     _policies.swap(policies);
208 
209     // Create the error reporter class if necessary
210     if (std::any_of(_fans.begin(), _fans.end(), [](const auto& fan) {
211             return std::get<std::optional<size_t>>(std::get<Fan>(fan)) !=
212                    std::nullopt;
213         }))
214     {
215         _reporter = std::make_unique<ErrorReporter>(_bus, _fans);
216     }
217 }
218 
getPolicy(const json & rpolicy,const fanPolicy & fpolicy,std::unique_ptr<EEPROMDevice> eepromDevice)219 std::unique_ptr<RedundancyPolicy> JsonConfig::getPolicy(
220     const json& rpolicy, const fanPolicy& fpolicy,
221     std::unique_ptr<EEPROMDevice> eepromDevice)
222 {
223     if (!rpolicy.contains("type"))
224     {
225         lg2::error(
226             "Missing required fan presence policy type for fan {FAN_NAME}",
227             "FAN_NAME", std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)));
228         throw std::runtime_error("Missing required fan presence policy type");
229     }
230 
231     // The redundancy policy type for fan presence detection
232     // (Must have a supported function within the rpolicy namespace)
233     auto type = rpolicy["type"].get<std::string>();
234     std::transform(type.begin(), type.end(), type.begin(), tolower);
235     auto func = _rpolicies.find(type);
236     if (func != _rpolicies.end())
237     {
238         // Call function for redundancy policy type and return the policy
239         return func->second(fpolicy, std::move(eepromDevice));
240     }
241     else
242     {
243         lg2::error(
244             "Invalid fan presence policy type {RPOLICY_TYPE} for fan {FAN_NAME}",
245             "FAN_NAME", std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)),
246             "RPOLICY_TYPE", type);
247         throw std::runtime_error("Invalid fan presence methods policy type");
248     }
249 }
250 
251 /**
252  * Methods of fan presence detection function definitions
253  */
254 namespace method
255 {
256 // Get a constructed presence sensor for fan presence detection by tach
getTach(size_t fanIndex,const json & method)257 std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
258 {
259     if (!method.contains("sensors") || method["sensors"].size() == 0)
260     {
261         lg2::error(
262             "Missing required tach method property 'sensors' for fan index {FAN_ENTRY}",
263             "FAN_ENTRY", fanIndex);
264         throw std::runtime_error("Missing required tach method properties");
265     }
266 
267     std::vector<std::string> sensors;
268     for (auto& sensor : method["sensors"])
269     {
270         sensors.emplace_back(sensor.get<std::string>());
271     }
272 
273     return std::make_unique<PolicyAccess<Tach, JsonConfig>>(
274         fanIndex, std::move(sensors));
275 }
276 
277 // Get a constructed presence sensor for fan presence detection by gpio
getGpio(size_t fanIndex,const json & method)278 std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
279 {
280     if (!method.contains("physpath") || !method.contains("devpath") ||
281         !method.contains("key"))
282     {
283         lg2::error(
284             "Missing one of the required gpio method properties for fan index {FAN_ENTRY}, which are: 'physpath, devpath, key'",
285             "FAN_ENTRY", fanIndex);
286         throw std::runtime_error("Missing required gpio method properties");
287     }
288 
289     auto physpath = method["physpath"].get<std::string>();
290     auto devpath = method["devpath"].get<std::string>();
291     auto key = method["key"].get<unsigned int>();
292 
293     try
294     {
295         return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(
296             fanIndex, physpath, devpath, key);
297     }
298     catch (const sdbusplus::exception_t& e)
299     {
300         namespace sdlogging = sdbusplus::xyz::openbmc_project::Logging::server;
301 
302         lg2::error(
303             "Error creating Gpio device bridge, hardware not detected: {ERROR}",
304             "ERROR", e);
305 
306         auto severity =
307             sdlogging::convertForMessage(sdlogging::Entry::Level::Error);
308 
309         std::map<std::string, std::string> additionalData{
310             {"PHYSPATH", physpath},
311             {"DEVPATH", devpath},
312             {"FANINDEX", std::to_string(fanIndex)}};
313 
314         try
315         {
316             util::SDBusPlus::lookupAndCallMethod(
317                 loggingPath, loggingCreateIface, "Create",
318                 "xyz.openbmc_project.Fan.Presence.Error.GPIODeviceUnavailable",
319                 severity, additionalData);
320         }
321         catch (const util::DBusError& e)
322         {
323             lg2::error(
324                 "Call to create an error log for presence-sensor failure failed: {ERROR}",
325                 "ERROR", e);
326         }
327 
328         return std::make_unique<PolicyAccess<NullGpio, JsonConfig>>();
329     }
330 }
331 
332 } // namespace method
333 
334 /**
335  * Redundancy policies for fan presence detection function definitions
336  */
337 namespace rpolicy
338 {
339 // Get an `Anyof` redundancy policy for the fan
getAnyof(const fanPolicy & fan,std::unique_ptr<EEPROMDevice> eepromDevice)340 std::unique_ptr<RedundancyPolicy> getAnyof(
341     const fanPolicy& fan, std::unique_ptr<EEPROMDevice> eepromDevice)
342 {
343     std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
344     for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
345     {
346         pSensors.emplace_back(*fanSensor);
347     }
348 
349     return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors,
350                                    std::move(eepromDevice));
351 }
352 
353 // Get a `Fallback` redundancy policy for the fan
getFallback(const fanPolicy & fan,std::unique_ptr<EEPROMDevice> eepromDevice)354 std::unique_ptr<RedundancyPolicy> getFallback(
355     const fanPolicy& fan, std::unique_ptr<EEPROMDevice> eepromDevice)
356 {
357     std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
358     for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
359     {
360         // Place in the order given to fallback correctly
361         pSensors.emplace_back(*fanSensor);
362     }
363 
364     return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors,
365                                       std::move(eepromDevice));
366 }
367 
368 } // namespace rpolicy
369 
370 } // namespace presence
371 } // namespace fan
372 } // namespace phosphor
373