blob: dbdc54bb222c1de53eefdeafe230023f1f23374a [file] [log] [blame]
Matthew Barthfd05d642019-11-14 15:01:57 -06001/**
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 */
Jolie Ku1a568652020-08-24 16:32:15 +080016#include "json_parser.hpp"
Matthew Barth2d2caa32020-05-26 11:07:24 -050017
18#include "anyof.hpp"
19#include "fallback.hpp"
20#include "gpio.hpp"
Jolie Ku1a568652020-08-24 16:32:15 +080021#include "json_config.hpp"
Matthew Barth2d2caa32020-05-26 11:07:24 -050022#include "sdbusplus.hpp"
23#include "tach.hpp"
24
Matthew Barth0961fae2019-11-15 09:00:27 -060025#include <nlohmann/json.hpp>
26#include <phosphor-logging/log.hpp>
Matthew Barth5060b102019-12-16 10:46:35 -060027#include <sdbusplus/bus.hpp>
Mike Cappsa35a8902021-06-10 10:20:14 -040028#include <xyz/openbmc_project/Logging/Create/server.hpp>
29#include <xyz/openbmc_project/Logging/Entry/server.hpp>
Matthew Barthfd05d642019-11-14 15:01:57 -060030
Matthew Barth2d2caa32020-05-26 11:07:24 -050031#include <filesystem>
32#include <fstream>
33#include <string>
Matthew Barthfd05d642019-11-14 15:01:57 -060034
35namespace phosphor
36{
37namespace fan
38{
39namespace presence
40{
41
Matthew Barth0961fae2019-11-15 09:00:27 -060042using json = nlohmann::json;
43namespace fs = std::filesystem;
44using namespace phosphor::logging;
45
Matthew Barthfd05d642019-11-14 15:01:57 -060046policies JsonConfig::_policies;
Matthew Barth2d2caa32020-05-26 11:07:24 -050047const std::map<std::string, methodHandler> JsonConfig::_methods = {
48 {"tach", method::getTach}, {"gpio", method::getGpio}};
49const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies = {
50 {"anyof", rpolicy::getAnyof}, {"fallback", rpolicy::getFallback}};
Matthew Barthfd05d642019-11-14 15:01:57 -060051
Mike Cappsa35a8902021-06-10 10:20:14 -040052const auto loggingPath = "/xyz/openbmc_project/logging";
53const auto loggingCreateIface = "xyz.openbmc_project.Logging.Create";
54
Patrick Williamscb356d42022-07-22 19:26:53 -050055JsonConfig::JsonConfig(sdbusplus::bus_t& bus) : _bus(bus)
Matt Spinler0daedd12021-01-25 14:46:03 -060056{}
Jolie Ku1a568652020-08-24 16:32:15 +080057
Matthew Barth5b839912021-06-21 14:46:34 -050058void JsonConfig::start()
Matt Spinler0daedd12021-01-25 14:46:03 -060059{
Matthew Barth5b839912021-06-21 14:46:34 -050060 using config = fan::JsonConfig;
61
Matt Spinlerdfc8c4d2022-06-22 16:52:27 -050062 if (!_loaded)
Matt Spinler0daedd12021-01-25 14:46:03 -060063 {
Mike Capps808d7fe2022-06-13 10:12:16 -040064 process(config::load(config::getConfFile(confAppName, confFileName)));
Matt Spinlerdfc8c4d2022-06-22 16:52:27 -050065
66 _loaded = true;
67
68 for (auto& p : _policies)
69 {
70 p->monitor();
71 }
Matt Spinler0daedd12021-01-25 14:46:03 -060072 }
Matthew Barthfd05d642019-11-14 15:01:57 -060073}
74
75const policies& JsonConfig::get()
76{
77 return _policies;
78}
79
Mike Capps808d7fe2022-06-13 10:12:16 -040080void JsonConfig::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/,
81 const struct signalfd_siginfo* /*sigInfo*/)
Matthew Barthf3e70472019-12-03 13:33:20 -060082{
83 try
84 {
Jolie Ku1a568652020-08-24 16:32:15 +080085 using config = fan::JsonConfig;
86
Matt Spinler9e9f5992020-09-30 08:29:24 -050087 _reporter.reset();
88
Matthew Barthf3e70472019-12-03 13:33:20 -060089 // Load and process the json configuration
Mike Capps808d7fe2022-06-13 10:12:16 -040090 process(config::load(config::getConfFile(confAppName, confFileName)));
Jolie Ku1a568652020-08-24 16:32:15 +080091
Matthew Barth2d2caa32020-05-26 11:07:24 -050092 for (auto& p : _policies)
Matthew Barthf3e70472019-12-03 13:33:20 -060093 {
94 p->monitor();
95 }
96 log<level::INFO>("Configuration loaded successfully");
97 }
Patrick Williamsddb773b2021-10-06 11:24:49 -050098 catch (const std::runtime_error& re)
Matthew Barthf3e70472019-12-03 13:33:20 -060099 {
100 log<level::ERR>("Error loading config, no config changes made",
101 entry("LOAD_ERROR=%s", re.what()));
102 }
103}
104
Matthew Barth4a94dec2019-11-15 10:40:47 -0600105void JsonConfig::process(const json& jsonConf)
106{
Matthew Barthf3e70472019-12-03 13:33:20 -0600107 policies policies;
108 std::vector<fanPolicy> fans;
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600109 // Set the expected number of fan entries
110 // to be size of the list of fan json config entries
111 // (Must be done to eliminate vector reallocation of fan references)
Matt Spinler9e9f5992020-09-30 08:29:24 -0500112 fans.reserve(jsonConf.size());
113 for (auto& member : jsonConf)
Matthew Barth4a94dec2019-11-15 10:40:47 -0600114 {
Matthew Barthe7566632019-11-18 16:13:04 -0600115 if (!member.contains("name") || !member.contains("path") ||
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600116 !member.contains("methods") || !member.contains("rpolicy"))
Matthew Barth4a94dec2019-11-15 10:40:47 -0600117 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500118 log<level::ERR>("Missing required fan presence properties",
119 entry("REQUIRED_PROPERTIES=%s",
120 "{name, path, methods, rpolicy}"));
Matthew Barth4a94dec2019-11-15 10:40:47 -0600121 throw std::runtime_error(
122 "Missing required fan presence properties");
123 }
Matthew Barthe7566632019-11-18 16:13:04 -0600124
125 // Loop thru the configured methods of presence detection
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600126 std::vector<std::unique_ptr<PresenceSensor>> sensors;
Matthew Barthe7566632019-11-18 16:13:04 -0600127 for (auto& method : member["methods"].items())
128 {
129 if (!method.value().contains("type"))
130 {
131 log<level::ERR>(
132 "Missing required fan presence method type",
133 entry("FAN_NAME=%s",
Matthew Barth2d2caa32020-05-26 11:07:24 -0500134 member["name"].get<std::string>().c_str()));
Matthew Barthe7566632019-11-18 16:13:04 -0600135 throw std::runtime_error(
136 "Missing required fan presence method type");
137 }
138 // The method type of fan presence detection
139 // (Must have a supported function within the method namespace)
140 auto type = method.value()["type"].get<std::string>();
141 std::transform(type.begin(), type.end(), type.begin(), tolower);
142 auto func = _methods.find(type);
143 if (func != _methods.end())
144 {
145 // Call function for method type
Matthew Barthf3e70472019-12-03 13:33:20 -0600146 auto sensor = func->second(fans.size(), method.value());
Matthew Barthe7566632019-11-18 16:13:04 -0600147 if (sensor)
148 {
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600149 sensors.emplace_back(std::move(sensor));
Matthew Barthe7566632019-11-18 16:13:04 -0600150 }
151 }
152 else
153 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500154 log<level::ERR>(
155 "Invalid fan presence method type",
156 entry("FAN_NAME=%s",
157 member["name"].get<std::string>().c_str()),
158 entry("METHOD_TYPE=%s", type.c_str()));
Matthew Barthe7566632019-11-18 16:13:04 -0600159 throw std::runtime_error("Invalid fan presence method type");
160 }
161 }
Matt Spinler9e9f5992020-09-30 08:29:24 -0500162
163 // Get the amount of time a fan must be not present before
164 // creating an error.
165 std::optional<size_t> timeUntilError;
166 if (member.contains("fan_missing_error_time"))
167 {
168 timeUntilError = member["fan_missing_error_time"].get<size_t>();
169 }
170
Matt Spinlerbc4179e2022-10-04 15:15:06 -0500171 std::unique_ptr<EEPROMDevice> eepromDevice;
172 if (member.contains("eeprom"))
173 {
174 const auto& eeprom = member.at("eeprom");
175 if (!eeprom.contains("bus_address") ||
176 !eeprom.contains("driver_name") ||
177 !eeprom.contains("bind_delay_ms"))
178 {
179 log<level::ERR>(
180 "Missing address, driver_name, or bind_delay_ms in eeprom "
181 "section",
182 entry("FAN_NAME=%s",
183 member["name"].get<std::string>().c_str()));
184
185 throw std::runtime_error("Missing address, driver_name, or "
186 "bind_delay_ms in eeprom section");
187 }
188 eepromDevice = std::make_unique<EEPROMDevice>(
189 eeprom["bus_address"].get<std::string>(),
190 eeprom["driver_name"].get<std::string>(),
191 eeprom["bind_delay_ms"].get<size_t>());
192 }
193
Matt Spinler9e9f5992020-09-30 08:29:24 -0500194 auto fan =
195 std::make_tuple(member["name"], member["path"], timeUntilError);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600196 // Create a fan object
Matthew Barthf3e70472019-12-03 13:33:20 -0600197 fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600198
199 // Add fan presence policy
Matt Spinlerbc4179e2022-10-04 15:15:06 -0500200 auto policy =
201 getPolicy(member["rpolicy"], fans.back(), std::move(eepromDevice));
Matthew Barthf3e70472019-12-03 13:33:20 -0600202 if (policy)
203 {
204 policies.emplace_back(std::move(policy));
205 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600206 }
Matthew Barthf3e70472019-12-03 13:33:20 -0600207
208 // Success, refresh fans and policies lists
209 _fans.clear();
210 _fans.swap(fans);
211
212 _policies.clear();
213 _policies.swap(policies);
Matt Spinlere8122392020-09-24 13:22:18 -0500214
215 // Create the error reporter class if necessary
Matt Spinler9e9f5992020-09-30 08:29:24 -0500216 if (std::any_of(_fans.begin(), _fans.end(), [](const auto& fan) {
217 return std::get<std::optional<size_t>>(std::get<Fan>(fan)) !=
218 std::nullopt;
219 }))
Matt Spinlere8122392020-09-24 13:22:18 -0500220 {
Matt Spinler9e9f5992020-09-30 08:29:24 -0500221 _reporter = std::make_unique<ErrorReporter>(_bus, _fans);
Matt Spinlere8122392020-09-24 13:22:18 -0500222 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600223}
224
Matthew Barth2d2caa32020-05-26 11:07:24 -0500225std::unique_ptr<RedundancyPolicy>
Matt Spinlerbc4179e2022-10-04 15:15:06 -0500226 JsonConfig::getPolicy(const json& rpolicy, const fanPolicy& fpolicy,
227 std::unique_ptr<EEPROMDevice> eepromDevice)
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600228{
229 if (!rpolicy.contains("type"))
230 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500231 log<level::ERR>(
232 "Missing required fan presence policy type",
233 entry("FAN_NAME=%s",
234 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
235 entry("REQUIRED_PROPERTIES=%s", "{type}"));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600236 throw std::runtime_error("Missing required fan presence policy type");
237 }
238
239 // The redundancy policy type for fan presence detection
240 // (Must have a supported function within the rpolicy namespace)
241 auto type = rpolicy["type"].get<std::string>();
242 std::transform(type.begin(), type.end(), type.begin(), tolower);
243 auto func = _rpolicies.find(type);
244 if (func != _rpolicies.end())
245 {
Matthew Barthf3e70472019-12-03 13:33:20 -0600246 // Call function for redundancy policy type and return the policy
Matt Spinlerbc4179e2022-10-04 15:15:06 -0500247 return func->second(fpolicy, std::move(eepromDevice));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600248 }
249 else
250 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500251 log<level::ERR>(
252 "Invalid fan presence policy type",
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600253 entry("FAN_NAME=%s",
Matthew Barth2d2caa32020-05-26 11:07:24 -0500254 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600255 entry("RPOLICY_TYPE=%s", type.c_str()));
256 throw std::runtime_error("Invalid fan presence methods policy type");
Matthew Barth4a94dec2019-11-15 10:40:47 -0600257 }
258}
259
Matthew Barthe7566632019-11-18 16:13:04 -0600260/**
261 * Methods of fan presence detection function definitions
262 */
263namespace method
264{
Matthew Barth2d2caa32020-05-26 11:07:24 -0500265// Get a constructed presence sensor for fan presence detection by tach
266std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
267{
268 if (!method.contains("sensors") || method["sensors"].size() == 0)
Matthew Barthe7566632019-11-18 16:13:04 -0600269 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500270 log<level::ERR>("Missing required tach method properties",
271 entry("FAN_ENTRY=%d", fanIndex),
272 entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
273 throw std::runtime_error("Missing required tach method properties");
Matthew Barthe7566632019-11-18 16:13:04 -0600274 }
275
Matthew Barth2d2caa32020-05-26 11:07:24 -0500276 std::vector<std::string> sensors;
277 for (auto& sensor : method["sensors"])
Matthew Barthe7566632019-11-18 16:13:04 -0600278 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500279 sensors.emplace_back(sensor.get<std::string>());
Matthew Barthe7566632019-11-18 16:13:04 -0600280 }
281
Matthew Barth2d2caa32020-05-26 11:07:24 -0500282 return std::make_unique<PolicyAccess<Tach, JsonConfig>>(fanIndex,
283 std::move(sensors));
284}
285
286// Get a constructed presence sensor for fan presence detection by gpio
287std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
288{
289 if (!method.contains("physpath") || !method.contains("devpath") ||
290 !method.contains("key"))
291 {
292 log<level::ERR>(
293 "Missing required gpio method properties",
294 entry("FAN_ENTRY=%d", fanIndex),
295 entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
296 throw std::runtime_error("Missing required gpio method properties");
297 }
298
299 auto physpath = method["physpath"].get<std::string>();
300 auto devpath = method["devpath"].get<std::string>();
301 auto key = method["key"].get<unsigned int>();
302
Mike Cappsa35a8902021-06-10 10:20:14 -0400303 try
304 {
305 return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(
306 fanIndex, physpath, devpath, key);
307 }
308 catch (const sdbusplus::exception_t& e)
309 {
310 namespace sdlogging = sdbusplus::xyz::openbmc_project::Logging::server;
311
312 log<level::ERR>(
313 fmt::format(
314 "Error creating Gpio device bridge, hardware not detected: {}",
315 e.what())
316 .c_str());
317
318 auto severity =
319 sdlogging::convertForMessage(sdlogging::Entry::Level::Error);
320
321 std::map<std::string, std::string> additionalData{
322 {"PHYSPATH", physpath},
323 {"DEVPATH", devpath},
324 {"FANINDEX", std::to_string(fanIndex)}};
325
326 try
327 {
328
329 util::SDBusPlus::lookupAndCallMethod(
330 loggingPath, loggingCreateIface, "Create",
331 "xyz.openbmc_project.Fan.Presence.Error.GPIODeviceUnavailable",
332 severity, additionalData);
333 }
334 catch (const util::DBusError& e)
335 {
336 log<level::ERR>(fmt::format("Call to create an error log for "
337 "presence-sensor failure failed: {}",
338 e.what())
339 .c_str());
340 }
341
342 return std::make_unique<PolicyAccess<NullGpio, JsonConfig>>();
343 }
Matthew Barth2d2caa32020-05-26 11:07:24 -0500344}
345
Matthew Barthe7566632019-11-18 16:13:04 -0600346} // namespace method
347
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600348/**
349 * Redundancy policies for fan presence detection function definitions
350 */
351namespace rpolicy
352{
Matthew Barth2d2caa32020-05-26 11:07:24 -0500353// Get an `Anyof` redundancy policy for the fan
Matt Spinlerbc4179e2022-10-04 15:15:06 -0500354std::unique_ptr<RedundancyPolicy>
355 getAnyof(const fanPolicy& fan, std::unique_ptr<EEPROMDevice> eepromDevice)
Matthew Barth2d2caa32020-05-26 11:07:24 -0500356{
357 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
358 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600359 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500360 pSensors.emplace_back(*fanSensor);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600361 }
362
Matt Spinlerbc4179e2022-10-04 15:15:06 -0500363 return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors,
364 std::move(eepromDevice));
Matthew Barth2d2caa32020-05-26 11:07:24 -0500365}
Matthew Barth26ad44a2019-11-22 15:37:14 -0600366
Matthew Barth2d2caa32020-05-26 11:07:24 -0500367// Get a `Fallback` redundancy policy for the fan
Matt Spinlerbc4179e2022-10-04 15:15:06 -0500368std::unique_ptr<RedundancyPolicy>
369 getFallback(const fanPolicy& fan,
370 std::unique_ptr<EEPROMDevice> eepromDevice)
Matthew Barth2d2caa32020-05-26 11:07:24 -0500371{
372 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
373 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
374 {
375 // Place in the order given to fallback correctly
376 pSensors.emplace_back(*fanSensor);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600377 }
378
Matt Spinlerbc4179e2022-10-04 15:15:06 -0500379 return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors,
380 std::move(eepromDevice));
Matthew Barth2d2caa32020-05-26 11:07:24 -0500381}
382
383} // namespace rpolicy
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600384
Matthew Barthfd05d642019-11-14 15:01:57 -0600385} // namespace presence
386} // namespace fan
387} // namespace phosphor