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