blob: 1974c760cb97c64401b43811b62b520079f077f4 [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 */
Matthew Barth2d2caa32020-05-26 11:07:24 -050016#include "json_config.hpp"
17
18#include "anyof.hpp"
19#include "fallback.hpp"
20#include "gpio.hpp"
21#include "sdbusplus.hpp"
22#include "tach.hpp"
23
Matthew Barth0961fae2019-11-15 09:00:27 -060024#include <nlohmann/json.hpp>
25#include <phosphor-logging/log.hpp>
Matthew Barth5060b102019-12-16 10:46:35 -060026#include <sdbusplus/bus.hpp>
Matthew Barthfd05d642019-11-14 15:01:57 -060027
Matthew Barth2d2caa32020-05-26 11:07:24 -050028#include <filesystem>
29#include <fstream>
30#include <string>
Matthew Barthfd05d642019-11-14 15:01:57 -060031
32namespace phosphor
33{
34namespace fan
35{
36namespace presence
37{
38
Matthew Barth0961fae2019-11-15 09:00:27 -060039using json = nlohmann::json;
40namespace fs = std::filesystem;
41using namespace phosphor::logging;
42
Matthew Barthfd05d642019-11-14 15:01:57 -060043policies JsonConfig::_policies;
Matthew Barth2d2caa32020-05-26 11:07:24 -050044const std::map<std::string, methodHandler> JsonConfig::_methods = {
45 {"tach", method::getTach}, {"gpio", method::getGpio}};
46const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies = {
47 {"anyof", rpolicy::getAnyof}, {"fallback", rpolicy::getFallback}};
Matthew Barthfd05d642019-11-14 15:01:57 -060048
Matthew Barth2d2caa32020-05-26 11:07:24 -050049JsonConfig::JsonConfig(sdbusplus::bus::bus& bus) : _bus(bus)
Matthew Barthfd05d642019-11-14 15:01:57 -060050{
Matthew Barth5060b102019-12-16 10:46:35 -060051 // Determine the configuration file to use
52 _confFile = getConfFile();
53 // Load and process the json configuration
54 load();
Matthew Barthfd05d642019-11-14 15:01:57 -060055}
56
57const policies& JsonConfig::get()
58{
59 return _policies;
60}
61
Matthew Barthf3e70472019-12-03 13:33:20 -060062void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc,
63 const struct signalfd_siginfo* sigInfo)
64{
65 try
66 {
Matthew Barth5060b102019-12-16 10:46:35 -060067 // Determine the configuration file to use
68 _confFile = getConfFile();
69 log<level::INFO>("Loading configuration",
70 entry("JSON_FILE=%s", _confFile.c_str()));
Matthew Barthf3e70472019-12-03 13:33:20 -060071 // Load and process the json configuration
72 load();
Matthew Barth2d2caa32020-05-26 11:07:24 -050073 for (auto& p : _policies)
Matthew Barthf3e70472019-12-03 13:33:20 -060074 {
75 p->monitor();
76 }
77 log<level::INFO>("Configuration loaded successfully");
78 }
79 catch (std::runtime_error& re)
80 {
81 log<level::ERR>("Error loading config, no config changes made",
82 entry("LOAD_ERROR=%s", re.what()));
83 }
84}
85
Matthew Barth5060b102019-12-16 10:46:35 -060086const fs::path JsonConfig::getConfFile()
Matthew Barthf3e70472019-12-03 13:33:20 -060087{
Matthew Barth5060b102019-12-16 10:46:35 -060088 // Check override location
89 fs::path confFile = fs::path{confOverridePath} / confFileName;
90 if (fs::exists(confFile))
Matthew Barth4fb0af92019-12-04 09:15:00 -060091 {
Matthew Barth5060b102019-12-16 10:46:35 -060092 return confFile;
93 }
94
95 // Check base path location
96 confFile = fs::path{confBasePath} / confFileName;
97 if (fs::exists(confFile))
98 {
99 return confFile;
100 }
101
102 // Check dbus interface & property
103 // Use first object returned in the subtree
104 // (Should really be only one object with the config interface)
105 auto objects = util::SDBusPlus::getSubTreeRaw(_bus, "/", confDbusIntf, 0);
106 auto itObj = objects.begin();
107 if (itObj != objects.end())
108 {
109 auto itServ = itObj->second.begin();
110 if (itServ != itObj->second.end())
111 {
112 // Retrieve json config relative path location from dbus
113 auto relPathLoc = util::SDBusPlus::getProperty<std::string>(
Matthew Barth2d2caa32020-05-26 11:07:24 -0500114 _bus, itServ->first, itObj->first, confDbusIntf, confDbusProp);
Matthew Barth5060b102019-12-16 10:46:35 -0600115 confFile = fs::path{confBasePath} / relPathLoc / confFileName;
116 if (!fs::exists(confFile))
117 {
118 log<level::ERR>("No JSON config file found",
119 entry("NO_FILE=%s", confFile.c_str()));
120 throw std::runtime_error("No JSON config file found");
121 }
122 }
123 else
124 {
125 log<level::ERR>("No JSON config file found",
126 entry("NO_SERVICE=%s", itObj->first.c_str()));
127 throw std::runtime_error("No JSON config file found");
128 }
Matthew Barth4fb0af92019-12-04 09:15:00 -0600129 }
130 else
131 {
Matthew Barth5060b102019-12-16 10:46:35 -0600132 log<level::ERR>("No JSON config file found",
133 entry("NO_INTERFACE=%s", confDbusIntf));
134 throw std::runtime_error("No JSON config file found");
Matthew Barth4fb0af92019-12-04 09:15:00 -0600135 }
Matthew Barth5060b102019-12-16 10:46:35 -0600136
137 return confFile;
138}
139
140void JsonConfig::load()
141{
Matthew Barthf3e70472019-12-03 13:33:20 -0600142 std::ifstream file;
143 json jsonConf;
144
Matthew Barth5060b102019-12-16 10:46:35 -0600145 if (fs::exists(_confFile))
Matthew Barthf3e70472019-12-03 13:33:20 -0600146 {
Matthew Barth5060b102019-12-16 10:46:35 -0600147 file.open(_confFile);
Matthew Barthf3e70472019-12-03 13:33:20 -0600148 try
149 {
150 jsonConf = json::parse(file);
151 }
152 catch (std::exception& e)
153 {
154 log<level::ERR>("Failed to parse JSON config file",
Matthew Barth5060b102019-12-16 10:46:35 -0600155 entry("JSON_FILE=%s", _confFile.c_str()),
Matthew Barthf3e70472019-12-03 13:33:20 -0600156 entry("JSON_ERROR=%s", e.what()));
157 throw std::runtime_error("Failed to parse JSON config file");
158 }
159 }
160 else
161 {
162 log<level::ERR>("Unable to open JSON config file",
Matthew Barth5060b102019-12-16 10:46:35 -0600163 entry("JSON_FILE=%s", _confFile.c_str()));
Matthew Barthf3e70472019-12-03 13:33:20 -0600164 throw std::runtime_error("Unable to open JSON config file");
165 }
166
167 process(jsonConf);
168}
169
Matthew Barth4a94dec2019-11-15 10:40:47 -0600170void JsonConfig::process(const json& jsonConf)
171{
Matthew Barthf3e70472019-12-03 13:33:20 -0600172 policies policies;
173 std::vector<fanPolicy> fans;
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600174 // Set the expected number of fan entries
175 // to be size of the list of fan json config entries
176 // (Must be done to eliminate vector reallocation of fan references)
Matthew Barthf3e70472019-12-03 13:33:20 -0600177 fans.reserve(jsonConf.size());
Matthew Barth4a94dec2019-11-15 10:40:47 -0600178 for (auto& member : jsonConf)
179 {
Matthew Barthe7566632019-11-18 16:13:04 -0600180 if (!member.contains("name") || !member.contains("path") ||
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600181 !member.contains("methods") || !member.contains("rpolicy"))
Matthew Barth4a94dec2019-11-15 10:40:47 -0600182 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500183 log<level::ERR>("Missing required fan presence properties",
184 entry("REQUIRED_PROPERTIES=%s",
185 "{name, path, methods, rpolicy}"));
Matthew Barth4a94dec2019-11-15 10:40:47 -0600186 throw std::runtime_error(
187 "Missing required fan presence properties");
188 }
Matthew Barthe7566632019-11-18 16:13:04 -0600189
190 // Loop thru the configured methods of presence detection
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600191 std::vector<std::unique_ptr<PresenceSensor>> sensors;
Matthew Barthe7566632019-11-18 16:13:04 -0600192 for (auto& method : member["methods"].items())
193 {
194 if (!method.value().contains("type"))
195 {
196 log<level::ERR>(
197 "Missing required fan presence method type",
198 entry("FAN_NAME=%s",
Matthew Barth2d2caa32020-05-26 11:07:24 -0500199 member["name"].get<std::string>().c_str()));
Matthew Barthe7566632019-11-18 16:13:04 -0600200 throw std::runtime_error(
201 "Missing required fan presence method type");
202 }
203 // The method type of fan presence detection
204 // (Must have a supported function within the method namespace)
205 auto type = method.value()["type"].get<std::string>();
206 std::transform(type.begin(), type.end(), type.begin(), tolower);
207 auto func = _methods.find(type);
208 if (func != _methods.end())
209 {
210 // Call function for method type
Matthew Barthf3e70472019-12-03 13:33:20 -0600211 auto sensor = func->second(fans.size(), method.value());
Matthew Barthe7566632019-11-18 16:13:04 -0600212 if (sensor)
213 {
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600214 sensors.emplace_back(std::move(sensor));
Matthew Barthe7566632019-11-18 16:13:04 -0600215 }
216 }
217 else
218 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500219 log<level::ERR>(
220 "Invalid fan presence method type",
221 entry("FAN_NAME=%s",
222 member["name"].get<std::string>().c_str()),
223 entry("METHOD_TYPE=%s", type.c_str()));
Matthew Barthe7566632019-11-18 16:13:04 -0600224 throw std::runtime_error("Invalid fan presence method type");
225 }
226 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600227 auto fan = std::make_tuple(member["name"], member["path"]);
228 // Create a fan object
Matthew Barthf3e70472019-12-03 13:33:20 -0600229 fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600230
231 // Add fan presence policy
Matthew Barthf3e70472019-12-03 13:33:20 -0600232 auto policy = getPolicy(member["rpolicy"], fans.back());
233 if (policy)
234 {
235 policies.emplace_back(std::move(policy));
236 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600237 }
Matthew Barthf3e70472019-12-03 13:33:20 -0600238
239 // Success, refresh fans and policies lists
240 _fans.clear();
241 _fans.swap(fans);
242
243 _policies.clear();
244 _policies.swap(policies);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600245}
246
Matthew Barth2d2caa32020-05-26 11:07:24 -0500247std::unique_ptr<RedundancyPolicy>
248 JsonConfig::getPolicy(const json& rpolicy, const fanPolicy& fpolicy)
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600249{
250 if (!rpolicy.contains("type"))
251 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500252 log<level::ERR>(
253 "Missing required fan presence policy type",
254 entry("FAN_NAME=%s",
255 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
256 entry("REQUIRED_PROPERTIES=%s", "{type}"));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600257 throw std::runtime_error("Missing required fan presence policy type");
258 }
259
260 // The redundancy policy type for fan presence detection
261 // (Must have a supported function within the rpolicy namespace)
262 auto type = rpolicy["type"].get<std::string>();
263 std::transform(type.begin(), type.end(), type.begin(), tolower);
264 auto func = _rpolicies.find(type);
265 if (func != _rpolicies.end())
266 {
Matthew Barthf3e70472019-12-03 13:33:20 -0600267 // Call function for redundancy policy type and return the policy
268 return func->second(fpolicy);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600269 }
270 else
271 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500272 log<level::ERR>(
273 "Invalid fan presence policy type",
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600274 entry("FAN_NAME=%s",
Matthew Barth2d2caa32020-05-26 11:07:24 -0500275 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600276 entry("RPOLICY_TYPE=%s", type.c_str()));
277 throw std::runtime_error("Invalid fan presence methods policy type");
Matthew Barth4a94dec2019-11-15 10:40:47 -0600278 }
279}
280
Matthew Barthe7566632019-11-18 16:13:04 -0600281/**
282 * Methods of fan presence detection function definitions
283 */
284namespace method
285{
Matthew Barth2d2caa32020-05-26 11:07:24 -0500286// Get a constructed presence sensor for fan presence detection by tach
287std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
288{
289 if (!method.contains("sensors") || method["sensors"].size() == 0)
Matthew Barthe7566632019-11-18 16:13:04 -0600290 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500291 log<level::ERR>("Missing required tach method properties",
292 entry("FAN_ENTRY=%d", fanIndex),
293 entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
294 throw std::runtime_error("Missing required tach method properties");
Matthew Barthe7566632019-11-18 16:13:04 -0600295 }
296
Matthew Barth2d2caa32020-05-26 11:07:24 -0500297 std::vector<std::string> sensors;
298 for (auto& sensor : method["sensors"])
Matthew Barthe7566632019-11-18 16:13:04 -0600299 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500300 sensors.emplace_back(sensor.get<std::string>());
Matthew Barthe7566632019-11-18 16:13:04 -0600301 }
302
Matthew Barth2d2caa32020-05-26 11:07:24 -0500303 return std::make_unique<PolicyAccess<Tach, JsonConfig>>(fanIndex,
304 std::move(sensors));
305}
306
307// Get a constructed presence sensor for fan presence detection by gpio
308std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
309{
310 if (!method.contains("physpath") || !method.contains("devpath") ||
311 !method.contains("key"))
312 {
313 log<level::ERR>(
314 "Missing required gpio method properties",
315 entry("FAN_ENTRY=%d", fanIndex),
316 entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
317 throw std::runtime_error("Missing required gpio method properties");
318 }
319
320 auto physpath = method["physpath"].get<std::string>();
321 auto devpath = method["devpath"].get<std::string>();
322 auto key = method["key"].get<unsigned int>();
323
324 return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(fanIndex, physpath,
325 devpath, key);
326}
327
Matthew Barthe7566632019-11-18 16:13:04 -0600328} // namespace method
329
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600330/**
331 * Redundancy policies for fan presence detection function definitions
332 */
333namespace rpolicy
334{
Matthew Barth2d2caa32020-05-26 11:07:24 -0500335// Get an `Anyof` redundancy policy for the fan
336std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
337{
338 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
339 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600340 {
Matthew Barth2d2caa32020-05-26 11:07:24 -0500341 pSensors.emplace_back(*fanSensor);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600342 }
343
Matthew Barth2d2caa32020-05-26 11:07:24 -0500344 return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors);
345}
Matthew Barth26ad44a2019-11-22 15:37:14 -0600346
Matthew Barth2d2caa32020-05-26 11:07:24 -0500347// Get a `Fallback` redundancy policy for the fan
348std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
349{
350 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
351 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
352 {
353 // Place in the order given to fallback correctly
354 pSensors.emplace_back(*fanSensor);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600355 }
356
Matthew Barth2d2caa32020-05-26 11:07:24 -0500357 return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors);
358}
359
360} // namespace rpolicy
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600361
Matthew Barthfd05d642019-11-14 15:01:57 -0600362} // namespace presence
363} // namespace fan
364} // namespace phosphor