blob: d4f121f9d0bef1f567ad0fa0b82f7ed9a31a592c [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
Matthew Barth2d2caa32020-05-26 11:07:24 -050055JsonConfig::JsonConfig(sdbusplus::bus::bus& 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
62 process(config::load(config::getConfFile(_bus, confAppName, confFileName)));
Matt Spinler0daedd12021-01-25 14:46:03 -060063
64 for (auto& p : _policies)
65 {
66 p->monitor();
67 }
Matthew Barthfd05d642019-11-14 15:01:57 -060068}
69
70const policies& JsonConfig::get()
71{
72 return _policies;
73}
74
Matthew Barthf3e70472019-12-03 13:33:20 -060075void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc,
76 const struct signalfd_siginfo* sigInfo)
77{
78 try
79 {
Jolie Ku1a568652020-08-24 16:32:15 +080080 using config = fan::JsonConfig;
81
Matt Spinler9e9f5992020-09-30 08:29:24 -050082 _reporter.reset();
83
Matthew Barthf3e70472019-12-03 13:33:20 -060084 // Load and process the json configuration
Jolie Ku1a568652020-08-24 16:32:15 +080085 process(
86 config::load(config::getConfFile(_bus, confAppName, confFileName)));
87
Matthew Barth2d2caa32020-05-26 11:07:24 -050088 for (auto& p : _policies)
Matthew Barthf3e70472019-12-03 13:33:20 -060089 {
90 p->monitor();
91 }
92 log<level::INFO>("Configuration loaded successfully");
93 }
Patrick Williamsddb773b2021-10-06 11:24:49 -050094 catch (const std::runtime_error& re)
Matthew Barthf3e70472019-12-03 13:33:20 -060095 {
96 log<level::ERR>("Error loading config, no config changes made",
97 entry("LOAD_ERROR=%s", re.what()));
98 }
99}
100
Matthew Barth4a94dec2019-11-15 10:40:47 -0600101void JsonConfig::process(const json& jsonConf)
102{
Matthew Barthf3e70472019-12-03 13:33:20 -0600103 policies policies;
104 std::vector<fanPolicy> fans;
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600105 // Set the expected number of fan entries
106 // to be size of the list of fan json config entries
107 // (Must be done to eliminate vector reallocation of fan references)
Matt Spinler9e9f5992020-09-30 08:29:24 -0500108 fans.reserve(jsonConf.size());
109 for (auto& member : jsonConf)
Matthew Barth4a94dec2019-11-15 10:40:47 -0600110 {
Matthew Barthe7566632019-11-18 16:13:04 -0600111 if (!member.contains("name") || !member.contains("path") ||
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600112 !member.contains("methods") || !member.contains("rpolicy"))
Matthew Barth4a94dec2019-11-15 10:40:47 -0600113 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500114 log<level::ERR>("Missing required fan presence properties",
115 entry("REQUIRED_PROPERTIES=%s",
116 "{name, path, methods, rpolicy}"));
Matthew Barth4a94dec2019-11-15 10:40:47 -0600117 throw std::runtime_error(
118 "Missing required fan presence properties");
119 }
Matthew Barthe7566632019-11-18 16:13:04 -0600120
121 // Loop thru the configured methods of presence detection
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600122 std::vector<std::unique_ptr<PresenceSensor>> sensors;
Matthew Barthe7566632019-11-18 16:13:04 -0600123 for (auto& method : member["methods"].items())
124 {
125 if (!method.value().contains("type"))
126 {
127 log<level::ERR>(
128 "Missing required fan presence method type",
129 entry("FAN_NAME=%s",
Matthew Barth2d2caa32020-05-26 11:07:24 -0500130 member["name"].get<std::string>().c_str()));
Matthew Barthe7566632019-11-18 16:13:04 -0600131 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
Matthew Barthf3e70472019-12-03 13:33:20 -0600142 auto sensor = func->second(fans.size(), method.value());
Matthew Barthe7566632019-11-18 16:13:04 -0600143 if (sensor)
144 {
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600145 sensors.emplace_back(std::move(sensor));
Matthew Barthe7566632019-11-18 16:13:04 -0600146 }
147 }
148 else
149 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500150 log<level::ERR>(
151 "Invalid fan presence method type",
152 entry("FAN_NAME=%s",
153 member["name"].get<std::string>().c_str()),
154 entry("METHOD_TYPE=%s", type.c_str()));
Matthew Barthe7566632019-11-18 16:13:04 -0600155 throw std::runtime_error("Invalid fan presence method type");
156 }
157 }
Matt Spinler9e9f5992020-09-30 08:29:24 -0500158
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 auto fan =
168 std::make_tuple(member["name"], member["path"], timeUntilError);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600169 // Create a fan object
Matthew Barthf3e70472019-12-03 13:33:20 -0600170 fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600171
172 // Add fan presence policy
Matthew Barthf3e70472019-12-03 13:33:20 -0600173 auto policy = getPolicy(member["rpolicy"], fans.back());
174 if (policy)
175 {
176 policies.emplace_back(std::move(policy));
177 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600178 }
Matthew Barthf3e70472019-12-03 13:33:20 -0600179
180 // Success, refresh fans and policies lists
181 _fans.clear();
182 _fans.swap(fans);
183
184 _policies.clear();
185 _policies.swap(policies);
Matt Spinlere8122392020-09-24 13:22:18 -0500186
187 // Create the error reporter class if necessary
Matt Spinler9e9f5992020-09-30 08:29:24 -0500188 if (std::any_of(_fans.begin(), _fans.end(), [](const auto& fan) {
189 return std::get<std::optional<size_t>>(std::get<Fan>(fan)) !=
190 std::nullopt;
191 }))
Matt Spinlere8122392020-09-24 13:22:18 -0500192 {
Matt Spinler9e9f5992020-09-30 08:29:24 -0500193 _reporter = std::make_unique<ErrorReporter>(_bus, _fans);
Matt Spinlere8122392020-09-24 13:22:18 -0500194 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600195}
196
Matthew Barth2d2caa32020-05-26 11:07:24 -0500197std::unique_ptr<RedundancyPolicy>
198 JsonConfig::getPolicy(const json& rpolicy, const fanPolicy& fpolicy)
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600199{
200 if (!rpolicy.contains("type"))
201 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500202 log<level::ERR>(
203 "Missing required fan presence policy type",
204 entry("FAN_NAME=%s",
205 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
206 entry("REQUIRED_PROPERTIES=%s", "{type}"));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600207 throw std::runtime_error("Missing required fan presence policy type");
208 }
209
210 // The redundancy policy type for fan presence detection
211 // (Must have a supported function within the rpolicy namespace)
212 auto type = rpolicy["type"].get<std::string>();
213 std::transform(type.begin(), type.end(), type.begin(), tolower);
214 auto func = _rpolicies.find(type);
215 if (func != _rpolicies.end())
216 {
Matthew Barthf3e70472019-12-03 13:33:20 -0600217 // Call function for redundancy policy type and return the policy
218 return func->second(fpolicy);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600219 }
220 else
221 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500222 log<level::ERR>(
223 "Invalid fan presence policy type",
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600224 entry("FAN_NAME=%s",
Matthew Barth2d2caa32020-05-26 11:07:24 -0500225 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600226 entry("RPOLICY_TYPE=%s", type.c_str()));
227 throw std::runtime_error("Invalid fan presence methods policy type");
Matthew Barth4a94dec2019-11-15 10:40:47 -0600228 }
229}
230
Matthew Barthe7566632019-11-18 16:13:04 -0600231/**
232 * Methods of fan presence detection function definitions
233 */
234namespace method
235{
Matthew Barth2d2caa32020-05-26 11:07:24 -0500236// Get a constructed presence sensor for fan presence detection by tach
237std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
238{
239 if (!method.contains("sensors") || method["sensors"].size() == 0)
Matthew Barthe7566632019-11-18 16:13:04 -0600240 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500241 log<level::ERR>("Missing required tach method properties",
242 entry("FAN_ENTRY=%d", fanIndex),
243 entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
244 throw std::runtime_error("Missing required tach method properties");
Matthew Barthe7566632019-11-18 16:13:04 -0600245 }
246
Matthew Barth2d2caa32020-05-26 11:07:24 -0500247 std::vector<std::string> sensors;
248 for (auto& sensor : method["sensors"])
Matthew Barthe7566632019-11-18 16:13:04 -0600249 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500250 sensors.emplace_back(sensor.get<std::string>());
Matthew Barthe7566632019-11-18 16:13:04 -0600251 }
252
Matthew Barth2d2caa32020-05-26 11:07:24 -0500253 return std::make_unique<PolicyAccess<Tach, JsonConfig>>(fanIndex,
254 std::move(sensors));
255}
256
257// Get a constructed presence sensor for fan presence detection by gpio
258std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
259{
260 if (!method.contains("physpath") || !method.contains("devpath") ||
261 !method.contains("key"))
262 {
263 log<level::ERR>(
264 "Missing required gpio method properties",
265 entry("FAN_ENTRY=%d", fanIndex),
266 entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
267 throw std::runtime_error("Missing required gpio method properties");
268 }
269
270 auto physpath = method["physpath"].get<std::string>();
271 auto devpath = method["devpath"].get<std::string>();
272 auto key = method["key"].get<unsigned int>();
273
Mike Cappsa35a8902021-06-10 10:20:14 -0400274 try
275 {
276 return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(
277 fanIndex, physpath, devpath, key);
278 }
279 catch (const sdbusplus::exception_t& e)
280 {
281 namespace sdlogging = sdbusplus::xyz::openbmc_project::Logging::server;
282
283 log<level::ERR>(
284 fmt::format(
285 "Error creating Gpio device bridge, hardware not detected: {}",
286 e.what())
287 .c_str());
288
289 auto severity =
290 sdlogging::convertForMessage(sdlogging::Entry::Level::Error);
291
292 std::map<std::string, std::string> additionalData{
293 {"PHYSPATH", physpath},
294 {"DEVPATH", devpath},
295 {"FANINDEX", std::to_string(fanIndex)}};
296
297 try
298 {
299
300 util::SDBusPlus::lookupAndCallMethod(
301 loggingPath, loggingCreateIface, "Create",
302 "xyz.openbmc_project.Fan.Presence.Error.GPIODeviceUnavailable",
303 severity, additionalData);
304 }
305 catch (const util::DBusError& e)
306 {
307 log<level::ERR>(fmt::format("Call to create an error log for "
308 "presence-sensor failure failed: {}",
309 e.what())
310 .c_str());
311 }
312
313 return std::make_unique<PolicyAccess<NullGpio, JsonConfig>>();
314 }
Matthew Barth2d2caa32020-05-26 11:07:24 -0500315}
316
Matthew Barthe7566632019-11-18 16:13:04 -0600317} // namespace method
318
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600319/**
320 * Redundancy policies for fan presence detection function definitions
321 */
322namespace rpolicy
323{
Matthew Barth2d2caa32020-05-26 11:07:24 -0500324// Get an `Anyof` redundancy policy for the fan
325std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
326{
327 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
328 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600329 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500330 pSensors.emplace_back(*fanSensor);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600331 }
332
Matthew Barth2d2caa32020-05-26 11:07:24 -0500333 return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors);
334}
Matthew Barth26ad44a2019-11-22 15:37:14 -0600335
Matthew Barth2d2caa32020-05-26 11:07:24 -0500336// Get a `Fallback` redundancy policy for the fan
337std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
338{
339 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
340 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
341 {
342 // Place in the order given to fallback correctly
343 pSensors.emplace_back(*fanSensor);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600344 }
345
Matthew Barth2d2caa32020-05-26 11:07:24 -0500346 return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors);
347}
348
349} // namespace rpolicy
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600350
Matthew Barthfd05d642019-11-14 15:01:57 -0600351} // namespace presence
352} // namespace fan
353} // namespace phosphor