xref: /openbmc/phosphor-fan-presence/presence/json_parser.cpp (revision b99ce0ed9fbb06a765eeb801e61fdba6abcdf9dc)
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/log.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  
55  JsonConfig::JsonConfig(sdbusplus::bus_t& bus) : _bus(bus) {}
56  
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  
74  const policies& JsonConfig::get()
75  {
76      return _policies;
77  }
78  
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          log<level::INFO>("Configuration loaded successfully");
96      }
97      catch (const std::runtime_error& re)
98      {
99          log<level::ERR>("Error loading config, no config changes made",
100                          entry("LOAD_ERROR=%s", re.what()));
101      }
102  }
103  
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              log<level::ERR>("Missing required fan presence properties",
118                              entry("REQUIRED_PROPERTIES=%s",
119                                    "{name, path, methods, rpolicy}"));
120              throw std::runtime_error(
121                  "Missing required fan presence properties");
122          }
123  
124          // Loop thru the configured methods of presence detection
125          std::vector<std::unique_ptr<PresenceSensor>> sensors;
126          for (auto& method : member["methods"].items())
127          {
128              if (!method.value().contains("type"))
129              {
130                  log<level::ERR>(
131                      "Missing required fan presence method type",
132                      entry("FAN_NAME=%s",
133                            member["name"].get<std::string>().c_str()));
134                  throw std::runtime_error(
135                      "Missing required fan presence method type");
136              }
137              // The method type of fan presence detection
138              // (Must have a supported function within the method namespace)
139              auto type = method.value()["type"].get<std::string>();
140              std::transform(type.begin(), type.end(), type.begin(), tolower);
141              auto func = _methods.find(type);
142              if (func != _methods.end())
143              {
144                  // Call function for method type
145                  auto sensor = func->second(fans.size(), method.value());
146                  if (sensor)
147                  {
148                      sensors.emplace_back(std::move(sensor));
149                  }
150              }
151              else
152              {
153                  log<level::ERR>(
154                      "Invalid fan presence method type",
155                      entry("FAN_NAME=%s",
156                            member["name"].get<std::string>().c_str()),
157                      entry("METHOD_TYPE=%s", type.c_str()));
158                  throw std::runtime_error("Invalid fan presence method type");
159              }
160          }
161  
162          // Get the amount of time a fan must be not present before
163          // creating an error.
164          std::optional<size_t> timeUntilError;
165          if (member.contains("fan_missing_error_time"))
166          {
167              timeUntilError = member["fan_missing_error_time"].get<size_t>();
168          }
169  
170          std::unique_ptr<EEPROMDevice> eepromDevice;
171          if (member.contains("eeprom"))
172          {
173              const auto& eeprom = member.at("eeprom");
174              if (!eeprom.contains("bus_address") ||
175                  !eeprom.contains("driver_name") ||
176                  !eeprom.contains("bind_delay_ms"))
177              {
178                  log<level::ERR>(
179                      "Missing address, driver_name, or bind_delay_ms in eeprom "
180                      "section",
181                      entry("FAN_NAME=%s",
182                            member["name"].get<std::string>().c_str()));
183  
184                  throw std::runtime_error("Missing address, driver_name, or "
185                                           "bind_delay_ms in eeprom section");
186              }
187              eepromDevice = std::make_unique<EEPROMDevice>(
188                  eeprom["bus_address"].get<std::string>(),
189                  eeprom["driver_name"].get<std::string>(),
190                  eeprom["bind_delay_ms"].get<size_t>());
191          }
192  
193          auto fan = std::make_tuple(member["name"], member["path"],
194                                     timeUntilError);
195          // Create a fan object
196          fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
197  
198          // Add fan presence policy
199          auto policy = getPolicy(member["rpolicy"], fans.back(),
200                                  std::move(eepromDevice));
201          if (policy)
202          {
203              policies.emplace_back(std::move(policy));
204          }
205      }
206  
207      // Success, refresh fans and policies lists
208      _fans.clear();
209      _fans.swap(fans);
210  
211      _policies.clear();
212      _policies.swap(policies);
213  
214      // Create the error reporter class if necessary
215      if (std::any_of(_fans.begin(), _fans.end(), [](const auto& fan) {
216          return std::get<std::optional<size_t>>(std::get<Fan>(fan)) !=
217                 std::nullopt;
218      }))
219      {
220          _reporter = std::make_unique<ErrorReporter>(_bus, _fans);
221      }
222  }
223  
224  std::unique_ptr<RedundancyPolicy>
225      JsonConfig::getPolicy(const json& rpolicy, const fanPolicy& fpolicy,
226                            std::unique_ptr<EEPROMDevice> eepromDevice)
227  {
228      if (!rpolicy.contains("type"))
229      {
230          log<level::ERR>(
231              "Missing required fan presence policy type",
232              entry("FAN_NAME=%s",
233                    std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
234              entry("REQUIRED_PROPERTIES=%s", "{type}"));
235          throw std::runtime_error("Missing required fan presence policy type");
236      }
237  
238      // The redundancy policy type for fan presence detection
239      // (Must have a supported function within the rpolicy namespace)
240      auto type = rpolicy["type"].get<std::string>();
241      std::transform(type.begin(), type.end(), type.begin(), tolower);
242      auto func = _rpolicies.find(type);
243      if (func != _rpolicies.end())
244      {
245          // Call function for redundancy policy type and return the policy
246          return func->second(fpolicy, std::move(eepromDevice));
247      }
248      else
249      {
250          log<level::ERR>(
251              "Invalid fan presence policy type",
252              entry("FAN_NAME=%s",
253                    std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
254              entry("RPOLICY_TYPE=%s", type.c_str()));
255          throw std::runtime_error("Invalid fan presence methods policy type");
256      }
257  }
258  
259  /**
260   * Methods of fan presence detection function definitions
261   */
262  namespace method
263  {
264  // Get a constructed presence sensor for fan presence detection by tach
265  std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
266  {
267      if (!method.contains("sensors") || method["sensors"].size() == 0)
268      {
269          log<level::ERR>("Missing required tach method properties",
270                          entry("FAN_ENTRY=%d", fanIndex),
271                          entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
272          throw std::runtime_error("Missing required tach method properties");
273      }
274  
275      std::vector<std::string> sensors;
276      for (auto& sensor : method["sensors"])
277      {
278          sensors.emplace_back(sensor.get<std::string>());
279      }
280  
281      return std::make_unique<PolicyAccess<Tach, JsonConfig>>(fanIndex,
282                                                              std::move(sensors));
283  }
284  
285  // Get a constructed presence sensor for fan presence detection by gpio
286  std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
287  {
288      if (!method.contains("physpath") || !method.contains("devpath") ||
289          !method.contains("key"))
290      {
291          log<level::ERR>(
292              "Missing required gpio method properties",
293              entry("FAN_ENTRY=%d", fanIndex),
294              entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
295          throw std::runtime_error("Missing required gpio method properties");
296      }
297  
298      auto physpath = method["physpath"].get<std::string>();
299      auto devpath = method["devpath"].get<std::string>();
300      auto key = method["key"].get<unsigned int>();
301  
302      try
303      {
304          return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(
305              fanIndex, physpath, devpath, key);
306      }
307      catch (const sdbusplus::exception_t& e)
308      {
309          namespace sdlogging = sdbusplus::xyz::openbmc_project::Logging::server;
310  
311          log<level::ERR>(
312              std::format(
313                  "Error creating Gpio device bridge, hardware not detected: {}",
314                  e.what())
315                  .c_str());
316  
317          auto severity =
318              sdlogging::convertForMessage(sdlogging::Entry::Level::Error);
319  
320          std::map<std::string, std::string> additionalData{
321              {"PHYSPATH", physpath},
322              {"DEVPATH", devpath},
323              {"FANINDEX", std::to_string(fanIndex)}};
324  
325          try
326          {
327              util::SDBusPlus::lookupAndCallMethod(
328                  loggingPath, loggingCreateIface, "Create",
329                  "xyz.openbmc_project.Fan.Presence.Error.GPIODeviceUnavailable",
330                  severity, additionalData);
331          }
332          catch (const util::DBusError& e)
333          {
334              log<level::ERR>(std::format("Call to create an error log for "
335                                          "presence-sensor failure failed: {}",
336                                          e.what())
337                                  .c_str());
338          }
339  
340          return std::make_unique<PolicyAccess<NullGpio, JsonConfig>>();
341      }
342  }
343  
344  } // namespace method
345  
346  /**
347   * Redundancy policies for fan presence detection function definitions
348   */
349  namespace rpolicy
350  {
351  // Get an `Anyof` redundancy policy for the fan
352  std::unique_ptr<RedundancyPolicy>
353      getAnyof(const fanPolicy& fan, std::unique_ptr<EEPROMDevice> eepromDevice)
354  {
355      std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
356      for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
357      {
358          pSensors.emplace_back(*fanSensor);
359      }
360  
361      return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors,
362                                     std::move(eepromDevice));
363  }
364  
365  // Get a `Fallback` redundancy policy for the fan
366  std::unique_ptr<RedundancyPolicy>
367      getFallback(const fanPolicy& fan,
368                  std::unique_ptr<EEPROMDevice> eepromDevice)
369  {
370      std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
371      for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
372      {
373          // Place in the order given to fallback correctly
374          pSensors.emplace_back(*fanSensor);
375      }
376  
377      return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors,
378                                        std::move(eepromDevice));
379  }
380  
381  } // namespace rpolicy
382  
383  } // namespace presence
384  } // namespace fan
385  } // namespace phosphor
386