blob: a13f5f92e291430b23dc3f6884f9d104539db8cd [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 Barthf3e70472019-12-03 13:33:20 -060051JsonConfig::JsonConfig(const std::string& jsonFile) :
52 _defaultFile(fs::path(jsonFile))
Matthew Barthfd05d642019-11-14 15:01:57 -060053{
Matthew Barthf3e70472019-12-03 13:33:20 -060054 // Load and process the json configuration
55 load();
Matthew Barthfd05d642019-11-14 15:01:57 -060056}
57
58const policies& JsonConfig::get()
59{
60 return _policies;
61}
62
Matthew Barthf3e70472019-12-03 13:33:20 -060063void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc,
64 const struct signalfd_siginfo* sigInfo)
65{
66 try
67 {
68 // Load and process the json configuration
69 load();
70 for (auto& p: _policies)
71 {
72 p->monitor();
73 }
74 log<level::INFO>("Configuration loaded successfully");
75 }
76 catch (std::runtime_error& re)
77 {
78 log<level::ERR>("Error loading config, no config changes made",
79 entry("LOAD_ERROR=%s", re.what()));
80 }
81}
82
83void JsonConfig::load()
84{
85 std::ifstream file;
86 json jsonConf;
87
88 if (fs::exists(_defaultFile))
89 {
90 file.open(_defaultFile);
91 try
92 {
93 jsonConf = json::parse(file);
94 }
95 catch (std::exception& e)
96 {
97 log<level::ERR>("Failed to parse JSON config file",
98 entry("JSON_FILE=%s", _defaultFile.c_str()),
99 entry("JSON_ERROR=%s", e.what()));
100 throw std::runtime_error("Failed to parse JSON config file");
101 }
102 }
103 else
104 {
105 log<level::ERR>("Unable to open JSON config file",
106 entry("JSON_FILE=%s", _defaultFile.c_str()));
107 throw std::runtime_error("Unable to open JSON config file");
108 }
109
110 process(jsonConf);
111}
112
Matthew Barth4a94dec2019-11-15 10:40:47 -0600113void JsonConfig::process(const json& jsonConf)
114{
Matthew Barthf3e70472019-12-03 13:33:20 -0600115 policies policies;
116 std::vector<fanPolicy> fans;
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600117 // Set the expected number of fan entries
118 // to be size of the list of fan json config entries
119 // (Must be done to eliminate vector reallocation of fan references)
Matthew Barthf3e70472019-12-03 13:33:20 -0600120 fans.reserve(jsonConf.size());
Matthew Barth4a94dec2019-11-15 10:40:47 -0600121 for (auto& member : jsonConf)
122 {
Matthew Barthe7566632019-11-18 16:13:04 -0600123 if (!member.contains("name") || !member.contains("path") ||
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600124 !member.contains("methods") || !member.contains("rpolicy"))
Matthew Barth4a94dec2019-11-15 10:40:47 -0600125 {
126 log<level::ERR>(
127 "Missing required fan presence properties",
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600128 entry("REQUIRED_PROPERTIES=%s",
129 "{name, path, methods, rpolicy}"));
Matthew Barth4a94dec2019-11-15 10:40:47 -0600130 throw std::runtime_error(
131 "Missing required fan presence properties");
132 }
Matthew Barthe7566632019-11-18 16:13:04 -0600133
134 // Loop thru the configured methods of presence detection
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600135 std::vector<std::unique_ptr<PresenceSensor>> sensors;
Matthew Barthe7566632019-11-18 16:13:04 -0600136 for (auto& method : member["methods"].items())
137 {
138 if (!method.value().contains("type"))
139 {
140 log<level::ERR>(
141 "Missing required fan presence method type",
142 entry("FAN_NAME=%s",
143 member["name"].get<std::string>().c_str()));
144 throw std::runtime_error(
145 "Missing required fan presence method type");
146 }
147 // The method type of fan presence detection
148 // (Must have a supported function within the method namespace)
149 auto type = method.value()["type"].get<std::string>();
150 std::transform(type.begin(), type.end(), type.begin(), tolower);
151 auto func = _methods.find(type);
152 if (func != _methods.end())
153 {
154 // Call function for method type
Matthew Barthf3e70472019-12-03 13:33:20 -0600155 auto sensor = func->second(fans.size(), method.value());
Matthew Barthe7566632019-11-18 16:13:04 -0600156 if (sensor)
157 {
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600158 sensors.emplace_back(std::move(sensor));
Matthew Barthe7566632019-11-18 16:13:04 -0600159 }
160 }
161 else
162 {
163 log<level::ERR>("Invalid fan presence method type",
164 entry("FAN_NAME=%s",
165 member["name"].get<std::string>().c_str()),
166 entry("METHOD_TYPE=%s", type.c_str()));
167 throw std::runtime_error("Invalid fan presence method type");
168 }
169 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600170 auto fan = std::make_tuple(member["name"], member["path"]);
171 // Create a fan object
Matthew Barthf3e70472019-12-03 13:33:20 -0600172 fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600173
174 // Add fan presence policy
Matthew Barthf3e70472019-12-03 13:33:20 -0600175 auto policy = getPolicy(member["rpolicy"], fans.back());
176 if (policy)
177 {
178 policies.emplace_back(std::move(policy));
179 }
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600180 }
Matthew Barthf3e70472019-12-03 13:33:20 -0600181
182 // Success, refresh fans and policies lists
183 _fans.clear();
184 _fans.swap(fans);
185
186 _policies.clear();
187 _policies.swap(policies);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600188}
189
Matthew Barthf3e70472019-12-03 13:33:20 -0600190std::unique_ptr<RedundancyPolicy> JsonConfig::getPolicy(
191 const json& rpolicy,
192 const fanPolicy& fpolicy)
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600193{
194 if (!rpolicy.contains("type"))
195 {
196 log<level::ERR>("Missing required fan presence policy type",
197 entry("FAN_NAME=%s",
198 std::get<fanPolicyFanPos>(
Matthew Barthf3e70472019-12-03 13:33:20 -0600199 std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600200 entry("REQUIRED_PROPERTIES=%s", "{type}"));
201 throw std::runtime_error("Missing required fan presence policy type");
202 }
203
204 // The redundancy policy type for fan presence detection
205 // (Must have a supported function within the rpolicy namespace)
206 auto type = rpolicy["type"].get<std::string>();
207 std::transform(type.begin(), type.end(), type.begin(), tolower);
208 auto func = _rpolicies.find(type);
209 if (func != _rpolicies.end())
210 {
Matthew Barthf3e70472019-12-03 13:33:20 -0600211 // Call function for redundancy policy type and return the policy
212 return func->second(fpolicy);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600213 }
214 else
215 {
216 log<level::ERR>("Invalid fan presence policy type",
217 entry("FAN_NAME=%s",
Matthew Barthf3e70472019-12-03 13:33:20 -0600218 std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600219 entry("RPOLICY_TYPE=%s", type.c_str()));
220 throw std::runtime_error("Invalid fan presence methods policy type");
Matthew Barth4a94dec2019-11-15 10:40:47 -0600221 }
222}
223
Matthew Barthe7566632019-11-18 16:13:04 -0600224/**
225 * Methods of fan presence detection function definitions
226 */
227namespace method
228{
229 // Get a constructed presence sensor for fan presence detection by tach
230 std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
231 {
Matthew Barthe0d98a32019-11-19 15:40:57 -0600232 if (!method.contains("sensors") ||
233 method["sensors"].size() == 0)
234 {
235 log<level::ERR>(
236 "Missing required tach method properties",
237 entry("FAN_ENTRY=%d", fanIndex),
238 entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
239 throw std::runtime_error("Missing required tach method properties");
240 }
241
242 std::vector<std::string> sensors;
243 for (auto& sensor : method["sensors"])
244 {
245 sensors.emplace_back(sensor.get<std::string>());
246 }
247
248 return std::make_unique<PolicyAccess<Tach, JsonConfig>>(
249 fanIndex, std::move(sensors));
Matthew Barthe7566632019-11-18 16:13:04 -0600250 }
251
252 // Get a constructed presence sensor for fan presence detection by gpio
253 std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
254 {
Matthew Barthe0d98a32019-11-19 15:40:57 -0600255 if (!method.contains("physpath") ||
256 !method.contains("devpath") ||
257 !method.contains("key"))
258 {
259 log<level::ERR>(
260 "Missing required gpio method properties",
261 entry("FAN_ENTRY=%d", fanIndex),
262 entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
263 throw std::runtime_error("Missing required gpio method properties");
264 }
265
266 auto physpath = method["physpath"].get<std::string>();
267 auto devpath = method["devpath"].get<std::string>();
268 auto key = method["key"].get<unsigned int>();
269
270 return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(
271 fanIndex, physpath, devpath, key);
Matthew Barthe7566632019-11-18 16:13:04 -0600272 }
273
274} // namespace method
275
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600276/**
277 * Redundancy policies for fan presence detection function definitions
278 */
279namespace rpolicy
280{
281 // Get an `Anyof` redundancy policy for the fan
282 std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
283 {
Matthew Barth26ad44a2019-11-22 15:37:14 -0600284 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
285 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
286 {
287 pSensors.emplace_back(*fanSensor);
288 }
289
290 return std::make_unique<AnyOf>(
291 std::get<fanPolicyFanPos>(fan), pSensors);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600292 }
293
294 // Get a `Fallback` redundancy policy for the fan
295 std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
296 {
Matthew Barth26ad44a2019-11-22 15:37:14 -0600297 std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
298 for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
299 {
300 // Place in the order given to fallback correctly
301 pSensors.emplace_back(*fanSensor);
302 }
303
304 return std::make_unique<Fallback>(
305 std::get<fanPolicyFanPos>(fan), pSensors);
Matthew Barthaa8d81d2019-11-21 14:07:31 -0600306 }
307
308} // namespace policy
309
Matthew Barthfd05d642019-11-14 15:01:57 -0600310} // namespace presence
311} // namespace fan
312} // namespace phosphor