blob: d85f721502b0f816e8b3af89fa3ac60d7b9a655b [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 Barthf3e70472019-12-03 13:33:20 -060056 // Load and process the json configuration
57 load();
Matthew Barthfd05d642019-11-14 15:01:57 -060058}
59
60const policies& JsonConfig::get()
61{
62 return _policies;
63}
64
Matthew Barthf3e70472019-12-03 13:33:20 -060065void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc,
66 const struct signalfd_siginfo* sigInfo)
67{
68 try
69 {
70 // Load and process the json configuration
71 load();
72 for (auto& p: _policies)
73 {
74 p->monitor();
75 }
76 log<level::INFO>("Configuration loaded successfully");
77 }
78 catch (std::runtime_error& re)
79 {
80 log<level::ERR>("Error loading config, no config changes made",
81 entry("LOAD_ERROR=%s", re.what()));
82 }
83}
84
85void JsonConfig::load()
86{
Matthew Barth4fb0af92019-12-04 09:15:00 -060087 fs::path confFile{jsonFileAlt};
88 if (!fs::exists(confFile))
89 {
90 confFile = _defaultFile;
91 }
92 else
93 {
94 log<level::INFO>("Loading alternate configuration",
95 entry("JSON_FILE=%s", confFile.c_str()));
96 }
Matthew Barthf3e70472019-12-03 13:33:20 -060097 std::ifstream file;
98 json jsonConf;
99
Matthew Barth4fb0af92019-12-04 09:15:00 -0600100 if (fs::exists(confFile))
Matthew Barthf3e70472019-12-03 13:33:20 -0600101 {
Matthew Barth4fb0af92019-12-04 09:15:00 -0600102 file.open(confFile);
Matthew Barthf3e70472019-12-03 13:33:20 -0600103 try
104 {
105 jsonConf = json::parse(file);
106 }
107 catch (std::exception& e)
108 {
109 log<level::ERR>("Failed to parse JSON config file",
Matthew Barth4fb0af92019-12-04 09:15:00 -0600110 entry("JSON_FILE=%s", confFile.c_str()),
Matthew Barthf3e70472019-12-03 13:33:20 -0600111 entry("JSON_ERROR=%s", e.what()));
112 throw std::runtime_error("Failed to parse JSON config file");
113 }
114 }
115 else
116 {
117 log<level::ERR>("Unable to open 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 throw std::runtime_error("Unable to open JSON config file");
120 }
121
122 process(jsonConf);
123}
124
Matthew Barth4a94dec2019-11-15 10:40:47 -0600125void JsonConfig::process(const json& jsonConf)
126{
Matthew Barthf3e70472019-12-03 13:33:20 -0600127 policies policies;
128 std::vector<fanPolicy> fans;
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600129 // Set the expected number of fan entries
130 // to be size of the list of fan json config entries
131 // (Must be done to eliminate vector reallocation of fan references)
Matthew Barthf3e70472019-12-03 13:33:20 -0600132 fans.reserve(jsonConf.size());
Matthew Barth4a94dec2019-11-15 10:40:47 -0600133 for (auto& member : jsonConf)
134 {
Matthew Barthe7566632019-11-18 16:13:04 -0600135 if (!member.contains("name") || !member.contains("path") ||
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600136 !member.contains("methods") || !member.contains("rpolicy"))
Matthew Barth4a94dec2019-11-15 10:40:47 -0600137 {
138 log<level::ERR>(
139 "Missing required fan presence properties",
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600140 entry("REQUIRED_PROPERTIES=%s",
141 "{name, path, methods, rpolicy}"));
Matthew Barth4a94dec2019-11-15 10:40:47 -0600142 throw std::runtime_error(
143 "Missing required fan presence properties");
144 }
Matthew Barthe7566632019-11-18 16:13:04 -0600145
146 // Loop thru the configured methods of presence detection
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600147 std::vector<std::unique_ptr<PresenceSensor>> sensors;
Matthew Barthe7566632019-11-18 16:13:04 -0600148 for (auto& method : member["methods"].items())
149 {
150 if (!method.value().contains("type"))
151 {
152 log<level::ERR>(
153 "Missing required fan presence method type",
154 entry("FAN_NAME=%s",
155 member["name"].get<std::string>().c_str()));
156 throw std::runtime_error(
157 "Missing required fan presence method type");
158 }
159 // The method type of fan presence detection
160 // (Must have a supported function within the method namespace)
161 auto type = method.value()["type"].get<std::string>();
162 std::transform(type.begin(), type.end(), type.begin(), tolower);
163 auto func = _methods.find(type);
164 if (func != _methods.end())
165 {
166 // Call function for method type
Matthew Barthf3e70472019-12-03 13:33:20 -0600167 auto sensor = func->second(fans.size(), method.value());
Matthew Barthe7566632019-11-18 16:13:04 -0600168 if (sensor)
169 {
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600170 sensors.emplace_back(std::move(sensor));
Matthew Barthe7566632019-11-18 16:13:04 -0600171 }
172 }
173 else
174 {
175 log<level::ERR>("Invalid fan presence method type",
176 entry("FAN_NAME=%s",
177 member["name"].get<std::string>().c_str()),
178 entry("METHOD_TYPE=%s", type.c_str()));
179 throw std::runtime_error("Invalid fan presence method type");
180 }
181 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600182 auto fan = std::make_tuple(member["name"], member["path"]);
183 // Create a fan object
Matthew Barthf3e70472019-12-03 13:33:20 -0600184 fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600185
186 // Add fan presence policy
Matthew Barthf3e70472019-12-03 13:33:20 -0600187 auto policy = getPolicy(member["rpolicy"], fans.back());
188 if (policy)
189 {
190 policies.emplace_back(std::move(policy));
191 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600192 }
Matthew Barthf3e70472019-12-03 13:33:20 -0600193
194 // Success, refresh fans and policies lists
195 _fans.clear();
196 _fans.swap(fans);
197
198 _policies.clear();
199 _policies.swap(policies);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600200}
201
Matthew Barthf3e70472019-12-03 13:33:20 -0600202std::unique_ptr<RedundancyPolicy> JsonConfig::getPolicy(
203 const json& rpolicy,
204 const fanPolicy& fpolicy)
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600205{
206 if (!rpolicy.contains("type"))
207 {
208 log<level::ERR>("Missing required fan presence policy type",
209 entry("FAN_NAME=%s",
210 std::get<fanPolicyFanPos>(
Matthew Barthf3e70472019-12-03 13:33:20 -0600211 std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600212 entry("REQUIRED_PROPERTIES=%s", "{type}"));
213 throw std::runtime_error("Missing required fan presence policy type");
214 }
215
216 // The redundancy policy type for fan presence detection
217 // (Must have a supported function within the rpolicy namespace)
218 auto type = rpolicy["type"].get<std::string>();
219 std::transform(type.begin(), type.end(), type.begin(), tolower);
220 auto func = _rpolicies.find(type);
221 if (func != _rpolicies.end())
222 {
Matthew Barthf3e70472019-12-03 13:33:20 -0600223 // Call function for redundancy policy type and return the policy
224 return func->second(fpolicy);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600225 }
226 else
227 {
228 log<level::ERR>("Invalid fan presence policy type",
229 entry("FAN_NAME=%s",
Matthew Barthf3e70472019-12-03 13:33:20 -0600230 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600231 entry("RPOLICY_TYPE=%s", type.c_str()));
232 throw std::runtime_error("Invalid fan presence methods policy type");
Matthew Barth4a94dec2019-11-15 10:40:47 -0600233 }
234}
235
Matthew Barthe7566632019-11-18 16:13:04 -0600236/**
237 * Methods of fan presence detection function definitions
238 */
239namespace method
240{
241 // Get a constructed presence sensor for fan presence detection by tach
242 std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
243 {
Matthew Barthe0d98a32019-11-19 15:40:57 -0600244 if (!method.contains("sensors") ||
245 method["sensors"].size() == 0)
246 {
247 log<level::ERR>(
248 "Missing required tach method properties",
249 entry("FAN_ENTRY=%d", fanIndex),
250 entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
251 throw std::runtime_error("Missing required tach method properties");
252 }
253
254 std::vector<std::string> sensors;
255 for (auto& sensor : method["sensors"])
256 {
257 sensors.emplace_back(sensor.get<std::string>());
258 }
259
260 return std::make_unique<PolicyAccess<Tach, JsonConfig>>(
261 fanIndex, std::move(sensors));
Matthew Barthe7566632019-11-18 16:13:04 -0600262 }
263
264 // Get a constructed presence sensor for fan presence detection by gpio
265 std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
266 {
Matthew Barthe0d98a32019-11-19 15:40:57 -0600267 if (!method.contains("physpath") ||
268 !method.contains("devpath") ||
269 !method.contains("key"))
270 {
271 log<level::ERR>(
272 "Missing required gpio method properties",
273 entry("FAN_ENTRY=%d", fanIndex),
274 entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
275 throw std::runtime_error("Missing required gpio method properties");
276 }
277
278 auto physpath = method["physpath"].get<std::string>();
279 auto devpath = method["devpath"].get<std::string>();
280 auto key = method["key"].get<unsigned int>();
281
282 return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(
283 fanIndex, physpath, devpath, key);
Matthew Barthe7566632019-11-18 16:13:04 -0600284 }
285
286} // namespace method
287
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600288/**
289 * Redundancy policies for fan presence detection function definitions
290 */
291namespace rpolicy
292{
293 // Get an `Anyof` redundancy policy for the fan
294 std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
295 {
Matthew Barth26ad44a2019-11-22 15:37:14 -0600296 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
297 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
298 {
299 pSensors.emplace_back(*fanSensor);
300 }
301
302 return std::make_unique<AnyOf>(
303 std::get<fanPolicyFanPos>(fan), pSensors);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600304 }
305
306 // Get a `Fallback` redundancy policy for the fan
307 std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
308 {
Matthew Barth26ad44a2019-11-22 15:37:14 -0600309 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
310 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
311 {
312 // Place in the order given to fallback correctly
313 pSensors.emplace_back(*fanSensor);
314 }
315
316 return std::make_unique<Fallback>(
317 std::get<fanPolicyFanPos>(fan), pSensors);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600318 }
319
320} // namespace policy
321
Matthew Barthfd05d642019-11-14 15:01:57 -0600322} // namespace presence
323} // namespace fan
324} // namespace phosphor