blob: 6dc06c2a02da2fe06f95f7238cc628c93f642d5b [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 Barth4fb0af92019-12-04 09:15:00 -060039constexpr auto jsonFileAlt = "/etc/phosphor-fan-presence/presence/config.json";
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 Barthf3e70472019-12-03 13:33:20 -060053JsonConfig::JsonConfig(const std::string& jsonFile) :
54 _defaultFile(fs::path(jsonFile))
Matthew Barthfd05d642019-11-14 15:01:57 -060055{
Matthew Barth3a429d92019-12-05 16:58:27 -060056 if (!_defaultFile.empty())
57 {
58 // Load and process the json configuration
59 load();
60 }
61 else
62 {
63 log<level::ERR>("No JSON config file provided");
64 throw std::runtime_error("No JSON config file provided");
65 }
Matthew Barthfd05d642019-11-14 15:01:57 -060066}
67
68const policies& JsonConfig::get()
69{
70 return _policies;
71}
72
Matthew Barthf3e70472019-12-03 13:33:20 -060073void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc,
74 const struct signalfd_siginfo* sigInfo)
75{
76 try
77 {
78 // Load and process the json configuration
79 load();
80 for (auto& p: _policies)
81 {
82 p->monitor();
83 }
84 log<level::INFO>("Configuration loaded successfully");
85 }
86 catch (std::runtime_error& re)
87 {
88 log<level::ERR>("Error loading config, no config changes made",
89 entry("LOAD_ERROR=%s", re.what()));
90 }
91}
92
93void JsonConfig::load()
94{
Matthew Barth4fb0af92019-12-04 09:15:00 -060095 fs::path confFile{jsonFileAlt};
96 if (!fs::exists(confFile))
97 {
98 confFile = _defaultFile;
99 }
100 else
101 {
102 log<level::INFO>("Loading alternate configuration",
103 entry("JSON_FILE=%s", confFile.c_str()));
104 }
Matthew Barthf3e70472019-12-03 13:33:20 -0600105 std::ifstream file;
106 json jsonConf;
107
Matthew Barth4fb0af92019-12-04 09:15:00 -0600108 if (fs::exists(confFile))
Matthew Barthf3e70472019-12-03 13:33:20 -0600109 {
Matthew Barth4fb0af92019-12-04 09:15:00 -0600110 file.open(confFile);
Matthew Barthf3e70472019-12-03 13:33:20 -0600111 try
112 {
113 jsonConf = json::parse(file);
114 }
115 catch (std::exception& e)
116 {
117 log<level::ERR>("Failed to parse JSON config file",
Matthew Barth4fb0af92019-12-04 09:15:00 -0600118 entry("JSON_FILE=%s", confFile.c_str()),
Matthew Barthf3e70472019-12-03 13:33:20 -0600119 entry("JSON_ERROR=%s", e.what()));
120 throw std::runtime_error("Failed to parse JSON config file");
121 }
122 }
123 else
124 {
125 log<level::ERR>("Unable to open JSON config file",
Matthew Barth4fb0af92019-12-04 09:15:00 -0600126 entry("JSON_FILE=%s", confFile.c_str()));
Matthew Barthf3e70472019-12-03 13:33:20 -0600127 throw std::runtime_error("Unable to open JSON config file");
128 }
129
130 process(jsonConf);
131}
132
Matthew Barth4a94dec2019-11-15 10:40:47 -0600133void JsonConfig::process(const json& jsonConf)
134{
Matthew Barthf3e70472019-12-03 13:33:20 -0600135 policies policies;
136 std::vector<fanPolicy> fans;
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600137 // Set the expected number of fan entries
138 // to be size of the list of fan json config entries
139 // (Must be done to eliminate vector reallocation of fan references)
Matthew Barthf3e70472019-12-03 13:33:20 -0600140 fans.reserve(jsonConf.size());
Matthew Barth4a94dec2019-11-15 10:40:47 -0600141 for (auto& member : jsonConf)
142 {
Matthew Barthe7566632019-11-18 16:13:04 -0600143 if (!member.contains("name") || !member.contains("path") ||
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600144 !member.contains("methods") || !member.contains("rpolicy"))
Matthew Barth4a94dec2019-11-15 10:40:47 -0600145 {
146 log<level::ERR>(
147 "Missing required fan presence properties",
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600148 entry("REQUIRED_PROPERTIES=%s",
149 "{name, path, methods, rpolicy}"));
Matthew Barth4a94dec2019-11-15 10:40:47 -0600150 throw std::runtime_error(
151 "Missing required fan presence properties");
152 }
Matthew Barthe7566632019-11-18 16:13:04 -0600153
154 // Loop thru the configured methods of presence detection
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600155 std::vector<std::unique_ptr<PresenceSensor>> sensors;
Matthew Barthe7566632019-11-18 16:13:04 -0600156 for (auto& method : member["methods"].items())
157 {
158 if (!method.value().contains("type"))
159 {
160 log<level::ERR>(
161 "Missing required fan presence method type",
162 entry("FAN_NAME=%s",
163 member["name"].get<std::string>().c_str()));
164 throw std::runtime_error(
165 "Missing required fan presence method type");
166 }
167 // The method type of fan presence detection
168 // (Must have a supported function within the method namespace)
169 auto type = method.value()["type"].get<std::string>();
170 std::transform(type.begin(), type.end(), type.begin(), tolower);
171 auto func = _methods.find(type);
172 if (func != _methods.end())
173 {
174 // Call function for method type
Matthew Barthf3e70472019-12-03 13:33:20 -0600175 auto sensor = func->second(fans.size(), method.value());
Matthew Barthe7566632019-11-18 16:13:04 -0600176 if (sensor)
177 {
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600178 sensors.emplace_back(std::move(sensor));
Matthew Barthe7566632019-11-18 16:13:04 -0600179 }
180 }
181 else
182 {
183 log<level::ERR>("Invalid fan presence method type",
184 entry("FAN_NAME=%s",
185 member["name"].get<std::string>().c_str()),
186 entry("METHOD_TYPE=%s", type.c_str()));
187 throw std::runtime_error("Invalid fan presence method type");
188 }
189 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600190 auto fan = std::make_tuple(member["name"], member["path"]);
191 // Create a fan object
Matthew Barthf3e70472019-12-03 13:33:20 -0600192 fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600193
194 // Add fan presence policy
Matthew Barthf3e70472019-12-03 13:33:20 -0600195 auto policy = getPolicy(member["rpolicy"], fans.back());
196 if (policy)
197 {
198 policies.emplace_back(std::move(policy));
199 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600200 }
Matthew Barthf3e70472019-12-03 13:33:20 -0600201
202 // Success, refresh fans and policies lists
203 _fans.clear();
204 _fans.swap(fans);
205
206 _policies.clear();
207 _policies.swap(policies);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600208}
209
Matthew Barthf3e70472019-12-03 13:33:20 -0600210std::unique_ptr<RedundancyPolicy> JsonConfig::getPolicy(
211 const json& rpolicy,
212 const fanPolicy& fpolicy)
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600213{
214 if (!rpolicy.contains("type"))
215 {
216 log<level::ERR>("Missing required fan presence policy type",
217 entry("FAN_NAME=%s",
218 std::get<fanPolicyFanPos>(
Matthew Barthf3e70472019-12-03 13:33:20 -0600219 std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600220 entry("REQUIRED_PROPERTIES=%s", "{type}"));
221 throw std::runtime_error("Missing required fan presence policy type");
222 }
223
224 // The redundancy policy type for fan presence detection
225 // (Must have a supported function within the rpolicy namespace)
226 auto type = rpolicy["type"].get<std::string>();
227 std::transform(type.begin(), type.end(), type.begin(), tolower);
228 auto func = _rpolicies.find(type);
229 if (func != _rpolicies.end())
230 {
Matthew Barthf3e70472019-12-03 13:33:20 -0600231 // Call function for redundancy policy type and return the policy
232 return func->second(fpolicy);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600233 }
234 else
235 {
236 log<level::ERR>("Invalid fan presence policy type",
237 entry("FAN_NAME=%s",
Matthew Barthf3e70472019-12-03 13:33:20 -0600238 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600239 entry("RPOLICY_TYPE=%s", type.c_str()));
240 throw std::runtime_error("Invalid fan presence methods policy type");
Matthew Barth4a94dec2019-11-15 10:40:47 -0600241 }
242}
243
Matthew Barthe7566632019-11-18 16:13:04 -0600244/**
245 * Methods of fan presence detection function definitions
246 */
247namespace method
248{
249 // Get a constructed presence sensor for fan presence detection by tach
250 std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
251 {
Matthew Barthe0d98a32019-11-19 15:40:57 -0600252 if (!method.contains("sensors") ||
253 method["sensors"].size() == 0)
254 {
255 log<level::ERR>(
256 "Missing required tach method properties",
257 entry("FAN_ENTRY=%d", fanIndex),
258 entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
259 throw std::runtime_error("Missing required tach method properties");
260 }
261
262 std::vector<std::string> sensors;
263 for (auto& sensor : method["sensors"])
264 {
265 sensors.emplace_back(sensor.get<std::string>());
266 }
267
268 return std::make_unique<PolicyAccess<Tach, JsonConfig>>(
269 fanIndex, std::move(sensors));
Matthew Barthe7566632019-11-18 16:13:04 -0600270 }
271
272 // Get a constructed presence sensor for fan presence detection by gpio
273 std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
274 {
Matthew Barthe0d98a32019-11-19 15:40:57 -0600275 if (!method.contains("physpath") ||
276 !method.contains("devpath") ||
277 !method.contains("key"))
278 {
279 log<level::ERR>(
280 "Missing required gpio method properties",
281 entry("FAN_ENTRY=%d", fanIndex),
282 entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
283 throw std::runtime_error("Missing required gpio method properties");
284 }
285
286 auto physpath = method["physpath"].get<std::string>();
287 auto devpath = method["devpath"].get<std::string>();
288 auto key = method["key"].get<unsigned int>();
289
290 return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(
291 fanIndex, physpath, devpath, key);
Matthew Barthe7566632019-11-18 16:13:04 -0600292 }
293
294} // namespace method
295
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600296/**
297 * Redundancy policies for fan presence detection function definitions
298 */
299namespace rpolicy
300{
301 // Get an `Anyof` redundancy policy for the fan
302 std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
303 {
Matthew Barth26ad44a2019-11-22 15:37:14 -0600304 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
305 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
306 {
307 pSensors.emplace_back(*fanSensor);
308 }
309
310 return std::make_unique<AnyOf>(
311 std::get<fanPolicyFanPos>(fan), pSensors);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600312 }
313
314 // Get a `Fallback` redundancy policy for the fan
315 std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
316 {
Matthew Barth26ad44a2019-11-22 15:37:14 -0600317 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
318 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
319 {
320 // Place in the order given to fallback correctly
321 pSensors.emplace_back(*fanSensor);
322 }
323
324 return std::make_unique<Fallback>(
325 std::get<fanPolicyFanPos>(fan), pSensors);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600326 }
327
328} // namespace policy
329
Matthew Barthfd05d642019-11-14 15:01:57 -0600330} // namespace presence
331} // namespace fan
332} // namespace phosphor