blob: 59d452a6eb1044d6d1a7d59575f4f5a55c29e1b6 [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 Barthfd05d642019-11-14 15:01:57 -060021
22#include "json_config.hpp"
Matthew Barthe7566632019-11-18 16:13:04 -060023#include "tach.hpp"
24#include "gpio.hpp"
Matthew Barthaa8d81d2019-11-21 14:07:31 -060025#include "anyof.hpp"
26#include "fallback.hpp"
Matthew Barthfd05d642019-11-14 15:01:57 -060027
28namespace phosphor
29{
30namespace fan
31{
32namespace presence
33{
34
Matthew Barth0961fae2019-11-15 09:00:27 -060035using json = nlohmann::json;
36namespace fs = std::filesystem;
37using namespace phosphor::logging;
38
Matthew Barthfd05d642019-11-14 15:01:57 -060039policies JsonConfig::_policies;
Matthew Barthe7566632019-11-18 16:13:04 -060040const std::map<std::string, methodHandler> JsonConfig::_methods =
41{
42 {"tach", method::getTach},
43 {"gpio", method::getGpio}
44};
Matthew Barthaa8d81d2019-11-21 14:07:31 -060045const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies =
46{
47 {"anyof", rpolicy::getAnyof},
48 {"fallback", rpolicy::getFallback}
49};
Matthew Barthfd05d642019-11-14 15:01:57 -060050
Matthew Barthf6d7f612019-12-10 15:22:54 -060051JsonConfig::JsonConfig(const std::string& jsonConfigPath) :
52 _defaultFile(fs::path(fs::path{jsonConfigPath} / jsonFileName))
Matthew Barthfd05d642019-11-14 15:01:57 -060053{
Matthew Barth3a429d92019-12-05 16:58:27 -060054 if (!_defaultFile.empty())
55 {
56 // Load and process the json configuration
57 load();
58 }
59 else
60 {
61 log<level::ERR>("No JSON config file provided");
62 throw std::runtime_error("No JSON config file provided");
63 }
Matthew Barthfd05d642019-11-14 15:01:57 -060064}
65
66const policies& JsonConfig::get()
67{
68 return _policies;
69}
70
Matthew Barthf3e70472019-12-03 13:33:20 -060071void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc,
72 const struct signalfd_siginfo* sigInfo)
73{
74 try
75 {
76 // 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
91void JsonConfig::load()
92{
Matthew Barthf6d7f612019-12-10 15:22:54 -060093 fs::path confFile{fs::path{jsonOverridePath} / jsonFileName};
Matthew Barth4fb0af92019-12-04 09:15:00 -060094 if (!fs::exists(confFile))
95 {
96 confFile = _defaultFile;
97 }
98 else
99 {
100 log<level::INFO>("Loading alternate configuration",
101 entry("JSON_FILE=%s", confFile.c_str()));
102 }
Matthew Barthf3e70472019-12-03 13:33:20 -0600103 std::ifstream file;
104 json jsonConf;
105
Matthew Barth4fb0af92019-12-04 09:15:00 -0600106 if (fs::exists(confFile))
Matthew Barthf3e70472019-12-03 13:33:20 -0600107 {
Matthew Barth4fb0af92019-12-04 09:15:00 -0600108 file.open(confFile);
Matthew Barthf3e70472019-12-03 13:33:20 -0600109 try
110 {
111 jsonConf = json::parse(file);
112 }
113 catch (std::exception& e)
114 {
115 log<level::ERR>("Failed to parse JSON config file",
Matthew Barth4fb0af92019-12-04 09:15:00 -0600116 entry("JSON_FILE=%s", confFile.c_str()),
Matthew Barthf3e70472019-12-03 13:33:20 -0600117 entry("JSON_ERROR=%s", e.what()));
118 throw std::runtime_error("Failed to parse JSON config file");
119 }
120 }
121 else
122 {
123 log<level::ERR>("Unable to open JSON config file",
Matthew Barth4fb0af92019-12-04 09:15:00 -0600124 entry("JSON_FILE=%s", confFile.c_str()));
Matthew Barthf3e70472019-12-03 13:33:20 -0600125 throw std::runtime_error("Unable to open JSON config file");
126 }
127
128 process(jsonConf);
129}
130
Matthew Barth4a94dec2019-11-15 10:40:47 -0600131void JsonConfig::process(const json& jsonConf)
132{
Matthew Barthf3e70472019-12-03 13:33:20 -0600133 policies policies;
134 std::vector<fanPolicy> fans;
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600135 // Set the expected number of fan entries
136 // to be size of the list of fan json config entries
137 // (Must be done to eliminate vector reallocation of fan references)
Matthew Barthf3e70472019-12-03 13:33:20 -0600138 fans.reserve(jsonConf.size());
Matthew Barth4a94dec2019-11-15 10:40:47 -0600139 for (auto& member : jsonConf)
140 {
Matthew Barthe7566632019-11-18 16:13:04 -0600141 if (!member.contains("name") || !member.contains("path") ||
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600142 !member.contains("methods") || !member.contains("rpolicy"))
Matthew Barth4a94dec2019-11-15 10:40:47 -0600143 {
144 log<level::ERR>(
145 "Missing required fan presence properties",
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600146 entry("REQUIRED_PROPERTIES=%s",
147 "{name, path, methods, rpolicy}"));
Matthew Barth4a94dec2019-11-15 10:40:47 -0600148 throw std::runtime_error(
149 "Missing required fan presence properties");
150 }
Matthew Barthe7566632019-11-18 16:13:04 -0600151
152 // Loop thru the configured methods of presence detection
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600153 std::vector<std::unique_ptr<PresenceSensor>> sensors;
Matthew Barthe7566632019-11-18 16:13:04 -0600154 for (auto& method : member["methods"].items())
155 {
156 if (!method.value().contains("type"))
157 {
158 log<level::ERR>(
159 "Missing required fan presence method type",
160 entry("FAN_NAME=%s",
161 member["name"].get<std::string>().c_str()));
162 throw std::runtime_error(
163 "Missing required fan presence method type");
164 }
165 // The method type of fan presence detection
166 // (Must have a supported function within the method namespace)
167 auto type = method.value()["type"].get<std::string>();
168 std::transform(type.begin(), type.end(), type.begin(), tolower);
169 auto func = _methods.find(type);
170 if (func != _methods.end())
171 {
172 // Call function for method type
Matthew Barthf3e70472019-12-03 13:33:20 -0600173 auto sensor = func->second(fans.size(), method.value());
Matthew Barthe7566632019-11-18 16:13:04 -0600174 if (sensor)
175 {
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600176 sensors.emplace_back(std::move(sensor));
Matthew Barthe7566632019-11-18 16:13:04 -0600177 }
178 }
179 else
180 {
181 log<level::ERR>("Invalid fan presence method type",
182 entry("FAN_NAME=%s",
183 member["name"].get<std::string>().c_str()),
184 entry("METHOD_TYPE=%s", type.c_str()));
185 throw std::runtime_error("Invalid fan presence method type");
186 }
187 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600188 auto fan = std::make_tuple(member["name"], member["path"]);
189 // Create a fan object
Matthew Barthf3e70472019-12-03 13:33:20 -0600190 fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600191
192 // Add fan presence policy
Matthew Barthf3e70472019-12-03 13:33:20 -0600193 auto policy = getPolicy(member["rpolicy"], fans.back());
194 if (policy)
195 {
196 policies.emplace_back(std::move(policy));
197 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600198 }
Matthew Barthf3e70472019-12-03 13:33:20 -0600199
200 // Success, refresh fans and policies lists
201 _fans.clear();
202 _fans.swap(fans);
203
204 _policies.clear();
205 _policies.swap(policies);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600206}
207
Matthew Barthf3e70472019-12-03 13:33:20 -0600208std::unique_ptr<RedundancyPolicy> JsonConfig::getPolicy(
209 const json& rpolicy,
210 const fanPolicy& fpolicy)
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600211{
212 if (!rpolicy.contains("type"))
213 {
214 log<level::ERR>("Missing required fan presence policy type",
215 entry("FAN_NAME=%s",
216 std::get<fanPolicyFanPos>(
Matthew Barthf3e70472019-12-03 13:33:20 -0600217 std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600218 entry("REQUIRED_PROPERTIES=%s", "{type}"));
219 throw std::runtime_error("Missing required fan presence policy type");
220 }
221
222 // The redundancy policy type for fan presence detection
223 // (Must have a supported function within the rpolicy namespace)
224 auto type = rpolicy["type"].get<std::string>();
225 std::transform(type.begin(), type.end(), type.begin(), tolower);
226 auto func = _rpolicies.find(type);
227 if (func != _rpolicies.end())
228 {
Matthew Barthf3e70472019-12-03 13:33:20 -0600229 // Call function for redundancy policy type and return the policy
230 return func->second(fpolicy);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600231 }
232 else
233 {
234 log<level::ERR>("Invalid fan presence policy type",
235 entry("FAN_NAME=%s",
Matthew Barthf3e70472019-12-03 13:33:20 -0600236 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600237 entry("RPOLICY_TYPE=%s", type.c_str()));
238 throw std::runtime_error("Invalid fan presence methods policy type");
Matthew Barth4a94dec2019-11-15 10:40:47 -0600239 }
240}
241
Matthew Barthe7566632019-11-18 16:13:04 -0600242/**
243 * Methods of fan presence detection function definitions
244 */
245namespace method
246{
247 // Get a constructed presence sensor for fan presence detection by tach
248 std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
249 {
Matthew Barthe0d98a32019-11-19 15:40:57 -0600250 if (!method.contains("sensors") ||
251 method["sensors"].size() == 0)
252 {
253 log<level::ERR>(
254 "Missing required tach method properties",
255 entry("FAN_ENTRY=%d", fanIndex),
256 entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
257 throw std::runtime_error("Missing required tach method properties");
258 }
259
260 std::vector<std::string> sensors;
261 for (auto& sensor : method["sensors"])
262 {
263 sensors.emplace_back(sensor.get<std::string>());
264 }
265
266 return std::make_unique<PolicyAccess<Tach, JsonConfig>>(
267 fanIndex, std::move(sensors));
Matthew Barthe7566632019-11-18 16:13:04 -0600268 }
269
270 // Get a constructed presence sensor for fan presence detection by gpio
271 std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
272 {
Matthew Barthe0d98a32019-11-19 15:40:57 -0600273 if (!method.contains("physpath") ||
274 !method.contains("devpath") ||
275 !method.contains("key"))
276 {
277 log<level::ERR>(
278 "Missing required gpio method properties",
279 entry("FAN_ENTRY=%d", fanIndex),
280 entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
281 throw std::runtime_error("Missing required gpio method properties");
282 }
283
284 auto physpath = method["physpath"].get<std::string>();
285 auto devpath = method["devpath"].get<std::string>();
286 auto key = method["key"].get<unsigned int>();
287
288 return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(
289 fanIndex, physpath, devpath, key);
Matthew Barthe7566632019-11-18 16:13:04 -0600290 }
291
292} // namespace method
293
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600294/**
295 * Redundancy policies for fan presence detection function definitions
296 */
297namespace rpolicy
298{
299 // Get an `Anyof` redundancy policy for the fan
300 std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
301 {
Matthew Barth26ad44a2019-11-22 15:37:14 -0600302 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
303 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
304 {
305 pSensors.emplace_back(*fanSensor);
306 }
307
308 return std::make_unique<AnyOf>(
309 std::get<fanPolicyFanPos>(fan), pSensors);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600310 }
311
312 // Get a `Fallback` redundancy policy for the fan
313 std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
314 {
Matthew Barth26ad44a2019-11-22 15:37:14 -0600315 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
316 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
317 {
318 // Place in the order given to fallback correctly
319 pSensors.emplace_back(*fanSensor);
320 }
321
322 return std::make_unique<Fallback>(
323 std::get<fanPolicyFanPos>(fan), pSensors);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600324 }
325
326} // namespace policy
327
Matthew Barthfd05d642019-11-14 15:01:57 -0600328} // namespace presence
329} // namespace fan
330} // namespace phosphor