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