blob: 4c4a55ca356ead81711a2119f9beac0453de6ef8 [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>
Matthew Barthfd05d642019-11-14 15:01:57 -060028
Matthew Barth2d2caa32020-05-26 11:07:24 -050029#include <filesystem>
30#include <fstream>
31#include <string>
Matthew Barthfd05d642019-11-14 15:01:57 -060032
33namespace phosphor
34{
35namespace fan
36{
37namespace presence
38{
39
Matthew Barth0961fae2019-11-15 09:00:27 -060040using json = nlohmann::json;
41namespace fs = std::filesystem;
42using namespace phosphor::logging;
43
Matthew Barthfd05d642019-11-14 15:01:57 -060044policies JsonConfig::_policies;
Matthew Barth2d2caa32020-05-26 11:07:24 -050045const std::map<std::string, methodHandler> JsonConfig::_methods = {
46 {"tach", method::getTach}, {"gpio", method::getGpio}};
47const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies = {
48 {"anyof", rpolicy::getAnyof}, {"fallback", rpolicy::getFallback}};
Matthew Barthfd05d642019-11-14 15:01:57 -060049
Matthew Barth2d2caa32020-05-26 11:07:24 -050050JsonConfig::JsonConfig(sdbusplus::bus::bus& bus) : _bus(bus)
Matt Spinler0daedd12021-01-25 14:46:03 -060051{}
Jolie Ku1a568652020-08-24 16:32:15 +080052
Matthew Barth5b839912021-06-21 14:46:34 -050053void JsonConfig::start()
Matt Spinler0daedd12021-01-25 14:46:03 -060054{
Matthew Barth5b839912021-06-21 14:46:34 -050055 using config = fan::JsonConfig;
56
57 process(config::load(config::getConfFile(_bus, confAppName, confFileName)));
Matt Spinler0daedd12021-01-25 14:46:03 -060058
59 for (auto& p : _policies)
60 {
61 p->monitor();
62 }
Matthew Barthfd05d642019-11-14 15:01:57 -060063}
64
65const policies& JsonConfig::get()
66{
67 return _policies;
68}
69
Matthew Barthf3e70472019-12-03 13:33:20 -060070void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc,
71 const struct signalfd_siginfo* sigInfo)
72{
73 try
74 {
Jolie Ku1a568652020-08-24 16:32:15 +080075 using config = fan::JsonConfig;
76
Matt Spinler9e9f5992020-09-30 08:29:24 -050077 _reporter.reset();
78
Matthew Barthf3e70472019-12-03 13:33:20 -060079 // Load and process the json configuration
Jolie Ku1a568652020-08-24 16:32:15 +080080 process(
81 config::load(config::getConfFile(_bus, confAppName, confFileName)));
82
Matthew Barth2d2caa32020-05-26 11:07:24 -050083 for (auto& p : _policies)
Matthew Barthf3e70472019-12-03 13:33:20 -060084 {
85 p->monitor();
86 }
87 log<level::INFO>("Configuration loaded successfully");
88 }
89 catch (std::runtime_error& re)
90 {
91 log<level::ERR>("Error loading config, no config changes made",
92 entry("LOAD_ERROR=%s", re.what()));
93 }
94}
95
Matthew Barth4a94dec2019-11-15 10:40:47 -060096void JsonConfig::process(const json& jsonConf)
97{
Matthew Barthf3e70472019-12-03 13:33:20 -060098 policies policies;
99 std::vector<fanPolicy> fans;
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600100 // Set the expected number of fan entries
101 // to be size of the list of fan json config entries
102 // (Must be done to eliminate vector reallocation of fan references)
Matt Spinler9e9f5992020-09-30 08:29:24 -0500103 fans.reserve(jsonConf.size());
104 for (auto& member : jsonConf)
Matthew Barth4a94dec2019-11-15 10:40:47 -0600105 {
Matthew Barthe7566632019-11-18 16:13:04 -0600106 if (!member.contains("name") || !member.contains("path") ||
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600107 !member.contains("methods") || !member.contains("rpolicy"))
Matthew Barth4a94dec2019-11-15 10:40:47 -0600108 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500109 log<level::ERR>("Missing required fan presence properties",
110 entry("REQUIRED_PROPERTIES=%s",
111 "{name, path, methods, rpolicy}"));
Matthew Barth4a94dec2019-11-15 10:40:47 -0600112 throw std::runtime_error(
113 "Missing required fan presence properties");
114 }
Matthew Barthe7566632019-11-18 16:13:04 -0600115
116 // Loop thru the configured methods of presence detection
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600117 std::vector<std::unique_ptr<PresenceSensor>> sensors;
Matthew Barthe7566632019-11-18 16:13:04 -0600118 for (auto& method : member["methods"].items())
119 {
120 if (!method.value().contains("type"))
121 {
122 log<level::ERR>(
123 "Missing required fan presence method type",
124 entry("FAN_NAME=%s",
Matthew Barth2d2caa32020-05-26 11:07:24 -0500125 member["name"].get<std::string>().c_str()));
Matthew Barthe7566632019-11-18 16:13:04 -0600126 throw std::runtime_error(
127 "Missing required fan presence method type");
128 }
129 // The method type of fan presence detection
130 // (Must have a supported function within the method namespace)
131 auto type = method.value()["type"].get<std::string>();
132 std::transform(type.begin(), type.end(), type.begin(), tolower);
133 auto func = _methods.find(type);
134 if (func != _methods.end())
135 {
136 // Call function for method type
Matthew Barthf3e70472019-12-03 13:33:20 -0600137 auto sensor = func->second(fans.size(), method.value());
Matthew Barthe7566632019-11-18 16:13:04 -0600138 if (sensor)
139 {
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600140 sensors.emplace_back(std::move(sensor));
Matthew Barthe7566632019-11-18 16:13:04 -0600141 }
142 }
143 else
144 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500145 log<level::ERR>(
146 "Invalid fan presence method type",
147 entry("FAN_NAME=%s",
148 member["name"].get<std::string>().c_str()),
149 entry("METHOD_TYPE=%s", type.c_str()));
Matthew Barthe7566632019-11-18 16:13:04 -0600150 throw std::runtime_error("Invalid fan presence method type");
151 }
152 }
Matt Spinler9e9f5992020-09-30 08:29:24 -0500153
154 // Get the amount of time a fan must be not present before
155 // creating an error.
156 std::optional<size_t> timeUntilError;
157 if (member.contains("fan_missing_error_time"))
158 {
159 timeUntilError = member["fan_missing_error_time"].get<size_t>();
160 }
161
162 auto fan =
163 std::make_tuple(member["name"], member["path"], timeUntilError);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600164 // Create a fan object
Matthew Barthf3e70472019-12-03 13:33:20 -0600165 fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600166
167 // Add fan presence policy
Matthew Barthf3e70472019-12-03 13:33:20 -0600168 auto policy = getPolicy(member["rpolicy"], fans.back());
169 if (policy)
170 {
171 policies.emplace_back(std::move(policy));
172 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600173 }
Matthew Barthf3e70472019-12-03 13:33:20 -0600174
175 // Success, refresh fans and policies lists
176 _fans.clear();
177 _fans.swap(fans);
178
179 _policies.clear();
180 _policies.swap(policies);
Matt Spinlere8122392020-09-24 13:22:18 -0500181
182 // Create the error reporter class if necessary
Matt Spinler9e9f5992020-09-30 08:29:24 -0500183 if (std::any_of(_fans.begin(), _fans.end(), [](const auto& fan) {
184 return std::get<std::optional<size_t>>(std::get<Fan>(fan)) !=
185 std::nullopt;
186 }))
Matt Spinlere8122392020-09-24 13:22:18 -0500187 {
Matt Spinler9e9f5992020-09-30 08:29:24 -0500188 _reporter = std::make_unique<ErrorReporter>(_bus, _fans);
Matt Spinlere8122392020-09-24 13:22:18 -0500189 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600190}
191
Matthew Barth2d2caa32020-05-26 11:07:24 -0500192std::unique_ptr<RedundancyPolicy>
193 JsonConfig::getPolicy(const json& rpolicy, const fanPolicy& fpolicy)
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600194{
195 if (!rpolicy.contains("type"))
196 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500197 log<level::ERR>(
198 "Missing required fan presence policy type",
199 entry("FAN_NAME=%s",
200 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
201 entry("REQUIRED_PROPERTIES=%s", "{type}"));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600202 throw std::runtime_error("Missing required fan presence policy type");
203 }
204
205 // The redundancy policy type for fan presence detection
206 // (Must have a supported function within the rpolicy namespace)
207 auto type = rpolicy["type"].get<std::string>();
208 std::transform(type.begin(), type.end(), type.begin(), tolower);
209 auto func = _rpolicies.find(type);
210 if (func != _rpolicies.end())
211 {
Matthew Barthf3e70472019-12-03 13:33:20 -0600212 // Call function for redundancy policy type and return the policy
213 return func->second(fpolicy);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600214 }
215 else
216 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500217 log<level::ERR>(
218 "Invalid fan presence policy type",
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600219 entry("FAN_NAME=%s",
Matthew Barth2d2caa32020-05-26 11:07:24 -0500220 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600221 entry("RPOLICY_TYPE=%s", type.c_str()));
222 throw std::runtime_error("Invalid fan presence methods policy type");
Matthew Barth4a94dec2019-11-15 10:40:47 -0600223 }
224}
225
Matthew Barthe7566632019-11-18 16:13:04 -0600226/**
227 * Methods of fan presence detection function definitions
228 */
229namespace method
230{
Matthew Barth2d2caa32020-05-26 11:07:24 -0500231// Get a constructed presence sensor for fan presence detection by tach
232std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
233{
234 if (!method.contains("sensors") || method["sensors"].size() == 0)
Matthew Barthe7566632019-11-18 16:13:04 -0600235 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500236 log<level::ERR>("Missing required tach method properties",
237 entry("FAN_ENTRY=%d", fanIndex),
238 entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
239 throw std::runtime_error("Missing required tach method properties");
Matthew Barthe7566632019-11-18 16:13:04 -0600240 }
241
Matthew Barth2d2caa32020-05-26 11:07:24 -0500242 std::vector<std::string> sensors;
243 for (auto& sensor : method["sensors"])
Matthew Barthe7566632019-11-18 16:13:04 -0600244 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500245 sensors.emplace_back(sensor.get<std::string>());
Matthew Barthe7566632019-11-18 16:13:04 -0600246 }
247
Matthew Barth2d2caa32020-05-26 11:07:24 -0500248 return std::make_unique<PolicyAccess<Tach, JsonConfig>>(fanIndex,
249 std::move(sensors));
250}
251
252// Get a constructed presence sensor for fan presence detection by gpio
253std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
254{
255 if (!method.contains("physpath") || !method.contains("devpath") ||
256 !method.contains("key"))
257 {
258 log<level::ERR>(
259 "Missing required gpio method properties",
260 entry("FAN_ENTRY=%d", fanIndex),
261 entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
262 throw std::runtime_error("Missing required gpio method properties");
263 }
264
265 auto physpath = method["physpath"].get<std::string>();
266 auto devpath = method["devpath"].get<std::string>();
267 auto key = method["key"].get<unsigned int>();
268
269 return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(fanIndex, physpath,
270 devpath, key);
271}
272
Matthew Barthe7566632019-11-18 16:13:04 -0600273} // namespace method
274
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600275/**
276 * Redundancy policies for fan presence detection function definitions
277 */
278namespace rpolicy
279{
Matthew Barth2d2caa32020-05-26 11:07:24 -0500280// Get an `Anyof` redundancy policy for the fan
281std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
282{
283 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
284 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600285 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500286 pSensors.emplace_back(*fanSensor);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600287 }
288
Matthew Barth2d2caa32020-05-26 11:07:24 -0500289 return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors);
290}
Matthew Barth26ad44a2019-11-22 15:37:14 -0600291
Matthew Barth2d2caa32020-05-26 11:07:24 -0500292// Get a `Fallback` redundancy policy for the fan
293std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
294{
295 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
296 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
297 {
298 // Place in the order given to fallback correctly
299 pSensors.emplace_back(*fanSensor);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600300 }
301
Matthew Barth2d2caa32020-05-26 11:07:24 -0500302 return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors);
303}
304
305} // namespace rpolicy
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600306
Matthew Barthfd05d642019-11-14 15:01:57 -0600307} // namespace presence
308} // namespace fan
309} // namespace phosphor