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> 28*a35a890bSMike Capps #include <xyz/openbmc_project/Logging/Create/server.hpp> 29*a35a890bSMike 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 52*a35a890bSMike Capps const auto loggingPath = "/xyz/openbmc_project/logging"; 53*a35a890bSMike Capps const auto loggingCreateIface = "xyz.openbmc_project.Logging.Create"; 54*a35a890bSMike Capps 551a568650SJolie Ku JsonConfig::JsonConfig(sdbusplus::bus::bus& bus) : _bus(bus) 560daedd18SMatt Spinler {} 571a568650SJolie Ku 585b839919SMatthew Barth void JsonConfig::start() 590daedd18SMatt Spinler { 605b839919SMatthew Barth using config = fan::JsonConfig; 615b839919SMatthew Barth 625b839919SMatthew Barth process(config::load(config::getConfFile(_bus, confAppName, confFileName))); 630daedd18SMatt Spinler 640daedd18SMatt Spinler for (auto& p : _policies) 650daedd18SMatt Spinler { 660daedd18SMatt Spinler p->monitor(); 670daedd18SMatt Spinler } 681a568650SJolie Ku } 691a568650SJolie Ku 701a568650SJolie Ku const policies& JsonConfig::get() 711a568650SJolie Ku { 721a568650SJolie Ku return _policies; 731a568650SJolie Ku } 741a568650SJolie Ku 751a568650SJolie Ku void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc, 761a568650SJolie Ku const struct signalfd_siginfo* sigInfo) 771a568650SJolie Ku { 781a568650SJolie Ku try 791a568650SJolie Ku { 801a568650SJolie Ku using config = fan::JsonConfig; 811a568650SJolie Ku 829e9f599cSMatt Spinler _reporter.reset(); 839e9f599cSMatt Spinler 841a568650SJolie Ku // Load and process the json configuration 851a568650SJolie Ku process( 861a568650SJolie Ku config::load(config::getConfFile(_bus, confAppName, confFileName))); 871a568650SJolie Ku 881a568650SJolie Ku for (auto& p : _policies) 891a568650SJolie Ku { 901a568650SJolie Ku p->monitor(); 911a568650SJolie Ku } 921a568650SJolie Ku log<level::INFO>("Configuration loaded successfully"); 931a568650SJolie Ku } 941a568650SJolie Ku catch (std::runtime_error& re) 951a568650SJolie Ku { 961a568650SJolie Ku log<level::ERR>("Error loading config, no config changes made", 971a568650SJolie Ku entry("LOAD_ERROR=%s", re.what())); 981a568650SJolie Ku } 991a568650SJolie Ku } 1001a568650SJolie Ku 1011a568650SJolie Ku void JsonConfig::process(const json& jsonConf) 1021a568650SJolie Ku { 1031a568650SJolie Ku policies policies; 1041a568650SJolie Ku std::vector<fanPolicy> fans; 1051a568650SJolie Ku // Set the expected number of fan entries 1061a568650SJolie Ku // to be size of the list of fan json config entries 1071a568650SJolie Ku // (Must be done to eliminate vector reallocation of fan references) 1089e9f599cSMatt Spinler fans.reserve(jsonConf.size()); 1099e9f599cSMatt Spinler for (auto& member : jsonConf) 1101a568650SJolie Ku { 1111a568650SJolie Ku if (!member.contains("name") || !member.contains("path") || 1121a568650SJolie Ku !member.contains("methods") || !member.contains("rpolicy")) 1131a568650SJolie Ku { 1141a568650SJolie Ku log<level::ERR>("Missing required fan presence properties", 1151a568650SJolie Ku entry("REQUIRED_PROPERTIES=%s", 1161a568650SJolie Ku "{name, path, methods, rpolicy}")); 1171a568650SJolie Ku throw std::runtime_error( 1181a568650SJolie Ku "Missing required fan presence properties"); 1191a568650SJolie Ku } 1201a568650SJolie Ku 1211a568650SJolie Ku // Loop thru the configured methods of presence detection 1221a568650SJolie Ku std::vector<std::unique_ptr<PresenceSensor>> sensors; 1231a568650SJolie Ku for (auto& method : member["methods"].items()) 1241a568650SJolie Ku { 1251a568650SJolie Ku if (!method.value().contains("type")) 1261a568650SJolie Ku { 1271a568650SJolie Ku log<level::ERR>( 1281a568650SJolie Ku "Missing required fan presence method type", 1291a568650SJolie Ku entry("FAN_NAME=%s", 1301a568650SJolie Ku member["name"].get<std::string>().c_str())); 1311a568650SJolie Ku throw std::runtime_error( 1321a568650SJolie Ku "Missing required fan presence method type"); 1331a568650SJolie Ku } 1341a568650SJolie Ku // The method type of fan presence detection 1351a568650SJolie Ku // (Must have a supported function within the method namespace) 1361a568650SJolie Ku auto type = method.value()["type"].get<std::string>(); 1371a568650SJolie Ku std::transform(type.begin(), type.end(), type.begin(), tolower); 1381a568650SJolie Ku auto func = _methods.find(type); 1391a568650SJolie Ku if (func != _methods.end()) 1401a568650SJolie Ku { 1411a568650SJolie Ku // Call function for method type 1421a568650SJolie Ku auto sensor = func->second(fans.size(), method.value()); 1431a568650SJolie Ku if (sensor) 1441a568650SJolie Ku { 1451a568650SJolie Ku sensors.emplace_back(std::move(sensor)); 1461a568650SJolie Ku } 1471a568650SJolie Ku } 1481a568650SJolie Ku else 1491a568650SJolie Ku { 1501a568650SJolie Ku log<level::ERR>( 1511a568650SJolie Ku "Invalid fan presence method type", 1521a568650SJolie Ku entry("FAN_NAME=%s", 1531a568650SJolie Ku member["name"].get<std::string>().c_str()), 1541a568650SJolie Ku entry("METHOD_TYPE=%s", type.c_str())); 1551a568650SJolie Ku throw std::runtime_error("Invalid fan presence method type"); 1561a568650SJolie Ku } 1571a568650SJolie Ku } 1589e9f599cSMatt Spinler 1599e9f599cSMatt Spinler // Get the amount of time a fan must be not present before 1609e9f599cSMatt Spinler // creating an error. 1619e9f599cSMatt Spinler std::optional<size_t> timeUntilError; 1629e9f599cSMatt Spinler if (member.contains("fan_missing_error_time")) 1639e9f599cSMatt Spinler { 1649e9f599cSMatt Spinler timeUntilError = member["fan_missing_error_time"].get<size_t>(); 1659e9f599cSMatt Spinler } 1669e9f599cSMatt Spinler 1679e9f599cSMatt Spinler auto fan = 1689e9f599cSMatt Spinler std::make_tuple(member["name"], member["path"], timeUntilError); 1691a568650SJolie Ku // Create a fan object 1701a568650SJolie Ku fans.emplace_back(std::make_tuple(fan, std::move(sensors))); 1711a568650SJolie Ku 1721a568650SJolie Ku // Add fan presence policy 1731a568650SJolie Ku auto policy = getPolicy(member["rpolicy"], fans.back()); 1741a568650SJolie Ku if (policy) 1751a568650SJolie Ku { 1761a568650SJolie Ku policies.emplace_back(std::move(policy)); 1771a568650SJolie Ku } 1781a568650SJolie Ku } 1791a568650SJolie Ku 1801a568650SJolie Ku // Success, refresh fans and policies lists 1811a568650SJolie Ku _fans.clear(); 1821a568650SJolie Ku _fans.swap(fans); 1831a568650SJolie Ku 1841a568650SJolie Ku _policies.clear(); 1851a568650SJolie Ku _policies.swap(policies); 186e8122390SMatt Spinler 187e8122390SMatt Spinler // Create the error reporter class if necessary 1889e9f599cSMatt Spinler if (std::any_of(_fans.begin(), _fans.end(), [](const auto& fan) { 1899e9f599cSMatt Spinler return std::get<std::optional<size_t>>(std::get<Fan>(fan)) != 1909e9f599cSMatt Spinler std::nullopt; 1919e9f599cSMatt Spinler })) 192e8122390SMatt Spinler { 1939e9f599cSMatt Spinler _reporter = std::make_unique<ErrorReporter>(_bus, _fans); 194e8122390SMatt Spinler } 1951a568650SJolie Ku } 1961a568650SJolie Ku 1971a568650SJolie Ku std::unique_ptr<RedundancyPolicy> 1981a568650SJolie Ku JsonConfig::getPolicy(const json& rpolicy, const fanPolicy& fpolicy) 1991a568650SJolie Ku { 2001a568650SJolie Ku if (!rpolicy.contains("type")) 2011a568650SJolie Ku { 2021a568650SJolie Ku log<level::ERR>( 2031a568650SJolie Ku "Missing required fan presence policy type", 2041a568650SJolie Ku entry("FAN_NAME=%s", 2051a568650SJolie Ku std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()), 2061a568650SJolie Ku entry("REQUIRED_PROPERTIES=%s", "{type}")); 2071a568650SJolie Ku throw std::runtime_error("Missing required fan presence policy type"); 2081a568650SJolie Ku } 2091a568650SJolie Ku 2101a568650SJolie Ku // The redundancy policy type for fan presence detection 2111a568650SJolie Ku // (Must have a supported function within the rpolicy namespace) 2121a568650SJolie Ku auto type = rpolicy["type"].get<std::string>(); 2131a568650SJolie Ku std::transform(type.begin(), type.end(), type.begin(), tolower); 2141a568650SJolie Ku auto func = _rpolicies.find(type); 2151a568650SJolie Ku if (func != _rpolicies.end()) 2161a568650SJolie Ku { 2171a568650SJolie Ku // Call function for redundancy policy type and return the policy 2181a568650SJolie Ku return func->second(fpolicy); 2191a568650SJolie Ku } 2201a568650SJolie Ku else 2211a568650SJolie Ku { 2221a568650SJolie Ku log<level::ERR>( 2231a568650SJolie Ku "Invalid fan presence policy type", 2241a568650SJolie Ku entry("FAN_NAME=%s", 2251a568650SJolie Ku std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()), 2261a568650SJolie Ku entry("RPOLICY_TYPE=%s", type.c_str())); 2271a568650SJolie Ku throw std::runtime_error("Invalid fan presence methods policy type"); 2281a568650SJolie Ku } 2291a568650SJolie Ku } 2301a568650SJolie Ku 2311a568650SJolie Ku /** 2321a568650SJolie Ku * Methods of fan presence detection function definitions 2331a568650SJolie Ku */ 2341a568650SJolie Ku namespace method 2351a568650SJolie Ku { 2361a568650SJolie Ku // Get a constructed presence sensor for fan presence detection by tach 2371a568650SJolie Ku std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method) 2381a568650SJolie Ku { 2391a568650SJolie Ku if (!method.contains("sensors") || method["sensors"].size() == 0) 2401a568650SJolie Ku { 2411a568650SJolie Ku log<level::ERR>("Missing required tach method properties", 2421a568650SJolie Ku entry("FAN_ENTRY=%d", fanIndex), 2431a568650SJolie Ku entry("REQUIRED_PROPERTIES=%s", "{sensors}")); 2441a568650SJolie Ku throw std::runtime_error("Missing required tach method properties"); 2451a568650SJolie Ku } 2461a568650SJolie Ku 2471a568650SJolie Ku std::vector<std::string> sensors; 2481a568650SJolie Ku for (auto& sensor : method["sensors"]) 2491a568650SJolie Ku { 2501a568650SJolie Ku sensors.emplace_back(sensor.get<std::string>()); 2511a568650SJolie Ku } 2521a568650SJolie Ku 2531a568650SJolie Ku return std::make_unique<PolicyAccess<Tach, JsonConfig>>(fanIndex, 2541a568650SJolie Ku std::move(sensors)); 2551a568650SJolie Ku } 2561a568650SJolie Ku 2571a568650SJolie Ku // Get a constructed presence sensor for fan presence detection by gpio 2581a568650SJolie Ku std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method) 2591a568650SJolie Ku { 2601a568650SJolie Ku if (!method.contains("physpath") || !method.contains("devpath") || 2611a568650SJolie Ku !method.contains("key")) 2621a568650SJolie Ku { 2631a568650SJolie Ku log<level::ERR>( 2641a568650SJolie Ku "Missing required gpio method properties", 2651a568650SJolie Ku entry("FAN_ENTRY=%d", fanIndex), 2661a568650SJolie Ku entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}")); 2671a568650SJolie Ku throw std::runtime_error("Missing required gpio method properties"); 2681a568650SJolie Ku } 2691a568650SJolie Ku 2701a568650SJolie Ku auto physpath = method["physpath"].get<std::string>(); 2711a568650SJolie Ku auto devpath = method["devpath"].get<std::string>(); 2721a568650SJolie Ku auto key = method["key"].get<unsigned int>(); 2731a568650SJolie Ku 274*a35a890bSMike Capps try 275*a35a890bSMike Capps { 276*a35a890bSMike Capps return std::make_unique<PolicyAccess<Gpio, JsonConfig>>( 277*a35a890bSMike Capps fanIndex, physpath, devpath, key); 278*a35a890bSMike Capps } 279*a35a890bSMike Capps catch (const sdbusplus::exception_t& e) 280*a35a890bSMike Capps { 281*a35a890bSMike Capps namespace sdlogging = sdbusplus::xyz::openbmc_project::Logging::server; 282*a35a890bSMike Capps 283*a35a890bSMike Capps log<level::ERR>( 284*a35a890bSMike Capps fmt::format( 285*a35a890bSMike Capps "Error creating Gpio device bridge, hardware not detected: {}", 286*a35a890bSMike Capps e.what()) 287*a35a890bSMike Capps .c_str()); 288*a35a890bSMike Capps 289*a35a890bSMike Capps auto severity = 290*a35a890bSMike Capps sdlogging::convertForMessage(sdlogging::Entry::Level::Error); 291*a35a890bSMike Capps 292*a35a890bSMike Capps std::map<std::string, std::string> additionalData{ 293*a35a890bSMike Capps {"PHYSPATH", physpath}, 294*a35a890bSMike Capps {"DEVPATH", devpath}, 295*a35a890bSMike Capps {"FANINDEX", std::to_string(fanIndex)}}; 296*a35a890bSMike Capps 297*a35a890bSMike Capps try 298*a35a890bSMike Capps { 299*a35a890bSMike Capps 300*a35a890bSMike Capps util::SDBusPlus::lookupAndCallMethod( 301*a35a890bSMike Capps loggingPath, loggingCreateIface, "Create", 302*a35a890bSMike Capps "xyz.openbmc_project.Fan.Presence.Error.GPIODeviceUnavailable", 303*a35a890bSMike Capps severity, additionalData); 304*a35a890bSMike Capps } 305*a35a890bSMike Capps catch (const util::DBusError& e) 306*a35a890bSMike Capps { 307*a35a890bSMike Capps log<level::ERR>(fmt::format("Call to create an error log for " 308*a35a890bSMike Capps "presence-sensor failure failed: {}", 309*a35a890bSMike Capps e.what()) 310*a35a890bSMike Capps .c_str()); 311*a35a890bSMike Capps } 312*a35a890bSMike Capps 313*a35a890bSMike Capps return std::make_unique<PolicyAccess<NullGpio, JsonConfig>>(); 314*a35a890bSMike Capps } 3151a568650SJolie Ku } 3161a568650SJolie Ku 3171a568650SJolie Ku } // namespace method 3181a568650SJolie Ku 3191a568650SJolie Ku /** 3201a568650SJolie Ku * Redundancy policies for fan presence detection function definitions 3211a568650SJolie Ku */ 3221a568650SJolie Ku namespace rpolicy 3231a568650SJolie Ku { 3241a568650SJolie Ku // Get an `Anyof` redundancy policy for the fan 3251a568650SJolie Ku std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan) 3261a568650SJolie Ku { 3271a568650SJolie Ku std::vector<std::reference_wrapper<PresenceSensor>> pSensors; 3281a568650SJolie Ku for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan)) 3291a568650SJolie Ku { 3301a568650SJolie Ku pSensors.emplace_back(*fanSensor); 3311a568650SJolie Ku } 3321a568650SJolie Ku 3331a568650SJolie Ku return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors); 3341a568650SJolie Ku } 3351a568650SJolie Ku 3361a568650SJolie Ku // Get a `Fallback` redundancy policy for the fan 3371a568650SJolie Ku std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan) 3381a568650SJolie Ku { 3391a568650SJolie Ku std::vector<std::reference_wrapper<PresenceSensor>> pSensors; 3401a568650SJolie Ku for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan)) 3411a568650SJolie Ku { 3421a568650SJolie Ku // Place in the order given to fallback correctly 3431a568650SJolie Ku pSensors.emplace_back(*fanSensor); 3441a568650SJolie Ku } 3451a568650SJolie Ku 3461a568650SJolie Ku return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors); 3471a568650SJolie Ku } 3481a568650SJolie Ku 3491a568650SJolie Ku } // namespace rpolicy 3501a568650SJolie Ku 3511a568650SJolie Ku } // namespace presence 3521a568650SJolie Ku } // namespace fan 3531a568650SJolie Ku } // namespace phosphor 354