11a568650SJolie Ku /** 21a568650SJolie Ku * Copyright © 2019 IBM Corporation 31a568650SJolie Ku * 41a568650SJolie Ku * Licensed under the Apache License, Version 2.0 (the "License"); 51a568650SJolie Ku * you may not use this file except in compliance with the License. 61a568650SJolie Ku * You may obtain a copy of the License at 71a568650SJolie Ku * 81a568650SJolie Ku * http://www.apache.org/licenses/LICENSE-2.0 91a568650SJolie Ku * 101a568650SJolie Ku * Unless required by applicable law or agreed to in writing, software 111a568650SJolie Ku * distributed under the License is distributed on an "AS IS" BASIS, 121a568650SJolie Ku * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131a568650SJolie Ku * See the License for the specific language governing permissions and 141a568650SJolie Ku * limitations under the License. 151a568650SJolie Ku */ 161a568650SJolie Ku #include "json_parser.hpp" 171a568650SJolie Ku 181a568650SJolie Ku #include "anyof.hpp" 191a568650SJolie Ku #include "fallback.hpp" 201a568650SJolie Ku #include "gpio.hpp" 211a568650SJolie Ku #include "json_config.hpp" 221a568650SJolie Ku #include "sdbusplus.hpp" 231a568650SJolie Ku #include "tach.hpp" 241a568650SJolie Ku 251a568650SJolie Ku #include <nlohmann/json.hpp> 261a568650SJolie Ku #include <phosphor-logging/log.hpp> 271a568650SJolie Ku #include <sdbusplus/bus.hpp> 28a35a890bSMike Capps #include <xyz/openbmc_project/Logging/Create/server.hpp> 29a35a890bSMike Capps #include <xyz/openbmc_project/Logging/Entry/server.hpp> 301a568650SJolie Ku 311a568650SJolie Ku #include <filesystem> 321a568650SJolie Ku #include <fstream> 331a568650SJolie Ku #include <string> 341a568650SJolie Ku 351a568650SJolie Ku namespace phosphor 361a568650SJolie Ku { 371a568650SJolie Ku namespace fan 381a568650SJolie Ku { 391a568650SJolie Ku namespace presence 401a568650SJolie Ku { 411a568650SJolie Ku 421a568650SJolie Ku using json = nlohmann::json; 431a568650SJolie Ku namespace fs = std::filesystem; 441a568650SJolie Ku using namespace phosphor::logging; 451a568650SJolie Ku 461a568650SJolie Ku policies JsonConfig::_policies; 471a568650SJolie Ku const std::map<std::string, methodHandler> JsonConfig::_methods = { 481a568650SJolie Ku {"tach", method::getTach}, {"gpio", method::getGpio}}; 491a568650SJolie Ku const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies = { 501a568650SJolie Ku {"anyof", rpolicy::getAnyof}, {"fallback", rpolicy::getFallback}}; 511a568650SJolie Ku 52a35a890bSMike Capps const auto loggingPath = "/xyz/openbmc_project/logging"; 53a35a890bSMike Capps const auto loggingCreateIface = "xyz.openbmc_project.Logging.Create"; 54a35a890bSMike Capps 55*cb356d48SPatrick Williams JsonConfig::JsonConfig(sdbusplus::bus_t& bus) : _bus(bus) 560daedd18SMatt Spinler {} 571a568650SJolie Ku 585b839919SMatthew Barth void JsonConfig::start() 590daedd18SMatt Spinler { 605b839919SMatthew Barth using config = fan::JsonConfig; 615b839919SMatthew Barth 62dfc8c4d0SMatt Spinler if (!_loaded) 63dfc8c4d0SMatt Spinler { 64808d7fe8SMike Capps process(config::load(config::getConfFile(confAppName, confFileName))); 65dfc8c4d0SMatt Spinler 66dfc8c4d0SMatt Spinler _loaded = true; 670daedd18SMatt Spinler 680daedd18SMatt Spinler for (auto& p : _policies) 690daedd18SMatt Spinler { 700daedd18SMatt Spinler p->monitor(); 710daedd18SMatt Spinler } 721a568650SJolie Ku } 73dfc8c4d0SMatt Spinler } 741a568650SJolie Ku 751a568650SJolie Ku const policies& JsonConfig::get() 761a568650SJolie Ku { 771a568650SJolie Ku return _policies; 781a568650SJolie Ku } 791a568650SJolie Ku 80808d7fe8SMike Capps void JsonConfig::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/, 81808d7fe8SMike Capps const struct signalfd_siginfo* /*sigInfo*/) 821a568650SJolie Ku { 831a568650SJolie Ku try 841a568650SJolie Ku { 851a568650SJolie Ku using config = fan::JsonConfig; 861a568650SJolie Ku 879e9f599cSMatt Spinler _reporter.reset(); 889e9f599cSMatt Spinler 891a568650SJolie Ku // Load and process the json configuration 90808d7fe8SMike Capps process(config::load(config::getConfFile(confAppName, confFileName))); 911a568650SJolie Ku 921a568650SJolie Ku for (auto& p : _policies) 931a568650SJolie Ku { 941a568650SJolie Ku p->monitor(); 951a568650SJolie Ku } 961a568650SJolie Ku log<level::INFO>("Configuration loaded successfully"); 971a568650SJolie Ku } 98ddb773b2SPatrick Williams catch (const std::runtime_error& re) 991a568650SJolie Ku { 1001a568650SJolie Ku log<level::ERR>("Error loading config, no config changes made", 1011a568650SJolie Ku entry("LOAD_ERROR=%s", re.what())); 1021a568650SJolie Ku } 1031a568650SJolie Ku } 1041a568650SJolie Ku 1051a568650SJolie Ku void JsonConfig::process(const json& jsonConf) 1061a568650SJolie Ku { 1071a568650SJolie Ku policies policies; 1081a568650SJolie Ku std::vector<fanPolicy> fans; 1091a568650SJolie Ku // Set the expected number of fan entries 1101a568650SJolie Ku // to be size of the list of fan json config entries 1111a568650SJolie Ku // (Must be done to eliminate vector reallocation of fan references) 1129e9f599cSMatt Spinler fans.reserve(jsonConf.size()); 1139e9f599cSMatt Spinler for (auto& member : jsonConf) 1141a568650SJolie Ku { 1151a568650SJolie Ku if (!member.contains("name") || !member.contains("path") || 1161a568650SJolie Ku !member.contains("methods") || !member.contains("rpolicy")) 1171a568650SJolie Ku { 1181a568650SJolie Ku log<level::ERR>("Missing required fan presence properties", 1191a568650SJolie Ku entry("REQUIRED_PROPERTIES=%s", 1201a568650SJolie Ku "{name, path, methods, rpolicy}")); 1211a568650SJolie Ku throw std::runtime_error( 1221a568650SJolie Ku "Missing required fan presence properties"); 1231a568650SJolie Ku } 1241a568650SJolie Ku 1251a568650SJolie Ku // Loop thru the configured methods of presence detection 1261a568650SJolie Ku std::vector<std::unique_ptr<PresenceSensor>> sensors; 1271a568650SJolie Ku for (auto& method : member["methods"].items()) 1281a568650SJolie Ku { 1291a568650SJolie Ku if (!method.value().contains("type")) 1301a568650SJolie Ku { 1311a568650SJolie Ku log<level::ERR>( 1321a568650SJolie Ku "Missing required fan presence method type", 1331a568650SJolie Ku entry("FAN_NAME=%s", 1341a568650SJolie Ku member["name"].get<std::string>().c_str())); 1351a568650SJolie Ku throw std::runtime_error( 1361a568650SJolie Ku "Missing required fan presence method type"); 1371a568650SJolie Ku } 1381a568650SJolie Ku // The method type of fan presence detection 1391a568650SJolie Ku // (Must have a supported function within the method namespace) 1401a568650SJolie Ku auto type = method.value()["type"].get<std::string>(); 1411a568650SJolie Ku std::transform(type.begin(), type.end(), type.begin(), tolower); 1421a568650SJolie Ku auto func = _methods.find(type); 1431a568650SJolie Ku if (func != _methods.end()) 1441a568650SJolie Ku { 1451a568650SJolie Ku // Call function for method type 1461a568650SJolie Ku auto sensor = func->second(fans.size(), method.value()); 1471a568650SJolie Ku if (sensor) 1481a568650SJolie Ku { 1491a568650SJolie Ku sensors.emplace_back(std::move(sensor)); 1501a568650SJolie Ku } 1511a568650SJolie Ku } 1521a568650SJolie Ku else 1531a568650SJolie Ku { 1541a568650SJolie Ku log<level::ERR>( 1551a568650SJolie Ku "Invalid fan presence method type", 1561a568650SJolie Ku entry("FAN_NAME=%s", 1571a568650SJolie Ku member["name"].get<std::string>().c_str()), 1581a568650SJolie Ku entry("METHOD_TYPE=%s", type.c_str())); 1591a568650SJolie Ku throw std::runtime_error("Invalid fan presence method type"); 1601a568650SJolie Ku } 1611a568650SJolie Ku } 1629e9f599cSMatt Spinler 1639e9f599cSMatt Spinler // Get the amount of time a fan must be not present before 1649e9f599cSMatt Spinler // creating an error. 1659e9f599cSMatt Spinler std::optional<size_t> timeUntilError; 1669e9f599cSMatt Spinler if (member.contains("fan_missing_error_time")) 1679e9f599cSMatt Spinler { 1689e9f599cSMatt Spinler timeUntilError = member["fan_missing_error_time"].get<size_t>(); 1699e9f599cSMatt Spinler } 1709e9f599cSMatt Spinler 1719e9f599cSMatt Spinler auto fan = 1729e9f599cSMatt Spinler std::make_tuple(member["name"], member["path"], timeUntilError); 1731a568650SJolie Ku // Create a fan object 1741a568650SJolie Ku fans.emplace_back(std::make_tuple(fan, std::move(sensors))); 1751a568650SJolie Ku 1761a568650SJolie Ku // Add fan presence policy 1771a568650SJolie Ku auto policy = getPolicy(member["rpolicy"], fans.back()); 1781a568650SJolie Ku if (policy) 1791a568650SJolie Ku { 1801a568650SJolie Ku policies.emplace_back(std::move(policy)); 1811a568650SJolie Ku } 1821a568650SJolie Ku } 1831a568650SJolie Ku 1841a568650SJolie Ku // Success, refresh fans and policies lists 1851a568650SJolie Ku _fans.clear(); 1861a568650SJolie Ku _fans.swap(fans); 1871a568650SJolie Ku 1881a568650SJolie Ku _policies.clear(); 1891a568650SJolie Ku _policies.swap(policies); 190e8122390SMatt Spinler 191e8122390SMatt Spinler // Create the error reporter class if necessary 1929e9f599cSMatt Spinler if (std::any_of(_fans.begin(), _fans.end(), [](const auto& fan) { 1939e9f599cSMatt Spinler return std::get<std::optional<size_t>>(std::get<Fan>(fan)) != 1949e9f599cSMatt Spinler std::nullopt; 1959e9f599cSMatt Spinler })) 196e8122390SMatt Spinler { 1979e9f599cSMatt Spinler _reporter = std::make_unique<ErrorReporter>(_bus, _fans); 198e8122390SMatt Spinler } 1991a568650SJolie Ku } 2001a568650SJolie Ku 2011a568650SJolie Ku std::unique_ptr<RedundancyPolicy> 2021a568650SJolie Ku JsonConfig::getPolicy(const json& rpolicy, const fanPolicy& fpolicy) 2031a568650SJolie Ku { 2041a568650SJolie Ku if (!rpolicy.contains("type")) 2051a568650SJolie Ku { 2061a568650SJolie Ku log<level::ERR>( 2071a568650SJolie Ku "Missing required fan presence policy type", 2081a568650SJolie Ku entry("FAN_NAME=%s", 2091a568650SJolie Ku std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()), 2101a568650SJolie Ku entry("REQUIRED_PROPERTIES=%s", "{type}")); 2111a568650SJolie Ku throw std::runtime_error("Missing required fan presence policy type"); 2121a568650SJolie Ku } 2131a568650SJolie Ku 2141a568650SJolie Ku // The redundancy policy type for fan presence detection 2151a568650SJolie Ku // (Must have a supported function within the rpolicy namespace) 2161a568650SJolie Ku auto type = rpolicy["type"].get<std::string>(); 2171a568650SJolie Ku std::transform(type.begin(), type.end(), type.begin(), tolower); 2181a568650SJolie Ku auto func = _rpolicies.find(type); 2191a568650SJolie Ku if (func != _rpolicies.end()) 2201a568650SJolie Ku { 2211a568650SJolie Ku // Call function for redundancy policy type and return the policy 2221a568650SJolie Ku return func->second(fpolicy); 2231a568650SJolie Ku } 2241a568650SJolie Ku else 2251a568650SJolie Ku { 2261a568650SJolie Ku log<level::ERR>( 2271a568650SJolie Ku "Invalid fan presence policy type", 2281a568650SJolie Ku entry("FAN_NAME=%s", 2291a568650SJolie Ku std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()), 2301a568650SJolie Ku entry("RPOLICY_TYPE=%s", type.c_str())); 2311a568650SJolie Ku throw std::runtime_error("Invalid fan presence methods policy type"); 2321a568650SJolie Ku } 2331a568650SJolie Ku } 2341a568650SJolie Ku 2351a568650SJolie Ku /** 2361a568650SJolie Ku * Methods of fan presence detection function definitions 2371a568650SJolie Ku */ 2381a568650SJolie Ku namespace method 2391a568650SJolie Ku { 2401a568650SJolie Ku // Get a constructed presence sensor for fan presence detection by tach 2411a568650SJolie Ku std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method) 2421a568650SJolie Ku { 2431a568650SJolie Ku if (!method.contains("sensors") || method["sensors"].size() == 0) 2441a568650SJolie Ku { 2451a568650SJolie Ku log<level::ERR>("Missing required tach method properties", 2461a568650SJolie Ku entry("FAN_ENTRY=%d", fanIndex), 2471a568650SJolie Ku entry("REQUIRED_PROPERTIES=%s", "{sensors}")); 2481a568650SJolie Ku throw std::runtime_error("Missing required tach method properties"); 2491a568650SJolie Ku } 2501a568650SJolie Ku 2511a568650SJolie Ku std::vector<std::string> sensors; 2521a568650SJolie Ku for (auto& sensor : method["sensors"]) 2531a568650SJolie Ku { 2541a568650SJolie Ku sensors.emplace_back(sensor.get<std::string>()); 2551a568650SJolie Ku } 2561a568650SJolie Ku 2571a568650SJolie Ku return std::make_unique<PolicyAccess<Tach, JsonConfig>>(fanIndex, 2581a568650SJolie Ku std::move(sensors)); 2591a568650SJolie Ku } 2601a568650SJolie Ku 2611a568650SJolie Ku // Get a constructed presence sensor for fan presence detection by gpio 2621a568650SJolie Ku std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method) 2631a568650SJolie Ku { 2641a568650SJolie Ku if (!method.contains("physpath") || !method.contains("devpath") || 2651a568650SJolie Ku !method.contains("key")) 2661a568650SJolie Ku { 2671a568650SJolie Ku log<level::ERR>( 2681a568650SJolie Ku "Missing required gpio method properties", 2691a568650SJolie Ku entry("FAN_ENTRY=%d", fanIndex), 2701a568650SJolie Ku entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}")); 2711a568650SJolie Ku throw std::runtime_error("Missing required gpio method properties"); 2721a568650SJolie Ku } 2731a568650SJolie Ku 2741a568650SJolie Ku auto physpath = method["physpath"].get<std::string>(); 2751a568650SJolie Ku auto devpath = method["devpath"].get<std::string>(); 2761a568650SJolie Ku auto key = method["key"].get<unsigned int>(); 2771a568650SJolie Ku 278a35a890bSMike Capps try 279a35a890bSMike Capps { 280a35a890bSMike Capps return std::make_unique<PolicyAccess<Gpio, JsonConfig>>( 281a35a890bSMike Capps fanIndex, physpath, devpath, key); 282a35a890bSMike Capps } 283a35a890bSMike Capps catch (const sdbusplus::exception_t& e) 284a35a890bSMike Capps { 285a35a890bSMike Capps namespace sdlogging = sdbusplus::xyz::openbmc_project::Logging::server; 286a35a890bSMike Capps 287a35a890bSMike Capps log<level::ERR>( 288a35a890bSMike Capps fmt::format( 289a35a890bSMike Capps "Error creating Gpio device bridge, hardware not detected: {}", 290a35a890bSMike Capps e.what()) 291a35a890bSMike Capps .c_str()); 292a35a890bSMike Capps 293a35a890bSMike Capps auto severity = 294a35a890bSMike Capps sdlogging::convertForMessage(sdlogging::Entry::Level::Error); 295a35a890bSMike Capps 296a35a890bSMike Capps std::map<std::string, std::string> additionalData{ 297a35a890bSMike Capps {"PHYSPATH", physpath}, 298a35a890bSMike Capps {"DEVPATH", devpath}, 299a35a890bSMike Capps {"FANINDEX", std::to_string(fanIndex)}}; 300a35a890bSMike Capps 301a35a890bSMike Capps try 302a35a890bSMike Capps { 303a35a890bSMike Capps 304a35a890bSMike Capps util::SDBusPlus::lookupAndCallMethod( 305a35a890bSMike Capps loggingPath, loggingCreateIface, "Create", 306a35a890bSMike Capps "xyz.openbmc_project.Fan.Presence.Error.GPIODeviceUnavailable", 307a35a890bSMike Capps severity, additionalData); 308a35a890bSMike Capps } 309a35a890bSMike Capps catch (const util::DBusError& e) 310a35a890bSMike Capps { 311a35a890bSMike Capps log<level::ERR>(fmt::format("Call to create an error log for " 312a35a890bSMike Capps "presence-sensor failure failed: {}", 313a35a890bSMike Capps e.what()) 314a35a890bSMike Capps .c_str()); 315a35a890bSMike Capps } 316a35a890bSMike Capps 317a35a890bSMike Capps return std::make_unique<PolicyAccess<NullGpio, JsonConfig>>(); 318a35a890bSMike Capps } 3191a568650SJolie Ku } 3201a568650SJolie Ku 3211a568650SJolie Ku } // namespace method 3221a568650SJolie Ku 3231a568650SJolie Ku /** 3241a568650SJolie Ku * Redundancy policies for fan presence detection function definitions 3251a568650SJolie Ku */ 3261a568650SJolie Ku namespace rpolicy 3271a568650SJolie Ku { 3281a568650SJolie Ku // Get an `Anyof` redundancy policy for the fan 3291a568650SJolie Ku std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan) 3301a568650SJolie Ku { 3311a568650SJolie Ku std::vector<std::reference_wrapper<PresenceSensor>> pSensors; 3321a568650SJolie Ku for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan)) 3331a568650SJolie Ku { 3341a568650SJolie Ku pSensors.emplace_back(*fanSensor); 3351a568650SJolie Ku } 3361a568650SJolie Ku 3371a568650SJolie Ku return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors); 3381a568650SJolie Ku } 3391a568650SJolie Ku 3401a568650SJolie Ku // Get a `Fallback` redundancy policy for the fan 3411a568650SJolie Ku std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan) 3421a568650SJolie Ku { 3431a568650SJolie Ku std::vector<std::reference_wrapper<PresenceSensor>> pSensors; 3441a568650SJolie Ku for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan)) 3451a568650SJolie Ku { 3461a568650SJolie Ku // Place in the order given to fallback correctly 3471a568650SJolie Ku pSensors.emplace_back(*fanSensor); 3481a568650SJolie Ku } 3491a568650SJolie Ku 3501a568650SJolie Ku return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors); 3511a568650SJolie Ku } 3521a568650SJolie Ku 3531a568650SJolie Ku } // namespace rpolicy 3541a568650SJolie Ku 3551a568650SJolie Ku } // namespace presence 3561a568650SJolie Ku } // namespace fan 3571a568650SJolie Ku } // namespace phosphor 358