diff options
author | Jonathan Doman <jonathan.doman@intel.com> | 2020-12-03 02:34:13 +0300 |
---|---|---|
committer | Ed Tanous <ed@tanous.net> | 2020-12-04 03:07:40 +0300 |
commit | dba0c291b5bbd8d256387ba43bb48bda82a191dc (patch) | |
tree | 019aed1f6933cc66c162e6ddb2f2381d5c8ae20a /redfish-core/lib/processor.hpp | |
parent | 2bab9831767c180bedd005dfcbeb65af8798f088 (diff) | |
download | bmcweb-dba0c291b5bbd8d256387ba43bb48bda82a191dc.tar.xz |
Add support for Redfish OperatingConfig resources
- Update Processor GET handler to look for a matching
CurrentOperatingConfig interface on D-Bus.
- Add OperatingConfig node and implement GET handler to look for
matching OperatingConfig interface on D-Bus.
- Add OperatingConfigCollection node and implement GET handler to look
for all OperatingConfig interfaces on D-Bus under the given cpu.
Tested:
- Ran Redfish Service Validator and verified no errors or warnings on
Processor, OperatingConfig, and OperatingConfigCollection.
- Browsed OperatingConfig links and resources in browser, confirmed
nonexistent configs returned 404 and didn't crash bmcweb.
- Killed D-Bus provider service and verified bmcweb didn't crash and
still served Processor as before, and passed service validator.
Change-Id: Iab94b7fd49a9462cb0eca6f8ea0754f5fb241053
Signed-off-by: Jonathan Doman <jonathan.doman@intel.com>
Diffstat (limited to 'redfish-core/lib/processor.hpp')
-rw-r--r-- | redfish-core/lib/processor.hpp | 450 |
1 files changed, 448 insertions, 2 deletions
diff --git a/redfish-core/lib/processor.hpp b/redfish-core/lib/processor.hpp index 82c01f949e..e85b616810 100644 --- a/redfish-core/lib/processor.hpp +++ b/redfish-core/lib/processor.hpp @@ -19,6 +19,8 @@ #include <boost/container/flat_map.hpp> #include <node.hpp> +#include <sdbusplus/message/native_types.hpp> +#include <sdbusplus/utility/dedup_variant.hpp> #include <utils/collection.hpp> #include <utils/json_utils.hpp> @@ -380,6 +382,168 @@ inline void getAcceleratorDataByService(std::shared_ptr<AsyncResp> aResp, service, objPath, "org.freedesktop.DBus.Properties", "GetAll", ""); } +// OperatingConfig D-Bus Types +using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>; +using BaseSpeedPrioritySettingsProperty = + std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>; +// uint32_t and size_t may or may not be the same type, requiring a dedup'd +// variant +using OperatingConfigProperties = std::vector<std::pair< + std::string, + sdbusplus::utility::dedup_variant<uint32_t, size_t, TurboProfileProperty, + BaseSpeedPrioritySettingsProperty>>>; + +/** + * Fill out the HighSpeedCoreIDs in a Processor resource from the given + * OperatingConfig D-Bus property. + * + * @param[in,out] aResp Async HTTP response. + * @param[in] baseSpeedSettings Full list of base speed priority groups, + * to use to determine the list of high + * speed cores. + */ +inline void highSpeedCoreIdsHandler( + const std::shared_ptr<AsyncResp>& aResp, + const BaseSpeedPrioritySettingsProperty& baseSpeedSettings) +{ + // The D-Bus property does not indicate which bucket is the "high + // priority" group, so let's discern that by looking for the one with + // highest base frequency. + auto highPriorityGroup = baseSpeedSettings.cend(); + uint32_t highestBaseSpeed = 0; + for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend(); + ++it) + { + const uint32_t baseFreq = std::get<uint32_t>(*it); + if (baseFreq > highestBaseSpeed) + { + highestBaseSpeed = baseFreq; + highPriorityGroup = it; + } + } + + nlohmann::json& jsonCoreIds = aResp->res.jsonValue["HighSpeedCoreIDs"]; + jsonCoreIds = nlohmann::json::array(); + + // There may not be any entries in the D-Bus property, so only populate + // if there was actually something there. + if (highPriorityGroup != baseSpeedSettings.cend()) + { + jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup); + } +} + +/** + * Fill out OperatingConfig related items in a Processor resource by requesting + * data from the given D-Bus object. + * + * @param[in,out] aResp Async HTTP response. + * @param[in] cpuId CPU D-Bus name. + * @param[in] service D-Bus service to query. + * @param[in] objPath D-Bus object to query. + */ +inline void getCpuConfigData(const std::shared_ptr<AsyncResp>& aResp, + const std::string& cpuId, + const std::string& service, + const std::string& objPath) +{ + BMCWEB_LOG_INFO << "Getting CPU operating configs for " << cpuId; + + // First, GetAll CurrentOperatingConfig properties on the object + crow::connections::systemBus->async_method_call( + [aResp, cpuId, service]( + const boost::system::error_code ec, + const std::vector< + std::pair<std::string, + std::variant<sdbusplus::message::object_path, bool>>>& + properties) { + if (ec) + { + BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " + << ec.message(); + messages::internalError(aResp->res); + return; + } + + nlohmann::json& json = aResp->res.jsonValue; + + for (const auto& [dbusPropName, variantVal] : properties) + { + if (dbusPropName == "AppliedConfig") + { + const sdbusplus::message::object_path* dbusPathWrapper = + std::get_if<sdbusplus::message::object_path>( + &variantVal); + if (dbusPathWrapper == nullptr) + { + continue; + } + + const std::string& dbusPath = dbusPathWrapper->str; + std::string uri = "/redfish/v1/Systems/system/Processors/" + + cpuId + "/OperatingConfigs"; + json["OperatingConfigs"] = {{"@odata.id", uri}}; + + // Reuse the D-Bus config object name for the Redfish + // URI + size_t baseNamePos = dbusPath.rfind('/'); + if (baseNamePos == std::string::npos || + baseNamePos == (dbusPath.size() - 1)) + { + // If the AppliedConfig was somehow not a valid path, + // skip adding any more properties, since everything + // else is tied to this applied config. + messages::internalError(aResp->res); + break; + } + uri += '/'; + uri += dbusPath.substr(baseNamePos + 1); + json["AppliedOperatingConfig"] = {{"@odata.id", uri}}; + + // Once we found the current applied config, queue another + // request to read the base freq core ids out of that + // config. + crow::connections::systemBus->async_method_call( + [aResp]( + const boost::system::error_code ec, + const std::variant< + BaseSpeedPrioritySettingsProperty>& property) { + if (ec) + { + BMCWEB_LOG_WARNING + << "D-Bus Property Get error: " << ec; + messages::internalError(aResp->res); + return; + } + auto baseSpeedList = + std::get_if<BaseSpeedPrioritySettingsProperty>( + &property); + if (baseSpeedList != nullptr) + { + highSpeedCoreIdsHandler(aResp, *baseSpeedList); + } + }, + service, dbusPath, "org.freedesktop.DBus.Properties", + "Get", + "xyz.openbmc_project.Inventory.Item.Cpu." + "OperatingConfig", + "BaseSpeedPrioritySettings"); + } + else if (dbusPropName == "BaseSpeedPriorityEnabled") + { + const bool* state = std::get_if<bool>(&variantVal); + if (state != nullptr) + { + json["BaseSpeedPriorityState"] = + *state ? "Enabled" : "Disabled"; + } + } + } + }, + service, objPath, "org.freedesktop.DBus.Properties", "GetAll", + "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"); +} + inline void getProcessorData(std::shared_ptr<AsyncResp> aResp, const std::string& processorId) { @@ -434,6 +598,13 @@ inline void getProcessorData(std::shared_ptr<AsyncResp> aResp, getAcceleratorDataByService( aResp, processorId, serviceName, objectPath); } + else if (interface == + "xyz.openbmc_project.Control.Processor." + "CurrentOperatingConfig") + { + getCpuConfigData(aResp, processorId, serviceName, + objectPath); + } } } return; @@ -446,13 +617,288 @@ inline void getProcessorData(std::shared_ptr<AsyncResp> aResp, "/xyz/openbmc_project/object_mapper", "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/xyz/openbmc_project/inventory", 0, - std::array<const char*, 4>{ + std::array<const char*, 5>{ "xyz.openbmc_project.Inventory.Decorator.Asset", "xyz.openbmc_project.Inventory.Decorator.Revision", "xyz.openbmc_project.Inventory.Item.Cpu", - "xyz.openbmc_project.Inventory.Item.Accelerator"}); + "xyz.openbmc_project.Inventory.Item.Accelerator", + "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}); } +/** + * Request all the properties for the given D-Bus object and fill out the + * related entries in the Redfish OperatingConfig response. + * + * @param[in,out] aResp Async HTTP response. + * @param[in] service D-Bus service name to query. + * @param[in] objPath D-Bus object to query. + */ +inline void getOperatingConfigData(const std::shared_ptr<AsyncResp>& aResp, + const std::string& service, + const std::string& objPath) +{ + crow::connections::systemBus->async_method_call( + [aResp](boost::system::error_code ec, + const OperatingConfigProperties& properties) { + if (ec) + { + BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " + << ec.message(); + messages::internalError(aResp->res); + return; + } + + nlohmann::json& json = aResp->res.jsonValue; + for (const auto& [key, variant] : properties) + { + if (key == "AvailableCoreCount") + { + const size_t* cores = std::get_if<size_t>(&variant); + if (cores != nullptr) + { + json["TotalAvailableCoreCount"] = *cores; + } + } + else if (key == "BaseSpeed") + { + const uint32_t* speed = std::get_if<uint32_t>(&variant); + if (speed != nullptr) + { + json["BaseSpeedMHz"] = *speed; + } + } + else if (key == "MaxJunctionTemperature") + { + const uint32_t* temp = std::get_if<uint32_t>(&variant); + if (temp != nullptr) + { + json["MaxJunctionTemperatureCelsius"] = *temp; + } + } + else if (key == "MaxSpeed") + { + const uint32_t* speed = std::get_if<uint32_t>(&variant); + if (speed != nullptr) + { + json["MaxSpeedMHz"] = *speed; + } + } + else if (key == "PowerLimit") + { + const uint32_t* tdp = std::get_if<uint32_t>(&variant); + if (tdp != nullptr) + { + json["TDPWatts"] = *tdp; + } + } + else if (key == "TurboProfile") + { + const auto* turboList = + std::get_if<TurboProfileProperty>(&variant); + if (turboList == nullptr) + { + continue; + } + + nlohmann::json& turboArray = json["TurboProfile"]; + turboArray = nlohmann::json::array(); + for (const auto& [turboSpeed, coreCount] : *turboList) + { + turboArray.push_back({{"ActiveCoreCount", coreCount}, + {"MaxSpeedMHz", turboSpeed}}); + } + } + else if (key == "BaseSpeedPrioritySettings") + { + const auto* baseSpeedList = + std::get_if<BaseSpeedPrioritySettingsProperty>( + &variant); + if (baseSpeedList == nullptr) + { + continue; + } + + nlohmann::json& baseSpeedArray = + json["BaseSpeedPrioritySettings"]; + baseSpeedArray = nlohmann::json::array(); + for (const auto& [baseSpeed, coreList] : *baseSpeedList) + { + baseSpeedArray.push_back( + {{"CoreCount", coreList.size()}, + {"CoreIDs", coreList}, + {"BaseSpeedMHz", baseSpeed}}); + } + } + } + }, + service, objPath, "org.freedesktop.DBus.Properties", "GetAll", + "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"); +} + +class OperatingConfigCollection : public Node +{ + public: + OperatingConfigCollection(App& app) : + Node(app, + "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/", + std::string()) + { + // Defined by Redfish spec privilege registry + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; + } + + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + if (params.size() != 1) + { + messages::internalError(res); + res.end(); + return; + } + + const std::string& cpuName = params[0]; + res.jsonValue["@odata.type"] = + "#OperatingConfigCollection.OperatingConfigCollection"; + res.jsonValue["@odata.id"] = req.url; + res.jsonValue["Name"] = "Operating Config Collection"; + + auto asyncResp = std::make_shared<AsyncResp>(res); + + // First find the matching CPU object so we know how to constrain our + // search for related Config objects. + crow::connections::systemBus->async_method_call( + [asyncResp, cpuName](const boost::system::error_code ec, + const std::vector<std::string>& objects) { + if (ec) + { + BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " + << ec.message(); + messages::internalError(asyncResp->res); + return; + } + + for (const std::string& object : objects) + { + if (!boost::ends_with(object, cpuName)) + { + continue; + } + + // Not expected that there will be multiple matching CPU + // objects, but if there are just use the first one. + + // Use the common search routine to construct the Collection + // of all Config objects under this CPU. + collection_util::getCollectionMembers( + asyncResp, + "/redfish/v1/Systems/system/Processors/" + cpuName + + "/OperatingConfigs", + {"xyz.openbmc_project.Inventory.Item.Cpu." + "OperatingConfig"}, + object.c_str()); + return; + } + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", + "/xyz/openbmc_project/inventory", 0, + std::array<const char*, 1>{"xyz.openbmc_project.Control.Processor." + "CurrentOperatingConfig"}); + } +}; + +class OperatingConfig : public Node +{ + public: + OperatingConfig(App& app) : + Node(app, + "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/" + "<str>/", + std::string(), std::string()) + { + // Defined by Redfish spec privilege registry + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, + {boost::beast::http::verb::head, {{"Login"}}}, + {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, + {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; + } + + private: + void doGet(crow::Response& res, const crow::Request& req, + const std::vector<std::string>& params) override + { + if (params.size() != 2) + { + messages::internalError(res); + res.end(); + return; + } + + const std::string& cpuName = params[0]; + const std::string& configName = params[1]; + + auto asyncResp = std::make_shared<AsyncResp>(res); + + // Ask for all objects implementing OperatingConfig so we can search for + // one with a matching name + crow::connections::systemBus->async_method_call( + [asyncResp, cpuName, configName, + reqUrl{req.url}](boost::system::error_code ec, + const MapperGetSubTreeResponse& subtree) { + if (ec) + { + BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " + << ec.message(); + messages::internalError(asyncResp->res); + return; + } + const std::string expectedEnding = cpuName + '/' + configName; + for (const auto& [objectPath, serviceMap] : subtree) + { + // Ignore any configs without matching cpuX/configY + if (!boost::ends_with(objectPath, expectedEnding) || + serviceMap.empty()) + { + continue; + } + + nlohmann::json& json = asyncResp->res.jsonValue; + json["@odata.type"] = + "#OperatingConfig.v1_0_0.OperatingConfig"; + json["@odata.id"] = reqUrl; + json["Name"] = "Processor Profile"; + json["Id"] = configName; + + // Just use the first implementation of the object - not + // expected that there would be multiple matching services + getOperatingConfigData(asyncResp, serviceMap.begin()->first, + objectPath); + return; + } + messages::resourceNotFound(asyncResp->res, "OperatingConfig", + configName); + }, + "xyz.openbmc_project.ObjectMapper", + "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTree", + "/xyz/openbmc_project/inventory", 0, + std::array<const char*, 1>{ + "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}); + } +}; + class ProcessorCollection : public Node { public: |