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