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