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