xref: /openbmc/phosphor-fan-presence/presence/json_parser.cpp (revision 9e9f599cece7fe6cdf99b1927a515fc8a552b7e9)
1 /**
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 "json_parser.hpp"
17 
18 #include "anyof.hpp"
19 #include "fallback.hpp"
20 #include "gpio.hpp"
21 #include "json_config.hpp"
22 #include "sdbusplus.hpp"
23 #include "tach.hpp"
24 
25 #include <nlohmann/json.hpp>
26 #include <phosphor-logging/log.hpp>
27 #include <sdbusplus/bus.hpp>
28 
29 #include <filesystem>
30 #include <fstream>
31 #include <string>
32 
33 namespace phosphor
34 {
35 namespace fan
36 {
37 namespace presence
38 {
39 
40 using json = nlohmann::json;
41 namespace fs = std::filesystem;
42 using namespace phosphor::logging;
43 
44 policies JsonConfig::_policies;
45 const std::map<std::string, methodHandler> JsonConfig::_methods = {
46     {"tach", method::getTach}, {"gpio", method::getGpio}};
47 const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies = {
48     {"anyof", rpolicy::getAnyof}, {"fallback", rpolicy::getFallback}};
49 
50 JsonConfig::JsonConfig(sdbusplus::bus::bus& bus) : _bus(bus)
51 {
52     using config = fan::JsonConfig;
53 
54     // Load and process the json configuration
55     process(config::load(config::getConfFile(bus, confAppName, confFileName)));
56 }
57 
58 const policies& JsonConfig::get()
59 {
60     return _policies;
61 }
62 
63 void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc,
64                                const struct signalfd_siginfo* sigInfo)
65 {
66     try
67     {
68         using config = fan::JsonConfig;
69 
70         _reporter.reset();
71 
72         // Load and process the json configuration
73         process(
74             config::load(config::getConfFile(_bus, confAppName, confFileName)));
75 
76         for (auto& p : _policies)
77         {
78             p->monitor();
79         }
80         log<level::INFO>("Configuration loaded successfully");
81     }
82     catch (std::runtime_error& re)
83     {
84         log<level::ERR>("Error loading config, no config changes made",
85                         entry("LOAD_ERROR=%s", re.what()));
86     }
87 }
88 
89 void JsonConfig::process(const json& jsonConf)
90 {
91     policies policies;
92     std::vector<fanPolicy> fans;
93     // Set the expected number of fan entries
94     // to be size of the list of fan json config entries
95     // (Must be done to eliminate vector reallocation of fan references)
96     fans.reserve(jsonConf.size());
97     for (auto& member : jsonConf)
98     {
99         if (!member.contains("name") || !member.contains("path") ||
100             !member.contains("methods") || !member.contains("rpolicy"))
101         {
102             log<level::ERR>("Missing required fan presence properties",
103                             entry("REQUIRED_PROPERTIES=%s",
104                                   "{name, path, methods, rpolicy}"));
105             throw std::runtime_error(
106                 "Missing required fan presence properties");
107         }
108 
109         // Loop thru the configured methods of presence detection
110         std::vector<std::unique_ptr<PresenceSensor>> sensors;
111         for (auto& method : member["methods"].items())
112         {
113             if (!method.value().contains("type"))
114             {
115                 log<level::ERR>(
116                     "Missing required fan presence method type",
117                     entry("FAN_NAME=%s",
118                           member["name"].get<std::string>().c_str()));
119                 throw std::runtime_error(
120                     "Missing required fan presence method type");
121             }
122             // The method type of fan presence detection
123             // (Must have a supported function within the method namespace)
124             auto type = method.value()["type"].get<std::string>();
125             std::transform(type.begin(), type.end(), type.begin(), tolower);
126             auto func = _methods.find(type);
127             if (func != _methods.end())
128             {
129                 // Call function for method type
130                 auto sensor = func->second(fans.size(), method.value());
131                 if (sensor)
132                 {
133                     sensors.emplace_back(std::move(sensor));
134                 }
135             }
136             else
137             {
138                 log<level::ERR>(
139                     "Invalid fan presence method type",
140                     entry("FAN_NAME=%s",
141                           member["name"].get<std::string>().c_str()),
142                     entry("METHOD_TYPE=%s", type.c_str()));
143                 throw std::runtime_error("Invalid fan presence method type");
144             }
145         }
146 
147         // Get the amount of time a fan must be not present before
148         // creating an error.
149         std::optional<size_t> timeUntilError;
150         if (member.contains("fan_missing_error_time"))
151         {
152             timeUntilError = member["fan_missing_error_time"].get<size_t>();
153         }
154 
155         auto fan =
156             std::make_tuple(member["name"], member["path"], timeUntilError);
157         // Create a fan object
158         fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
159 
160         // Add fan presence policy
161         auto policy = getPolicy(member["rpolicy"], fans.back());
162         if (policy)
163         {
164             policies.emplace_back(std::move(policy));
165         }
166     }
167 
168     // Success, refresh fans and policies lists
169     _fans.clear();
170     _fans.swap(fans);
171 
172     _policies.clear();
173     _policies.swap(policies);
174 
175     // Create the error reporter class if necessary
176     if (std::any_of(_fans.begin(), _fans.end(), [](const auto& fan) {
177             return std::get<std::optional<size_t>>(std::get<Fan>(fan)) !=
178                    std::nullopt;
179         }))
180     {
181         _reporter = std::make_unique<ErrorReporter>(_bus, _fans);
182     }
183 }
184 
185 std::unique_ptr<RedundancyPolicy>
186     JsonConfig::getPolicy(const json& rpolicy, const fanPolicy& fpolicy)
187 {
188     if (!rpolicy.contains("type"))
189     {
190         log<level::ERR>(
191             "Missing required fan presence policy type",
192             entry("FAN_NAME=%s",
193                   std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
194             entry("REQUIRED_PROPERTIES=%s", "{type}"));
195         throw std::runtime_error("Missing required fan presence policy type");
196     }
197 
198     // The redundancy policy type for fan presence detection
199     // (Must have a supported function within the rpolicy namespace)
200     auto type = rpolicy["type"].get<std::string>();
201     std::transform(type.begin(), type.end(), type.begin(), tolower);
202     auto func = _rpolicies.find(type);
203     if (func != _rpolicies.end())
204     {
205         // Call function for redundancy policy type and return the policy
206         return func->second(fpolicy);
207     }
208     else
209     {
210         log<level::ERR>(
211             "Invalid fan presence policy type",
212             entry("FAN_NAME=%s",
213                   std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
214             entry("RPOLICY_TYPE=%s", type.c_str()));
215         throw std::runtime_error("Invalid fan presence methods policy type");
216     }
217 }
218 
219 /**
220  * Methods of fan presence detection function definitions
221  */
222 namespace method
223 {
224 // Get a constructed presence sensor for fan presence detection by tach
225 std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
226 {
227     if (!method.contains("sensors") || method["sensors"].size() == 0)
228     {
229         log<level::ERR>("Missing required tach method properties",
230                         entry("FAN_ENTRY=%d", fanIndex),
231                         entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
232         throw std::runtime_error("Missing required tach method properties");
233     }
234 
235     std::vector<std::string> sensors;
236     for (auto& sensor : method["sensors"])
237     {
238         sensors.emplace_back(sensor.get<std::string>());
239     }
240 
241     return std::make_unique<PolicyAccess<Tach, JsonConfig>>(fanIndex,
242                                                             std::move(sensors));
243 }
244 
245 // Get a constructed presence sensor for fan presence detection by gpio
246 std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
247 {
248     if (!method.contains("physpath") || !method.contains("devpath") ||
249         !method.contains("key"))
250     {
251         log<level::ERR>(
252             "Missing required gpio method properties",
253             entry("FAN_ENTRY=%d", fanIndex),
254             entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
255         throw std::runtime_error("Missing required gpio method properties");
256     }
257 
258     auto physpath = method["physpath"].get<std::string>();
259     auto devpath = method["devpath"].get<std::string>();
260     auto key = method["key"].get<unsigned int>();
261 
262     return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(fanIndex, physpath,
263                                                             devpath, key);
264 }
265 
266 } // namespace method
267 
268 /**
269  * Redundancy policies for fan presence detection function definitions
270  */
271 namespace rpolicy
272 {
273 // Get an `Anyof` redundancy policy for the fan
274 std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
275 {
276     std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
277     for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
278     {
279         pSensors.emplace_back(*fanSensor);
280     }
281 
282     return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors);
283 }
284 
285 // Get a `Fallback` redundancy policy for the fan
286 std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
287 {
288     std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
289     for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
290     {
291         // Place in the order given to fallback correctly
292         pSensors.emplace_back(*fanSensor);
293     }
294 
295     return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors);
296 }
297 
298 } // namespace rpolicy
299 
300 } // namespace presence
301 } // namespace fan
302 } // namespace phosphor
303