| // SPDX-License-Identifier: Apache-2.0 |
| // SPDX-FileCopyrightText: Copyright OpenBMC Authors |
| // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation |
| #pragma once |
| |
| #include "bmcweb_config.h" |
| |
| #include "app.hpp" |
| #include "async_resp.hpp" |
| #include "dbus_singleton.hpp" |
| #include "dbus_utility.hpp" |
| #include "error_messages.hpp" |
| #include "generated/enums/action_info.hpp" |
| #include "generated/enums/manager.hpp" |
| #include "generated/enums/resource.hpp" |
| #include "http_request.hpp" |
| #include "logging.hpp" |
| #include "openbmc/openbmc_managers.hpp" |
| #include "persistent_data.hpp" |
| #include "query.hpp" |
| #include "redfish_util.hpp" |
| #include "registries/privilege_registry.hpp" |
| #include "utils/dbus_utils.hpp" |
| #include "utils/json_utils.hpp" |
| #include "utils/sw_utils.hpp" |
| #include "utils/systemd_utils.hpp" |
| #include "utils/time_utils.hpp" |
| |
| #include <systemd/sd-bus.h> |
| |
| #include <boost/beast/http/status.hpp> |
| #include <boost/beast/http/verb.hpp> |
| #include <boost/system/error_code.hpp> |
| #include <boost/url/format.hpp> |
| #include <boost/url/url.hpp> |
| #include <nlohmann/json.hpp> |
| #include <sdbusplus/asio/property.hpp> |
| #include <sdbusplus/message.hpp> |
| #include <sdbusplus/message/native_types.hpp> |
| #include <sdbusplus/unpack_properties.hpp> |
| |
| #include <array> |
| #include <cstddef> |
| #include <cstdint> |
| #include <format> |
| #include <functional> |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <ranges> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| #include <vector> |
| |
| namespace redfish |
| { |
| |
| inline std::string getBMCUpdateServiceName() |
| { |
| if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) |
| { |
| return "xyz.openbmc_project.Software.Manager"; |
| } |
| return "xyz.openbmc_project.Software.BMC.Updater"; |
| } |
| |
| inline std::string getBMCUpdateServicePath() |
| { |
| if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS) |
| { |
| return "/xyz/openbmc_project/software/bmc"; |
| } |
| return "/xyz/openbmc_project/software"; |
| } |
| |
| /** |
| * Function reboots the BMC. |
| * |
| * @param[in] asyncResp - Shared pointer for completing asynchronous calls |
| */ |
| inline void doBMCGracefulRestart( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| const char* processName = "xyz.openbmc_project.State.BMC"; |
| const char* objectPath = "/xyz/openbmc_project/state/bmc0"; |
| const char* interfaceName = "xyz.openbmc_project.State.BMC"; |
| const std::string& propertyValue = |
| "xyz.openbmc_project.State.BMC.Transition.Reboot"; |
| const char* destProperty = "RequestedBMCTransition"; |
| |
| // Create the D-Bus variant for D-Bus call. |
| sdbusplus::asio::setProperty( |
| *crow::connections::systemBus, processName, objectPath, interfaceName, |
| destProperty, propertyValue, |
| [asyncResp](const boost::system::error_code& ec) { |
| // Use "Set" method to set the property value. |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| messages::success(asyncResp->res); |
| }); |
| } |
| |
| inline void doBMCForceRestart( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| const char* processName = "xyz.openbmc_project.State.BMC"; |
| const char* objectPath = "/xyz/openbmc_project/state/bmc0"; |
| const char* interfaceName = "xyz.openbmc_project.State.BMC"; |
| const std::string& propertyValue = |
| "xyz.openbmc_project.State.BMC.Transition.HardReboot"; |
| const char* destProperty = "RequestedBMCTransition"; |
| |
| // Create the D-Bus variant for D-Bus call. |
| sdbusplus::asio::setProperty( |
| *crow::connections::systemBus, processName, objectPath, interfaceName, |
| destProperty, propertyValue, |
| [asyncResp](const boost::system::error_code& ec) { |
| // Use "Set" method to set the property value. |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| messages::success(asyncResp->res); |
| }); |
| } |
| |
| /** |
| * ManagerResetAction class supports the POST method for the Reset (reboot) |
| * action. |
| */ |
| inline void requestRoutesManagerResetAction(App& app) |
| { |
| /** |
| * Function handles POST method request. |
| * Analyzes POST body before sending Reset (Reboot) request data to D-Bus. |
| * OpenBMC supports ResetType "GracefulRestart" and "ForceRestart". |
| */ |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/Actions/Manager.Reset/") |
| .privileges(redfish::privileges::postManager) |
| .methods(boost::beast::http::verb::post)( |
| [&app](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& managerId) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) |
| { |
| messages::resourceNotFound(asyncResp->res, "Manager", |
| managerId); |
| return; |
| } |
| |
| BMCWEB_LOG_DEBUG("Post Manager Reset."); |
| |
| std::string resetType; |
| |
| if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", |
| resetType)) |
| { |
| return; |
| } |
| |
| if (resetType == "GracefulRestart") |
| { |
| BMCWEB_LOG_DEBUG("Proceeding with {}", resetType); |
| doBMCGracefulRestart(asyncResp); |
| return; |
| } |
| if (resetType == "ForceRestart") |
| { |
| BMCWEB_LOG_DEBUG("Proceeding with {}", resetType); |
| doBMCForceRestart(asyncResp); |
| return; |
| } |
| BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", |
| resetType); |
| messages::actionParameterNotSupported(asyncResp->res, resetType, |
| "ResetType"); |
| |
| return; |
| }); |
| } |
| |
| /** |
| * ManagerResetToDefaultsAction class supports POST method for factory reset |
| * action. |
| */ |
| inline void requestRoutesManagerResetToDefaultsAction(App& app) |
| { |
| /** |
| * Function handles ResetToDefaults POST method request. |
| * |
| * Analyzes POST body message and factory resets BMC by calling |
| * BMC code updater factory reset followed by a BMC reboot. |
| * |
| * BMC code updater factory reset wipes the whole BMC read-write |
| * filesystem which includes things like the network settings. |
| * |
| * OpenBMC only supports ResetToDefaultsType "ResetAll". |
| */ |
| |
| BMCWEB_ROUTE(app, |
| "/redfish/v1/Managers/<str>/Actions/Manager.ResetToDefaults/") |
| .privileges(redfish::privileges::postManager) |
| .methods( |
| boost::beast::http::verb:: |
| post)([&app]( |
| const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& managerId) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) |
| { |
| messages::resourceNotFound(asyncResp->res, "Manager", |
| managerId); |
| return; |
| } |
| |
| BMCWEB_LOG_DEBUG("Post ResetToDefaults."); |
| |
| std::optional<std::string> resetType; |
| std::optional<std::string> resetToDefaultsType; |
| |
| if (!json_util::readJsonAction( // |
| req, asyncResp->res, // |
| "ResetToDefaultsType", resetToDefaultsType, // |
| "ResetType", resetType // |
| )) |
| { |
| BMCWEB_LOG_DEBUG("Missing property ResetType."); |
| |
| messages::actionParameterMissing( |
| asyncResp->res, "ResetToDefaults", "ResetType"); |
| return; |
| } |
| |
| if (resetToDefaultsType && !resetType) |
| { |
| BMCWEB_LOG_WARNING( |
| "Using deprecated ResetToDefaultsType, should be ResetType." |
| "Support for the ResetToDefaultsType will be dropped in 2Q24"); |
| resetType = resetToDefaultsType; |
| } |
| |
| if (resetType != "ResetAll") |
| { |
| BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", |
| *resetType); |
| messages::actionParameterNotSupported(asyncResp->res, |
| *resetType, "ResetType"); |
| return; |
| } |
| |
| crow::connections::systemBus->async_method_call( |
| [asyncResp](const boost::system::error_code& ec) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG("Failed to ResetToDefaults: {}", ec); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| // Factory Reset doesn't actually happen until a reboot |
| // Can't erase what the BMC is running on |
| doBMCGracefulRestart(asyncResp); |
| }, |
| getBMCUpdateServiceName(), getBMCUpdateServicePath(), |
| "xyz.openbmc_project.Common.FactoryReset", "Reset"); |
| }); |
| } |
| |
| /** |
| * ManagerResetActionInfo derived class for delivering Manager |
| * ResetType AllowableValues using ResetInfo schema. |
| */ |
| inline void requestRoutesManagerResetActionInfo(App& app) |
| { |
| /** |
| * Functions triggers appropriate requests on DBus |
| */ |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/ResetActionInfo/") |
| .privileges(redfish::privileges::getActionInfo) |
| .methods(boost::beast::http::verb::get)( |
| [&app](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& managerId) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) |
| { |
| messages::resourceNotFound(asyncResp->res, "Manager", |
| managerId); |
| return; |
| } |
| |
| asyncResp->res.jsonValue["@odata.type"] = |
| "#ActionInfo.v1_1_2.ActionInfo"; |
| asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( |
| "/redfish/v1/Managers/{}/ResetActionInfo", |
| BMCWEB_REDFISH_MANAGER_URI_NAME); |
| asyncResp->res.jsonValue["Name"] = "Reset Action Info"; |
| asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; |
| nlohmann::json::object_t parameter; |
| parameter["Name"] = "ResetType"; |
| parameter["Required"] = true; |
| parameter["DataType"] = action_info::ParameterTypes::String; |
| |
| nlohmann::json::array_t allowableValues; |
| allowableValues.emplace_back("GracefulRestart"); |
| allowableValues.emplace_back("ForceRestart"); |
| parameter["AllowableValues"] = std::move(allowableValues); |
| |
| nlohmann::json::array_t parameters; |
| parameters.emplace_back(std::move(parameter)); |
| |
| asyncResp->res.jsonValue["Parameters"] = std::move(parameters); |
| }); |
| } |
| |
| /** |
| * @brief Retrieves BMC manager location data over DBus |
| * |
| * @param[in] asyncResp Shared pointer for completing asynchronous calls |
| * @param[in] connectionName - service name |
| * @param[in] path - object path |
| * @return none |
| */ |
| inline void getLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& connectionName, |
| const std::string& path) |
| { |
| BMCWEB_LOG_DEBUG("Get BMC manager Location data."); |
| |
| dbus::utility::getProperty<std::string>( |
| connectionName, path, |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", |
| [asyncResp](const boost::system::error_code& ec, |
| const std::string& property) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG("DBUS response error for " |
| "Location"); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| asyncResp->res |
| .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = |
| property; |
| }); |
| } |
| // avoid name collision systems.hpp |
| inline void managerGetLastResetTime( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| BMCWEB_LOG_DEBUG("Getting Manager Last Reset Time"); |
| |
| dbus::utility::getProperty<uint64_t>( |
| "xyz.openbmc_project.State.BMC", "/xyz/openbmc_project/state/bmc0", |
| "xyz.openbmc_project.State.BMC", "LastRebootTime", |
| [asyncResp](const boost::system::error_code& ec, |
| const uint64_t lastResetTime) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG("D-BUS response error {}", ec); |
| return; |
| } |
| |
| // LastRebootTime is epoch time, in milliseconds |
| // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19 |
| uint64_t lastResetTimeStamp = lastResetTime / 1000; |
| |
| // Convert to ISO 8601 standard |
| asyncResp->res.jsonValue["LastResetTime"] = |
| redfish::time_utils::getDateTimeUint(lastResetTimeStamp); |
| }); |
| } |
| |
| /** |
| * @brief Set the running firmware image |
| * |
| * @param[i,o] asyncResp - Async response object |
| * @param[i] runningFirmwareTarget - Image to make the running image |
| * |
| * @return void |
| */ |
| inline void setActiveFirmwareImage( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& runningFirmwareTarget) |
| { |
| // Get the Id from /redfish/v1/UpdateService/FirmwareInventory/<Id> |
| std::string::size_type idPos = runningFirmwareTarget.rfind('/'); |
| if (idPos == std::string::npos) |
| { |
| messages::propertyValueNotInList(asyncResp->res, runningFirmwareTarget, |
| "@odata.id"); |
| BMCWEB_LOG_DEBUG("Can't parse firmware ID!"); |
| return; |
| } |
| idPos++; |
| if (idPos >= runningFirmwareTarget.size()) |
| { |
| messages::propertyValueNotInList(asyncResp->res, runningFirmwareTarget, |
| "@odata.id"); |
| BMCWEB_LOG_DEBUG("Invalid firmware ID."); |
| return; |
| } |
| std::string firmwareId = runningFirmwareTarget.substr(idPos); |
| |
| // Make sure the image is valid before setting priority |
| sdbusplus::message::object_path objPath("/xyz/openbmc_project/software"); |
| dbus::utility::getManagedObjects( |
| getBMCUpdateServiceName(), objPath, |
| [asyncResp, firmwareId, runningFirmwareTarget]( |
| const boost::system::error_code& ec, |
| const dbus::utility::ManagedObjectType& subtree) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG("D-Bus response error getting objects."); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| if (subtree.empty()) |
| { |
| BMCWEB_LOG_DEBUG("Can't find image!"); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| bool foundImage = false; |
| for (const auto& object : subtree) |
| { |
| const std::string& path = |
| static_cast<const std::string&>(object.first); |
| std::size_t idPos2 = path.rfind('/'); |
| |
| if (idPos2 == std::string::npos) |
| { |
| continue; |
| } |
| |
| idPos2++; |
| if (idPos2 >= path.size()) |
| { |
| continue; |
| } |
| |
| if (path.substr(idPos2) == firmwareId) |
| { |
| foundImage = true; |
| break; |
| } |
| } |
| |
| if (!foundImage) |
| { |
| messages::propertyValueNotInList( |
| asyncResp->res, runningFirmwareTarget, "@odata.id"); |
| BMCWEB_LOG_DEBUG("Invalid firmware ID."); |
| return; |
| } |
| |
| BMCWEB_LOG_DEBUG("Setting firmware version {} to priority 0.", |
| firmwareId); |
| |
| // Only support Immediate |
| // An addition could be a Redfish Setting like |
| // ActiveSoftwareImageApplyTime and support OnReset |
| sdbusplus::asio::setProperty( |
| *crow::connections::systemBus, getBMCUpdateServiceName(), |
| "/xyz/openbmc_project/software/" + firmwareId, |
| "xyz.openbmc_project.Software.RedundancyPriority", "Priority", |
| static_cast<uint8_t>(0), |
| [asyncResp](const boost::system::error_code& ec2) { |
| if (ec2) |
| { |
| BMCWEB_LOG_DEBUG("D-Bus response error setting."); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| doBMCGracefulRestart(asyncResp); |
| }); |
| }); |
| } |
| |
| inline void afterSetDateTime( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const boost::system::error_code& ec, const sdbusplus::message_t& msg) |
| { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG("Failed to set elapsed time. DBUS response error {}", |
| ec); |
| const sd_bus_error* dbusError = msg.get_error(); |
| if (dbusError != nullptr) |
| { |
| std::string_view errorName(dbusError->name); |
| if (errorName == |
| "org.freedesktop.timedate1.AutomaticTimeSyncEnabled") |
| { |
| BMCWEB_LOG_DEBUG("Setting conflict"); |
| messages::propertyValueConflict( |
| asyncResp->res, "DateTime", |
| "Managers/NetworkProtocol/NTPProcotolEnabled"); |
| return; |
| } |
| } |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| asyncResp->res.result(boost::beast::http::status::no_content); |
| } |
| |
| inline void setDateTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& datetime) |
| { |
| BMCWEB_LOG_DEBUG("Set date time: {}", datetime); |
| |
| std::optional<redfish::time_utils::usSinceEpoch> us = |
| redfish::time_utils::dateStringToEpoch(datetime); |
| if (!us) |
| { |
| messages::propertyValueFormatError(asyncResp->res, datetime, |
| "DateTime"); |
| return; |
| } |
| // Set the absolute datetime |
| bool relative = false; |
| bool interactive = false; |
| crow::connections::systemBus->async_method_call( |
| [asyncResp](const boost::system::error_code& ec, |
| const sdbusplus::message_t& msg) { |
| afterSetDateTime(asyncResp, ec, msg); |
| }, |
| "org.freedesktop.timedate1", "/org/freedesktop/timedate1", |
| "org.freedesktop.timedate1", "SetTime", us->count(), relative, |
| interactive); |
| } |
| |
| inline void checkForQuiesced( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| dbus::utility::getProperty<std::string>( |
| "org.freedesktop.systemd1", |
| "/org/freedesktop/systemd1/unit/obmc-bmc-service-quiesce@0.target", |
| "org.freedesktop.systemd1.Unit", "ActiveState", |
| [asyncResp](const boost::system::error_code& ec, |
| const std::string& val) { |
| if (!ec) |
| { |
| if (val == "active") |
| { |
| asyncResp->res.jsonValue["Status"]["Health"] = |
| resource::Health::Critical; |
| asyncResp->res.jsonValue["Status"]["State"] = |
| resource::State::Quiesced; |
| return; |
| } |
| } |
| asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; |
| asyncResp->res.jsonValue["Status"]["State"] = |
| resource::State::Enabled; |
| }); |
| } |
| |
| inline void requestRoutesManager(App& app) |
| { |
| std::string uuid = persistent_data::getConfig().systemUuid; |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/") |
| .privileges(redfish::privileges::getManager) |
| .methods( |
| boost::beast::http::verb:: |
| get)([&app, |
| uuid](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& managerId) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) |
| { |
| messages::resourceNotFound(asyncResp->res, "Manager", |
| managerId); |
| return; |
| } |
| |
| asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( |
| "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); |
| asyncResp->res.jsonValue["@odata.type"] = |
| "#Manager.v1_14_0.Manager"; |
| asyncResp->res.jsonValue["Id"] = BMCWEB_REDFISH_MANAGER_URI_NAME; |
| asyncResp->res.jsonValue["Name"] = "OpenBmc Manager"; |
| asyncResp->res.jsonValue["Description"] = |
| "Baseboard Management Controller"; |
| asyncResp->res.jsonValue["PowerState"] = resource::PowerState::On; |
| |
| asyncResp->res.jsonValue["ManagerType"] = manager::ManagerType::BMC; |
| asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid(); |
| asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid; |
| asyncResp->res.jsonValue["Model"] = |
| "OpenBmc"; // TODO(ed), get model |
| |
| asyncResp->res.jsonValue["LogServices"]["@odata.id"] = |
| boost::urls::format("/redfish/v1/Managers/{}/LogServices", |
| BMCWEB_REDFISH_MANAGER_URI_NAME); |
| asyncResp->res.jsonValue["NetworkProtocol"]["@odata.id"] = |
| boost::urls::format("/redfish/v1/Managers/{}/NetworkProtocol", |
| BMCWEB_REDFISH_MANAGER_URI_NAME); |
| asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = |
| boost::urls::format( |
| "/redfish/v1/Managers/{}/EthernetInterfaces", |
| BMCWEB_REDFISH_MANAGER_URI_NAME); |
| |
| if constexpr (BMCWEB_VM_NBDPROXY) |
| { |
| asyncResp->res.jsonValue["VirtualMedia"]["@odata.id"] = |
| boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia", |
| BMCWEB_REDFISH_MANAGER_URI_NAME); |
| } |
| |
| getHandleOemOpenBmc(req, asyncResp, managerId); |
| |
| // Manager.Reset (an action) can be many values, OpenBMC only |
| // supports BMC reboot. |
| nlohmann::json& managerReset = |
| asyncResp->res.jsonValue["Actions"]["#Manager.Reset"]; |
| managerReset["target"] = boost::urls::format( |
| "/redfish/v1/Managers/{}/Actions/Manager.Reset", |
| BMCWEB_REDFISH_MANAGER_URI_NAME); |
| managerReset["@Redfish.ActionInfo"] = |
| boost::urls::format("/redfish/v1/Managers/{}/ResetActionInfo", |
| BMCWEB_REDFISH_MANAGER_URI_NAME); |
| |
| // ResetToDefaults (Factory Reset) has values like |
| // PreserveNetworkAndUsers and PreserveNetwork that aren't supported |
| // on OpenBMC |
| nlohmann::json& resetToDefaults = |
| asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"]; |
| resetToDefaults["target"] = boost::urls::format( |
| "/redfish/v1/Managers/{}/Actions/Manager.ResetToDefaults", |
| BMCWEB_REDFISH_MANAGER_URI_NAME); |
| resetToDefaults["ResetType@Redfish.AllowableValues"] = |
| nlohmann::json::array_t({"ResetAll"}); |
| |
| std::pair<std::string, std::string> redfishDateTimeOffset = |
| redfish::time_utils::getDateTimeOffsetNow(); |
| |
| asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first; |
| asyncResp->res.jsonValue["DateTimeLocalOffset"] = |
| redfishDateTimeOffset.second; |
| |
| if constexpr (BMCWEB_KVM) |
| { |
| // Fill in GraphicalConsole info |
| asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = |
| true; |
| asyncResp->res |
| .jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 4; |
| asyncResp->res |
| .jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = |
| nlohmann::json::array_t({"KVMIP"}); |
| } |
| if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) |
| { |
| asyncResp->res |
| .jsonValue["Links"]["ManagerForServers@odata.count"] = 1; |
| |
| nlohmann::json::array_t managerForServers; |
| nlohmann::json::object_t manager; |
| manager["@odata.id"] = std::format( |
| "/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); |
| managerForServers.emplace_back(std::move(manager)); |
| |
| asyncResp->res.jsonValue["Links"]["ManagerForServers"] = |
| std::move(managerForServers); |
| } |
| |
| sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose, |
| "FirmwareVersion", true); |
| |
| managerGetLastResetTime(asyncResp); |
| |
| // ManagerDiagnosticData is added for all BMCs. |
| nlohmann::json& managerDiagnosticData = |
| asyncResp->res.jsonValue["ManagerDiagnosticData"]; |
| managerDiagnosticData["@odata.id"] = boost::urls::format( |
| "/redfish/v1/Managers/{}/ManagerDiagnosticData", |
| BMCWEB_REDFISH_MANAGER_URI_NAME); |
| |
| if constexpr (BMCWEB_REDFISH_OEM_MANAGER_FAN_DATA) |
| { |
| auto pids = std::make_shared<GetPIDValues>(asyncResp); |
| pids->run(); |
| } |
| |
| getMainChassisId(asyncResp, [](const std::string& chassisId, |
| const std::shared_ptr< |
| bmcweb::AsyncResp>& aRsp) { |
| aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = |
| 1; |
| nlohmann::json::array_t managerForChassis; |
| nlohmann::json::object_t managerObj; |
| boost::urls::url chassiUrl = |
| boost::urls::format("/redfish/v1/Chassis/{}", chassisId); |
| managerObj["@odata.id"] = chassiUrl; |
| managerForChassis.emplace_back(std::move(managerObj)); |
| aRsp->res.jsonValue["Links"]["ManagerForChassis"] = |
| std::move(managerForChassis); |
| aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] = |
| chassiUrl; |
| }); |
| |
| dbus::utility::getProperty<double>( |
| "org.freedesktop.systemd1", "/org/freedesktop/systemd1", |
| "org.freedesktop.systemd1.Manager", "Progress", |
| [asyncResp](const boost::system::error_code& ec, double val) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR("Error while getting progress"); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| if (val < 1.0) |
| { |
| asyncResp->res.jsonValue["Status"]["Health"] = |
| resource::Health::OK; |
| asyncResp->res.jsonValue["Status"]["State"] = |
| resource::State::Starting; |
| return; |
| } |
| checkForQuiesced(asyncResp); |
| }); |
| |
| constexpr std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.Inventory.Item.Bmc"}; |
| dbus::utility::getSubTree( |
| "/xyz/openbmc_project/inventory", 0, interfaces, |
| [asyncResp]( |
| const boost::system::error_code& ec, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG( |
| "D-Bus response error on GetSubTree {}", ec); |
| return; |
| } |
| if (subtree.empty()) |
| { |
| BMCWEB_LOG_DEBUG("Can't find bmc D-Bus object!"); |
| return; |
| } |
| // Assume only 1 bmc D-Bus object |
| // Throw an error if there is more than 1 |
| if (subtree.size() > 1) |
| { |
| BMCWEB_LOG_DEBUG("Found more than 1 bmc D-Bus object!"); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| if (subtree[0].first.empty() || |
| subtree[0].second.size() != 1) |
| { |
| BMCWEB_LOG_DEBUG("Error getting bmc D-Bus object!"); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| const std::string& path = subtree[0].first; |
| const std::string& connectionName = |
| subtree[0].second[0].first; |
| |
| for (const auto& interfaceName : |
| subtree[0].second[0].second) |
| { |
| if (interfaceName == |
| "xyz.openbmc_project.Inventory.Decorator.Asset") |
| { |
| dbus::utility::getAllProperties( |
| *crow::connections::systemBus, connectionName, |
| path, |
| "xyz.openbmc_project.Inventory.Decorator.Asset", |
| [asyncResp]( |
| const boost::system::error_code& ec2, |
| const dbus::utility::DBusPropertiesMap& |
| propertiesList) { |
| if (ec2) |
| { |
| BMCWEB_LOG_DEBUG( |
| "Can't get bmc asset!"); |
| return; |
| } |
| |
| const std::string* partNumber = nullptr; |
| const std::string* serialNumber = nullptr; |
| const std::string* manufacturer = nullptr; |
| const std::string* model = nullptr; |
| const std::string* sparePartNumber = |
| nullptr; |
| |
| const bool success = |
| sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), |
| propertiesList, "PartNumber", |
| partNumber, "SerialNumber", |
| serialNumber, "Manufacturer", |
| manufacturer, "Model", model, |
| "SparePartNumber", sparePartNumber); |
| |
| if (!success) |
| { |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| if (partNumber != nullptr) |
| { |
| asyncResp->res.jsonValue["PartNumber"] = |
| *partNumber; |
| } |
| |
| if (serialNumber != nullptr) |
| { |
| asyncResp->res |
| .jsonValue["SerialNumber"] = |
| *serialNumber; |
| } |
| |
| if (manufacturer != nullptr) |
| { |
| asyncResp->res |
| .jsonValue["Manufacturer"] = |
| *manufacturer; |
| } |
| |
| if (model != nullptr) |
| { |
| asyncResp->res.jsonValue["Model"] = |
| *model; |
| } |
| |
| if (sparePartNumber != nullptr) |
| { |
| asyncResp->res |
| .jsonValue["SparePartNumber"] = |
| *sparePartNumber; |
| } |
| }); |
| } |
| else if ( |
| interfaceName == |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode") |
| { |
| getLocation(asyncResp, connectionName, path); |
| } |
| } |
| }); |
| }); |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/") |
| .privileges(redfish::privileges::patchManager) |
| .methods(boost::beast::http::verb::patch)( |
| [&app](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& managerId) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) |
| { |
| messages::resourceNotFound(asyncResp->res, "Manager", |
| managerId); |
| return; |
| } |
| |
| std::optional<std::string> activeSoftwareImageOdataId; |
| std::optional<std::string> datetime; |
| std::optional<nlohmann::json::object_t> pidControllers; |
| std::optional<nlohmann::json::object_t> fanControllers; |
| std::optional<nlohmann::json::object_t> fanZones; |
| std::optional<nlohmann::json::object_t> stepwiseControllers; |
| std::optional<std::string> profile; |
| |
| if (!json_util::readJsonPatch( // |
| req, asyncResp->res, // |
| "DateTime", datetime, // |
| "Links/ActiveSoftwareImage/@odata.id", |
| activeSoftwareImageOdataId, // |
| "Oem/OpenBmc/Fan/FanControllers", fanControllers, // |
| "Oem/OpenBmc/Fan/FanZones", fanZones, // |
| "Oem/OpenBmc/Fan/PidControllers", pidControllers, // |
| "Oem/OpenBmc/Fan/Profile", profile, // |
| "Oem/OpenBmc/Fan/StepwiseControllers", |
| stepwiseControllers // |
| )) |
| { |
| return; |
| } |
| |
| if (pidControllers || fanControllers || fanZones || |
| stepwiseControllers || profile) |
| { |
| if constexpr (BMCWEB_REDFISH_OEM_MANAGER_FAN_DATA) |
| { |
| std::vector< |
| std::pair<std::string, |
| std::optional<nlohmann::json::object_t>>> |
| configuration; |
| if (pidControllers) |
| { |
| configuration.emplace_back( |
| "PidControllers", std::move(pidControllers)); |
| } |
| if (fanControllers) |
| { |
| configuration.emplace_back( |
| "FanControllers", std::move(fanControllers)); |
| } |
| if (fanZones) |
| { |
| configuration.emplace_back("FanZones", |
| std::move(fanZones)); |
| } |
| if (stepwiseControllers) |
| { |
| configuration.emplace_back( |
| "StepwiseControllers", |
| std::move(stepwiseControllers)); |
| } |
| auto pid = std::make_shared<SetPIDValues>( |
| asyncResp, std::move(configuration), profile); |
| pid->run(); |
| } |
| else |
| { |
| messages::propertyUnknown(asyncResp->res, "Oem"); |
| return; |
| } |
| } |
| |
| if (activeSoftwareImageOdataId) |
| { |
| setActiveFirmwareImage(asyncResp, |
| *activeSoftwareImageOdataId); |
| } |
| |
| if (datetime) |
| { |
| setDateTime(asyncResp, *datetime); |
| } |
| }); |
| } |
| |
| inline void requestRoutesManagerCollection(App& app) |
| { |
| BMCWEB_ROUTE(app, "/redfish/v1/Managers/") |
| .privileges(redfish::privileges::getManagerCollection) |
| .methods(boost::beast::http::verb::get)( |
| [&app](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| // Collections don't include the static data added by SubRoute |
| // because it has a duplicate entry for members |
| asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; |
| asyncResp->res.jsonValue["@odata.type"] = |
| "#ManagerCollection.ManagerCollection"; |
| asyncResp->res.jsonValue["Name"] = "Manager Collection"; |
| asyncResp->res.jsonValue["Members@odata.count"] = 1; |
| nlohmann::json::array_t members; |
| nlohmann::json& bmc = members.emplace_back(); |
| bmc["@odata.id"] = boost::urls::format( |
| "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); |
| asyncResp->res.jsonValue["Members"] = std::move(members); |
| }); |
| } |
| } // namespace redfish |