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