blob: d09139c4026d1b95b431091fd4ecd95997c1216a [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 */
16#include <string>
Matthew Barth0961fae2019-11-15 09:00:27 -060017#include <filesystem>
18#include <fstream>
19#include <nlohmann/json.hpp>
20#include <phosphor-logging/log.hpp>
Matthew Barth5060b102019-12-16 10:46:35 -060021#include <sdbusplus/bus.hpp>
Matthew Barthfd05d642019-11-14 15:01:57 -060022
23#include "json_config.hpp"
Matthew Barthe7566632019-11-18 16:13:04 -060024#include "tach.hpp"
25#include "gpio.hpp"
Matthew Barthaa8d81d2019-11-21 14:07:31 -060026#include "anyof.hpp"
27#include "fallback.hpp"
Matthew Barth5060b102019-12-16 10:46:35 -060028#include "sdbusplus.hpp"
Matthew Barthfd05d642019-11-14 15:01:57 -060029
30namespace phosphor
31{
32namespace fan
33{
34namespace presence
35{
36
Matthew Barth0961fae2019-11-15 09:00:27 -060037using json = nlohmann::json;
38namespace fs = std::filesystem;
39using namespace phosphor::logging;
40
Matthew Barthfd05d642019-11-14 15:01:57 -060041policies JsonConfig::_policies;
Matthew Barthe7566632019-11-18 16:13:04 -060042const std::map<std::string, methodHandler> JsonConfig::_methods =
43{
44 {"tach", method::getTach},
45 {"gpio", method::getGpio}
46};
Matthew Barthaa8d81d2019-11-21 14:07:31 -060047const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies =
48{
49 {"anyof", rpolicy::getAnyof},
50 {"fallback", rpolicy::getFallback}
51};
Matthew Barthfd05d642019-11-14 15:01:57 -060052
Matthew Barth5060b102019-12-16 10:46:35 -060053JsonConfig::JsonConfig(sdbusplus::bus::bus& bus) :
54 _bus(bus)
Matthew Barthfd05d642019-11-14 15:01:57 -060055{
Matthew Barth5060b102019-12-16 10:46:35 -060056 // Determine the configuration file to use
57 _confFile = getConfFile();
58 // Load and process the json configuration
59 load();
Matthew Barthfd05d642019-11-14 15:01:57 -060060}
61
62const policies& JsonConfig::get()
63{
64 return _policies;
65}
66
Matthew Barthf3e70472019-12-03 13:33:20 -060067void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc,
68 const struct signalfd_siginfo* sigInfo)
69{
70 try
71 {
Matthew Barth5060b102019-12-16 10:46:35 -060072 // Determine the configuration file to use
73 _confFile = getConfFile();
74 log<level::INFO>("Loading configuration",
75 entry("JSON_FILE=%s", _confFile.c_str()));
Matthew Barthf3e70472019-12-03 13:33:20 -060076 // Load and process the json configuration
77 load();
78 for (auto& p: _policies)
79 {
80 p->monitor();
81 }
82 log<level::INFO>("Configuration loaded successfully");
83 }
84 catch (std::runtime_error& re)
85 {
86 log<level::ERR>("Error loading config, no config changes made",
87 entry("LOAD_ERROR=%s", re.what()));
88 }
89}
90
Matthew Barth5060b102019-12-16 10:46:35 -060091const fs::path JsonConfig::getConfFile()
Matthew Barthf3e70472019-12-03 13:33:20 -060092{
Matthew Barth5060b102019-12-16 10:46:35 -060093 // Check override location
94 fs::path confFile = fs::path{confOverridePath} / confFileName;
95 if (fs::exists(confFile))
Matthew Barth4fb0af92019-12-04 09:15:00 -060096 {
Matthew Barth5060b102019-12-16 10:46:35 -060097 return confFile;
98 }
99
100 // Check base path location
101 confFile = fs::path{confBasePath} / confFileName;
102 if (fs::exists(confFile))
103 {
104 return confFile;
105 }
106
107 // Check dbus interface & property
108 // Use first object returned in the subtree
109 // (Should really be only one object with the config interface)
110 auto objects = util::SDBusPlus::getSubTreeRaw(_bus, "/", confDbusIntf, 0);
111 auto itObj = objects.begin();
112 if (itObj != objects.end())
113 {
114 auto itServ = itObj->second.begin();
115 if (itServ != itObj->second.end())
116 {
117 // Retrieve json config relative path location from dbus
118 auto relPathLoc = util::SDBusPlus::getProperty<std::string>(
119 _bus, itServ->first, itObj->first,
120 confDbusIntf, confDbusProp);
121 confFile = fs::path{confBasePath} / relPathLoc / confFileName;
122 if (!fs::exists(confFile))
123 {
124 log<level::ERR>("No JSON config file found",
125 entry("NO_FILE=%s", confFile.c_str()));
126 throw std::runtime_error("No JSON config file found");
127 }
128 }
129 else
130 {
131 log<level::ERR>("No JSON config file found",
132 entry("NO_SERVICE=%s", itObj->first.c_str()));
133 throw std::runtime_error("No JSON config file found");
134 }
Matthew Barth4fb0af92019-12-04 09:15:00 -0600135 }
136 else
137 {
Matthew Barth5060b102019-12-16 10:46:35 -0600138 log<level::ERR>("No JSON config file found",
139 entry("NO_INTERFACE=%s", confDbusIntf));
140 throw std::runtime_error("No JSON config file found");
Matthew Barth4fb0af92019-12-04 09:15:00 -0600141 }
Matthew Barth5060b102019-12-16 10:46:35 -0600142
143 return confFile;
144}
145
146void JsonConfig::load()
147{
Matthew Barthf3e70472019-12-03 13:33:20 -0600148 std::ifstream file;
149 json jsonConf;
150
Matthew Barth5060b102019-12-16 10:46:35 -0600151 if (fs::exists(_confFile))
Matthew Barthf3e70472019-12-03 13:33:20 -0600152 {
Matthew Barth5060b102019-12-16 10:46:35 -0600153 file.open(_confFile);
Matthew Barthf3e70472019-12-03 13:33:20 -0600154 try
155 {
156 jsonConf = json::parse(file);
157 }
158 catch (std::exception& e)
159 {
160 log<level::ERR>("Failed to parse JSON config file",
Matthew Barth5060b102019-12-16 10:46:35 -0600161 entry("JSON_FILE=%s", _confFile.c_str()),
Matthew Barthf3e70472019-12-03 13:33:20 -0600162 entry("JSON_ERROR=%s", e.what()));
163 throw std::runtime_error("Failed to parse JSON config file");
164 }
165 }
166 else
167 {
168 log<level::ERR>("Unable to open JSON config file",
Matthew Barth5060b102019-12-16 10:46:35 -0600169 entry("JSON_FILE=%s", _confFile.c_str()));
Matthew Barthf3e70472019-12-03 13:33:20 -0600170 throw std::runtime_error("Unable to open JSON config file");
171 }
172
173 process(jsonConf);
174}
175
Matthew Barth4a94dec2019-11-15 10:40:47 -0600176void JsonConfig::process(const json& jsonConf)
177{
Matthew Barthf3e70472019-12-03 13:33:20 -0600178 policies policies;
179 std::vector<fanPolicy> fans;
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600180 // Set the expected number of fan entries
181 // to be size of the list of fan json config entries
182 // (Must be done to eliminate vector reallocation of fan references)
Matthew Barthf3e70472019-12-03 13:33:20 -0600183 fans.reserve(jsonConf.size());
Matthew Barth4a94dec2019-11-15 10:40:47 -0600184 for (auto& member : jsonConf)
185 {
Matthew Barthe7566632019-11-18 16:13:04 -0600186 if (!member.contains("name") || !member.contains("path") ||
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600187 !member.contains("methods") || !member.contains("rpolicy"))
Matthew Barth4a94dec2019-11-15 10:40:47 -0600188 {
189 log<level::ERR>(
190 "Missing required fan presence properties",
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600191 entry("REQUIRED_PROPERTIES=%s",
192 "{name, path, methods, rpolicy}"));
Matthew Barth4a94dec2019-11-15 10:40:47 -0600193 throw std::runtime_error(
194 "Missing required fan presence properties");
195 }
Matthew Barthe7566632019-11-18 16:13:04 -0600196
197 // Loop thru the configured methods of presence detection
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600198 std::vector<std::unique_ptr<PresenceSensor>> sensors;
Matthew Barthe7566632019-11-18 16:13:04 -0600199 for (auto& method : member["methods"].items())
200 {
201 if (!method.value().contains("type"))
202 {
203 log<level::ERR>(
204 "Missing required fan presence method type",
205 entry("FAN_NAME=%s",
206 member["name"].get<std::string>().c_str()));
207 throw std::runtime_error(
208 "Missing required fan presence method type");
209 }
210 // The method type of fan presence detection
211 // (Must have a supported function within the method namespace)
212 auto type = method.value()["type"].get<std::string>();
213 std::transform(type.begin(), type.end(), type.begin(), tolower);
214 auto func = _methods.find(type);
215 if (func != _methods.end())
216 {
217 // Call function for method type
Matthew Barthf3e70472019-12-03 13:33:20 -0600218 auto sensor = func->second(fans.size(), method.value());
Matthew Barthe7566632019-11-18 16:13:04 -0600219 if (sensor)
220 {
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600221 sensors.emplace_back(std::move(sensor));
Matthew Barthe7566632019-11-18 16:13:04 -0600222 }
223 }
224 else
225 {
226 log<level::ERR>("Invalid fan presence method type",
227 entry("FAN_NAME=%s",
228 member["name"].get<std::string>().c_str()),
229 entry("METHOD_TYPE=%s", type.c_str()));
230 throw std::runtime_error("Invalid fan presence method type");
231 }
232 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600233 auto fan = std::make_tuple(member["name"], member["path"]);
234 // Create a fan object
Matthew Barthf3e70472019-12-03 13:33:20 -0600235 fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600236
237 // Add fan presence policy
Matthew Barthf3e70472019-12-03 13:33:20 -0600238 auto policy = getPolicy(member["rpolicy"], fans.back());
239 if (policy)
240 {
241 policies.emplace_back(std::move(policy));
242 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600243 }
Matthew Barthf3e70472019-12-03 13:33:20 -0600244
245 // Success, refresh fans and policies lists
246 _fans.clear();
247 _fans.swap(fans);
248
249 _policies.clear();
250 _policies.swap(policies);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600251}
252
Matthew Barthf3e70472019-12-03 13:33:20 -0600253std::unique_ptr<RedundancyPolicy> JsonConfig::getPolicy(
254 const json& rpolicy,
255 const fanPolicy& fpolicy)
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600256{
257 if (!rpolicy.contains("type"))
258 {
259 log<level::ERR>("Missing required fan presence policy type",
260 entry("FAN_NAME=%s",
261 std::get<fanPolicyFanPos>(
Matthew Barthf3e70472019-12-03 13:33:20 -0600262 std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600263 entry("REQUIRED_PROPERTIES=%s", "{type}"));
264 throw std::runtime_error("Missing required fan presence policy type");
265 }
266
267 // The redundancy policy type for fan presence detection
268 // (Must have a supported function within the rpolicy namespace)
269 auto type = rpolicy["type"].get<std::string>();
270 std::transform(type.begin(), type.end(), type.begin(), tolower);
271 auto func = _rpolicies.find(type);
272 if (func != _rpolicies.end())
273 {
Matthew Barthf3e70472019-12-03 13:33:20 -0600274 // Call function for redundancy policy type and return the policy
275 return func->second(fpolicy);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600276 }
277 else
278 {
279 log<level::ERR>("Invalid fan presence policy type",
280 entry("FAN_NAME=%s",
Matthew Barthf3e70472019-12-03 13:33:20 -0600281 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600282 entry("RPOLICY_TYPE=%s", type.c_str()));
283 throw std::runtime_error("Invalid fan presence methods policy type");
Matthew Barth4a94dec2019-11-15 10:40:47 -0600284 }
285}
286
Matthew Barthe7566632019-11-18 16:13:04 -0600287/**
288 * Methods of fan presence detection function definitions
289 */
290namespace method
291{
292 // Get a constructed presence sensor for fan presence detection by tach
293 std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
294 {
Matthew Barthe0d98a32019-11-19 15:40:57 -0600295 if (!method.contains("sensors") ||
296 method["sensors"].size() == 0)
297 {
298 log<level::ERR>(
299 "Missing required tach method properties",
300 entry("FAN_ENTRY=%d", fanIndex),
301 entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
302 throw std::runtime_error("Missing required tach method properties");
303 }
304
305 std::vector<std::string> sensors;
306 for (auto& sensor : method["sensors"])
307 {
308 sensors.emplace_back(sensor.get<std::string>());
309 }
310
311 return std::make_unique<PolicyAccess<Tach, JsonConfig>>(
312 fanIndex, std::move(sensors));
Matthew Barthe7566632019-11-18 16:13:04 -0600313 }
314
315 // Get a constructed presence sensor for fan presence detection by gpio
316 std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
317 {
Matthew Barthe0d98a32019-11-19 15:40:57 -0600318 if (!method.contains("physpath") ||
319 !method.contains("devpath") ||
320 !method.contains("key"))
321 {
322 log<level::ERR>(
323 "Missing required gpio method properties",
324 entry("FAN_ENTRY=%d", fanIndex),
325 entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
326 throw std::runtime_error("Missing required gpio method properties");
327 }
328
329 auto physpath = method["physpath"].get<std::string>();
330 auto devpath = method["devpath"].get<std::string>();
331 auto key = method["key"].get<unsigned int>();
332
333 return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(
334 fanIndex, physpath, devpath, key);
Matthew Barthe7566632019-11-18 16:13:04 -0600335 }
336
337} // namespace method
338
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600339/**
340 * Redundancy policies for fan presence detection function definitions
341 */
342namespace rpolicy
343{
344 // Get an `Anyof` redundancy policy for the fan
345 std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
346 {
Matthew Barth26ad44a2019-11-22 15:37:14 -0600347 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
348 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
349 {
350 pSensors.emplace_back(*fanSensor);
351 }
352
353 return std::make_unique<AnyOf>(
354 std::get<fanPolicyFanPos>(fan), pSensors);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600355 }
356
357 // Get a `Fallback` redundancy policy for the fan
358 std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
359 {
Matthew Barth26ad44a2019-11-22 15:37:14 -0600360 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
361 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
362 {
363 // Place in the order given to fallback correctly
364 pSensors.emplace_back(*fanSensor);
365 }
366
367 return std::make_unique<Fallback>(
368 std::get<fanPolicyFanPos>(fan), pSensors);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600369 }
370
371} // namespace policy
372
Matthew Barthfd05d642019-11-14 15:01:57 -0600373} // namespace presence
374} // namespace fan
375} // namespace phosphor