blob: deccbf11772f926c1184ddd5718d5593cc539d8e [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 Barth5060b102019-12-16 10:46:35 -060024#include "config.h"
Matthew Barthe7566632019-11-18 16:13:04 -060025#include "tach.hpp"
26#include "gpio.hpp"
Matthew Barthaa8d81d2019-11-21 14:07:31 -060027#include "anyof.hpp"
28#include "fallback.hpp"
Matthew Barth5060b102019-12-16 10:46:35 -060029#include "sdbusplus.hpp"
Matthew Barthfd05d642019-11-14 15:01:57 -060030
31namespace phosphor
32{
33namespace fan
34{
35namespace presence
36{
37
Matthew Barth0961fae2019-11-15 09:00:27 -060038using json = nlohmann::json;
39namespace fs = std::filesystem;
40using namespace phosphor::logging;
41
Matthew Barthfd05d642019-11-14 15:01:57 -060042policies JsonConfig::_policies;
Matthew Barthe7566632019-11-18 16:13:04 -060043const std::map<std::string, methodHandler> JsonConfig::_methods =
44{
45 {"tach", method::getTach},
46 {"gpio", method::getGpio}
47};
Matthew Barthaa8d81d2019-11-21 14:07:31 -060048const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies =
49{
50 {"anyof", rpolicy::getAnyof},
51 {"fallback", rpolicy::getFallback}
52};
Matthew Barthfd05d642019-11-14 15:01:57 -060053
Matthew Barth5060b102019-12-16 10:46:35 -060054JsonConfig::JsonConfig(sdbusplus::bus::bus& bus) :
55 _bus(bus)
Matthew Barthfd05d642019-11-14 15:01:57 -060056{
Matthew Barth5060b102019-12-16 10:46:35 -060057 // Determine the configuration file to use
58 _confFile = getConfFile();
59 // Load and process the json configuration
60 load();
Matthew Barthfd05d642019-11-14 15:01:57 -060061}
62
63const policies& JsonConfig::get()
64{
65 return _policies;
66}
67
Matthew Barthf3e70472019-12-03 13:33:20 -060068void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc,
69 const struct signalfd_siginfo* sigInfo)
70{
71 try
72 {
Matthew Barth5060b102019-12-16 10:46:35 -060073 // Determine the configuration file to use
74 _confFile = getConfFile();
75 log<level::INFO>("Loading configuration",
76 entry("JSON_FILE=%s", _confFile.c_str()));
Matthew Barthf3e70472019-12-03 13:33:20 -060077 // Load and process the json configuration
78 load();
79 for (auto& p: _policies)
80 {
81 p->monitor();
82 }
83 log<level::INFO>("Configuration loaded successfully");
84 }
85 catch (std::runtime_error& re)
86 {
87 log<level::ERR>("Error loading config, no config changes made",
88 entry("LOAD_ERROR=%s", re.what()));
89 }
90}
91
Matthew Barth5060b102019-12-16 10:46:35 -060092const fs::path JsonConfig::getConfFile()
Matthew Barthf3e70472019-12-03 13:33:20 -060093{
Matthew Barth5060b102019-12-16 10:46:35 -060094 // Check override location
95 fs::path confFile = fs::path{confOverridePath} / confFileName;
96 if (fs::exists(confFile))
Matthew Barth4fb0af92019-12-04 09:15:00 -060097 {
Matthew Barth5060b102019-12-16 10:46:35 -060098 return confFile;
99 }
100
101 // Check base path location
102 confFile = fs::path{confBasePath} / confFileName;
103 if (fs::exists(confFile))
104 {
105 return confFile;
106 }
107
108 // Check dbus interface & property
109 // Use first object returned in the subtree
110 // (Should really be only one object with the config interface)
111 auto objects = util::SDBusPlus::getSubTreeRaw(_bus, "/", confDbusIntf, 0);
112 auto itObj = objects.begin();
113 if (itObj != objects.end())
114 {
115 auto itServ = itObj->second.begin();
116 if (itServ != itObj->second.end())
117 {
118 // Retrieve json config relative path location from dbus
119 auto relPathLoc = util::SDBusPlus::getProperty<std::string>(
120 _bus, itServ->first, itObj->first,
121 confDbusIntf, confDbusProp);
122 confFile = fs::path{confBasePath} / relPathLoc / confFileName;
123 if (!fs::exists(confFile))
124 {
125 log<level::ERR>("No JSON config file found",
126 entry("NO_FILE=%s", confFile.c_str()));
127 throw std::runtime_error("No JSON config file found");
128 }
129 }
130 else
131 {
132 log<level::ERR>("No JSON config file found",
133 entry("NO_SERVICE=%s", itObj->first.c_str()));
134 throw std::runtime_error("No JSON config file found");
135 }
Matthew Barth4fb0af92019-12-04 09:15:00 -0600136 }
137 else
138 {
Matthew Barth5060b102019-12-16 10:46:35 -0600139 log<level::ERR>("No JSON config file found",
140 entry("NO_INTERFACE=%s", confDbusIntf));
141 throw std::runtime_error("No JSON config file found");
Matthew Barth4fb0af92019-12-04 09:15:00 -0600142 }
Matthew Barth5060b102019-12-16 10:46:35 -0600143
144 return confFile;
145}
146
147void JsonConfig::load()
148{
Matthew Barthf3e70472019-12-03 13:33:20 -0600149 std::ifstream file;
150 json jsonConf;
151
Matthew Barth5060b102019-12-16 10:46:35 -0600152 if (fs::exists(_confFile))
Matthew Barthf3e70472019-12-03 13:33:20 -0600153 {
Matthew Barth5060b102019-12-16 10:46:35 -0600154 file.open(_confFile);
Matthew Barthf3e70472019-12-03 13:33:20 -0600155 try
156 {
157 jsonConf = json::parse(file);
158 }
159 catch (std::exception& e)
160 {
161 log<level::ERR>("Failed to parse JSON config file",
Matthew Barth5060b102019-12-16 10:46:35 -0600162 entry("JSON_FILE=%s", _confFile.c_str()),
Matthew Barthf3e70472019-12-03 13:33:20 -0600163 entry("JSON_ERROR=%s", e.what()));
164 throw std::runtime_error("Failed to parse JSON config file");
165 }
166 }
167 else
168 {
169 log<level::ERR>("Unable to open JSON config file",
Matthew Barth5060b102019-12-16 10:46:35 -0600170 entry("JSON_FILE=%s", _confFile.c_str()));
Matthew Barthf3e70472019-12-03 13:33:20 -0600171 throw std::runtime_error("Unable to open JSON config file");
172 }
173
174 process(jsonConf);
175}
176
Matthew Barth4a94dec2019-11-15 10:40:47 -0600177void JsonConfig::process(const json& jsonConf)
178{
Matthew Barthf3e70472019-12-03 13:33:20 -0600179 policies policies;
180 std::vector<fanPolicy> fans;
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600181 // Set the expected number of fan entries
182 // to be size of the list of fan json config entries
183 // (Must be done to eliminate vector reallocation of fan references)
Matthew Barthf3e70472019-12-03 13:33:20 -0600184 fans.reserve(jsonConf.size());
Matthew Barth4a94dec2019-11-15 10:40:47 -0600185 for (auto& member : jsonConf)
186 {
Matthew Barthe7566632019-11-18 16:13:04 -0600187 if (!member.contains("name") || !member.contains("path") ||
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600188 !member.contains("methods") || !member.contains("rpolicy"))
Matthew Barth4a94dec2019-11-15 10:40:47 -0600189 {
190 log<level::ERR>(
191 "Missing required fan presence properties",
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600192 entry("REQUIRED_PROPERTIES=%s",
193 "{name, path, methods, rpolicy}"));
Matthew Barth4a94dec2019-11-15 10:40:47 -0600194 throw std::runtime_error(
195 "Missing required fan presence properties");
196 }
Matthew Barthe7566632019-11-18 16:13:04 -0600197
198 // Loop thru the configured methods of presence detection
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600199 std::vector<std::unique_ptr<PresenceSensor>> sensors;
Matthew Barthe7566632019-11-18 16:13:04 -0600200 for (auto& method : member["methods"].items())
201 {
202 if (!method.value().contains("type"))
203 {
204 log<level::ERR>(
205 "Missing required fan presence method type",
206 entry("FAN_NAME=%s",
207 member["name"].get<std::string>().c_str()));
208 throw std::runtime_error(
209 "Missing required fan presence method type");
210 }
211 // The method type of fan presence detection
212 // (Must have a supported function within the method namespace)
213 auto type = method.value()["type"].get<std::string>();
214 std::transform(type.begin(), type.end(), type.begin(), tolower);
215 auto func = _methods.find(type);
216 if (func != _methods.end())
217 {
218 // Call function for method type
Matthew Barthf3e70472019-12-03 13:33:20 -0600219 auto sensor = func->second(fans.size(), method.value());
Matthew Barthe7566632019-11-18 16:13:04 -0600220 if (sensor)
221 {
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600222 sensors.emplace_back(std::move(sensor));
Matthew Barthe7566632019-11-18 16:13:04 -0600223 }
224 }
225 else
226 {
227 log<level::ERR>("Invalid fan presence method type",
228 entry("FAN_NAME=%s",
229 member["name"].get<std::string>().c_str()),
230 entry("METHOD_TYPE=%s", type.c_str()));
231 throw std::runtime_error("Invalid fan presence method type");
232 }
233 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600234 auto fan = std::make_tuple(member["name"], member["path"]);
235 // Create a fan object
Matthew Barthf3e70472019-12-03 13:33:20 -0600236 fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600237
238 // Add fan presence policy
Matthew Barthf3e70472019-12-03 13:33:20 -0600239 auto policy = getPolicy(member["rpolicy"], fans.back());
240 if (policy)
241 {
242 policies.emplace_back(std::move(policy));
243 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600244 }
Matthew Barthf3e70472019-12-03 13:33:20 -0600245
246 // Success, refresh fans and policies lists
247 _fans.clear();
248 _fans.swap(fans);
249
250 _policies.clear();
251 _policies.swap(policies);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600252}
253
Matthew Barthf3e70472019-12-03 13:33:20 -0600254std::unique_ptr<RedundancyPolicy> JsonConfig::getPolicy(
255 const json& rpolicy,
256 const fanPolicy& fpolicy)
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600257{
258 if (!rpolicy.contains("type"))
259 {
260 log<level::ERR>("Missing required fan presence policy type",
261 entry("FAN_NAME=%s",
262 std::get<fanPolicyFanPos>(
Matthew Barthf3e70472019-12-03 13:33:20 -0600263 std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600264 entry("REQUIRED_PROPERTIES=%s", "{type}"));
265 throw std::runtime_error("Missing required fan presence policy type");
266 }
267
268 // The redundancy policy type for fan presence detection
269 // (Must have a supported function within the rpolicy namespace)
270 auto type = rpolicy["type"].get<std::string>();
271 std::transform(type.begin(), type.end(), type.begin(), tolower);
272 auto func = _rpolicies.find(type);
273 if (func != _rpolicies.end())
274 {
Matthew Barthf3e70472019-12-03 13:33:20 -0600275 // Call function for redundancy policy type and return the policy
276 return func->second(fpolicy);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600277 }
278 else
279 {
280 log<level::ERR>("Invalid fan presence policy type",
281 entry("FAN_NAME=%s",
Matthew Barthf3e70472019-12-03 13:33:20 -0600282 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600283 entry("RPOLICY_TYPE=%s", type.c_str()));
284 throw std::runtime_error("Invalid fan presence methods policy type");
Matthew Barth4a94dec2019-11-15 10:40:47 -0600285 }
286}
287
Matthew Barthe7566632019-11-18 16:13:04 -0600288/**
289 * Methods of fan presence detection function definitions
290 */
291namespace method
292{
293 // Get a constructed presence sensor for fan presence detection by tach
294 std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
295 {
Matthew Barthe0d98a32019-11-19 15:40:57 -0600296 if (!method.contains("sensors") ||
297 method["sensors"].size() == 0)
298 {
299 log<level::ERR>(
300 "Missing required tach method properties",
301 entry("FAN_ENTRY=%d", fanIndex),
302 entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
303 throw std::runtime_error("Missing required tach method properties");
304 }
305
306 std::vector<std::string> sensors;
307 for (auto& sensor : method["sensors"])
308 {
309 sensors.emplace_back(sensor.get<std::string>());
310 }
311
312 return std::make_unique<PolicyAccess<Tach, JsonConfig>>(
313 fanIndex, std::move(sensors));
Matthew Barthe7566632019-11-18 16:13:04 -0600314 }
315
316 // Get a constructed presence sensor for fan presence detection by gpio
317 std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
318 {
Matthew Barthe0d98a32019-11-19 15:40:57 -0600319 if (!method.contains("physpath") ||
320 !method.contains("devpath") ||
321 !method.contains("key"))
322 {
323 log<level::ERR>(
324 "Missing required gpio method properties",
325 entry("FAN_ENTRY=%d", fanIndex),
326 entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
327 throw std::runtime_error("Missing required gpio method properties");
328 }
329
330 auto physpath = method["physpath"].get<std::string>();
331 auto devpath = method["devpath"].get<std::string>();
332 auto key = method["key"].get<unsigned int>();
333
334 return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(
335 fanIndex, physpath, devpath, key);
Matthew Barthe7566632019-11-18 16:13:04 -0600336 }
337
338} // namespace method
339
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600340/**
341 * Redundancy policies for fan presence detection function definitions
342 */
343namespace rpolicy
344{
345 // Get an `Anyof` redundancy policy for the fan
346 std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
347 {
Matthew Barth26ad44a2019-11-22 15:37:14 -0600348 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
349 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
350 {
351 pSensors.emplace_back(*fanSensor);
352 }
353
354 return std::make_unique<AnyOf>(
355 std::get<fanPolicyFanPos>(fan), pSensors);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600356 }
357
358 // Get a `Fallback` redundancy policy for the fan
359 std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
360 {
Matthew Barth26ad44a2019-11-22 15:37:14 -0600361 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
362 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
363 {
364 // Place in the order given to fallback correctly
365 pSensors.emplace_back(*fanSensor);
366 }
367
368 return std::make_unique<Fallback>(
369 std::get<fanPolicyFanPos>(fan), pSensors);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600370 }
371
372} // namespace policy
373
Matthew Barthfd05d642019-11-14 15:01:57 -0600374} // namespace presence
375} // namespace fan
376} // namespace phosphor