/* // Copyright (c) 2018 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #pragma once #include "bmcweb_config.h" #include "app.hpp" #include "dbus_singleton.hpp" #include "dbus_utility.hpp" #include "generated/enums/computer_system.hpp" #include "generated/enums/resource.hpp" #include "hypervisor_system.hpp" #include "led.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/pcie_util.hpp" #include "utils/sw_utils.hpp" #include "utils/time_utils.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace redfish { const static std::array, 2> protocolToDBusForSystems{ {{"SSH", "obmc-console-ssh"}, {"IPMI", "phosphor-ipmi-net"}}}; /** * @brief Updates the Functional State of DIMMs * * @param[in] asyncResp Shared pointer for completing asynchronous calls * @param[in] dimmState Dimm's Functional state, true/false * * @return None. */ inline void updateDimmProperties(const std::shared_ptr& asyncResp, bool isDimmFunctional) { BMCWEB_LOG_DEBUG("Dimm Functional: {}", isDimmFunctional); // Set it as Enabled if at least one DIMM is functional // Update STATE only if previous State was DISABLED and current Dimm is // ENABLED. const nlohmann::json& prevMemSummary = asyncResp->res.jsonValue["MemorySummary"]["Status"]["State"]; if (prevMemSummary == "Disabled") { if (isDimmFunctional) { asyncResp->res.jsonValue["MemorySummary"]["Status"]["State"] = "Enabled"; } } } /* * @brief Update "ProcessorSummary" "Status" "State" based on * CPU Functional State * * @param[in] asyncResp Shared pointer for completing asynchronous calls * @param[in] cpuFunctionalState is CPU functional true/false * * @return None. */ inline void modifyCpuFunctionalState( const std::shared_ptr& asyncResp, bool isCpuFunctional) { BMCWEB_LOG_DEBUG("Cpu Functional: {}", isCpuFunctional); const nlohmann::json& prevProcState = asyncResp->res.jsonValue["ProcessorSummary"]["Status"]["State"]; // Set it as Enabled if at least one CPU is functional // Update STATE only if previous State was Non_Functional and current CPU is // Functional. if (prevProcState == "Disabled") { if (isCpuFunctional) { asyncResp->res.jsonValue["ProcessorSummary"]["Status"]["State"] = "Enabled"; } } } /* * @brief Update "ProcessorSummary" "Count" based on Cpu PresenceState * * @param[in] asyncResp Shared pointer for completing asynchronous calls * @param[in] cpuPresenceState CPU present or not * * @return None. */ inline void modifyCpuPresenceState(const std::shared_ptr& asyncResp, bool isCpuPresent) { BMCWEB_LOG_DEBUG("Cpu Present: {}", isCpuPresent); if (isCpuPresent) { nlohmann::json& procCount = asyncResp->res.jsonValue["ProcessorSummary"]["Count"]; auto* procCountPtr = procCount.get_ptr(); if (procCountPtr != nullptr) { // shouldn't be possible to be nullptr *procCountPtr += 1; } } } inline void getProcessorProperties( const std::shared_ptr& asyncResp, const std::vector>& properties) { BMCWEB_LOG_DEBUG("Got {} Cpu properties.", properties.size()); // TODO: Get Model const uint16_t* coreCount = nullptr; const bool success = sdbusplus::unpackPropertiesNoThrow( dbus_utils::UnpackErrorPrinter(), properties, "CoreCount", coreCount); if (!success) { messages::internalError(asyncResp->res); return; } if (coreCount != nullptr) { nlohmann::json& coreCountJson = asyncResp->res.jsonValue["ProcessorSummary"]["CoreCount"]; uint64_t* coreCountJsonPtr = coreCountJson.get_ptr(); if (coreCountJsonPtr == nullptr) { coreCountJson = *coreCount; } else { *coreCountJsonPtr += *coreCount; } } } /* * @brief Get ProcessorSummary fields * * @param[in] asyncResp Shared pointer for completing asynchronous calls * @param[in] service dbus service for Cpu Information * @param[in] path dbus path for Cpu * * @return None. */ inline void getProcessorSummary(const std::shared_ptr& asyncResp, const std::string& service, const std::string& path) { auto getCpuPresenceState = [asyncResp](const boost::system::error_code& ec3, const bool cpuPresenceCheck) { if (ec3) { BMCWEB_LOG_ERROR("DBUS response error {}", ec3); return; } modifyCpuPresenceState(asyncResp, cpuPresenceCheck); }; // Get the Presence of CPU sdbusplus::asio::getProperty( *crow::connections::systemBus, service, path, "xyz.openbmc_project.Inventory.Item", "Present", std::move(getCpuPresenceState)); sdbusplus::asio::getAllProperties( *crow::connections::systemBus, service, path, "xyz.openbmc_project.Inventory.Item.Cpu", [asyncResp, service, path](const boost::system::error_code& ec2, const dbus::utility::DBusPropertiesMap& properties) { if (ec2) { BMCWEB_LOG_ERROR("DBUS response error {}", ec2); messages::internalError(asyncResp->res); return; } getProcessorProperties(asyncResp, properties); }); } /* * @brief processMemoryProperties fields * * @param[in] asyncResp Shared pointer for completing asynchronous calls * @param[in] DBUS properties for memory * * @return None. */ inline void processMemoryProperties(const std::shared_ptr& asyncResp, const dbus::utility::DBusPropertiesMap& properties) { BMCWEB_LOG_DEBUG("Got {} Dimm properties.", properties.size()); if (properties.empty()) { return; } const size_t* memorySizeInKB = nullptr; const bool success = sdbusplus::unpackPropertiesNoThrow( dbus_utils::UnpackErrorPrinter(), properties, "MemorySizeInKB", memorySizeInKB); if (!success) { messages::internalError(asyncResp->res); return; } if (memorySizeInKB != nullptr) { nlohmann::json& totalMemory = asyncResp->res.jsonValue["MemorySummary"]["TotalSystemMemoryGiB"]; const double* preValue = totalMemory.get_ptr(); if (preValue == nullptr) { asyncResp->res.jsonValue["MemorySummary"]["TotalSystemMemoryGiB"] = static_cast(*memorySizeInKB) / (1024 * 1024); } else { asyncResp->res.jsonValue["MemorySummary"]["TotalSystemMemoryGiB"] = static_cast(*memorySizeInKB) / (1024 * 1024) + *preValue; } } } /* * @brief Get getMemorySummary fields * * @param[in] asyncResp Shared pointer for completing asynchronous calls * @param[in] service dbus service for memory Information * @param[in] path dbus path for memory * * @return None. */ inline void getMemorySummary(const std::shared_ptr& asyncResp, const std::string& service, const std::string& path) { sdbusplus::asio::getAllProperties( *crow::connections::systemBus, service, path, "xyz.openbmc_project.Inventory.Item.Dimm", [asyncResp, service, path](const boost::system::error_code& ec2, const dbus::utility::DBusPropertiesMap& properties) { if (ec2) { BMCWEB_LOG_ERROR("DBUS response error {}", ec2); messages::internalError(asyncResp->res); return; } processMemoryProperties(asyncResp, properties); }); } inline void afterGetUUID(const std::shared_ptr& asyncResp, const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { if (ec) { BMCWEB_LOG_ERROR("DBUS response error {}", ec); messages::internalError(asyncResp->res); return; } BMCWEB_LOG_DEBUG("Got {} UUID properties.", properties.size()); const std::string* uUID = nullptr; const bool success = sdbusplus::unpackPropertiesNoThrow( dbus_utils::UnpackErrorPrinter(), properties, "UUID", uUID); if (!success) { messages::internalError(asyncResp->res); return; } if (uUID != nullptr) { std::string valueStr = *uUID; if (valueStr.size() == 32) { valueStr.insert(8, 1, '-'); valueStr.insert(13, 1, '-'); valueStr.insert(18, 1, '-'); valueStr.insert(23, 1, '-'); } BMCWEB_LOG_DEBUG("UUID = {}", valueStr); asyncResp->res.jsonValue["UUID"] = valueStr; } } inline void afterGetInventory(const std::shared_ptr& asyncResp, const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& propertiesList) { if (ec) { // doesn't have to include this // interface return; } BMCWEB_LOG_DEBUG("Got {} properties for system", propertiesList.size()); const std::string* partNumber = nullptr; const std::string* serialNumber = nullptr; const std::string* manufacturer = nullptr; const std::string* model = nullptr; const std::string* subModel = nullptr; const bool success = sdbusplus::unpackPropertiesNoThrow( dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber", partNumber, "SerialNumber", serialNumber, "Manufacturer", manufacturer, "Model", model, "SubModel", subModel); 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 (subModel != nullptr) { asyncResp->res.jsonValue["SubModel"] = *subModel; } // Grab the bios version sw_util::populateSoftwareInformation(asyncResp, sw_util::biosPurpose, "BiosVersion", false); } inline void afterGetAssetTag(const std::shared_ptr& asyncResp, const boost::system::error_code& ec, const std::string& value) { if (ec) { // doesn't have to include this // interface return; } asyncResp->res.jsonValue["AssetTag"] = value; } inline void afterSystemGetSubTree( const std::shared_ptr& asyncResp, const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { if (ec) { BMCWEB_LOG_ERROR("DBUS response error {}", ec); messages::internalError(asyncResp->res); return; } // Iterate over all retrieved ObjectPaths. for (const std::pair< std::string, std::vector>>>& object : subtree) { const std::string& path = object.first; BMCWEB_LOG_DEBUG("Got path: {}", path); const std::vector>>& connectionNames = object.second; if (connectionNames.empty()) { continue; } // This is not system, so check if it's cpu, dimm, UUID or // BiosVer for (const auto& connection : connectionNames) { for (const auto& interfaceName : connection.second) { if (interfaceName == "xyz.openbmc_project.Inventory.Item.Dimm") { BMCWEB_LOG_DEBUG("Found Dimm, now get its properties."); getMemorySummary(asyncResp, connection.first, path); } else if (interfaceName == "xyz.openbmc_project.Inventory.Item.Cpu") { BMCWEB_LOG_DEBUG("Found Cpu, now get its properties."); getProcessorSummary(asyncResp, connection.first, path); } else if (interfaceName == "xyz.openbmc_project.Common.UUID") { BMCWEB_LOG_DEBUG("Found UUID, now get its properties."); sdbusplus::asio::getAllProperties( *crow::connections::systemBus, connection.first, path, "xyz.openbmc_project.Common.UUID", [asyncResp](const boost::system::error_code& ec3, const dbus::utility::DBusPropertiesMap& properties) { afterGetUUID(asyncResp, ec3, properties); }); } else if (interfaceName == "xyz.openbmc_project.Inventory.Item.System") { sdbusplus::asio::getAllProperties( *crow::connections::systemBus, connection.first, path, "xyz.openbmc_project.Inventory.Decorator.Asset", [asyncResp](const boost::system::error_code& ec3, const dbus::utility::DBusPropertiesMap& properties) { afterGetInventory(asyncResp, ec3, properties); }); sdbusplus::asio::getProperty( *crow::connections::systemBus, connection.first, path, "xyz.openbmc_project.Inventory.Decorator." "AssetTag", "AssetTag", std::bind_front(afterGetAssetTag, asyncResp)); } } } } } /* * @brief Retrieves computer system properties over dbus * * @param[in] asyncResp Shared pointer for completing asynchronous calls * * @return None. */ inline void getComputerSystem(const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Get available system components."); constexpr std::array interfaces = { "xyz.openbmc_project.Inventory.Decorator.Asset", "xyz.openbmc_project.Inventory.Item.Cpu", "xyz.openbmc_project.Inventory.Item.Dimm", "xyz.openbmc_project.Inventory.Item.System", "xyz.openbmc_project.Common.UUID", }; dbus::utility::getSubTree( "/xyz/openbmc_project/inventory", 0, interfaces, std::bind_front(afterSystemGetSubTree, asyncResp)); } /** * @brief Retrieves host state properties over dbus * * @param[in] asyncResp Shared pointer for completing asynchronous calls. * * @return None. */ inline void getHostState(const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Get host information."); sdbusplus::asio::getProperty( *crow::connections::systemBus, "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host", "CurrentHostState", [asyncResp](const boost::system::error_code& ec, const std::string& hostState) { if (ec) { if (ec == boost::system::errc::host_unreachable) { // Service not available, no error, just don't return // host state info BMCWEB_LOG_DEBUG("Service not available {}", ec); return; } BMCWEB_LOG_ERROR("DBUS response error {}", ec); messages::internalError(asyncResp->res); return; } BMCWEB_LOG_DEBUG("Host state: {}", hostState); // Verify Host State if (hostState == "xyz.openbmc_project.State.Host.HostState.Running") { asyncResp->res.jsonValue["PowerState"] = "On"; asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; } else if (hostState == "xyz.openbmc_project.State.Host.HostState.Quiesced") { asyncResp->res.jsonValue["PowerState"] = "On"; asyncResp->res.jsonValue["Status"]["State"] = "Quiesced"; } else if (hostState == "xyz.openbmc_project.State.Host.HostState.DiagnosticMode") { asyncResp->res.jsonValue["PowerState"] = "On"; asyncResp->res.jsonValue["Status"]["State"] = "InTest"; } else if ( hostState == "xyz.openbmc_project.State.Host.HostState.TransitioningToRunning") { asyncResp->res.jsonValue["PowerState"] = "PoweringOn"; asyncResp->res.jsonValue["Status"]["State"] = "Starting"; } else if (hostState == "xyz.openbmc_project.State.Host.HostState.TransitioningToOff") { asyncResp->res.jsonValue["PowerState"] = "PoweringOff"; asyncResp->res.jsonValue["Status"]["State"] = "Disabled"; } else { asyncResp->res.jsonValue["PowerState"] = "Off"; asyncResp->res.jsonValue["Status"]["State"] = "Disabled"; } }); } /** * @brief Translates boot source DBUS property value to redfish. * * @param[in] dbusSource The boot source in DBUS speak. * * @return Returns as a string, the boot source in Redfish terms. If translation * cannot be done, returns an empty string. */ inline std::string dbusToRfBootSource(const std::string& dbusSource) { if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Default") { return "None"; } if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Disk") { return "Hdd"; } if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.ExternalMedia") { return "Cd"; } if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Network") { return "Pxe"; } if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.RemovableMedia") { return "Usb"; } return ""; } /** * @brief Translates boot type DBUS property value to redfish. * * @param[in] dbusType The boot type in DBUS speak. * * @return Returns as a string, the boot type in Redfish terms. If translation * cannot be done, returns an empty string. */ inline std::string dbusToRfBootType(const std::string& dbusType) { if (dbusType == "xyz.openbmc_project.Control.Boot.Type.Types.Legacy") { return "Legacy"; } if (dbusType == "xyz.openbmc_project.Control.Boot.Type.Types.EFI") { return "UEFI"; } return ""; } /** * @brief Translates boot mode DBUS property value to redfish. * * @param[in] dbusMode The boot mode in DBUS speak. * * @return Returns as a string, the boot mode in Redfish terms. If translation * cannot be done, returns an empty string. */ inline std::string dbusToRfBootMode(const std::string& dbusMode) { if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular") { return "None"; } if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe") { return "Diags"; } if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup") { return "BiosSetup"; } return ""; } /** * @brief Translates boot progress DBUS property value to redfish. * * @param[in] dbusBootProgress The boot progress in DBUS speak. * * @return Returns as a string, the boot progress in Redfish terms. If * translation cannot be done, returns "None". */ inline std::string dbusToRfBootProgress(const std::string& dbusBootProgress) { // Now convert the D-Bus BootProgress to the appropriate Redfish // enum std::string rfBpLastState = "None"; if (dbusBootProgress == "xyz.openbmc_project.State.Boot.Progress." "ProgressStages.Unspecified") { rfBpLastState = "None"; } else if (dbusBootProgress == "xyz.openbmc_project.State.Boot.Progress.ProgressStages." "PrimaryProcInit") { rfBpLastState = "PrimaryProcessorInitializationStarted"; } else if (dbusBootProgress == "xyz.openbmc_project.State.Boot.Progress.ProgressStages." "BusInit") { rfBpLastState = "BusInitializationStarted"; } else if (dbusBootProgress == "xyz.openbmc_project.State.Boot.Progress.ProgressStages." "MemoryInit") { rfBpLastState = "MemoryInitializationStarted"; } else if (dbusBootProgress == "xyz.openbmc_project.State.Boot.Progress.ProgressStages." "SecondaryProcInit") { rfBpLastState = "SecondaryProcessorInitializationStarted"; } else if (dbusBootProgress == "xyz.openbmc_project.State.Boot.Progress.ProgressStages." "PCIInit") { rfBpLastState = "PCIResourceConfigStarted"; } else if (dbusBootProgress == "xyz.openbmc_project.State.Boot.Progress.ProgressStages." "SystemSetup") { rfBpLastState = "SetupEntered"; } else if (dbusBootProgress == "xyz.openbmc_project.State.Boot.Progress.ProgressStages." "SystemInitComplete") { rfBpLastState = "SystemHardwareInitializationComplete"; } else if (dbusBootProgress == "xyz.openbmc_project.State.Boot.Progress.ProgressStages." "OSStart") { rfBpLastState = "OSBootStarted"; } else if (dbusBootProgress == "xyz.openbmc_project.State.Boot.Progress.ProgressStages." "OSRunning") { rfBpLastState = "OSRunning"; } else { BMCWEB_LOG_DEBUG("Unsupported D-Bus BootProgress {}", dbusBootProgress); // Just return the default } return rfBpLastState; } /** * @brief Translates boot source from Redfish to the DBus boot paths. * * @param[in] rfSource The boot source in Redfish. * @param[out] bootSource The DBus source * @param[out] bootMode the DBus boot mode * * @return Integer error code. */ inline int assignBootParameters(const std::shared_ptr& asyncResp, const std::string& rfSource, std::string& bootSource, std::string& bootMode) { bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Default"; bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"; if (rfSource == "None") { return 0; } if (rfSource == "Pxe") { bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Network"; } else if (rfSource == "Hdd") { bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Disk"; } else if (rfSource == "Diags") { bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe"; } else if (rfSource == "Cd") { bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.ExternalMedia"; } else if (rfSource == "BiosSetup") { bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup"; } else if (rfSource == "Usb") { bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.RemovableMedia"; } else { BMCWEB_LOG_DEBUG( "Invalid property value for BootSourceOverrideTarget: {}", bootSource); messages::propertyValueNotInList(asyncResp->res, rfSource, "BootSourceTargetOverride"); return -1; } return 0; } /** * @brief Retrieves boot progress of the system * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getBootProgress(const std::shared_ptr& asyncResp) { sdbusplus::asio::getProperty( *crow::connections::systemBus, "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Boot.Progress", "BootProgress", [asyncResp](const boost::system::error_code& ec, const std::string& bootProgressStr) { if (ec) { // BootProgress is an optional object so just do nothing if // not found return; } BMCWEB_LOG_DEBUG("Boot Progress: {}", bootProgressStr); asyncResp->res.jsonValue["BootProgress"]["LastState"] = dbusToRfBootProgress(bootProgressStr); }); } /** * @brief Retrieves boot progress Last Update of the system * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getBootProgressLastStateTime( const std::shared_ptr& asyncResp) { sdbusplus::asio::getProperty( *crow::connections::systemBus, "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Boot.Progress", "BootProgressLastUpdate", [asyncResp](const boost::system::error_code& ec, const uint64_t lastStateTime) { if (ec) { BMCWEB_LOG_DEBUG("D-BUS response error {}", ec); return; } // BootProgressLastUpdate is the last time the BootProgress property // was updated. The time is the Epoch time, number of microseconds // since 1 Jan 1970 00::00::00 UTC." // https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/ // yaml/xyz/openbmc_project/State/Boot/Progress.interface.yaml#L11 // Convert to ISO 8601 standard asyncResp->res.jsonValue["BootProgress"]["LastStateTime"] = redfish::time_utils::getDateTimeUintUs(lastStateTime); }); } /** * @brief Retrieves boot override type over DBUS and fills out the response * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getBootOverrideType(const std::shared_ptr& asyncResp) { sdbusplus::asio::getProperty( *crow::connections::systemBus, "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/boot", "xyz.openbmc_project.Control.Boot.Type", "BootType", [asyncResp](const boost::system::error_code& ec, const std::string& bootType) { if (ec) { // not an error, don't have to have the interface return; } BMCWEB_LOG_DEBUG("Boot type: {}", bootType); asyncResp->res .jsonValue["Boot"] ["BootSourceOverrideMode@Redfish.AllowableValues"] = nlohmann::json::array_t({"Legacy", "UEFI"}); auto rfType = dbusToRfBootType(bootType); if (rfType.empty()) { messages::internalError(asyncResp->res); return; } asyncResp->res.jsonValue["Boot"]["BootSourceOverrideMode"] = rfType; }); } /** * @brief Retrieves boot override mode over DBUS and fills out the response * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getBootOverrideMode(const std::shared_ptr& asyncResp) { sdbusplus::asio::getProperty( *crow::connections::systemBus, "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/boot", "xyz.openbmc_project.Control.Boot.Mode", "BootMode", [asyncResp](const boost::system::error_code& ec, const std::string& bootModeStr) { if (ec) { BMCWEB_LOG_ERROR("DBUS response error {}", ec); messages::internalError(asyncResp->res); return; } BMCWEB_LOG_DEBUG("Boot mode: {}", bootModeStr); nlohmann::json::array_t allowed; allowed.emplace_back("None"); allowed.emplace_back("Pxe"); allowed.emplace_back("Hdd"); allowed.emplace_back("Cd"); allowed.emplace_back("Diags"); allowed.emplace_back("BiosSetup"); allowed.emplace_back("Usb"); asyncResp->res .jsonValue["Boot"] ["BootSourceOverrideTarget@Redfish.AllowableValues"] = std::move(allowed); if (bootModeStr != "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular") { auto rfMode = dbusToRfBootMode(bootModeStr); if (!rfMode.empty()) { asyncResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = rfMode; } } }); } /** * @brief Retrieves boot override source over DBUS * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getBootOverrideSource(const std::shared_ptr& asyncResp) { sdbusplus::asio::getProperty( *crow::connections::systemBus, "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/boot", "xyz.openbmc_project.Control.Boot.Source", "BootSource", [asyncResp](const boost::system::error_code& ec, const std::string& bootSourceStr) { if (ec) { if (ec.value() == boost::asio::error::host_unreachable) { return; } BMCWEB_LOG_ERROR("DBUS response error {}", ec); messages::internalError(asyncResp->res); return; } BMCWEB_LOG_DEBUG("Boot source: {}", bootSourceStr); auto rfSource = dbusToRfBootSource(bootSourceStr); if (!rfSource.empty()) { asyncResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = rfSource; } // Get BootMode as BootSourceOverrideTarget is constructed // from both BootSource and BootMode getBootOverrideMode(asyncResp); }); } /** * @brief This functions abstracts all the logic behind getting a * "BootSourceOverrideEnabled" property from an overall boot override enable * state * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void processBootOverrideEnable( const std::shared_ptr& asyncResp, const bool bootOverrideEnableSetting) { if (!bootOverrideEnableSetting) { asyncResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = "Disabled"; return; } // If boot source override is enabled, we need to check 'one_time' // property to set a correct value for the "BootSourceOverrideEnabled" sdbusplus::asio::getProperty( *crow::connections::systemBus, "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/boot/one_time", "xyz.openbmc_project.Object.Enable", "Enabled", [asyncResp](const boost::system::error_code& ec, bool oneTimeSetting) { if (ec) { BMCWEB_LOG_ERROR("DBUS response error {}", ec); messages::internalError(asyncResp->res); return; } if (oneTimeSetting) { asyncResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = "Once"; } else { asyncResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = "Continuous"; } }); } /** * @brief Retrieves boot override enable over DBUS * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getBootOverrideEnable(const std::shared_ptr& asyncResp) { sdbusplus::asio::getProperty( *crow::connections::systemBus, "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/boot", "xyz.openbmc_project.Object.Enable", "Enabled", [asyncResp](const boost::system::error_code& ec, const bool bootOverrideEnable) { if (ec) { if (ec.value() == boost::asio::error::host_unreachable) { return; } BMCWEB_LOG_ERROR("DBUS response error {}", ec); messages::internalError(asyncResp->res); return; } processBootOverrideEnable(asyncResp, bootOverrideEnable); }); } /** * @brief Retrieves boot source override properties * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getBootProperties(const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Get boot information."); getBootOverrideSource(asyncResp); getBootOverrideType(asyncResp); getBootOverrideEnable(asyncResp); } /** * @brief Retrieves the Last Reset Time * * "Reset" is an overloaded term in Redfish, "Reset" includes power on * and power off. Even though this is the "system" Redfish object look at the * chassis D-Bus interface for the LastStateChangeTime since this has the * last power operation time. * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getLastResetTime(const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Getting System Last Reset Time"); sdbusplus::asio::getProperty( *crow::connections::systemBus, "xyz.openbmc_project.State.Chassis", "/xyz/openbmc_project/state/chassis0", "xyz.openbmc_project.State.Chassis", "LastStateChangeTime", [asyncResp](const boost::system::error_code& ec, uint64_t lastResetTime) { if (ec) { BMCWEB_LOG_DEBUG("D-BUS response error {}", ec); return; } // LastStateChangeTime is epoch time, in milliseconds // https://github.com/openbmc/phosphor-dbus-interfaces/blob/33e8e1dd64da53a66e888d33dc82001305cd0bf9/xyz/openbmc_project/State/Chassis.interface.yaml#L19 uint64_t lastResetTimeStamp = lastResetTime / 1000; // Convert to ISO 8601 standard asyncResp->res.jsonValue["LastResetTime"] = redfish::time_utils::getDateTimeUint(lastResetTimeStamp); }); } /** * @brief Retrieves the number of automatic boot Retry attempts allowed/left. * * The total number of automatic reboot retries allowed "RetryAttempts" and its * corresponding property "AttemptsLeft" that keeps track of the amount of * automatic retry attempts left are hosted in phosphor-state-manager through * dbus. * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getAutomaticRebootAttempts( const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Get Automatic Retry policy"); sdbusplus::asio::getAllProperties( *crow::connections::systemBus, "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.Control.Boot.RebootAttempts", [asyncResp{asyncResp}]( const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& propertiesList) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); messages::internalError(asyncResp->res); } return; } const uint32_t* attemptsLeft = nullptr; const uint32_t* retryAttempts = nullptr; const bool success = sdbusplus::unpackPropertiesNoThrow( dbus_utils::UnpackErrorPrinter(), propertiesList, "AttemptsLeft", attemptsLeft, "RetryAttempts", retryAttempts); if (!success) { messages::internalError(asyncResp->res); return; } if (attemptsLeft != nullptr) { asyncResp->res .jsonValue["Boot"]["RemainingAutomaticRetryAttempts"] = *attemptsLeft; } if (retryAttempts != nullptr) { asyncResp->res.jsonValue["Boot"]["AutomaticRetryAttempts"] = *retryAttempts; } }); } /** * @brief Retrieves Automatic Retry properties. Known on D-Bus as AutoReboot. * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getAutomaticRetryPolicy(const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Get Automatic Retry policy"); sdbusplus::asio::getProperty( *crow::connections::systemBus, "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/auto_reboot", "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", [asyncResp](const boost::system::error_code& ec, bool autoRebootEnabled) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); messages::internalError(asyncResp->res); } return; } BMCWEB_LOG_DEBUG("Auto Reboot: {}", autoRebootEnabled); if (autoRebootEnabled) { asyncResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = "RetryAttempts"; } else { asyncResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = "Disabled"; } getAutomaticRebootAttempts(asyncResp); // "AutomaticRetryConfig" can be 3 values, Disabled, RetryAlways, // and RetryAttempts. OpenBMC only supports Disabled and // RetryAttempts. nlohmann::json::array_t allowed; allowed.emplace_back("Disabled"); allowed.emplace_back("RetryAttempts"); asyncResp->res .jsonValue["Boot"]["AutomaticRetryConfig@Redfish.AllowableValues"] = std::move(allowed); }); } /** * @brief Sets RetryAttempts * * @param[in] asyncResp Shared pointer for generating response message. * @param[in] retryAttempts "AutomaticRetryAttempts" from request. * *@return None. */ inline void setAutomaticRetryAttempts( const std::shared_ptr& asyncResp, const uint32_t retryAttempts) { BMCWEB_LOG_DEBUG("Set Automatic Retry Attempts."); setDbusProperty( asyncResp, "Boot/AutomaticRetryAttempts", "xyz.openbmc_project.State.Host", sdbusplus::message::object_path("/xyz/openbmc_project/state/host0"), "xyz.openbmc_project.Control.Boot.RebootAttempts", "RetryAttempts", retryAttempts); } inline computer_system::PowerRestorePolicyTypes redfishPowerRestorePolicyFromDbus(std::string_view value) { if (value == "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn") { return computer_system::PowerRestorePolicyTypes::AlwaysOn; } if (value == "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOff") { return computer_system::PowerRestorePolicyTypes::AlwaysOff; } if (value == "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore") { return computer_system::PowerRestorePolicyTypes::LastState; } if (value == "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.None") { return computer_system::PowerRestorePolicyTypes::AlwaysOff; } return computer_system::PowerRestorePolicyTypes::Invalid; } /** * @brief Retrieves power restore policy over DBUS. * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getPowerRestorePolicy(const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Get power restore policy"); sdbusplus::asio::getProperty( *crow::connections::systemBus, "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/power_restore_policy", "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", [asyncResp](const boost::system::error_code& ec, const std::string& policy) { if (ec) { BMCWEB_LOG_DEBUG("DBUS response error {}", ec); return; } computer_system::PowerRestorePolicyTypes restore = redfishPowerRestorePolicyFromDbus(policy); if (restore == computer_system::PowerRestorePolicyTypes::Invalid) { messages::internalError(asyncResp->res); return; } asyncResp->res.jsonValue["PowerRestorePolicy"] = restore; }); } /** * @brief Stop Boot On Fault over DBUS. * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getStopBootOnFault(const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Get Stop Boot On Fault"); sdbusplus::asio::getProperty( *crow::connections::systemBus, "xyz.openbmc_project.Settings", "/xyz/openbmc_project/logging/settings", "xyz.openbmc_project.Logging.Settings", "QuiesceOnHwError", [asyncResp](const boost::system::error_code& ec, bool value) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR("DBUS response error {}", ec); messages::internalError(asyncResp->res); } return; } if (value) { asyncResp->res.jsonValue["Boot"]["StopBootOnFault"] = "AnyFault"; } else { asyncResp->res.jsonValue["Boot"]["StopBootOnFault"] = "Never"; } }); } /** * @brief Get TrustedModuleRequiredToBoot property. Determines whether or not * TPM is required for booting the host. * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getTrustedModuleRequiredToBoot( const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Get TPM required to boot."); constexpr std::array interfaces = { "xyz.openbmc_project.Control.TPM.Policy"}; dbus::utility::getSubTree( "/", 0, interfaces, [asyncResp](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { if (ec) { BMCWEB_LOG_DEBUG("DBUS response error on TPM.Policy GetSubTree{}", ec); // This is an optional D-Bus object so just return if // error occurs return; } if (subtree.empty()) { // As noted above, this is an optional interface so just return // if there is no instance found return; } /* When there is more than one TPMEnable object... */ if (subtree.size() > 1) { BMCWEB_LOG_DEBUG( "DBUS response has more than 1 TPM Enable object:{}", subtree.size()); // Throw an internal Error and return messages::internalError(asyncResp->res); return; } // Make sure the Dbus response map has a service and objectPath // field if (subtree[0].first.empty() || subtree[0].second.size() != 1) { BMCWEB_LOG_DEBUG("TPM.Policy mapper error!"); messages::internalError(asyncResp->res); return; } const std::string& path = subtree[0].first; const std::string& serv = subtree[0].second.begin()->first; // Valid TPM Enable object found, now reading the current value sdbusplus::asio::getProperty( *crow::connections::systemBus, serv, path, "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable", [asyncResp](const boost::system::error_code& ec2, bool tpmRequired) { if (ec2) { BMCWEB_LOG_ERROR("D-BUS response error on TPM.Policy Get{}", ec2); messages::internalError(asyncResp->res); return; } if (tpmRequired) { asyncResp->res .jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = "Required"; } else { asyncResp->res .jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = "Disabled"; } }); }); } /** * @brief Set TrustedModuleRequiredToBoot property. Determines whether or not * TPM is required for booting the host. * * @param[in] asyncResp Shared pointer for generating response message. * @param[in] tpmRequired Value to set TPM Required To Boot property to. * * @return None. */ inline void setTrustedModuleRequiredToBoot( const std::shared_ptr& asyncResp, const bool tpmRequired) { BMCWEB_LOG_DEBUG("Set TrustedModuleRequiredToBoot."); constexpr std::array interfaces = { "xyz.openbmc_project.Control.TPM.Policy"}; dbus::utility::getSubTree( "/", 0, interfaces, [asyncResp, tpmRequired](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { if (ec) { BMCWEB_LOG_ERROR("DBUS response error on TPM.Policy GetSubTree{}", ec); messages::internalError(asyncResp->res); return; } if (subtree.empty()) { messages::propertyValueNotInList(asyncResp->res, "ComputerSystem", "TrustedModuleRequiredToBoot"); return; } /* When there is more than one TPMEnable object... */ if (subtree.size() > 1) { BMCWEB_LOG_DEBUG( "DBUS response has more than 1 TPM Enable object:{}", subtree.size()); // Throw an internal Error and return messages::internalError(asyncResp->res); return; } // Make sure the Dbus response map has a service and objectPath // field if (subtree[0].first.empty() || subtree[0].second.size() != 1) { BMCWEB_LOG_DEBUG("TPM.Policy mapper error!"); messages::internalError(asyncResp->res); return; } const std::string& path = subtree[0].first; const std::string& serv = subtree[0].second.begin()->first; if (serv.empty()) { BMCWEB_LOG_DEBUG("TPM.Policy service mapper error!"); messages::internalError(asyncResp->res); return; } // Valid TPM Enable object found, now setting the value setDbusProperty(asyncResp, "Boot/TrustedModuleRequiredToBoot", serv, path, "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable", tpmRequired); }); } /** * @brief Sets boot properties into DBUS object(s). * * @param[in] asyncResp Shared pointer for generating response message. * @param[in] bootType The boot type to set. * @return Integer error code. */ inline void setBootType(const std::shared_ptr& asyncResp, const std::optional& bootType) { std::string bootTypeStr; if (!bootType) { return; } // Source target specified BMCWEB_LOG_DEBUG("Boot type: {}", *bootType); // Figure out which DBUS interface and property to use if (*bootType == "Legacy") { bootTypeStr = "xyz.openbmc_project.Control.Boot.Type.Types.Legacy"; } else if (*bootType == "UEFI") { bootTypeStr = "xyz.openbmc_project.Control.Boot.Type.Types.EFI"; } else { BMCWEB_LOG_DEBUG("Invalid property value for " "BootSourceOverrideMode: {}", *bootType); messages::propertyValueNotInList(asyncResp->res, *bootType, "BootSourceOverrideMode"); return; } // Act on validated parameters BMCWEB_LOG_DEBUG("DBUS boot type: {}", bootTypeStr); setDbusProperty(asyncResp, "Boot/BootSourceOverrideMode", "xyz.openbmc_project.Settings", sdbusplus::message::object_path( "/xyz/openbmc_project/control/host0/boot"), "xyz.openbmc_project.Control.Boot.Type", "BootType", bootTypeStr); } /** * @brief Sets boot properties into DBUS object(s). * * @param[in] asyncResp Shared pointer for generating response * message. * @param[in] bootType The boot type to set. * @return Integer error code. */ inline void setBootEnable(const std::shared_ptr& asyncResp, const std::optional& bootEnable) { if (!bootEnable) { return; } // Source target specified BMCWEB_LOG_DEBUG("Boot enable: {}", *bootEnable); bool bootOverrideEnable = false; bool bootOverridePersistent = false; // Figure out which DBUS interface and property to use if (*bootEnable == "Disabled") { bootOverrideEnable = false; } else if (*bootEnable == "Once") { bootOverrideEnable = true; bootOverridePersistent = false; } else if (*bootEnable == "Continuous") { bootOverrideEnable = true; bootOverridePersistent = true; } else { BMCWEB_LOG_DEBUG( "Invalid property value for BootSourceOverrideEnabled: {}", *bootEnable); messages::propertyValueNotInList(asyncResp->res, *bootEnable, "BootSourceOverrideEnabled"); return; } // Act on validated parameters BMCWEB_LOG_DEBUG("DBUS boot override enable: {}", bootOverrideEnable); setDbusProperty(asyncResp, "Boot/BootSourceOverrideEnabled", "xyz.openbmc_project.Settings", sdbusplus::message::object_path( "/xyz/openbmc_project/control/host0/boot"), "xyz.openbmc_project.Object.Enable", "Enabled", bootOverrideEnable); if (!bootOverrideEnable) { return; } // In case boot override is enabled we need to set correct value for the // 'one_time' enable DBus interface BMCWEB_LOG_DEBUG("DBUS boot override persistent: {}", bootOverridePersistent); setDbusProperty(asyncResp, "Boot/BootSourceOverrideEnabled", "xyz.openbmc_project.Settings", sdbusplus::message::object_path( "/xyz/openbmc_project/control/host0/boot/one_time"), "xyz.openbmc_project.Object.Enable", "Enabled", !bootOverridePersistent); } /** * @brief Sets boot properties into DBUS object(s). * * @param[in] asyncResp Shared pointer for generating response message. * @param[in] bootSource The boot source to set. * * @return Integer error code. */ inline void setBootModeOrSource(const std::shared_ptr& asyncResp, const std::optional& bootSource) { std::string bootSourceStr; std::string bootModeStr; if (!bootSource) { return; } // Source target specified BMCWEB_LOG_DEBUG("Boot source: {}", *bootSource); // Figure out which DBUS interface and property to use if (assignBootParameters(asyncResp, *bootSource, bootSourceStr, bootModeStr) != 0) { BMCWEB_LOG_DEBUG( "Invalid property value for BootSourceOverrideTarget: {}", *bootSource); messages::propertyValueNotInList(asyncResp->res, *bootSource, "BootSourceTargetOverride"); return; } // Act on validated parameters BMCWEB_LOG_DEBUG("DBUS boot source: {}", bootSourceStr); BMCWEB_LOG_DEBUG("DBUS boot mode: {}", bootModeStr); setDbusProperty(asyncResp, "Boot/BootSourceOverrideTarget", "xyz.openbmc_project.Settings", sdbusplus::message::object_path( "/xyz/openbmc_project/control/host0/boot"), "xyz.openbmc_project.Control.Boot.Source", "BootSource", bootSourceStr); setDbusProperty(asyncResp, "Boot/BootSourceOverrideTarget", "xyz.openbmc_project.Settings", sdbusplus::message::object_path( "/xyz/openbmc_project/control/host0/boot"), "xyz.openbmc_project.Control.Boot.Mode", "BootMode", bootModeStr); } /** * @brief Sets Boot source override properties. * * @param[in] asyncResp Shared pointer for generating response message. * @param[in] bootSource The boot source from incoming RF request. * @param[in] bootType The boot type from incoming RF request. * @param[in] bootEnable The boot override enable from incoming RF request. * * @return Integer error code. */ inline void setBootProperties(const std::shared_ptr& asyncResp, const std::optional& bootSource, const std::optional& bootType, const std::optional& bootEnable) { BMCWEB_LOG_DEBUG("Set boot information."); setBootModeOrSource(asyncResp, bootSource); setBootType(asyncResp, bootType); setBootEnable(asyncResp, bootEnable); } /** * @brief Sets AssetTag * * @param[in] asyncResp Shared pointer for generating response message. * @param[in] assetTag "AssetTag" from request. * * @return None. */ inline void setAssetTag(const std::shared_ptr& asyncResp, const std::string& assetTag) { constexpr std::array interfaces = { "xyz.openbmc_project.Inventory.Item.System"}; dbus::utility::getSubTree( "/xyz/openbmc_project/inventory", 0, interfaces, [asyncResp, assetTag](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { if (ec) { BMCWEB_LOG_DEBUG("D-Bus response error on GetSubTree {}", ec); messages::internalError(asyncResp->res); return; } if (subtree.empty()) { BMCWEB_LOG_DEBUG("Can't find system D-Bus object!"); messages::internalError(asyncResp->res); return; } // Assume only 1 system D-Bus object // Throw an error if there is more than 1 if (subtree.size() > 1) { BMCWEB_LOG_DEBUG("Found more than 1 system D-Bus object!"); messages::internalError(asyncResp->res); return; } if (subtree[0].first.empty() || subtree[0].second.size() != 1) { BMCWEB_LOG_DEBUG("Asset Tag Set mapper error!"); messages::internalError(asyncResp->res); return; } const std::string& path = subtree[0].first; const std::string& service = subtree[0].second.begin()->first; if (service.empty()) { BMCWEB_LOG_DEBUG("Asset Tag Set service mapper error!"); messages::internalError(asyncResp->res); return; } setDbusProperty(asyncResp, "AssetTag", service, path, "xyz.openbmc_project.Inventory.Decorator.AssetTag", "AssetTag", assetTag); }); } /** * @brief Validate the specified stopBootOnFault is valid and return the * stopBootOnFault name associated with that string * * @param[in] stopBootOnFaultString String representing the desired * stopBootOnFault * * @return stopBootOnFault value or empty if incoming value is not valid */ inline std::optional validstopBootOnFault(const std::string& stopBootOnFaultString) { if (stopBootOnFaultString == "AnyFault") { return true; } if (stopBootOnFaultString == "Never") { return false; } return std::nullopt; } /** * @brief Sets stopBootOnFault * * @param[in] asyncResp Shared pointer for generating response message. * @param[in] stopBootOnFault "StopBootOnFault" from request. * * @return None. */ inline void setStopBootOnFault(const std::shared_ptr& asyncResp, const std::string& stopBootOnFault) { BMCWEB_LOG_DEBUG("Set Stop Boot On Fault."); std::optional stopBootEnabled = validstopBootOnFault(stopBootOnFault); if (!stopBootEnabled) { BMCWEB_LOG_DEBUG("Invalid property value for StopBootOnFault: {}", stopBootOnFault); messages::propertyValueNotInList(asyncResp->res, stopBootOnFault, "StopBootOnFault"); return; } setDbusProperty(asyncResp, "Boot/StopBootOnFault", "xyz.openbmc_project.Settings", sdbusplus::message::object_path( "/xyz/openbmc_project/logging/settings"), "xyz.openbmc_project.Logging.Settings", "QuiesceOnHwError", *stopBootEnabled); } /** * @brief Sets automaticRetry (Auto Reboot) * * @param[in] asyncResp Shared pointer for generating response message. * @param[in] automaticRetryConfig "AutomaticRetryConfig" from request. * * @return None. */ inline void setAutomaticRetry(const std::shared_ptr& asyncResp, const std::string& automaticRetryConfig) { BMCWEB_LOG_DEBUG("Set Automatic Retry."); // OpenBMC only supports "Disabled" and "RetryAttempts". bool autoRebootEnabled = false; if (automaticRetryConfig == "Disabled") { autoRebootEnabled = false; } else if (automaticRetryConfig == "RetryAttempts") { autoRebootEnabled = true; } else { BMCWEB_LOG_DEBUG("Invalid property value for AutomaticRetryConfig: {}", automaticRetryConfig); messages::propertyValueNotInList(asyncResp->res, automaticRetryConfig, "AutomaticRetryConfig"); return; } setDbusProperty(asyncResp, "Boot/AutomaticRetryConfig", "xyz.openbmc_project.Settings", sdbusplus::message::object_path( "/xyz/openbmc_project/control/host0/auto_reboot"), "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", autoRebootEnabled); } inline std::string dbusPowerRestorePolicyFromRedfish(std::string_view policy) { if (policy == "AlwaysOn") { return "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn"; } if (policy == "AlwaysOff") { return "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOff"; } if (policy == "LastState") { return "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore"; } return ""; } /** * @brief Sets power restore policy properties. * * @param[in] asyncResp Shared pointer for generating response message. * @param[in] policy power restore policy properties from request. * * @return None. */ inline void setPowerRestorePolicy(const std::shared_ptr& asyncResp, std::string_view policy) { BMCWEB_LOG_DEBUG("Set power restore policy."); std::string powerRestorePolicy = dbusPowerRestorePolicyFromRedfish(policy); if (powerRestorePolicy.empty()) { messages::propertyValueNotInList(asyncResp->res, policy, "PowerRestorePolicy"); return; } setDbusProperty( asyncResp, "PowerRestorePolicy", "xyz.openbmc_project.Settings", sdbusplus::message::object_path( "/xyz/openbmc_project/control/host0/power_restore_policy"), "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", powerRestorePolicy); } /** * @brief Retrieves provisioning status * * @param[in] asyncResp Shared pointer for completing asynchronous * calls. * * @return None. */ inline void getProvisioningStatus(const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Get OEM information."); sdbusplus::asio::getAllProperties( *crow::connections::systemBus, "xyz.openbmc_project.PFR.Manager", "/xyz/openbmc_project/pfr", "xyz.openbmc_project.PFR.Attributes", [asyncResp](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& propertiesList) { nlohmann::json& oemPFR = asyncResp->res.jsonValue["Oem"]["OpenBmc"]["FirmwareProvisioning"]; asyncResp->res.jsonValue["Oem"]["OpenBmc"]["@odata.type"] = "#OemComputerSystem.OpenBmc"; oemPFR["@odata.type"] = "#OemComputerSystem.FirmwareProvisioning"; if (ec) { BMCWEB_LOG_DEBUG("DBUS response error {}", ec); // not an error, don't have to have the interface oemPFR["ProvisioningStatus"] = "NotProvisioned"; return; } const bool* provState = nullptr; const bool* lockState = nullptr; const bool success = sdbusplus::unpackPropertiesNoThrow( dbus_utils::UnpackErrorPrinter(), propertiesList, "UfmProvisioned", provState, "UfmLocked", lockState); if (!success) { messages::internalError(asyncResp->res); return; } if ((provState == nullptr) || (lockState == nullptr)) { BMCWEB_LOG_DEBUG("Unable to get PFR attributes."); messages::internalError(asyncResp->res); return; } if (*provState) { if (*lockState) { oemPFR["ProvisioningStatus"] = "ProvisionedAndLocked"; } else { oemPFR["ProvisioningStatus"] = "ProvisionedButNotLocked"; } } else { oemPFR["ProvisioningStatus"] = "NotProvisioned"; } }); } /** * @brief Translate the PowerMode string to enum value * * @param[in] modeString PowerMode string to be translated * * @return PowerMode enum */ inline computer_system::PowerMode translatePowerModeString(const std::string& modeString) { using PowerMode = computer_system::PowerMode; if (modeString == "xyz.openbmc_project.Control.Power.Mode.PowerMode.Static") { return PowerMode::Static; } if (modeString == "xyz.openbmc_project.Control.Power.Mode.PowerMode.MaximumPerformance") { return PowerMode::MaximumPerformance; } if (modeString == "xyz.openbmc_project.Control.Power.Mode.PowerMode.PowerSaving") { return PowerMode::PowerSaving; } if (modeString == "xyz.openbmc_project.Control.Power.Mode.PowerMode.BalancedPerformance") { return PowerMode::BalancedPerformance; } if (modeString == "xyz.openbmc_project.Control.Power.Mode.PowerMode.EfficiencyFavorPerformance") { return PowerMode::EfficiencyFavorPerformance; } if (modeString == "xyz.openbmc_project.Control.Power.Mode.PowerMode.EfficiencyFavorPower") { return PowerMode::EfficiencyFavorPower; } if (modeString == "xyz.openbmc_project.Control.Power.Mode.PowerMode.OEM") { return PowerMode::OEM; } // Any other values would be invalid BMCWEB_LOG_ERROR("PowerMode value was not valid: {}", modeString); return PowerMode::Invalid; } inline void afterGetPowerMode(const std::shared_ptr& asyncResp, const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { if (ec) { BMCWEB_LOG_ERROR("DBUS response error on PowerMode GetAll: {}", ec); messages::internalError(asyncResp->res); return; } std::string powerMode; const std::vector* allowedModes = nullptr; const bool success = sdbusplus::unpackPropertiesNoThrow( dbus_utils::UnpackErrorPrinter(), properties, "PowerMode", powerMode, "AllowedPowerModes", allowedModes); if (!success) { messages::internalError(asyncResp->res); return; } nlohmann::json::array_t modeList; if (allowedModes == nullptr) { modeList.emplace_back("Static"); modeList.emplace_back("MaximumPerformance"); modeList.emplace_back("PowerSaving"); } else { for (const auto& aMode : *allowedModes) { computer_system::PowerMode modeValue = translatePowerModeString(aMode); if (modeValue == computer_system::PowerMode::Invalid) { messages::internalError(asyncResp->res); continue; } modeList.emplace_back(modeValue); } } asyncResp->res.jsonValue["PowerMode@Redfish.AllowableValues"] = modeList; BMCWEB_LOG_DEBUG("Current power mode: {}", powerMode); const computer_system::PowerMode modeValue = translatePowerModeString(powerMode); if (modeValue == computer_system::PowerMode::Invalid) { messages::internalError(asyncResp->res); return; } asyncResp->res.jsonValue["PowerMode"] = modeValue; } /** * @brief Retrieves system power mode * * @param[in] asyncResp Shared pointer for generating response message. * * @return None. */ inline void getPowerMode(const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Get power mode."); // Get Power Mode object path: constexpr std::array interfaces = { "xyz.openbmc_project.Control.Power.Mode"}; dbus::utility::getSubTree( "/", 0, interfaces, [asyncResp](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { if (ec) { BMCWEB_LOG_DEBUG("DBUS response error on Power.Mode GetSubTree {}", ec); // This is an optional D-Bus object so just return if // error occurs return; } if (subtree.empty()) { // As noted above, this is an optional interface so just return // if there is no instance found return; } if (subtree.size() > 1) { // More then one PowerMode object is not supported and is an // error BMCWEB_LOG_DEBUG( "Found more than 1 system D-Bus Power.Mode objects: {}", subtree.size()); messages::internalError(asyncResp->res); return; } if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) { BMCWEB_LOG_DEBUG("Power.Mode mapper error!"); messages::internalError(asyncResp->res); return; } const std::string& path = subtree[0].first; const std::string& service = subtree[0].second.begin()->first; if (service.empty()) { BMCWEB_LOG_DEBUG("Power.Mode service mapper error!"); messages::internalError(asyncResp->res); return; } // Valid Power Mode object found, now read the mode properties sdbusplus::asio::getAllProperties( *crow::connections::systemBus, service, path, "xyz.openbmc_project.Control.Power.Mode", [asyncResp](const boost::system::error_code& ec2, const dbus::utility::DBusPropertiesMap& properties) { afterGetPowerMode(asyncResp, ec2, properties); }); }); } /** * @brief Validate the specified mode is valid and return the PowerMode * name associated with that string * * @param[in] asyncResp Shared pointer for generating response message. * @param[in] modeValue String representing the desired PowerMode * * @return PowerMode value or empty string if mode is not valid */ inline std::string validatePowerMode(const std::shared_ptr& asyncResp, const nlohmann::json& modeValue) { using PowerMode = computer_system::PowerMode; std::string mode; if (modeValue == PowerMode::Static) { mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.Static"; } else if (modeValue == PowerMode::MaximumPerformance) { mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.MaximumPerformance"; } else if (modeValue == PowerMode::PowerSaving) { mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.PowerSaving"; } else if (modeValue == PowerMode::BalancedPerformance) { mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.BalancedPerformance"; } else if (modeValue == PowerMode::EfficiencyFavorPerformance) { mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.EfficiencyFavorPerformance"; } else if (modeValue == PowerMode::EfficiencyFavorPower) { mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.EfficiencyFavorPower"; } else { messages::propertyValueNotInList(asyncResp->res, modeValue.dump(), "PowerMode"); } return mode; } /** * @brief Sets system power mode. * * @param[in] asyncResp Shared pointer for generating response message. * @param[in] pmode System power mode from request. * * @return None. */ inline void setPowerMode(const std::shared_ptr& asyncResp, const std::string& pmode) { BMCWEB_LOG_DEBUG("Set power mode."); std::string powerMode = validatePowerMode(asyncResp, pmode); if (powerMode.empty()) { return; } // Get Power Mode object path: constexpr std::array interfaces = { "xyz.openbmc_project.Control.Power.Mode"}; dbus::utility::getSubTree( "/", 0, interfaces, [asyncResp, powerMode](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { if (ec) { BMCWEB_LOG_ERROR("DBUS response error on Power.Mode GetSubTree {}", ec); // This is an optional D-Bus object, but user attempted to patch messages::internalError(asyncResp->res); return; } if (subtree.empty()) { // This is an optional D-Bus object, but user attempted to patch messages::resourceNotFound(asyncResp->res, "ComputerSystem", "PowerMode"); return; } if (subtree.size() > 1) { // More then one PowerMode object is not supported and is an // error BMCWEB_LOG_DEBUG( "Found more than 1 system D-Bus Power.Mode objects: {}", subtree.size()); messages::internalError(asyncResp->res); return; } if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) { BMCWEB_LOG_DEBUG("Power.Mode mapper error!"); messages::internalError(asyncResp->res); return; } const std::string& path = subtree[0].first; const std::string& service = subtree[0].second.begin()->first; if (service.empty()) { BMCWEB_LOG_DEBUG("Power.Mode service mapper error!"); messages::internalError(asyncResp->res); return; } BMCWEB_LOG_DEBUG("Setting power mode({}) -> {}", powerMode, path); // Set the Power Mode property setDbusProperty(asyncResp, "PowerMode", service, path, "xyz.openbmc_project.Control.Power.Mode", "PowerMode", powerMode); }); } /** * @brief Translates watchdog timeout action DBUS property value to redfish. * * @param[in] dbusAction The watchdog timeout action in D-BUS. * * @return Returns as a string, the timeout action in Redfish terms. If * translation cannot be done, returns an empty string. */ inline std::string dbusToRfWatchdogAction(const std::string& dbusAction) { if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.None") { return "None"; } if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.HardReset") { return "ResetSystem"; } if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.PowerOff") { return "PowerDown"; } if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.PowerCycle") { return "PowerCycle"; } return ""; } /** *@brief Translates timeout action from Redfish to DBUS property value. * *@param[in] rfAction The timeout action in Redfish. * *@return Returns as a string, the time_out action as expected by DBUS. *If translation cannot be done, returns an empty string. */ inline std::string rfToDbusWDTTimeOutAct(const std::string& rfAction) { if (rfAction == "None") { return "xyz.openbmc_project.State.Watchdog.Action.None"; } if (rfAction == "PowerCycle") { return "xyz.openbmc_project.State.Watchdog.Action.PowerCycle"; } if (rfAction == "PowerDown") { return "xyz.openbmc_project.State.Watchdog.Action.PowerOff"; } if (rfAction == "ResetSystem") { return "xyz.openbmc_project.State.Watchdog.Action.HardReset"; } return ""; } /** * @brief Retrieves host watchdog timer properties over DBUS * * @param[in] asyncResp Shared pointer for completing asynchronous calls. * * @return None. */ inline void getHostWatchdogTimer(const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Get host watchodg"); sdbusplus::asio::getAllProperties( *crow::connections::systemBus, "xyz.openbmc_project.Watchdog", "/xyz/openbmc_project/watchdog/host0", "xyz.openbmc_project.State.Watchdog", [asyncResp](const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { if (ec) { // watchdog service is stopped BMCWEB_LOG_DEBUG("DBUS response error {}", ec); return; } BMCWEB_LOG_DEBUG("Got {} wdt prop.", properties.size()); nlohmann::json& hostWatchdogTimer = asyncResp->res.jsonValue["HostWatchdogTimer"]; // watchdog service is running/enabled hostWatchdogTimer["Status"]["State"] = "Enabled"; const bool* enabled = nullptr; const std::string* expireAction = nullptr; const bool success = sdbusplus::unpackPropertiesNoThrow( dbus_utils::UnpackErrorPrinter(), properties, "Enabled", enabled, "ExpireAction", expireAction); if (!success) { messages::internalError(asyncResp->res); return; } if (enabled != nullptr) { hostWatchdogTimer["FunctionEnabled"] = *enabled; } if (expireAction != nullptr) { std::string action = dbusToRfWatchdogAction(*expireAction); if (action.empty()) { messages::internalError(asyncResp->res); return; } hostWatchdogTimer["TimeoutAction"] = action; } }); } /** * @brief Sets Host WatchDog Timer properties. * * @param[in] asyncResp Shared pointer for generating response message. * @param[in] wdtEnable The WDTimer Enable value (true/false) from incoming * RF request. * @param[in] wdtTimeOutAction The WDT Timeout action, from incoming RF request. * * @return None. */ inline void setWDTProperties(const std::shared_ptr& asyncResp, const std::optional wdtEnable, const std::optional& wdtTimeOutAction) { BMCWEB_LOG_DEBUG("Set host watchdog"); if (wdtTimeOutAction) { std::string wdtTimeOutActStr = rfToDbusWDTTimeOutAct(*wdtTimeOutAction); // check if TimeOut Action is Valid if (wdtTimeOutActStr.empty()) { BMCWEB_LOG_DEBUG("Unsupported value for TimeoutAction: {}", *wdtTimeOutAction); messages::propertyValueNotInList(asyncResp->res, *wdtTimeOutAction, "TimeoutAction"); return; } setDbusProperty(asyncResp, "HostWatchdogTimer/TimeoutAction", "xyz.openbmc_project.Watchdog", sdbusplus::message::object_path( "/xyz/openbmc_project/watchdog/host0"), "xyz.openbmc_project.State.Watchdog", "ExpireAction", wdtTimeOutActStr); } if (wdtEnable) { setDbusProperty(asyncResp, "HostWatchdogTimer/FunctionEnabled", "xyz.openbmc_project.Watchdog", sdbusplus::message::object_path( "/xyz/openbmc_project/watchdog/host0"), "xyz.openbmc_project.State.Watchdog", "Enabled", *wdtEnable); } } /** * @brief Parse the Idle Power Saver properties into json * * @param[in] asyncResp Shared pointer for completing asynchronous calls. * @param[in] properties IPS property data from DBus. * * @return true if successful */ inline bool parseIpsProperties(const std::shared_ptr& asyncResp, const dbus::utility::DBusPropertiesMap& properties) { const bool* enabled = nullptr; const uint8_t* enterUtilizationPercent = nullptr; const uint64_t* enterDwellTime = nullptr; const uint8_t* exitUtilizationPercent = nullptr; const uint64_t* exitDwellTime = nullptr; const bool success = sdbusplus::unpackPropertiesNoThrow( dbus_utils::UnpackErrorPrinter(), properties, "Enabled", enabled, "EnterUtilizationPercent", enterUtilizationPercent, "EnterDwellTime", enterDwellTime, "ExitUtilizationPercent", exitUtilizationPercent, "ExitDwellTime", exitDwellTime); if (!success) { return false; } if (enabled != nullptr) { asyncResp->res.jsonValue["IdlePowerSaver"]["Enabled"] = *enabled; } if (enterUtilizationPercent != nullptr) { asyncResp->res.jsonValue["IdlePowerSaver"]["EnterUtilizationPercent"] = *enterUtilizationPercent; } if (enterDwellTime != nullptr) { const std::chrono::duration ms(*enterDwellTime); asyncResp->res.jsonValue["IdlePowerSaver"]["EnterDwellTimeSeconds"] = std::chrono::duration_cast>(ms) .count(); } if (exitUtilizationPercent != nullptr) { asyncResp->res.jsonValue["IdlePowerSaver"]["ExitUtilizationPercent"] = *exitUtilizationPercent; } if (exitDwellTime != nullptr) { const std::chrono::duration ms(*exitDwellTime); asyncResp->res.jsonValue["IdlePowerSaver"]["ExitDwellTimeSeconds"] = std::chrono::duration_cast>(ms) .count(); } return true; } /** * @brief Retrieves host watchdog timer properties over DBUS * * @param[in] asyncResp Shared pointer for completing asynchronous calls. * * @return None. */ inline void getIdlePowerSaver(const std::shared_ptr& asyncResp) { BMCWEB_LOG_DEBUG("Get idle power saver parameters"); // Get IdlePowerSaver object path: constexpr std::array interfaces = { "xyz.openbmc_project.Control.Power.IdlePowerSaver"}; dbus::utility::getSubTree( "/", 0, interfaces, [asyncResp](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { if (ec) { BMCWEB_LOG_ERROR( "DBUS response error on Power.IdlePowerSaver GetSubTree {}", ec); messages::internalError(asyncResp->res); return; } if (subtree.empty()) { // This is an optional interface so just return // if there is no instance found BMCWEB_LOG_DEBUG("No instances found"); return; } if (subtree.size() > 1) { // More then one PowerIdlePowerSaver object is not supported and // is an error BMCWEB_LOG_DEBUG("Found more than 1 system D-Bus " "Power.IdlePowerSaver objects: {}", subtree.size()); messages::internalError(asyncResp->res); return; } if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) { BMCWEB_LOG_DEBUG("Power.IdlePowerSaver mapper error!"); messages::internalError(asyncResp->res); return; } const std::string& path = subtree[0].first; const std::string& service = subtree[0].second.begin()->first; if (service.empty()) { BMCWEB_LOG_DEBUG("Power.IdlePowerSaver service mapper error!"); messages::internalError(asyncResp->res); return; } // Valid IdlePowerSaver object found, now read the current values sdbusplus::asio::getAllProperties( *crow::connections::systemBus, service, path, "xyz.openbmc_project.Control.Power.IdlePowerSaver", [asyncResp](const boost::system::error_code& ec2, const dbus::utility::DBusPropertiesMap& properties) { if (ec2) { BMCWEB_LOG_ERROR( "DBUS response error on IdlePowerSaver GetAll: {}", ec2); messages::internalError(asyncResp->res); return; } if (!parseIpsProperties(asyncResp, properties)) { messages::internalError(asyncResp->res); return; } }); }); BMCWEB_LOG_DEBUG("EXIT: Get idle power saver parameters"); } /** * @brief Sets Idle Power Saver properties. * * @param[in] asyncResp Shared pointer for generating response message. * @param[in] ipsEnable The IPS Enable value (true/false) from incoming * RF request. * @param[in] ipsEnterUtil The utilization limit to enter idle state. * @param[in] ipsEnterTime The time the utilization must be below ipsEnterUtil * before entering idle state. * @param[in] ipsExitUtil The utilization limit when exiting idle state. * @param[in] ipsExitTime The time the utilization must be above ipsExutUtil * before exiting idle state * * @return None. */ inline void setIdlePowerSaver(const std::shared_ptr& asyncResp, const std::optional ipsEnable, const std::optional ipsEnterUtil, const std::optional ipsEnterTime, const std::optional ipsExitUtil, const std::optional ipsExitTime) { BMCWEB_LOG_DEBUG("Set idle power saver properties"); // Get IdlePowerSaver object path: constexpr std::array interfaces = { "xyz.openbmc_project.Control.Power.IdlePowerSaver"}; dbus::utility::getSubTree( "/", 0, interfaces, [asyncResp, ipsEnable, ipsEnterUtil, ipsEnterTime, ipsExitUtil, ipsExitTime](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& subtree) { if (ec) { BMCWEB_LOG_ERROR( "DBUS response error on Power.IdlePowerSaver GetSubTree {}", ec); messages::internalError(asyncResp->res); return; } if (subtree.empty()) { // This is an optional D-Bus object, but user attempted to patch messages::resourceNotFound(asyncResp->res, "ComputerSystem", "IdlePowerSaver"); return; } if (subtree.size() > 1) { // More then one PowerIdlePowerSaver object is not supported and // is an error BMCWEB_LOG_DEBUG( "Found more than 1 system D-Bus Power.IdlePowerSaver objects: {}", subtree.size()); messages::internalError(asyncResp->res); return; } if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) { BMCWEB_LOG_DEBUG("Power.IdlePowerSaver mapper error!"); messages::internalError(asyncResp->res); return; } const std::string& path = subtree[0].first; const std::string& service = subtree[0].second.begin()->first; if (service.empty()) { BMCWEB_LOG_DEBUG("Power.IdlePowerSaver service mapper error!"); messages::internalError(asyncResp->res); return; } // Valid Power IdlePowerSaver object found, now set any values that // need to be updated if (ipsEnable) { setDbusProperty(asyncResp, "IdlePowerSaver/Enabled", service, path, "xyz.openbmc_project.Control.Power.IdlePowerSaver", "Enabled", *ipsEnable); } if (ipsEnterUtil) { setDbusProperty(asyncResp, "IdlePowerSaver/EnterUtilizationPercent", service, path, "xyz.openbmc_project.Control.Power.IdlePowerSaver", "EnterUtilizationPercent", *ipsEnterUtil); } if (ipsEnterTime) { // Convert from seconds into milliseconds for DBus const uint64_t timeMilliseconds = *ipsEnterTime * 1000; setDbusProperty(asyncResp, "IdlePowerSaver/EnterDwellTimeSeconds", service, path, "xyz.openbmc_project.Control.Power.IdlePowerSaver", "EnterDwellTime", timeMilliseconds); } if (ipsExitUtil) { setDbusProperty(asyncResp, "IdlePowerSaver/ExitUtilizationPercent", service, path, "xyz.openbmc_project.Control.Power.IdlePowerSaver", "ExitUtilizationPercent", *ipsExitUtil); } if (ipsExitTime) { // Convert from seconds into milliseconds for DBus const uint64_t timeMilliseconds = *ipsExitTime * 1000; setDbusProperty(asyncResp, "IdlePowerSaver/ExitDwellTimeSeconds", service, path, "xyz.openbmc_project.Control.Power.IdlePowerSaver", "ExitDwellTime", timeMilliseconds); } }); BMCWEB_LOG_DEBUG("EXIT: Set idle power saver parameters"); } inline void handleComputerSystemCollectionHead( crow::App& app, const crow::Request& req, const std::shared_ptr& asyncResp) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } asyncResp->res.addHeader( boost::beast::http::field::link, "; rel=describedby"); } inline void handleComputerSystemCollectionGet( crow::App& app, const crow::Request& req, const std::shared_ptr& asyncResp) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } asyncResp->res.addHeader( boost::beast::http::field::link, "; rel=describedby"); asyncResp->res.jsonValue["@odata.type"] = "#ComputerSystemCollection.ComputerSystemCollection"; asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Systems"; asyncResp->res.jsonValue["Name"] = "Computer System Collection"; nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; ifaceArray = nlohmann::json::array(); if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { asyncResp->res.jsonValue["Members@odata.count"] = 0; // Option currently returns no systems. TBD return; } asyncResp->res.jsonValue["Members@odata.count"] = 1; nlohmann::json::object_t system; system["@odata.id"] = boost::urls::format("/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); ifaceArray.emplace_back(std::move(system)); sdbusplus::asio::getProperty( *crow::connections::systemBus, "xyz.openbmc_project.Settings", "/xyz/openbmc_project/network/hypervisor", "xyz.openbmc_project.Network.SystemConfiguration", "HostName", [asyncResp](const boost::system::error_code& ec2, const std::string& /*hostName*/) { if (ec2) { return; } auto val = asyncResp->res.jsonValue.find("Members@odata.count"); if (val == asyncResp->res.jsonValue.end()) { BMCWEB_LOG_CRITICAL("Count wasn't found??"); return; } uint64_t* count = val->get_ptr(); if (count == nullptr) { BMCWEB_LOG_CRITICAL("Count wasn't found??"); return; } *count = *count + 1; BMCWEB_LOG_DEBUG("Hypervisor is available"); nlohmann::json& ifaceArray2 = asyncResp->res.jsonValue["Members"]; nlohmann::json::object_t hypervisor; hypervisor["@odata.id"] = "/redfish/v1/Systems/hypervisor"; ifaceArray2.emplace_back(std::move(hypervisor)); }); } /** * Function transceives data with dbus directly. */ inline void doNMI(const std::shared_ptr& asyncResp) { constexpr const char* serviceName = "xyz.openbmc_project.Control.Host.NMI"; constexpr const char* objectPath = "/xyz/openbmc_project/control/host0/nmi"; constexpr const char* interfaceName = "xyz.openbmc_project.Control.Host.NMI"; constexpr const char* method = "NMI"; crow::connections::systemBus->async_method_call( [asyncResp](const boost::system::error_code& ec) { if (ec) { BMCWEB_LOG_ERROR(" Bad D-Bus request error: {}", ec); messages::internalError(asyncResp->res); return; } messages::success(asyncResp->res); }, serviceName, objectPath, interfaceName, method); } inline void handleComputerSystemResetActionPost( crow::App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& systemName) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) { messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { // Option currently returns no systems. TBD messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } std::string resetType; if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType)) { return; } // Get the command and host vs. chassis std::string command; bool hostCommand = true; if ((resetType == "On") || (resetType == "ForceOn")) { command = "xyz.openbmc_project.State.Host.Transition.On"; hostCommand = true; } else if (resetType == "ForceOff") { command = "xyz.openbmc_project.State.Chassis.Transition.Off"; hostCommand = false; } else if (resetType == "ForceRestart") { command = "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot"; hostCommand = true; } else if (resetType == "GracefulShutdown") { command = "xyz.openbmc_project.State.Host.Transition.Off"; hostCommand = true; } else if (resetType == "GracefulRestart") { command = "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot"; hostCommand = true; } else if (resetType == "PowerCycle") { command = "xyz.openbmc_project.State.Host.Transition.Reboot"; hostCommand = true; } else if (resetType == "Nmi") { doNMI(asyncResp); return; } else { messages::actionParameterUnknown(asyncResp->res, "Reset", resetType); return; } sdbusplus::message::object_path statePath("/xyz/openbmc_project/state"); if (hostCommand) { setDbusProperty(asyncResp, "Reset", "xyz.openbmc_project.State.Host", statePath / "host0", "xyz.openbmc_project.State.Host", "RequestedHostTransition", command); } else { setDbusProperty(asyncResp, "Reset", "xyz.openbmc_project.State.Chassis", statePath / "chassis0", "xyz.openbmc_project.State.Chassis", "RequestedPowerTransition", command); } } inline void handleComputerSystemHead( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& /*systemName*/) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } asyncResp->res.addHeader( boost::beast::http::field::link, "; rel=describedby"); } inline void afterPortRequest( const std::shared_ptr& asyncResp, const boost::system::error_code& ec, const std::vector>& socketData) { if (ec) { BMCWEB_LOG_ERROR("DBUS response error {}", ec); messages::internalError(asyncResp->res); return; } for (const auto& data : socketData) { const std::string& socketPath = get<0>(data); const std::string& protocolName = get<1>(data); bool isProtocolEnabled = get<2>(data); nlohmann::json& dataJson = asyncResp->res.jsonValue["SerialConsole"]; dataJson[protocolName]["ServiceEnabled"] = isProtocolEnabled; // need to retrieve port number for // obmc-console-ssh service if (protocolName == "SSH") { getPortNumber(socketPath, [asyncResp, protocolName]( const boost::system::error_code& ec1, int portNumber) { if (ec1) { BMCWEB_LOG_ERROR("DBUS response error {}", ec1); messages::internalError(asyncResp->res); return; } nlohmann::json& dataJson1 = asyncResp->res.jsonValue["SerialConsole"]; dataJson1[protocolName]["Port"] = portNumber; }); } } } inline void handleComputerSystemGet(crow::App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& systemName) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { // Option currently returns no systems. TBD messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } if (systemName == "hypervisor") { handleHypervisorSystemGet(asyncResp); return; } if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) { messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } asyncResp->res.addHeader( boost::beast::http::field::link, "; rel=describedby"); asyncResp->res.jsonValue["@odata.type"] = "#ComputerSystem.v1_22_0.ComputerSystem"; asyncResp->res.jsonValue["Name"] = BMCWEB_REDFISH_SYSTEM_URI_NAME; asyncResp->res.jsonValue["Id"] = BMCWEB_REDFISH_SYSTEM_URI_NAME; asyncResp->res.jsonValue["SystemType"] = "Physical"; asyncResp->res.jsonValue["Description"] = "Computer System"; asyncResp->res.jsonValue["ProcessorSummary"]["Count"] = 0; asyncResp->res.jsonValue["MemorySummary"]["TotalSystemMemoryGiB"] = double(0); asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( "/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["Processors"]["@odata.id"] = boost::urls::format( "/redfish/v1/Systems/{}/Processors", BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["Memory"]["@odata.id"] = boost::urls::format( "/redfish/v1/Systems/{}/Memory", BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["Storage"]["@odata.id"] = boost::urls::format( "/redfish/v1/Systems/{}/Storage", BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["FabricAdapters"]["@odata.id"] = boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters", BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"]["target"] = boost::urls::format( "/redfish/v1/Systems/{}/Actions/ComputerSystem.Reset", BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res .jsonValue["Actions"]["#ComputerSystem.Reset"]["@Redfish.ActionInfo"] = boost::urls::format("/redfish/v1/Systems/{}/ResetActionInfo", BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["LogServices"]["@odata.id"] = boost::urls::format( "/redfish/v1/Systems/{}/LogServices", BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["Bios"]["@odata.id"] = boost::urls::format( "/redfish/v1/Systems/{}/Bios", BMCWEB_REDFISH_SYSTEM_URI_NAME); nlohmann::json::array_t managedBy; nlohmann::json& manager = managedBy.emplace_back(); manager["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME); asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); asyncResp->res.jsonValue["Status"]["Health"] = "OK"; asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; // Fill in SerialConsole info asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15; asyncResp->res.jsonValue["SerialConsole"]["IPMI"]["ServiceEnabled"] = true; asyncResp->res.jsonValue["SerialConsole"]["SSH"]["ServiceEnabled"] = true; asyncResp->res.jsonValue["SerialConsole"]["SSH"]["Port"] = 2200; asyncResp->res.jsonValue["SerialConsole"]["SSH"]["HotKeySequenceDisplay"] = "Press ~. to exit console"; getPortStatusAndPath(std::span{protocolToDBusForSystems}, std::bind_front(afterPortRequest, asyncResp)); 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"}); } getMainChassisId(asyncResp, [](const std::string& chassisId, const std::shared_ptr& aRsp) { nlohmann::json::array_t chassisArray; nlohmann::json& chassis = chassisArray.emplace_back(); chassis["@odata.id"] = boost::urls::format("/redfish/v1/Chassis/{}", chassisId); aRsp->res.jsonValue["Links"]["Chassis"] = std::move(chassisArray); }); getSystemLocationIndicatorActive(asyncResp); // TODO (Gunnar): Remove IndicatorLED after enough time has passed getIndicatorLedState(asyncResp); getComputerSystem(asyncResp); getHostState(asyncResp); getBootProperties(asyncResp); getBootProgress(asyncResp); getBootProgressLastStateTime(asyncResp); pcie_util::getPCIeDeviceList(asyncResp, nlohmann::json::json_pointer("/PCIeDevices")); getHostWatchdogTimer(asyncResp); getPowerRestorePolicy(asyncResp); getStopBootOnFault(asyncResp); getAutomaticRetryPolicy(asyncResp); getLastResetTime(asyncResp); if constexpr (BMCWEB_REDFISH_PROVISIONING_FEATURE) { getProvisioningStatus(asyncResp); } getTrustedModuleRequiredToBoot(asyncResp); getPowerMode(asyncResp); getIdlePowerSaver(asyncResp); } inline void handleComputerSystemPatch( crow::App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& systemName) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { // Option currently returns no systems. TBD messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) { messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } asyncResp->res.addHeader( boost::beast::http::field::link, "; rel=describedby"); std::optional locationIndicatorActive; std::optional indicatorLed; std::optional assetTag; std::optional powerRestorePolicy; std::optional powerMode; std::optional wdtEnable; std::optional wdtTimeOutAction; std::optional bootSource; std::optional bootType; std::optional bootEnable; std::optional bootAutomaticRetry; std::optional bootAutomaticRetryAttempts; std::optional bootTrustedModuleRequired; std::optional stopBootOnFault; std::optional ipsEnable; std::optional ipsEnterUtil; std::optional ipsEnterTime; std::optional ipsExitUtil; std::optional ipsExitTime; // clang-format off if (!json_util::readJsonPatch( req, asyncResp->res, "IndicatorLED", indicatorLed, "LocationIndicatorActive", locationIndicatorActive, "AssetTag", assetTag, "PowerRestorePolicy", powerRestorePolicy, "PowerMode", powerMode, "HostWatchdogTimer/FunctionEnabled", wdtEnable, "HostWatchdogTimer/TimeoutAction", wdtTimeOutAction, "Boot/BootSourceOverrideTarget", bootSource, "Boot/BootSourceOverrideMode", bootType, "Boot/BootSourceOverrideEnabled", bootEnable, "Boot/AutomaticRetryConfig", bootAutomaticRetry, "Boot/AutomaticRetryAttempts", bootAutomaticRetryAttempts, "Boot/TrustedModuleRequiredToBoot", bootTrustedModuleRequired, "Boot/StopBootOnFault", stopBootOnFault, "IdlePowerSaver/Enabled", ipsEnable, "IdlePowerSaver/EnterUtilizationPercent", ipsEnterUtil, "IdlePowerSaver/EnterDwellTimeSeconds", ipsEnterTime, "IdlePowerSaver/ExitUtilizationPercent", ipsExitUtil, "IdlePowerSaver/ExitDwellTimeSeconds", ipsExitTime)) { return; } // clang-format on asyncResp->res.result(boost::beast::http::status::no_content); if (assetTag) { setAssetTag(asyncResp, *assetTag); } if (wdtEnable || wdtTimeOutAction) { setWDTProperties(asyncResp, wdtEnable, wdtTimeOutAction); } if (bootSource || bootType || bootEnable) { setBootProperties(asyncResp, bootSource, bootType, bootEnable); } if (bootAutomaticRetry) { setAutomaticRetry(asyncResp, *bootAutomaticRetry); } if (bootAutomaticRetryAttempts) { setAutomaticRetryAttempts(asyncResp, bootAutomaticRetryAttempts.value()); } if (bootTrustedModuleRequired) { setTrustedModuleRequiredToBoot(asyncResp, *bootTrustedModuleRequired); } if (stopBootOnFault) { setStopBootOnFault(asyncResp, *stopBootOnFault); } if (locationIndicatorActive) { setSystemLocationIndicatorActive(asyncResp, *locationIndicatorActive); } // TODO (Gunnar): Remove IndicatorLED after enough time has // passed if (indicatorLed) { setIndicatorLedState(asyncResp, *indicatorLed); asyncResp->res.addHeader(boost::beast::http::field::warning, "299 - \"IndicatorLED is deprecated. Use " "LocationIndicatorActive instead.\""); } if (powerRestorePolicy) { setPowerRestorePolicy(asyncResp, *powerRestorePolicy); } if (powerMode) { setPowerMode(asyncResp, *powerMode); } if (ipsEnable || ipsEnterUtil || ipsEnterTime || ipsExitUtil || ipsExitTime) { setIdlePowerSaver(asyncResp, ipsEnable, ipsEnterUtil, ipsEnterTime, ipsExitUtil, ipsExitTime); } } inline void handleSystemCollectionResetActionHead( crow::App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& /*systemName*/) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } asyncResp->res.addHeader( boost::beast::http::field::link, "; rel=describedby"); } /** * @brief Translates allowed host transitions to redfish string * * @param[in] dbusAllowedHostTran The allowed host transition on dbus * @param[out] allowableValues The translated host transition(s) * * @return Emplaces corresponding Redfish translated value(s) in * allowableValues. If translation not possible, does nothing to * allowableValues. */ inline void dbusToRfAllowedHostTransitions(const std::string& dbusAllowedHostTran, nlohmann::json::array_t& allowableValues) { if (dbusAllowedHostTran == "xyz.openbmc_project.State.Host.Transition.On") { allowableValues.emplace_back(resource::ResetType::On); allowableValues.emplace_back(resource::ResetType::ForceOn); } else if (dbusAllowedHostTran == "xyz.openbmc_project.State.Host.Transition.Off") { allowableValues.emplace_back(resource::ResetType::GracefulShutdown); } else if (dbusAllowedHostTran == "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot") { allowableValues.emplace_back(resource::ResetType::GracefulRestart); } else if (dbusAllowedHostTran == "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot") { allowableValues.emplace_back(resource::ResetType::ForceRestart); } else { BMCWEB_LOG_WARNING("Unsupported host tran {}", dbusAllowedHostTran); } } inline void afterGetAllowedHostTransitions( const std::shared_ptr& asyncResp, const boost::system::error_code& ec, const std::vector& allowedHostTransitions) { nlohmann::json::array_t allowableValues; // Supported on all systems currently allowableValues.emplace_back(resource::ResetType::ForceOff); allowableValues.emplace_back(resource::ResetType::PowerCycle); allowableValues.emplace_back(resource::ResetType::Nmi); if (ec) { if ((ec.value() == boost::system::linux_error::bad_request_descriptor) || (ec.value() == boost::asio::error::basic_errors::host_unreachable)) { // Property not implemented so just return defaults BMCWEB_LOG_DEBUG("Property not available {}", ec); allowableValues.emplace_back(resource::ResetType::On); allowableValues.emplace_back(resource::ResetType::ForceOn); allowableValues.emplace_back(resource::ResetType::ForceRestart); allowableValues.emplace_back(resource::ResetType::GracefulRestart); allowableValues.emplace_back(resource::ResetType::GracefulShutdown); } else { BMCWEB_LOG_ERROR("DBUS response error {}", ec); messages::internalError(asyncResp->res); return; } } else { for (const std::string& transition : allowedHostTransitions) { BMCWEB_LOG_DEBUG("Found allowed host tran {}", transition); dbusToRfAllowedHostTransitions(transition, allowableValues); } } nlohmann::json::object_t parameter; parameter["Name"] = "ResetType"; parameter["Required"] = true; parameter["DataType"] = "String"; parameter["AllowableValues"] = std::move(allowableValues); nlohmann::json::array_t parameters; parameters.emplace_back(std::move(parameter)); asyncResp->res.jsonValue["Parameters"] = std::move(parameters); } inline void handleSystemCollectionResetActionGet( crow::App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& systemName) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { // Option currently returns no systems. TBD messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } if (systemName == "hypervisor") { handleHypervisorResetActionGet(asyncResp); return; } if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) { messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName); return; } asyncResp->res.addHeader( boost::beast::http::field::link, "; rel=describedby"); asyncResp->res.jsonValue["@odata.id"] = boost::urls::format("/redfish/v1/Systems/{}/ResetActionInfo", BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo"; asyncResp->res.jsonValue["Name"] = "Reset Action Info"; asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; // Look to see if system defines AllowedHostTransitions sdbusplus::asio::getProperty>( *crow::connections::systemBus, "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host", "AllowedHostTransitions", [asyncResp](const boost::system::error_code& ec, const std::vector& allowedHostTransitions) { afterGetAllowedHostTransitions(asyncResp, ec, allowedHostTransitions); }); } /** * SystemResetActionInfo derived class for delivering Computer Systems * ResetType AllowableValues using ResetInfo schema. */ inline void requestRoutesSystems(App& app) { BMCWEB_ROUTE(app, "/redfish/v1/Systems/") .privileges(redfish::privileges::headComputerSystemCollection) .methods(boost::beast::http::verb::head)( std::bind_front(handleComputerSystemCollectionHead, std::ref(app))); BMCWEB_ROUTE(app, "/redfish/v1/Systems/") .privileges(redfish::privileges::getComputerSystemCollection) .methods(boost::beast::http::verb::get)( std::bind_front(handleComputerSystemCollectionGet, std::ref(app))); BMCWEB_ROUTE(app, "/redfish/v1/Systems//") .privileges(redfish::privileges::headComputerSystem) .methods(boost::beast::http::verb::head)( std::bind_front(handleComputerSystemHead, std::ref(app))); BMCWEB_ROUTE(app, "/redfish/v1/Systems//") .privileges(redfish::privileges::getComputerSystem) .methods(boost::beast::http::verb::get)( std::bind_front(handleComputerSystemGet, std::ref(app))); BMCWEB_ROUTE(app, "/redfish/v1/Systems//") .privileges(redfish::privileges::patchComputerSystem) .methods(boost::beast::http::verb::patch)( std::bind_front(handleComputerSystemPatch, std::ref(app))); BMCWEB_ROUTE(app, "/redfish/v1/Systems//Actions/ComputerSystem.Reset/") .privileges(redfish::privileges::postComputerSystem) .methods(boost::beast::http::verb::post)(std::bind_front( handleComputerSystemResetActionPost, std::ref(app))); BMCWEB_ROUTE(app, "/redfish/v1/Systems//ResetActionInfo/") .privileges(redfish::privileges::headActionInfo) .methods(boost::beast::http::verb::head)(std::bind_front( handleSystemCollectionResetActionHead, std::ref(app))); BMCWEB_ROUTE(app, "/redfish/v1/Systems//ResetActionInfo/") .privileges(redfish::privileges::getActionInfo) .methods(boost::beast::http::verb::get)(std::bind_front( handleSystemCollectionResetActionGet, std::ref(app))); } } // namespace redfish