| #ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_SYSTEMS_H_ | |
| #define THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_SYSTEMS_H_ | |
| /* | |
| // 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. | |
| */ | |
| #include <array> | |
| #include <charconv> | |
| #include <chrono> // NOLINT | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <cstdio> | |
| #include <filesystem> // NOLINT | |
| #include <fstream> | |
| #include <functional> | |
| #include <iomanip> | |
| #include <ios> | |
| #include <memory> | |
| #include <optional> | |
| #include <ratio> // NOLINT | |
| #include <span> // NOLINT | |
| #include <sstream> | |
| #include <string> | |
| #include <string_view> | |
| #include <system_error> // NOLINT | |
| #include <tuple> | |
| #include <utility> | |
| #include <variant> | |
| #include <vector> | |
| #include "gbmc_settings/bios_settings.pb.h" | |
| #include "absl/functional/any_invocable.h" | |
| #include "absl/status/status.h" | |
| #include "absl/status/statusor.h" | |
| #include "absl/strings/match.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "boost/container/flat_map.hpp" // NOLINT | |
| #include "boost/system/error_code.hpp" // NOLINT | |
| #include "bm_config.h" | |
| #include "bmcweb_config.h" | |
| #include "app.hpp" | |
| #include "http_request.hpp" | |
| #include "logging.hpp" | |
| #include "utility.hpp" | |
| #include "async_resp.hpp" | |
| #include "dbus_utility.hpp" | |
| #include "error_messages.hpp" | |
| #include "query.hpp" | |
| #include "registries/privilege_registry.hpp" | |
| #include "boottime.hpp" | |
| #include "dbus_utils.hpp" | |
| #include "json_utils.hpp" | |
| #include "sw_utils.hpp" | |
| #include "system_utils.hpp" | |
| #include "time_utils.hpp" | |
| #include "health.hpp" | |
| #include "led.hpp" | |
| #include "pcie.hpp" | |
| #include "redfish_util.hpp" | |
| #include <nlohmann/json.hpp> | |
| #include "managed_store.hpp" | |
| #include "managed_store_types.hpp" | |
| #include "sdbusplus/message/native_types.hpp" | |
| #include "sdbusplus/unpack_properties.hpp" | |
| namespace redfish { | |
| static const std::array<std::pair<std::string_view, std::string_view>, 2> | |
| protocolToDBusForSystems{ | |
| {{"SSH", "obmc-console-ssh"}, {"IPMI", "phosphor-ipmi-net"}}}; | |
| constexpr std::array<std::string_view, 1> systemInterfaces{ | |
| {"xyz.openbmc_project.Inventory.Item.System"}}; | |
| constexpr std::array<std::string_view, 1> hostStateInterfaces{ | |
| {"xyz.openbmc_project.State.Host"}}; | |
| constexpr std::array<std::string_view, 1> chassisStateInterfaces{ | |
| {"xyz.openbmc_project.State.Chassis"}}; | |
| /** | |
| * @brief Updates the Functional State of DIMMs | |
| * | |
| * @param[in] aResp Shared pointer for completing asynchronous calls | |
| * @param[in] dimmState Dimm's Functional state, true/false | |
| * | |
| * @return None. | |
| */ | |
| inline void updateDimmProperties( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, 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 = | |
| aResp->res.jsonValue["MemorySummary"]["Status"]["State"]; | |
| if (prevMemSummary == "Disabled") { | |
| if (isDimmFunctional) { | |
| aResp->res.jsonValue["MemorySummary"]["Status"]["State"] = "Enabled"; | |
| } | |
| } | |
| } | |
| /* | |
| * @brief Update "ProcessorSummary" "Count" based on Cpu PresenceState | |
| * | |
| * @param[in] aResp Shared pointer for completing asynchronous calls | |
| * @param[in] cpuPresenceState CPU present or not | |
| * | |
| * @return None. | |
| */ | |
| inline void modifyCpuPresenceState( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, bool isCpuPresent) { | |
| BMCWEB_LOG_DEBUG << "Cpu Present: " << isCpuPresent; | |
| if (isCpuPresent) { | |
| nlohmann::json& procCount = | |
| aResp->res.jsonValue["ProcessorSummary"]["Count"]; | |
| auto* procCountPtr = procCount.get_ptr<nlohmann::json::number_integer_t*>(); | |
| if (procCountPtr != nullptr) { | |
| // shouldn't be possible to be nullptr | |
| *procCountPtr += 1; | |
| } | |
| } | |
| } | |
| /* | |
| * @brief Update "ProcessorSummary" "Status" "State" based on | |
| * CPU Functional State | |
| * | |
| * @param[in] aResp Shared pointer for completing asynchronous calls | |
| * @param[in] cpuFunctionalState is CPU functional true/false | |
| * | |
| * @return None. | |
| */ | |
| inline void modifyCpuFunctionalState( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, bool isCpuFunctional) { | |
| BMCWEB_LOG_DEBUG << "Cpu Functional: " << isCpuFunctional; | |
| const nlohmann::json& prevProcState = | |
| aResp->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) { | |
| aResp->res.jsonValue["ProcessorSummary"]["Status"]["State"] = "Enabled"; | |
| } | |
| } | |
| } | |
| inline void getProcessorProperties( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>& | |
| properties) { | |
| BMCWEB_LOG_DEBUG << "Got " << properties.size() << " Cpu properties."; | |
| // TODO: Get Model | |
| const uint16_t* coreCount = nullptr; | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "CoreCount", coreCount); | |
| if (!success) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (coreCount != nullptr) { | |
| nlohmann::json& coreCountJson = | |
| aResp->res.jsonValue["ProcessorSummary"]["CoreCount"]; | |
| uint64_t* coreCountJsonPtr = coreCountJson.get_ptr<uint64_t*>(); | |
| if (coreCountJsonPtr == nullptr) { | |
| coreCountJson = *coreCount; | |
| } else { | |
| *coreCountJsonPtr += *coreCount; | |
| } | |
| } | |
| } | |
| /* | |
| * @brief Get ProcessorSummary fields | |
| * | |
| * @param[in] aResp 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<bmcweb::AsyncResp>& aResp, | |
| const std::string& service, | |
| const std::string& path) { | |
| auto getCpuPresenceState = [aResp](const boost::system::error_code& ec3, | |
| const bool cpuPresenceCheck) { | |
| if (ec3) { | |
| BMCWEB_LOG_ERROR << "DBUS response error " << ec3; | |
| return; | |
| } | |
| modifyCpuPresenceState(aResp, cpuPresenceCheck); | |
| }; | |
| auto getCpuFunctionalState = [aResp](const boost::system::error_code& ec3, | |
| const bool cpuFunctionalCheck) { | |
| if (ec3) { | |
| BMCWEB_LOG_ERROR << "DBUS response error " << ec3; | |
| return; | |
| } | |
| modifyCpuFunctionalState(aResp, cpuFunctionalCheck); | |
| }; | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| // Get the Presence of CPU | |
| dbus_utils::getProperty<bool>(service, path, | |
| "xyz.openbmc_project.Inventory.Item", "Present", | |
| context, std::move(getCpuPresenceState)); | |
| // Get the Functional State | |
| dbus_utils::getProperty<bool>( | |
| service, path, "xyz.openbmc_project.State.Decorator.OperationalStatus", | |
| "Functional", context, std::move(getCpuFunctionalState)); | |
| managedStore::GetManagedObjectStore()->getAllProperties( | |
| service, path, "xyz.openbmc_project.Inventory.Item.Cpu", context, | |
| [aResp, service, path]( | |
| const boost::system::error_code& ec2, | |
| const dbus::utility::DBusPropertiesMap& properties) { | |
| if (ec2) { | |
| BMCWEB_LOG_ERROR << "DBUS response error " << ec2; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| getProcessorProperties(aResp, properties); | |
| }); | |
| } | |
| /* | |
| * @brief Retrieves computer system properties over dbus | |
| * | |
| * @param[in] aResp Shared pointer for completing asynchronous calls | |
| * @param[in] systemHealth Shared HealthPopulate pointer | |
| * | |
| * @return None. | |
| */ | |
| inline void getComputerSystem( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::shared_ptr<HealthPopulate>& systemHealth, | |
| const std::string& systemName) { | |
| BMCWEB_LOG_DEBUG << "Get available system components."; | |
| constexpr std::array<std::string_view, 5> 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", | |
| }; | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| "/xyz/openbmc_project/inventory", 0, interfaces, context, | |
| [aResp, systemHealth, context, systemName]( | |
| const boost::system::error_code& ec, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error"; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| // Iterate over all retrieved ObjectPaths. | |
| for (const std::pair< | |
| std::string, | |
| std::vector<std::pair<std::string, std::vector<std::string>>>>& | |
| object : subtree) { | |
| const std::string& path = object.first; | |
| BMCWEB_LOG_DEBUG << "Got path: " << path; | |
| const std::vector<std::pair<std::string, std::vector<std::string>>>& | |
| connectionNames = object.second; | |
| if (connectionNames.empty()) { | |
| continue; | |
| } | |
| auto memoryHealth = std::make_shared<HealthPopulate>( | |
| aResp, "/MemorySummary/Status"_json_pointer); | |
| auto cpuHealth = std::make_shared<HealthPopulate>( | |
| aResp, "/ProcessorSummary/Status"_json_pointer); | |
| #ifdef HEALTH_POPULATE | |
| systemHealth->children.emplace_back(memoryHealth); | |
| systemHealth->children.emplace_back(cpuHealth); | |
| #endif | |
| // This is not system, so check if it's cpu, dimm, UUID or | |
| // BiosVer | |
| const auto& connection = | |
| connectionNames[0]; // connectionNames not empty, so safe to | |
| // access [0] | |
| for (const auto& interfaceName : connection.second) { | |
| if (interfaceName == "xyz.openbmc_project.Inventory.Item.Dimm") { | |
| BMCWEB_LOG_DEBUG << "Found Dimm, now get its properties."; | |
| managedStore::GetManagedObjectStore()->getAllProperties( | |
| connection.first, path, | |
| "xyz.openbmc_project.Inventory.Item.Dimm", context, | |
| [aResp, service{connection.first}, path, context]( | |
| const boost::system::error_code& ec2, | |
| const dbus::utility::DBusPropertiesMap& properties) { | |
| if (ec2) { | |
| BMCWEB_LOG_ERROR << "DBUS response error " << ec2; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Got " << properties.size() | |
| << " Dimm properties."; | |
| if (properties.empty()) { | |
| dbus_utils::getProperty<bool>( | |
| service, path, | |
| "xyz.openbmc_project.State." | |
| "Decorator.OperationalStatus", | |
| "Functional", context, | |
| [aResp](const boost::system::error_code& ec3, | |
| bool dimmState) { | |
| if (ec3) { | |
| BMCWEB_LOG_ERROR << "DBUS response error " << ec3; | |
| return; | |
| } | |
| updateDimmProperties(aResp, dimmState); | |
| }); | |
| return; | |
| } | |
| const size_t* memorySizeInKB = nullptr; | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, | |
| "MemorySizeInKB", memorySizeInKB); | |
| if (!success) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (memorySizeInKB != nullptr) { | |
| nlohmann::json& totalMemory = | |
| aResp->res.jsonValue["MemorySummary"] | |
| ["TotalSystemMemoryGiB"]; | |
| const uint64_t* preValue = | |
| totalMemory.get_ptr<const uint64_t*>(); | |
| if (preValue == nullptr) { | |
| aResp->res.jsonValue["MemorySummary"] | |
| ["TotalSystemMemoryGiB"] = | |
| *memorySizeInKB / static_cast<size_t>(1024 * 1024); | |
| } else { | |
| aResp->res.jsonValue["MemorySummary"] | |
| ["TotalSystemMemoryGiB"] = | |
| *memorySizeInKB / static_cast<size_t>(1024 * 1024) + | |
| *preValue; | |
| } | |
| aResp->res.jsonValue["MemorySummary"]["Status"]["State"] = | |
| "Enabled"; | |
| } | |
| }); | |
| memoryHealth->inventory.emplace_back(path); | |
| } else if (interfaceName == | |
| "xyz.openbmc_project.Inventory.Item.Cpu") { | |
| BMCWEB_LOG_DEBUG << "Found Cpu, now get its properties."; | |
| getProcessorSummary(aResp, connection.first, path); | |
| cpuHealth->inventory.emplace_back(path); | |
| } else if (interfaceName == "xyz.openbmc_project.Common.UUID") { | |
| BMCWEB_LOG_DEBUG << "Found UUID, now get its properties."; | |
| managedStore::GetManagedObjectStore()->getAllProperties( | |
| connection.first, path, "xyz.openbmc_project.Common.UUID", | |
| context, | |
| [aResp](const boost::system::error_code& ec3, | |
| const dbus::utility::DBusPropertiesMap& properties) { | |
| if (ec3) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec3; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Got " << properties.size() | |
| << " UUID properties."; | |
| const std::string* uUID = nullptr; | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "UUID", | |
| uUID); | |
| if (!success) { | |
| messages::internalError(aResp->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; | |
| aResp->res.jsonValue["UUID"] = valueStr; | |
| } | |
| }); | |
| } else if (interfaceName == | |
| "xyz.openbmc_project.Inventory.Item.System") { | |
| managedStore::GetManagedObjectStore()->getAllProperties( | |
| connection.first, path, | |
| "xyz.openbmc_project.Inventory.Decorator.Asset", context, | |
| [aResp, context, systemName]( | |
| const boost::system::error_code& ec2, | |
| const dbus::utility::DBusPropertiesMap& propertiesList) { | |
| if (ec2) { | |
| // doesn't have to include this | |
| // interface | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Got " << propertiesList.size() | |
| << " properties for system"; | |
| 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(aResp->res); | |
| return; | |
| } | |
| if (partNumber != nullptr) { | |
| aResp->res.jsonValue["PartNumber"] = *partNumber; | |
| } | |
| if (serialNumber != nullptr) { | |
| aResp->res.jsonValue["SerialNumber"] = *serialNumber; | |
| } | |
| if (manufacturer != nullptr) { | |
| aResp->res.jsonValue["Manufacturer"] = *manufacturer; | |
| } | |
| if (model != nullptr) { | |
| aResp->res.jsonValue["Model"] = *model; | |
| } | |
| if (subModel != nullptr) { | |
| aResp->res.jsonValue["SubModel"] = *subModel; | |
| } | |
| // Grab the bios version | |
| sw_util::populateSoftwareInformation( | |
| aResp, sw_util::biosPurpose, "BiosVersion", false, | |
| sw_util::SwAssociation::System, systemName); | |
| }); | |
| dbus_utils::getProperty<std::string>( | |
| connection.first, path, | |
| "xyz.openbmc_project.Inventory.Decorator." | |
| "AssetTag", | |
| "AssetTag", context, | |
| [aResp](const boost::system::error_code& ec2, | |
| const std::string& value) { | |
| if (ec2) { | |
| // doesn't have to include this | |
| // interface | |
| return; | |
| } | |
| aResp->res.jsonValue["AssetTag"] = value; | |
| }); | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| inline void populateHostState(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& service, | |
| const std::string& path) { | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| dbus_utils::getProperty<std::string>( | |
| service, path, "xyz.openbmc_project.State.Host", "CurrentHostState", | |
| context, | |
| [aResp](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(aResp->res); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Host state: " << hostState; | |
| // Verify Host State | |
| if (hostState == "xyz.openbmc_project.State.Host.HostState.Running") { | |
| aResp->res.jsonValue["PowerState"] = "On"; | |
| aResp->res.jsonValue["Status"]["State"] = "Enabled"; | |
| } else if (hostState == | |
| "xyz.openbmc_project.State.Host.HostState.Quiesced") { | |
| aResp->res.jsonValue["PowerState"] = "On"; | |
| aResp->res.jsonValue["Status"]["State"] = "Quiesced"; | |
| } else if (hostState == | |
| "xyz.openbmc_project.State.Host.HostState.DiagnosticMode") { | |
| aResp->res.jsonValue["PowerState"] = "On"; | |
| aResp->res.jsonValue["Status"]["State"] = "InTest"; | |
| } else if (hostState == | |
| "xyz.openbmc_project.State.Host.HostState." | |
| "TransitioningToRunning") { | |
| aResp->res.jsonValue["PowerState"] = "PoweringOn"; | |
| aResp->res.jsonValue["Status"]["State"] = "Starting"; | |
| } else if (hostState == | |
| "xyz.openbmc_project.State.Host.HostState." | |
| "TransitioningToOff") { | |
| aResp->res.jsonValue["PowerState"] = "PoweringOff"; | |
| aResp->res.jsonValue["Status"]["State"] = "Disabled"; | |
| } else { | |
| aResp->res.jsonValue["PowerState"] = "Off"; | |
| aResp->res.jsonValue["Status"]["State"] = "Disabled"; | |
| } | |
| }); | |
| } | |
| /** | |
| * @brief Retrieves host state properties over dbus | |
| * | |
| * @param[in] aResp Shared pointer for completing asynchronous calls. | |
| * | |
| * @return None. | |
| */ | |
| inline void getHostState( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& systemPath, | |
| const dbus::utility::MapperGetSubTreeResponse& objects) { | |
| BMCWEB_LOG_DEBUG << "Get host information."; | |
| // For single host systems, the path is hardcoded to be | |
| // /xyz/openbmc_project/state/host0 and the service is | |
| // xyz.openbmc_project.State.Host | |
| if (systemPath.empty()) { | |
| populateHostState(aResp, "xyz.openbmc_project.State.Host", | |
| "/xyz/openbmc_project/state/host0"); | |
| return; | |
| } | |
| // Objects should never be empty in a multi-host system | |
| if (objects.empty()) { | |
| BMCWEB_LOG_ERROR << "Host has no Power Control Objects"; | |
| // Don't return error since there may be no x86-power-control. | |
| aResp->res.jsonValue["PowerState"] = "Unknown"; | |
| aResp->res.jsonValue["Status"]["State"] = "Unknown"; | |
| return; | |
| } | |
| // Map1: [key, val] -> [objectPath, Map2] | |
| // Map2: [key, val] -> [serviceName, list of interfaces] | |
| const std::string powerControlPath = objects[0].first; | |
| // This object path can have multiple services, we need the one that | |
| // contains "State.Host" | |
| for (const auto& serviceMapping : objects[0].second) { | |
| if (absl::StrContains(serviceMapping.first, "State.Host")) { | |
| populateHostState(aResp, serviceMapping.first, powerControlPath); | |
| return; | |
| } | |
| } | |
| BMCWEB_LOG_ERROR << "Could not find correct host Power Control Object"; | |
| messages::internalError(aResp->res); | |
| } | |
| /** | |
| * @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 if (dbusBootProgress == | |
| "xyz.openbmc_project.State.Boot.Progress.ProgressStages." | |
| "OEM") { | |
| rfBpLastState = "OEM"; | |
| } 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<bmcweb::AsyncResp>& aResp, | |
| 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(aResp->res, rfSource, | |
| "BootSourceTargetOverride"); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| /** | |
| * @brief Retrieves boot progress of the system | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getBootProgress(const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| dbus_utils::getProperty<std::string>( | |
| "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0", | |
| "xyz.openbmc_project.State.Boot.Progress", "BootProgress", context, | |
| [aResp](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; | |
| aResp->res.jsonValue["BootProgress"]["LastState"] = | |
| dbusToRfBootProgress(bootProgressStr); | |
| }); | |
| } | |
| /** | |
| * @brief Retrieves boot progress Last Update of the system | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getBootProgressLastStateTime( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| dbus_utils::getProperty<uint64_t>( | |
| "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0", | |
| "xyz.openbmc_project.State.Boot.Progress", "BootProgressLastUpdate", | |
| context, | |
| [aResp](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 | |
| aResp->res.jsonValue["BootProgress"]["LastStateTime"] = | |
| redfish::time_utils::getDateTimeUintUs(lastStateTime); | |
| }); | |
| } | |
| /** | |
| * @brief Retrieves boot progress OemLastState of the system | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getBootProgressOemLastState( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| /* Check the boot progress LastState */ | |
| managedStore::ManagedObjectStoreContext requestContext(aResp); | |
| dbus_utils::getProperty<std::string>( | |
| "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0", | |
| "xyz.openbmc_project.State.Boot.Progress", "BootProgress", requestContext, | |
| [aResp, requestContext](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; | |
| } | |
| /* The OemLastState only present if LastState is "OEM" */ | |
| if (dbusToRfBootProgress(bootProgressStr) == "OEM") { | |
| // TODO: Move to dbus_utils::getProperty once tuple support is added | |
| // b/293925308 | |
| dbus_utils::getProperty<std::tuple<uint64_t, std::vector<uint8_t>>>( | |
| "xyz.openbmc_project.State.Boot.Raw", | |
| "/xyz/openbmc_project/state/boot/raw0", | |
| "xyz.openbmc_project.State.Boot.Raw", "Value", requestContext, | |
| [aResp](const boost::system::error_code ec2, | |
| const std::tuple<uint64_t, std::vector<uint8_t>>& | |
| bootProgressOemLastState) { | |
| if (ec2) { | |
| return; | |
| } | |
| std::string bootProgressOemLastStateStr = "0x"; | |
| std::stringstream tmp; | |
| uint64_t bootProgress1st = 0; | |
| std::vector<uint8_t> bootProgress2nd; | |
| /* The OemLastState updated to the PostCodes Boot.Raw: | |
| * https://github.com/openbmc/phosphor-dbus-interfaces/blob/ | |
| * master/yaml/xyz/openbmc_project/State/Boot/Raw.interface.yaml#L6 | |
| * The type of the Boot.Raw value is struct[uint64,array[byte]] | |
| */ | |
| bootProgress1st = std::get<0>(bootProgressOemLastState); | |
| bootProgress2nd = std::get<1>(bootProgressOemLastState); | |
| /* Formatting boot progress code */ | |
| tmp << std::hex << std::setfill('0'); | |
| /* Set "OemLastState": to "" if there is no bootprogress code */ | |
| if (bootProgress1st == 0) { | |
| bootProgressOemLastStateStr = ""; | |
| } else { | |
| /* Get first bytes of the boot progress code */ | |
| tmp << std::setw(2) << static_cast<uint64_t>(bootProgress1st); | |
| if (!bootProgress2nd.empty()) { | |
| /* Get the last byte of the boot progress code */ | |
| for (uint8_t i : bootProgress2nd) { | |
| tmp << std::setw(2) << static_cast<uint16_t>(i); | |
| } | |
| } | |
| /* Padding, Ex: 0x1020304 to 0x01020304 */ | |
| if ((tmp.str().size() % 2) != 0) { | |
| bootProgressOemLastStateStr = "0x0"; | |
| } | |
| bootProgressOemLastStateStr += tmp.str(); | |
| } | |
| aResp->res.jsonValue["BootProgress"]["OemLastState"] = | |
| bootProgressOemLastStateStr; | |
| }); | |
| } | |
| }); | |
| } | |
| /** | |
| * @brief Retrieves boot override type over DBUS and fills out the response | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getBootOverrideType( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| dbus_utils::getProperty<std::string>( | |
| "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/boot", | |
| "xyz.openbmc_project.Control.Boot.Type", "BootType", context, | |
| [aResp](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; | |
| aResp->res.jsonValue["Boot"] | |
| ["BootSourceOverrideMode@Redfish.AllowableValues"] = | |
| nlohmann::json::array_t({"Legacy", "UEFI"}); | |
| auto rfType = dbusToRfBootType(bootType); | |
| if (rfType.empty()) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| aResp->res.jsonValue["Boot"]["BootSourceOverrideMode"] = rfType; | |
| }); | |
| } | |
| /** | |
| * @brief Retrieves boot override mode over DBUS and fills out the response | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getBootOverrideMode( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| dbus_utils::getProperty<std::string>( | |
| "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/boot", | |
| "xyz.openbmc_project.Control.Boot.Mode", "BootMode", context, | |
| [aResp](const boost::system::error_code& ec, | |
| const std::string& bootModeStr) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Boot mode: " << bootModeStr; | |
| aResp->res | |
| .jsonValue["Boot"] | |
| ["BootSourceOverrideTarget@Redfish.AllowableValues"] = { | |
| "None", "Pxe", "Hdd", "Cd", "Diags", "BiosSetup", "Usb"}; | |
| if (bootModeStr != | |
| "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular") { | |
| auto rfMode = dbusToRfBootMode(bootModeStr); | |
| if (!rfMode.empty()) { | |
| aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = rfMode; | |
| } | |
| } | |
| }); | |
| } | |
| /** | |
| * @brief Retrieves boot override source over DBUS | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getBootOverrideSource( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| dbus_utils::getProperty<std::string>( | |
| "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/boot", | |
| "xyz.openbmc_project.Control.Boot.Source", "BootSource", context, | |
| [aResp](const boost::system::error_code& ec, | |
| const std::string& bootSourceStr) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; | |
| if (ec.value() == boost::asio::error::host_unreachable) { | |
| return; | |
| } | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Boot source: " << bootSourceStr; | |
| auto rfSource = dbusToRfBootSource(bootSourceStr); | |
| if (!rfSource.empty()) { | |
| aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = rfSource; | |
| } | |
| // Get BootMode as BootSourceOverrideTarget is constructed | |
| // from both BootSource and BootMode | |
| getBootOverrideMode(aResp); | |
| }); | |
| } | |
| /** | |
| * @brief This functions abstracts all the logic behind getting a | |
| * "BootSourceOverrideEnabled" property from an overall boot override enable | |
| * state | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void processBootOverrideEnable( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const bool bootOverrideEnableSetting) { | |
| if (!bootOverrideEnableSetting) { | |
| aResp->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" | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| dbus_utils::getProperty<bool>( | |
| "xyz.openbmc_project.Settings", | |
| "/xyz/openbmc_project/control/host0/boot/one_time", | |
| "xyz.openbmc_project.Object.Enable", "Enabled", context, | |
| [aResp](const boost::system::error_code& ec, bool oneTimeSetting) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (oneTimeSetting) { | |
| aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = "Once"; | |
| } else { | |
| aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = | |
| "Continuous"; | |
| } | |
| }); | |
| } | |
| /** | |
| * @brief Retrieves boot override enable over DBUS | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getBootOverrideEnable( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| dbus_utils::getProperty<bool>( | |
| "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/boot", | |
| "xyz.openbmc_project.Object.Enable", "Enabled", context, | |
| [aResp](const boost::system::error_code& ec, | |
| const bool bootOverrideEnable) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; | |
| if (ec.value() == boost::asio::error::host_unreachable) { | |
| return; | |
| } | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| processBootOverrideEnable(aResp, bootOverrideEnable); | |
| }); | |
| } | |
| /** | |
| * @brief Retrieves mapping of systemName to its BIOS setting file path. | |
| * | |
| * @param[in] systemName The name of the system (e.g. "system", "system1", | |
| * "system2"). | |
| * | |
| * @return The BIOS setting file path as a string. | |
| */ | |
| inline absl::StatusOr<std::string> getBiosSettingPath( | |
| const std::string& systemName) { | |
| // Legacy oem_bios_setting file paths are for single host machines. | |
| if (systemName == "system") { | |
| return "/var/google/oem_bios_setting"; | |
| } | |
| if (systemName == "system1") { | |
| return "/var/google/system1/oem_bios_setting"; | |
| } | |
| if (systemName == "system2") { | |
| return "/var/google/system2/oem_bios_setting"; | |
| } | |
| return absl::InvalidArgumentError(absl::StrCat( | |
| "BIOS setting path not found: unsupported system: ", systemName)); | |
| } | |
| /** | |
| * @brief Retrieves boot order from oem_bios_setting file | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getBootOrder(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& biosSettingFile) { | |
| std::ifstream file(biosSettingFile, std::ios::binary | std::ios::ate); | |
| if (!file.is_open()) { | |
| BMCWEB_LOG_DEBUG << biosSettingFile << " not found."; | |
| return; | |
| } | |
| std::streamsize size = file.tellg(); | |
| file.seekg(0, std::ios::beg); | |
| if (size < 0 || size % 2 != 0) { | |
| BMCWEB_LOG_ERROR << "Invalid file size for " << biosSettingFile; | |
| return; | |
| } | |
| std::vector<char> buffer(static_cast<size_t>(size)); | |
| if (!file.read(buffer.data(), size)) { | |
| BMCWEB_LOG_ERROR << "Failed to read " << biosSettingFile; | |
| return; | |
| } | |
| std::vector<std::string> bootOrder; | |
| for (size_t i = 0; i < buffer.size(); i += 2) { | |
| // Assuming little-endian as per hexdump example | |
| uint16_t bootIndex = | |
| static_cast<uint16_t>((static_cast<uint8_t>(buffer[i + 1]) << 8) | | |
| static_cast<uint8_t>(buffer[i])); | |
| std::stringstream stream; | |
| stream << "Boot" << std::setw(4) << std::setfill('0') << std::hex | |
| << std::uppercase << bootIndex; | |
| bootOrder.push_back(stream.str()); | |
| } | |
| aResp->res.jsonValue["Boot"]["BootOrder"] = bootOrder; | |
| } | |
| /** | |
| * @brief Parses a UEFI boot option reference of the form "Boot" followed by | |
| * hex digits (e.g. "Boot0001") into its 16-bit boot index. | |
| * | |
| * @param[in] bootReference The boot option reference string. | |
| * | |
| * @return The boot index, or std::nullopt if the reference is malformed or | |
| * the index does not fit in 16 bits. | |
| */ | |
| inline std::optional<uint16_t> parseBootReference( | |
| std::string_view bootReference) { | |
| if (!bootReference.starts_with("Boot") || bootReference.length() <= 4) { | |
| BMCWEB_LOG_ERROR << "Invalid boot reference format: " << bootReference; | |
| return std::nullopt; | |
| } | |
| std::string_view hexPart = bootReference.substr(4); | |
| uint16_t bootIndex = 0; | |
| auto [ptr, ec] = std::from_chars( | |
| hexPart.data(), hexPart.data() + hexPart.size(), bootIndex, 16); | |
| if (ec != std::errc() || ptr != hexPart.data() + hexPart.size()) { | |
| BMCWEB_LOG_ERROR << "Invalid hex in boot reference: " << bootReference; | |
| return std::nullopt; | |
| } | |
| return bootIndex; | |
| } | |
| /** | |
| * @brief Sets boot order to oem-bios-setting file | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * @param[in] bootOrder The boot order from incoming RF request. | |
| * | |
| * @return None. | |
| */ | |
| inline void setBootOrder(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::vector<std::string>& bootOrder, | |
| const std::string& biosSettingFile) { | |
| // Ensure the parent directory of the setting file exists. | |
| std::error_code dirEc; | |
| std::filesystem::path parentDir = | |
| std::filesystem::path(biosSettingFile).parent_path(); | |
| std::filesystem::create_directories(parentDir, dirEc); | |
| if (dirEc) { | |
| BMCWEB_LOG_ERROR << "Failed to create directory " << parentDir.string() | |
| << ": " << dirEc.message(); | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| // Create a temporary file to ensure atomic write | |
| std::string tempFile = biosSettingFile + ".tmp"; | |
| std::ofstream file(tempFile, std::ios::binary | std::ios::trunc); | |
| if (!file.is_open()) { | |
| BMCWEB_LOG_ERROR << "Failed to open " << tempFile << " for writing."; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| for (const std::string& bootDevice : bootOrder) { | |
| std::optional<uint16_t> bootIndex = parseBootReference(bootDevice); | |
| if (!bootIndex.has_value()) { | |
| messages::propertyValueFormatError(aResp->res, bootDevice, "BootOrder"); | |
| file.close(); | |
| std::remove(tempFile.c_str()); | |
| return; | |
| } | |
| // Write as little-endian | |
| file.write(reinterpret_cast<const char*>(&*bootIndex), sizeof(*bootIndex)); | |
| } | |
| file.close(); | |
| // Atomically rename the temporary file to the final destination | |
| if (std::rename(tempFile.c_str(), biosSettingFile.c_str()) != 0) { | |
| BMCWEB_LOG_ERROR << "Failed to rename " << tempFile << " to " | |
| << biosSettingFile; | |
| messages::internalError(aResp->res); | |
| std::remove(tempFile.c_str()); | |
| return; | |
| } | |
| } | |
| // LINT.IfChange(boot_source_override_target) | |
| /** | |
| * @brief Converts BootSourceOverrideTarget enum to Redfish string | |
| * representation. | |
| */ | |
| inline std::string bootSourceOverrideTargetToString( | |
| gbmc::settings::BootSourceOverrideTarget target) { | |
| switch (target) { | |
| case gbmc::settings::BootSourceOverrideTarget:: | |
| BOOT_SOURCE_OVERRIDE_TARGET_UEFI_BOOT_NEXT: | |
| return "UefiBootNext"; | |
| case gbmc::settings::BootSourceOverrideTarget:: | |
| BOOT_SOURCE_OVERRIDE_TARGET_DIAGS: | |
| return "Diags"; | |
| case gbmc::settings::BootSourceOverrideTarget:: | |
| BOOT_SOURCE_OVERRIDE_TARGET_UEFI_HTTP: | |
| return "UefiHttp"; | |
| case gbmc::settings::BootSourceOverrideTarget:: | |
| BOOT_SOURCE_OVERRIDE_TARGET_UNSPECIFIED: | |
| default: | |
| return "None"; | |
| } | |
| } | |
| /** | |
| * @brief Converts Redfish string representation to BootSourceOverrideTarget | |
| * enum. | |
| */ | |
| inline std::optional<gbmc::settings::BootSourceOverrideTarget> | |
| bootSourceOverrideTargetFromString(std::string_view target) { | |
| if (target == "None") { | |
| return gbmc::settings::BootSourceOverrideTarget:: | |
| BOOT_SOURCE_OVERRIDE_TARGET_UNSPECIFIED; | |
| } | |
| if (target == "UefiBootNext") { | |
| return gbmc::settings::BootSourceOverrideTarget:: | |
| BOOT_SOURCE_OVERRIDE_TARGET_UEFI_BOOT_NEXT; | |
| } | |
| if (target == "Diags") { | |
| return gbmc::settings::BootSourceOverrideTarget:: | |
| BOOT_SOURCE_OVERRIDE_TARGET_DIAGS; | |
| } | |
| if (target == "UefiHttp") { | |
| return gbmc::settings::BootSourceOverrideTarget:: | |
| BOOT_SOURCE_OVERRIDE_TARGET_UEFI_HTTP; | |
| } | |
| return std::nullopt; | |
| } | |
| // LINT.ThenChange(//depot/google3/platforms/gbmc/g3_shared_libs/subprojects/gbmc_settings/bios_settings.proto:boot_source_override_target) | |
| // LINT.IfChange(boot_source_override_enabled) | |
| /** | |
| * @brief Converts BootSourceOverrideEnabled enum to Redfish string | |
| * representation. | |
| */ | |
| inline std::string bootSourceOverrideEnabledToString( | |
| gbmc::settings::BootSourceOverrideEnabled enabled) { | |
| switch (enabled) { | |
| case gbmc::settings::BootSourceOverrideEnabled:: | |
| BOOT_SOURCE_OVERRIDE_ENABLED_ONCE: | |
| return "Once"; | |
| case gbmc::settings::BootSourceOverrideEnabled:: | |
| BOOT_SOURCE_OVERRIDE_ENABLED_CONTINUOUS: | |
| return "Continuous"; | |
| case gbmc::settings::BootSourceOverrideEnabled:: | |
| BOOT_SOURCE_OVERRIDE_ENABLED_DISABLED: | |
| case gbmc::settings::BootSourceOverrideEnabled:: | |
| BOOT_SOURCE_OVERRIDE_ENABLED_UNSPECIFIED: | |
| default: | |
| return "Disabled"; | |
| } | |
| } | |
| /** | |
| * @brief Converts Redfish string representation to BootSourceOverrideEnabled | |
| * enum. | |
| */ | |
| inline std::optional<gbmc::settings::BootSourceOverrideEnabled> | |
| bootSourceOverrideEnabledFromString(std::string_view enabled) { | |
| if (enabled == "Disabled") { | |
| return gbmc::settings::BootSourceOverrideEnabled:: | |
| BOOT_SOURCE_OVERRIDE_ENABLED_DISABLED; | |
| } | |
| if (enabled == "Once") { | |
| return gbmc::settings::BootSourceOverrideEnabled:: | |
| BOOT_SOURCE_OVERRIDE_ENABLED_ONCE; | |
| } | |
| if (enabled == "Continuous") { | |
| return gbmc::settings::BootSourceOverrideEnabled:: | |
| BOOT_SOURCE_OVERRIDE_ENABLED_CONTINUOUS; | |
| } | |
| return std::nullopt; | |
| } | |
| // LINT.ThenChange(//depot/google3/platforms/gbmc/g3_shared_libs/subprojects/gbmc_settings/bios_settings.proto:boot_source_override_enabled) | |
| /** | |
| * @brief Retrieves BootNext property from SystemRegistry. | |
| */ | |
| inline void getBootNext( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::shared_ptr<milotic_tlbmc::SystemRegistry>& registry, | |
| const std::string& systemName) { | |
| absl::StatusOr<std::string> bootNext = registry->GetBootNext(systemName); | |
| if (!bootNext.ok()) { | |
| if (!absl::IsNotFound(bootNext.status())) { | |
| BMCWEB_LOG_ERROR << "Failed to get BootNext: " << bootNext.status(); | |
| } | |
| return; | |
| } | |
| aResp->res.jsonValue["Boot"]["BootNext"] = *bootNext; | |
| } | |
| /** | |
| * @brief Retrieves BootSourceOverrideTarget and allowable values from | |
| * SystemRegistry. | |
| */ | |
| inline void getBootSourceOverrideTarget( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::shared_ptr<milotic_tlbmc::SystemRegistry>& registry, | |
| const std::string& systemName) { | |
| // LINT.IfChange(boot_source_override_target_allowable_values) | |
| aResp->res | |
| .jsonValue["Boot"]["BootSourceOverrideTarget@Redfish.AllowableValues"] = { | |
| "None", "UefiBootNext", "Diags", "UefiHttp"}; | |
| // LINT.ThenChange(//depot/google3/platforms/gbmc/g3_shared_libs/subprojects/gbmc_settings/bios_settings.proto:boot_source_override_target) | |
| absl::StatusOr<gbmc::settings::BootSourceOverrideTarget> | |
| bootSourceOverrideTarget = | |
| registry->GetBootSourceOverrideTarget(systemName); | |
| if (!bootSourceOverrideTarget.ok()) { | |
| if (absl::IsNotFound(bootSourceOverrideTarget.status())) { | |
| // If not configured in storage, default to "None" per Redfish spec. | |
| aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = "None"; | |
| return; | |
| } | |
| BMCWEB_LOG_ERROR << "Failed to get BootSourceOverrideTarget: " | |
| << bootSourceOverrideTarget.status(); | |
| return; | |
| } | |
| aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = | |
| bootSourceOverrideTargetToString(*bootSourceOverrideTarget); | |
| } | |
| /** | |
| * @brief Retrieves BootSourceOverrideEnabled and allowable values from | |
| * SystemRegistry. | |
| */ | |
| inline void getBootSourceOverrideEnabled( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::shared_ptr<milotic_tlbmc::SystemRegistry>& registry, | |
| const std::string& systemName) { | |
| // LINT.IfChange(boot_source_override_enabled_allowable_values) | |
| aResp->res.jsonValue["Boot"] | |
| ["BootSourceOverrideEnabled@Redfish.AllowableValues"] = { | |
| "Disabled", "Once", "Continuous"}; | |
| // LINT.ThenChange(//depot/google3/platforms/gbmc/g3_shared_libs/subprojects/gbmc_settings/bios_settings.proto:boot_source_override_enabled) | |
| absl::StatusOr<gbmc::settings::BootSourceOverrideEnabled> | |
| bootSourceOverrideEnabled = | |
| registry->GetBootSourceOverrideEnabled(systemName); | |
| if (!bootSourceOverrideEnabled.ok()) { | |
| if (absl::IsNotFound(bootSourceOverrideEnabled.status())) { | |
| // If not configured in storage, default to "Disabled" per Redfish spec. | |
| aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = "Disabled"; | |
| return; | |
| } | |
| BMCWEB_LOG_ERROR << "Failed to get BootSourceOverrideEnabled: " | |
| << bootSourceOverrideEnabled.status(); | |
| return; | |
| } | |
| aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = | |
| bootSourceOverrideEnabledToString(*bootSourceOverrideEnabled); | |
| } | |
| /** | |
| * @brief Retrieves boot source override properties | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getBootProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& systemName, bool multiHost) { | |
| BMCWEB_LOG_DEBUG << "Get boot information."; | |
| std::shared_ptr<milotic_tlbmc::SystemRegistry> registry = | |
| managedStore::GetManagedObjectStore()->GetSystemRegistry(); | |
| // Legacy DBus boot override properties (single-host only). | |
| // BootSourceOverrideTarget and BootSourceOverrideEnabled are retrieved via | |
| // SystemRegistry below rather than DBus. | |
| if (!multiHost) { | |
| getBootOverrideType(aResp); | |
| } | |
| // Server 3.0 / tlBMC boot properties via SystemRegistry. | |
| if (registry) { | |
| aResp->res.jsonValue["Boot"]["BootOptions"]["@odata.id"] = | |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName, | |
| "Boot", "BootOptions"); | |
| getBootNext(aResp, registry, systemName); | |
| getBootSourceOverrideTarget(aResp, registry, systemName); | |
| getBootSourceOverrideEnabled(aResp, registry, systemName); | |
| } | |
| absl::StatusOr<std::string> biosSettingFile = getBiosSettingPath(systemName); | |
| if (!biosSettingFile.ok()) { | |
| return; | |
| } | |
| getBootOrder(aResp, *biosSettingFile); | |
| } | |
| inline void populateLastResetTime( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, const std::string& service, | |
| const std::string& path) { | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| dbus_utils::getProperty<uint64_t>( | |
| service, path, "xyz.openbmc_project.State.Chassis", "LastStateChangeTime", | |
| context, | |
| [aResp](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 | |
| aResp->res.jsonValue["LastResetTime"] = | |
| redfish::time_utils::getDateTimeUint(lastResetTimeStamp); | |
| }); | |
| } | |
| /** | |
| * @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] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getLastResetTime( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& systemPath, | |
| const dbus::utility::MapperGetSubTreeResponse& objects) { | |
| BMCWEB_LOG_DEBUG << "Getting System Last Reset Time"; | |
| // Single system hardcodes chassis power control object | |
| if (systemPath.empty()) { | |
| populateLastResetTime(aResp, "xyz.openbmc_project.State.Chassis", | |
| "/xyz/openbmc_project/state/chassis0"); | |
| return; | |
| } | |
| // Objects should never be empty in a multi-host system | |
| if (objects.empty()) { | |
| BMCWEB_LOG_ERROR << "Host has no Power Control Objects"; | |
| // Do not throw an error since x86-power-control may not be there. | |
| aResp->res.jsonValue["LastResetTime"] = "Unknown"; | |
| return; | |
| } | |
| // Map1: [key, val] -> [objectPath, Map2] | |
| // Map2: [key, val] -> [serviceName, list of interfaces] | |
| const std::string powerControlPath = objects[0].first; | |
| // This object path can have multiple services, we need the one that | |
| // contains "State.Host" | |
| for (const auto& serviceMapping : objects[0].second) { | |
| if (absl::StrContains(serviceMapping.first, "State.Chassis")) { | |
| populateLastResetTime(aResp, serviceMapping.first, powerControlPath); | |
| return; | |
| } | |
| } | |
| BMCWEB_LOG_ERROR << "Could not find correct chassis Power Control Object"; | |
| messages::internalError(aResp->res); | |
| } | |
| /** | |
| * @brief Retrieves Automatic Retry properties. Known on D-Bus as AutoReboot. | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getAutomaticRetry(const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| BMCWEB_LOG_DEBUG << "Get Automatic Retry policy"; | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| dbus_utils::getProperty<bool>( | |
| "xyz.openbmc_project.Settings", | |
| "/xyz/openbmc_project/control/host0/auto_reboot", | |
| "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", context, | |
| [aResp, context](const boost::system::error_code& ec, | |
| bool autoRebootEnabled) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Auto Reboot: " << autoRebootEnabled; | |
| if (autoRebootEnabled) { | |
| aResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = | |
| "RetryAttempts"; | |
| // If AutomaticRetry (AutoReboot) is enabled see how many | |
| // attempts are left | |
| dbus_utils::getProperty<uint32_t>( | |
| "xyz.openbmc_project.State.Host", | |
| "/xyz/openbmc_project/state/host0", | |
| "xyz.openbmc_project.Control.Boot.RebootAttempts", "AttemptsLeft", | |
| context, | |
| [aResp](const boost::system::error_code& ec2, | |
| const uint32_t autoRebootAttemptsLeft) { | |
| if (ec2) { | |
| BMCWEB_LOG_DEBUG << "D-BUS response error " << ec2; | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Auto Reboot Attempts Left: " | |
| << autoRebootAttemptsLeft; | |
| aResp->res | |
| .jsonValue["Boot"]["RemainingAutomaticRetryAttempts"] = | |
| autoRebootAttemptsLeft; | |
| }); | |
| } else { | |
| aResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = "Disabled"; | |
| } | |
| // Not on D-Bus. Hardcoded here: | |
| // https://github.com/openbmc/phosphor-state-manager/blob/1dbbef42675e94fb1f78edb87d6b11380260535a/meson_options.txt#L71 | |
| aResp->res.jsonValue["Boot"]["AutomaticRetryAttempts"] = 3; | |
| // "AutomaticRetryConfig" can be 3 values, Disabled, RetryAlways, | |
| // and RetryAttempts. OpenBMC only supports Disabled and | |
| // RetryAttempts. | |
| aResp->res.jsonValue["Boot"] | |
| ["AutomaticRetryConfig@Redfish.AllowableValues"] = { | |
| "Disabled", "RetryAttempts"}; | |
| }); | |
| } | |
| /** | |
| * @brief Retrieves power restore policy over DBUS. | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getPowerRestorePolicy( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| BMCWEB_LOG_DEBUG << "Get power restore policy"; | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| dbus_utils::getProperty<std::string>( | |
| "xyz.openbmc_project.Settings", | |
| "/xyz/openbmc_project/control/host0/power_restore_policy", | |
| "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", | |
| context, | |
| [aResp](const boost::system::error_code& ec, const std::string& policy) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; | |
| return; | |
| } | |
| const boost::container::flat_map<std::string, std::string> policyMaps = | |
| {{"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn", | |
| "AlwaysOn"}, | |
| {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy." | |
| "AlwaysOff", | |
| "AlwaysOff"}, | |
| {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore", | |
| "LastState"}, | |
| // Return `AlwaysOff` when power restore policy set to "None" | |
| {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.None", | |
| "AlwaysOff"}}; | |
| auto policyMapsIt = policyMaps.find(policy); | |
| if (policyMapsIt == policyMaps.end()) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| aResp->res.jsonValue["PowerRestorePolicy"] = policyMapsIt->second; | |
| }); | |
| } | |
| /** | |
| * @brief Get TrustedModuleRequiredToBoot property. Determines whether or not | |
| * TPM is required for booting the host. | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getTrustedModuleRequiredToBoot( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| BMCWEB_LOG_DEBUG << "Get TPM required to boot."; | |
| constexpr std::array<std::string_view, 1> interfaces = { | |
| "xyz.openbmc_project.Control.TPM.Policy"}; | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| "/", 0, interfaces, context, | |
| [aResp, context](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(aResp->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(aResp->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 | |
| dbus_utils::getProperty<bool>( | |
| serv, path, "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable", | |
| context, | |
| [aResp](const boost::system::error_code& ec2, bool tpmRequired) { | |
| if (ec2) { | |
| BMCWEB_LOG_DEBUG << "D-BUS response error on TPM.Policy Get" | |
| << ec2; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (tpmRequired) { | |
| aResp->res.jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = | |
| "Required"; | |
| } else { | |
| aResp->res.jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = | |
| "Disabled"; | |
| } | |
| }); | |
| }); | |
| } | |
| /** | |
| * @brief Set TrustedModuleRequiredToBoot property. Determines whether or not | |
| * TPM is required for booting the host. | |
| * | |
| * @param[in] aResp 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<bmcweb::AsyncResp>& aResp, const bool tpmRequired) { | |
| BMCWEB_LOG_DEBUG << "Set TrustedModuleRequiredToBoot."; | |
| constexpr std::array<std::string_view, 1> interfaces = { | |
| "xyz.openbmc_project.Control.TPM.Policy"}; | |
| managedStore::ManagedObjectStoreContext requestContext(aResp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| "/", 0, interfaces, requestContext, | |
| [aResp, tpmRequired]( | |
| const boost::system::error_code& ec, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error on TPM.Policy GetSubTree" | |
| << ec; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (subtree.empty()) { | |
| messages::propertyValueNotInList(aResp->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(aResp->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(aResp->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(aResp->res); | |
| return; | |
| } | |
| // Valid TPM Enable object found, now setting the value | |
| managedStore::GetManagedObjectStore() | |
| ->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec2) { | |
| if (ec2) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error: Set " | |
| "TrustedModuleRequiredToBoot" | |
| << ec2; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG | |
| << "Set TrustedModuleRequiredToBoot done."; | |
| }), | |
| serv, path, "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable", | |
| dbus::utility::DbusVariantType(tpmRequired)); | |
| }); | |
| } | |
| /** | |
| * @brief Sets boot properties into DBUS object(s). | |
| * | |
| * @param[in] aResp 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<bmcweb::AsyncResp>& aResp, | |
| const std::optional<std::string>& 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(aResp->res, *bootType, | |
| "BootSourceOverrideMode"); | |
| return; | |
| } | |
| // Act on validated parameters | |
| BMCWEB_LOG_DEBUG << "DBUS boot type: " << bootTypeStr; | |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; | |
| if (ec.value() == boost::asio::error::host_unreachable) { | |
| messages::resourceNotFound(aResp->res, "Set", "BootType"); | |
| return; | |
| } | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Boot type update done."; | |
| }), | |
| "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/boot", | |
| "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Control.Boot.Type", "BootType", | |
| dbus::utility::DbusVariantType(bootTypeStr)); | |
| } | |
| /** | |
| * @brief Sets boot properties into DBUS object(s). | |
| * | |
| * @param[in] aResp 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<bmcweb::AsyncResp>& aResp, | |
| const std::optional<std::string>& 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(aResp->res, *bootEnable, | |
| "BootSourceOverrideEnabled"); | |
| return; | |
| } | |
| // Act on validated parameters | |
| BMCWEB_LOG_DEBUG << "DBUS boot override enable: " << bootOverrideEnable; | |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec2) { | |
| if (ec2) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Boot override enable update done."; | |
| }), | |
| "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/boot", | |
| "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Object.Enable", "Enabled", | |
| dbus::utility::DbusVariantType(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; | |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Boot one_time update done."; | |
| }), | |
| "xyz.openbmc_project.Settings", | |
| "/xyz/openbmc_project/control/host0/boot/one_time", | |
| "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Object.Enable", "Enabled", | |
| dbus::utility::DbusVariantType(!bootOverridePersistent)); | |
| } | |
| /** | |
| * @brief Sets boot properties into DBUS object(s). | |
| * | |
| * @param[in] aResp 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<bmcweb::AsyncResp>& aResp, | |
| const std::optional<std::string>& 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(aResp, *bootSource, bootSourceStr, bootModeStr) != | |
| 0) { | |
| BMCWEB_LOG_DEBUG << "Invalid property value for BootSourceOverrideTarget: " | |
| << *bootSource; | |
| messages::propertyValueNotInList(aResp->res, *bootSource, | |
| "BootSourceTargetOverride"); | |
| return; | |
| } | |
| // Act on validated parameters | |
| BMCWEB_LOG_DEBUG << "DBUS boot source: " << bootSourceStr; | |
| BMCWEB_LOG_DEBUG << "DBUS boot mode: " << bootModeStr; | |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Boot source update done."; | |
| }), | |
| "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/boot", | |
| "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Control.Boot.Source", "BootSource", | |
| dbus::utility::DbusVariantType(bootSourceStr)); | |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Boot mode update done."; | |
| }), | |
| "xyz.openbmc_project.Settings", "/xyz/openbmc_project/control/host0/boot", | |
| "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Control.Boot.Mode", "BootMode", | |
| dbus::utility::DbusVariantType(bootModeStr)); | |
| } | |
| /** | |
| * @brief Sets BootSourceOverrideTarget in SystemRegistry. | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * @param[in] registry SystemRegistry instance. | |
| * @param[in] systemName System name. | |
| * @param[in] bootSource BootSourceOverrideTarget string from request. | |
| */ | |
| inline void setBootSourceOverrideTarget( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::shared_ptr<milotic_tlbmc::SystemRegistry>& registry, | |
| const std::string& systemName, const std::string& bootSource) { | |
| if (!registry) { | |
| BMCWEB_LOG_ERROR | |
| << "BootSourceOverrideTarget is only supported on systems with " | |
| "SystemRegistry"; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| std::optional<gbmc::settings::BootSourceOverrideTarget> target = | |
| bootSourceOverrideTargetFromString(bootSource); | |
| if (!target.has_value()) { | |
| messages::propertyValueNotInList(aResp->res, bootSource, | |
| "BootSourceOverrideTarget"); | |
| return; | |
| } | |
| absl::Status status = | |
| registry->SetBootSourceOverrideTarget(systemName, *target); | |
| if (!status.ok()) { | |
| BMCWEB_LOG_ERROR << "Failed to set BootSourceOverrideTarget: " << status; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| } | |
| /** | |
| * @brief Sets BootSourceOverrideEnabled in SystemRegistry. | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * @param[in] registry SystemRegistry instance. | |
| * @param[in] systemName System name. | |
| * @param[in] bootEnable BootSourceOverrideEnabled string from request. | |
| */ | |
| inline void setBootSourceOverrideEnabled( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::shared_ptr<milotic_tlbmc::SystemRegistry>& registry, | |
| const std::string& systemName, const std::string& bootEnable) { | |
| if (!registry) { | |
| BMCWEB_LOG_ERROR | |
| << "BootSourceOverrideEnabled is only supported on systems with " | |
| "SystemRegistry"; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| std::optional<gbmc::settings::BootSourceOverrideEnabled> enabled = | |
| bootSourceOverrideEnabledFromString(bootEnable); | |
| if (!enabled.has_value()) { | |
| messages::propertyValueNotInList(aResp->res, bootEnable, | |
| "BootSourceOverrideEnabled"); | |
| return; | |
| } | |
| absl::Status status = | |
| registry->SetBootSourceOverrideEnabled(systemName, *enabled); | |
| if (!status.ok()) { | |
| BMCWEB_LOG_ERROR << "Failed to set BootSourceOverrideEnabled: " << status; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| } | |
| /** | |
| * @brief Sets BootNext in SystemRegistry. | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * @param[in] registry SystemRegistry instance. | |
| * @param[in] systemName System name. | |
| * @param[in] bootNext BootNext string from request. | |
| */ | |
| inline void setBootNext( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::shared_ptr<milotic_tlbmc::SystemRegistry>& registry, | |
| const std::string& systemName, const std::string& bootNext) { | |
| if (!registry) { | |
| BMCWEB_LOG_ERROR | |
| << "BootNext is only supported on systems with SystemRegistry"; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| absl::Status status = registry->SetBootNext(systemName, bootNext); | |
| if (!status.ok()) { | |
| BMCWEB_LOG_ERROR << "Failed to set BootNext: " << status; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| } | |
| /** | |
| * @brief Sets Boot source override properties. | |
| * | |
| * @param[in] aResp 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. | |
| * @param[in] bootOrder The boot order from incoming RF request. | |
| * @param[in] bootNext The boot next from incoming RF request. | |
| * | |
| * @return None. | |
| */ | |
| inline void setBootProperties( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::optional<std::string>& bootSource, | |
| const std::optional<std::string>& bootType, | |
| const std::optional<std::string>& bootEnable, | |
| const std::optional<std::vector<std::string>>& bootOrder, | |
| const std::optional<std::string>& bootNext, | |
| const std::string& systemName, bool multiHost) { | |
| BMCWEB_LOG_DEBUG << "Set boot information."; | |
| std::shared_ptr<milotic_tlbmc::SystemRegistry> registry = | |
| managedStore::GetManagedObjectStore()->GetSystemRegistry(); | |
| // Only supported in single host | |
| if (!multiHost) { | |
| setBootType(aResp, bootType); | |
| } | |
| if (bootSource) { | |
| setBootSourceOverrideTarget(aResp, registry, systemName, *bootSource); | |
| } | |
| if (bootEnable) { | |
| setBootSourceOverrideEnabled(aResp, registry, systemName, *bootEnable); | |
| } | |
| if (bootNext) { | |
| setBootNext(aResp, registry, systemName, *bootNext); | |
| } | |
| if (bootOrder) { | |
| absl::StatusOr<std::string> biosSettingFile = | |
| getBiosSettingPath(systemName); | |
| if (!biosSettingFile.ok()) { | |
| BMCWEB_LOG_ERROR << biosSettingFile.status().message(); | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| setBootOrder(aResp, *bootOrder, *biosSettingFile); | |
| } | |
| } | |
| /** | |
| * @brief Sets AssetTag | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * @param[in] assetTag "AssetTag" from request. | |
| * | |
| * @return None. | |
| */ | |
| inline void setAssetTag(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& assetTag) { | |
| constexpr std::array<std::string_view, 1> interfaces = { | |
| "xyz.openbmc_project.Inventory.Item.System"}; | |
| managedStore::ManagedObjectStoreContext requestContext(aResp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| "/xyz/openbmc_project/inventory", 0, interfaces, requestContext, | |
| [aResp, 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(aResp->res); | |
| return; | |
| } | |
| if (subtree.empty()) { | |
| BMCWEB_LOG_DEBUG << "Can't find system D-Bus object!"; | |
| messages::internalError(aResp->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(aResp->res); | |
| return; | |
| } | |
| if (subtree[0].first.empty() || subtree[0].second.size() != 1) { | |
| BMCWEB_LOG_DEBUG << "Asset Tag Set mapper error!"; | |
| messages::internalError(aResp->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(aResp->res); | |
| return; | |
| } | |
| managedStore::GetManagedObjectStore() | |
| ->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec2) { | |
| if (ec2) { | |
| BMCWEB_LOG_DEBUG | |
| << "D-Bus response error on AssetTag Set " << ec2; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| }), | |
| service, path, "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Inventory.Decorator.AssetTag", "AssetTag", | |
| dbus::utility::DbusVariantType(assetTag)); | |
| }); | |
| } | |
| /** | |
| * @brief Sets automaticRetry (Auto Reboot) | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * @param[in] automaticRetryConfig "AutomaticRetryConfig" from request. | |
| * | |
| * @return None. | |
| */ | |
| inline void setAutomaticRetry(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| 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(aResp->res, automaticRetryConfig, | |
| "AutomaticRetryConfig"); | |
| return; | |
| } | |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec) { | |
| if (ec) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| }), | |
| "xyz.openbmc_project.Settings", | |
| "/xyz/openbmc_project/control/host0/auto_reboot", | |
| "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", | |
| dbus::utility::DbusVariantType(autoRebootEnabled)); | |
| } | |
| /** | |
| * @brief Sets power restore policy properties. | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * @param[in] policy power restore policy properties from request. | |
| * | |
| * @return None. | |
| */ | |
| inline void setPowerRestorePolicy( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& policy) { | |
| BMCWEB_LOG_DEBUG << "Set power restore policy."; | |
| const boost::container::flat_map<std::string, std::string> policyMaps = { | |
| {"AlwaysOn", | |
| "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn"}, | |
| {"AlwaysOff", | |
| "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOff"}, | |
| {"LastState", | |
| "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore"}}; | |
| std::string powerRestorPolicy; | |
| auto policyMapsIt = policyMaps.find(policy); | |
| if (policyMapsIt == policyMaps.end()) { | |
| messages::propertyValueNotInList(aResp->res, policy, "PowerRestorePolicy"); | |
| return; | |
| } | |
| powerRestorPolicy = policyMapsIt->second; | |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec) { | |
| if (ec) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| }), | |
| "xyz.openbmc_project.Settings", | |
| "/xyz/openbmc_project/control/host0/power_restore_policy", | |
| "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", | |
| dbus::utility::DbusVariantType(powerRestorPolicy)); | |
| } | |
| #ifdef BMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE | |
| /** | |
| * @brief Retrieves provisioning status | |
| * | |
| * @param[in] aResp Shared pointer for completing asynchronous calls. | |
| * | |
| * @return None. | |
| */ | |
| inline void getProvisioningStatus(std::shared_ptr<bmcweb::AsyncResp> aResp) { | |
| BMCWEB_LOG_DEBUG << "Get OEM information."; | |
| managedStore::ManagedObjectStoreContext context(asyncResp); | |
| managedStore::GetManagedObjectStore()->getAllProperties( | |
| "xyz.openbmc_project.PFR.Manager", "/xyz/openbmc_project/pfr", | |
| "xyz.openbmc_project.PFR.Attributes", context, | |
| [aResp](const boost::system::error_code& ec, | |
| const dbus::utility::DBusPropertiesMap& propertiesList) { | |
| nlohmann::json& oemPFR = | |
| aResp->res.jsonValue["Oem"]["OpenBmc"]["FirmwareProvisioning"]; | |
| aResp->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(aResp->res); | |
| return; | |
| } | |
| if ((provState == nullptr) || (lockState == nullptr)) { | |
| BMCWEB_LOG_DEBUG << "Unable to get PFR attributes."; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (*provState == true) { | |
| if (*lockState == true) { | |
| oemPFR["ProvisioningStatus"] = "ProvisionedAndLocked"; | |
| } else { | |
| oemPFR["ProvisioningStatus"] = "ProvisionedButNotLocked"; | |
| } | |
| } else { | |
| oemPFR["ProvisioningStatus"] = "NotProvisioned"; | |
| } | |
| }); | |
| } | |
| #endif | |
| /** | |
| * @brief Translate the PowerMode to a response message. | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * @param[in] modeValue PowerMode value to be translated | |
| * | |
| * @return None. | |
| */ | |
| inline void translatePowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& modeValue) { | |
| if (modeValue == "xyz.openbmc_project.Control.Power.Mode.PowerMode.Static") { | |
| aResp->res.jsonValue["PowerMode"] = "Static"; | |
| } else if (modeValue == | |
| "xyz.openbmc_project.Control.Power.Mode.PowerMode." | |
| "MaximumPerformance") { | |
| aResp->res.jsonValue["PowerMode"] = "MaximumPerformance"; | |
| } else if (modeValue == | |
| "xyz.openbmc_project.Control.Power.Mode.PowerMode.PowerSaving") { | |
| aResp->res.jsonValue["PowerMode"] = "PowerSaving"; | |
| } else if (modeValue == | |
| "xyz.openbmc_project.Control.Power.Mode.PowerMode.OEM") { | |
| aResp->res.jsonValue["PowerMode"] = "OEM"; | |
| } else { | |
| // Any other values would be invalid | |
| BMCWEB_LOG_DEBUG << "PowerMode value was not valid: " << modeValue; | |
| messages::internalError(aResp->res); | |
| } | |
| } | |
| /** | |
| * @brief Retrieves system power mode | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * | |
| * @return None. | |
| */ | |
| inline void getPowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| BMCWEB_LOG_DEBUG << "Get power mode."; | |
| // Get Power Mode object path: | |
| constexpr std::array<std::string_view, 1> interfaces = { | |
| "xyz.openbmc_project.Control.Power.Mode"}; | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| "/", 0, interfaces, context, | |
| [aResp, context](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(aResp->res); | |
| return; | |
| } | |
| if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) { | |
| BMCWEB_LOG_DEBUG << "Power.Mode mapper error!"; | |
| messages::internalError(aResp->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(aResp->res); | |
| return; | |
| } | |
| // Valid Power Mode object found, now read the current value | |
| dbus_utils::getProperty<std::string>( | |
| service, path, "xyz.openbmc_project.Control.Power.Mode", | |
| "PowerMode", context, | |
| [aResp](const boost::system::error_code& ec2, | |
| const std::string& pmode) { | |
| if (ec2) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error on PowerMode Get: " | |
| << ec2; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| aResp->res.jsonValue["PowerMode@Redfish.AllowableValues"] = { | |
| "Static", "MaximumPerformance", "PowerSaving"}; | |
| BMCWEB_LOG_DEBUG << "Current power mode: " << pmode; | |
| translatePowerMode(aResp, pmode); | |
| }); | |
| }); | |
| } | |
| /** | |
| * @brief Validate the specified mode is valid and return the PowerMode | |
| * name associated with that string | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * @param[in] modeString String representing the desired PowerMode | |
| * | |
| * @return PowerMode value or empty string if mode is not valid | |
| */ | |
| inline std::string validatePowerMode( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& modeString) { | |
| std::string mode; | |
| if (modeString == "Static") { | |
| mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.Static"; | |
| } else if (modeString == "MaximumPerformance") { | |
| mode = | |
| "xyz.openbmc_project.Control.Power.Mode.PowerMode.MaximumPerformance"; | |
| } else if (modeString == "PowerSaving") { | |
| mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.PowerSaving"; | |
| } else { | |
| messages::propertyValueNotInList(aResp->res, modeString, "PowerMode"); | |
| } | |
| return mode; | |
| } | |
| /** | |
| * @brief Sets system power mode. | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * @param[in] pmode System power mode from request. | |
| * | |
| * @return None. | |
| */ | |
| inline void setPowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& pmode) { | |
| BMCWEB_LOG_DEBUG << "Set power mode."; | |
| std::string powerMode = validatePowerMode(aResp, pmode); | |
| if (powerMode.empty()) { | |
| return; | |
| } | |
| // Get Power Mode object path: | |
| constexpr std::array<std::string_view, 1> interfaces = { | |
| "xyz.openbmc_project.Control.Power.Mode"}; | |
| managedStore::ManagedObjectStoreContext requestContext(aResp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| "/", 0, interfaces, requestContext, | |
| [aResp, powerMode]( | |
| 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, but user attempted to patch | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (subtree.empty()) { | |
| // This is an optional D-Bus object, but user attempted to patch | |
| messages::resourceNotFound(aResp->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(aResp->res); | |
| return; | |
| } | |
| if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) { | |
| BMCWEB_LOG_DEBUG << "Power.Mode mapper error!"; | |
| messages::internalError(aResp->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(aResp->res); | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Setting power mode(" << powerMode << ") -> " | |
| << path; | |
| // Set the Power Mode property | |
| managedStore::GetManagedObjectStore() | |
| ->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec2) { | |
| if (ec2) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| }), | |
| service, path, "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Control.Power.Mode", "PowerMode", | |
| dbus::utility::DbusVariantType(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] aResp Shared pointer for completing asynchronous calls. | |
| * | |
| * @return None. | |
| */ | |
| inline void getHostWatchdogTimer( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| BMCWEB_LOG_DEBUG << "Get host watchodg"; | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| managedStore::GetManagedObjectStore()->getAllProperties( | |
| "xyz.openbmc_project.Watchdog", "/xyz/openbmc_project/watchdog/host0", | |
| "xyz.openbmc_project.State.Watchdog", context, | |
| [aResp](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 " << properties.size() << " wdt prop."; | |
| nlohmann::json& hostWatchdogTimer = | |
| aResp->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(aResp->res); | |
| return; | |
| } | |
| if (enabled != nullptr) { | |
| hostWatchdogTimer["FunctionEnabled"] = *enabled; | |
| } | |
| if (expireAction != nullptr) { | |
| std::string action = dbusToRfWatchdogAction(*expireAction); | |
| if (action.empty()) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| hostWatchdogTimer["TimeoutAction"] = action; | |
| } | |
| }); | |
| } | |
| /** | |
| * @brief Sets Host WatchDog Timer properties. | |
| * | |
| * @param[in] aResp 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<bmcweb::AsyncResp>& aResp, | |
| const std::optional<bool> wdtEnable, | |
| const std::optional<std::string>& 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(aResp->res, *wdtTimeOutAction, | |
| "TimeoutAction"); | |
| return; | |
| } | |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| }), | |
| "xyz.openbmc_project.Watchdog", "/xyz/openbmc_project/watchdog/host0", | |
| "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.State.Watchdog", "ExpireAction", | |
| dbus::utility::DbusVariantType(wdtTimeOutActStr)); | |
| } | |
| if (wdtEnable) { | |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| }), | |
| "xyz.openbmc_project.Watchdog", "/xyz/openbmc_project/watchdog/host0", | |
| "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.State.Watchdog", "Enabled", | |
| dbus::utility::DbusVariantType(*wdtEnable)); | |
| } | |
| } | |
| /** | |
| * @brief Parse the Idle Power Saver properties into json | |
| * | |
| * @param[in] aResp 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<bmcweb::AsyncResp>& aResp, | |
| 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) { | |
| aResp->res.jsonValue["IdlePowerSaver"]["Enabled"] = *enabled; | |
| } | |
| if (enterUtilizationPercent != nullptr) { | |
| aResp->res.jsonValue["IdlePowerSaver"]["EnterUtilizationPercent"] = | |
| *enterUtilizationPercent; | |
| } | |
| if (enterDwellTime != nullptr) { | |
| const std::chrono::duration<uint64_t, std::milli> ms(*enterDwellTime); | |
| aResp->res.jsonValue["IdlePowerSaver"]["EnterDwellTimeSeconds"] = | |
| std::chrono::duration_cast<std::chrono::duration<uint64_t>>(ms).count(); | |
| } | |
| if (exitUtilizationPercent != nullptr) { | |
| aResp->res.jsonValue["IdlePowerSaver"]["ExitUtilizationPercent"] = | |
| *exitUtilizationPercent; | |
| } | |
| if (exitDwellTime != nullptr) { | |
| const std::chrono::duration<uint64_t, std::milli> ms(*exitDwellTime); | |
| aResp->res.jsonValue["IdlePowerSaver"]["ExitDwellTimeSeconds"] = | |
| std::chrono::duration_cast<std::chrono::duration<uint64_t>>(ms).count(); | |
| } | |
| return true; | |
| } | |
| /** | |
| * @brief Retrieves host watchdog timer properties over DBUS | |
| * | |
| * @param[in] aResp Shared pointer for completing asynchronous calls. | |
| * | |
| * @return None. | |
| */ | |
| inline void getIdlePowerSaver(const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| BMCWEB_LOG_DEBUG << "Get idle power saver parameters"; | |
| // Get IdlePowerSaver object path: | |
| constexpr std::array<std::string_view, 1> interfaces = { | |
| "xyz.openbmc_project.Control.Power.IdlePowerSaver"}; | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| "/", 0, interfaces, context, | |
| [aResp, context](const boost::system::error_code& ec, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG | |
| << "DBUS response error on Power.IdlePowerSaver GetSubTree " | |
| << ec; | |
| messages::internalError(aResp->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(aResp->res); | |
| return; | |
| } | |
| if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) { | |
| BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver mapper error!"; | |
| messages::internalError(aResp->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(aResp->res); | |
| return; | |
| } | |
| // Valid IdlePowerSaver object found, now read the current values | |
| managedStore::GetManagedObjectStore()->getAllProperties( | |
| service, path, "xyz.openbmc_project.Control.Power.IdlePowerSaver", | |
| context, | |
| [aResp](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(aResp->res); | |
| return; | |
| } | |
| if (!parseIpsProperties(aResp, properties)) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| }); | |
| }); | |
| BMCWEB_LOG_DEBUG << "EXIT: Get idle power saver parameters"; | |
| } | |
| /** | |
| * @brief Retrieve the systemName suffix | |
| * | |
| * @param[in] systemName String of systemName, assume it's "systemX" | |
| * | |
| * @return string of the suffix, empty string if it's invalid or empty | |
| */ | |
| inline std::string getSystemSuffix(const std::string& systemName) { | |
| constexpr std::string_view systemPrefix = "system"; | |
| if (systemName.rfind(systemPrefix, 0) != 0) { | |
| // The systemName did not start with the prefix - return empty | |
| return ""; | |
| } | |
| // Extract the part after "system" - empty string if there is no suffix | |
| return systemName.substr(systemPrefix.size()); | |
| } | |
| /** | |
| * @brief Retrieves BM readiness signal from the file system | |
| * | |
| * @param[in] aResp Shared pointer for completing asynchronous calls. | |
| * @param[in] systemName String of systemName | |
| * | |
| * @return None. | |
| */ | |
| inline void getBMReady(const std::shared_ptr<bmcweb::AsyncResp>& aResp) { | |
| BMCWEB_LOG_DEBUG << "Get BMReady signal"; | |
| nlohmann::json& googleBM = | |
| aResp->res.jsonValue["Oem"]["Google"]["BareMetalStatus"]; | |
| googleBM["BareMetal"] = "Disabled"; | |
| if (std::filesystem::exists(BM_SIGNAL_PATH)) { | |
| BMCWEB_LOG_DEBUG << "BMReady flag found"; | |
| googleBM["BareMetal"] = "BareMetalReady"; | |
| } | |
| BMCWEB_LOG_DEBUG << "EXIT: Get BMReady signal"; | |
| } | |
| /** | |
| * @brief Retrieves NERF status from the file system | |
| * | |
| * @param[in] aResp Shared pointer for completing asynchronous calls. | |
| * @param[in] systemName String of systemName | |
| * | |
| * @return None. | |
| */ | |
| inline void getNERFStatus(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& systemName) { | |
| BMCWEB_LOG_DEBUG << "Get NERF status signal"; | |
| nlohmann::json& googleNERF = | |
| aResp->res.jsonValue["Oem"]["Google"]["NERFStatus"]; | |
| googleNERF["NERF"] = "NonBMMode"; | |
| const std::string systemSuffix = getSystemSuffix(systemName); | |
| const std::string bmDriveCleaningFlagPath = | |
| "/run/bm-drive-cleaning.flag" + systemSuffix; | |
| const std::string bmDriveCleaningDoneAckFlagPath = | |
| "/run/bm-drive-cleaning-done-ack.flag" + systemSuffix; | |
| if (std::filesystem::exists(bmDriveCleaningDoneAckFlagPath)) { | |
| BMCWEB_LOG_DEBUG << "Cleaning was finished and acked by NERF"; | |
| googleNERF["NERF"] = "UEFIboot"; | |
| } else if (std::filesystem::exists(bmDriveCleaningFlagPath)) { | |
| BMCWEB_LOG_DEBUG << "Cleaning flag found, NERF must be in rendevouz"; | |
| googleNERF["NERF"] = "SteadyState"; | |
| } | |
| BMCWEB_LOG_DEBUG << "EXIT: Get NERF Status signal"; | |
| } | |
| inline void getBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& systemName, | |
| const std::string& systemPath) { | |
| BMCWEB_LOG_INFO << "Get BootNumber property"; | |
| aResp->res.jsonValue["@odata.id"] = | |
| "/redfish/v1/Systems/" + systemName + "/Oem/Google/BootNumber"; | |
| aResp->res.jsonValue["@odata.type"] = | |
| "#GoogleBootNumber.v1_0_0.GoogleBootNumber"; | |
| std::string object; | |
| // Object Path for multi-host systems is | |
| // /xyz/openbmc_project/inventory/system/board/<platform>/<systemname>/chassis/motherboard/bios. | |
| // Whereas for single-host systems is /xyz/openbmc_project/inventory/<system | |
| // name>/chassis/motherboard/bios. In order to make sure that the correct path | |
| // is used we get it from dbus utilities. | |
| if (systemPath.empty()) { | |
| object = absl::StrCat("/xyz/openbmc_project/inventory/", systemName, | |
| "/chassis/motherboard/bios"); | |
| } else { | |
| object = absl::StrCat(systemPath, "/chassis/motherboard/bios"); | |
| } | |
| const std::string hostStateInterface = "xyz.openbmc_project.State.Host"; | |
| const std::string bootCountProperty = "BootCount"; | |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&, | |
| const dbus::utility::DbusVariantType&)>( | |
| [aResp](const boost::system::error_code& ec, | |
| const dbus::utility::DbusVariantType& property) { | |
| if (ec) { | |
| BMCWEB_LOG_ERROR << "DBUS BOOT COUNT response error: " | |
| << ec.message(); | |
| return; | |
| } | |
| const uint32_t* number = std::get_if<uint32_t>(&property); | |
| if (number == nullptr) { | |
| BMCWEB_LOG_ERROR << "DBUS BOOT COUNT invalid property type"; | |
| return; | |
| } | |
| aResp->res.jsonValue["BootNumber"] = *number; | |
| }), | |
| "xyz.openbmc_project.Smbios.MDR_V2", object, | |
| "org.freedesktop.DBus.Properties", "Get", hostStateInterface, | |
| bootCountProperty); | |
| } | |
| /** | |
| * @brief Retrieves BM Instance properties from the file system | |
| * | |
| * @param[in] aResp Shared pointer for completing asynchronous calls. | |
| * @param[in] systemName String of systemName | |
| * | |
| * @return None. | |
| */ | |
| inline void getBMInstance(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& systemName) { | |
| BMCWEB_LOG_DEBUG << "Get BM Instance property"; | |
| aResp->res.jsonValue["@odata.id"] = | |
| "/redfish/v1/Systems/" + systemName + "/Oem/Google/BareMetalInstance"; | |
| aResp->res.jsonValue["@odata.type"] = | |
| "#GoogleBareMetalInstance.v1_0_0.GoogleBareMetalInstance"; | |
| const auto systemSuffix = getSystemSuffix(systemName); | |
| const std::array<std::pair<std::string_view, std::string>, 7> | |
| bmPropertyToPath = { | |
| {{"AssetTag", "/run/bm-instance/asset-tag" + systemSuffix}, | |
| {"BoardSerialNumber", | |
| "/run/bm-instance/board-serial-number" + systemSuffix}, | |
| {"Family", "/run/bm-instance/family" + systemSuffix}, | |
| {"ProductName", "/run/bm-instance/product-name" + systemSuffix}, | |
| {"SKU", "/run/bm-instance/sku" + systemSuffix}, | |
| {"SystemSerialNumber", | |
| "/run/bm-instance/system-serial-number" + systemSuffix}, | |
| {"UUID", "/run/bm-instance/uuid" + systemSuffix}}}; | |
| for (const auto& [propertyName, filePath] : bmPropertyToPath) { | |
| // If the path doesn't exist, set it to an empty string | |
| if (!std::filesystem::exists(filePath)) { | |
| aResp->res.jsonValue[propertyName] = ""; | |
| continue; | |
| } | |
| std::ifstream ifs; | |
| ifs.exceptions(std::ifstream::failbit); | |
| std::string property; | |
| try { | |
| ifs.open(filePath); | |
| std::getline(ifs, property); | |
| } catch (std::ios_base::failure& fail) { | |
| BMCWEB_LOG_DEBUG << "Faield to read - setting to default value: " | |
| << filePath; | |
| aResp->res.jsonValue[propertyName] = ""; | |
| continue; | |
| } | |
| aResp->res.jsonValue[propertyName] = property; | |
| } | |
| BMCWEB_LOG_DEBUG << "EXIT: Get BM Instance property"; | |
| } | |
| /** | |
| * @brief Sets Idle Power Saver properties. | |
| * | |
| * @param[in] aResp 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<bmcweb::AsyncResp>& aResp, | |
| const std::optional<bool> ipsEnable, | |
| const std::optional<uint8_t> ipsEnterUtil, | |
| const std::optional<uint64_t> ipsEnterTime, | |
| const std::optional<uint8_t> ipsExitUtil, | |
| const std::optional<uint64_t> ipsExitTime) { | |
| BMCWEB_LOG_DEBUG << "Set idle power saver properties"; | |
| // Get IdlePowerSaver object path: | |
| constexpr std::array<std::string_view, 1> interfaces = { | |
| "xyz.openbmc_project.Control.Power.IdlePowerSaver"}; | |
| managedStore::ManagedObjectStoreContext requestContext(aResp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| "/", 0, interfaces, requestContext, | |
| [aResp, ipsEnable, ipsEnterUtil, ipsEnterTime, ipsExitUtil, ipsExitTime]( | |
| const boost::system::error_code& ec, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG | |
| << "DBUS response error on Power.IdlePowerSaver GetSubTree " | |
| << ec; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| if (subtree.empty()) { | |
| // This is an optional D-Bus object, but user attempted to patch | |
| messages::resourceNotFound(aResp->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(aResp->res); | |
| return; | |
| } | |
| if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) { | |
| BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver mapper error!"; | |
| messages::internalError(aResp->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(aResp->res); | |
| return; | |
| } | |
| // Valid Power IdlePowerSaver object found, now set any values that | |
| // need to be updated | |
| if (ipsEnable) { | |
| managedStore::GetManagedObjectStore() | |
| ->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec2) { | |
| if (ec2) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| }), | |
| service, path, "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Control.Power.IdlePowerSaver", "Enabled", | |
| dbus::utility::DbusVariantType(*ipsEnable)); | |
| } | |
| if (ipsEnterUtil) { | |
| managedStore::GetManagedObjectStore() | |
| ->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec2) { | |
| if (ec2) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| }), | |
| service, path, "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Control.Power.IdlePowerSaver", | |
| "EnterUtilizationPercent", | |
| dbus::utility::DbusVariantType(*ipsEnterUtil)); | |
| } | |
| if (ipsEnterTime) { | |
| // Convert from seconds into milliseconds for DBus | |
| const uint64_t timeMilliseconds = *ipsEnterTime * 1000; | |
| managedStore::GetManagedObjectStore() | |
| ->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec2) { | |
| if (ec2) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| }), | |
| service, path, "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Control.Power.IdlePowerSaver", | |
| "EnterDwellTime", | |
| dbus::utility::DbusVariantType(timeMilliseconds)); | |
| } | |
| if (ipsExitUtil) { | |
| managedStore::GetManagedObjectStore() | |
| ->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec2) { | |
| if (ec2) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| }), | |
| service, path, "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Control.Power.IdlePowerSaver", | |
| "ExitUtilizationPercent", | |
| dbus::utility::DbusVariantType(*ipsExitUtil)); | |
| } | |
| if (ipsExitTime) { | |
| // Convert from seconds into milliseconds for DBus | |
| const uint64_t timeMilliseconds = *ipsExitTime * 1000; | |
| managedStore::GetManagedObjectStore() | |
| ->PostDbusCallToIoContextThreadSafe( | |
| aResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [aResp](const boost::system::error_code& ec2) { | |
| if (ec2) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| }), | |
| service, path, "org.freedesktop.DBus.Properties", "Set", | |
| "xyz.openbmc_project.Control.Power.IdlePowerSaver", | |
| "ExitDwellTime", | |
| dbus::utility::DbusVariantType(timeMilliseconds)); | |
| } | |
| }); | |
| BMCWEB_LOG_DEBUG << "EXIT: Set idle power saver parameters"; | |
| } | |
| /** | |
| * @brief Sets BMInstnace properties. | |
| * | |
| * @param[in] aResp Shared pointer for generating response message. | |
| * @param[in] systemName String of systemName | |
| * @param[in] bmiAssetTag BM instance asset tag to be set | |
| * @param[in] bmiBoardSerialNumber BM instance board serial number to be set | |
| * @param[in] bmiFamily BM instance family to be set | |
| * @param[in] bmiProductName BM instance product name to be set | |
| * @param[in] bmiSKU BM instance SKU to be set | |
| * @param[in] bmiSystemSerialNumber BM instance syste serial number to be set | |
| * @param[in] bmiUUID BM instance UUID to be set | |
| * | |
| * @return None. | |
| */ | |
| inline void setBMInstance( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& systemName, const std::string& bmiAssetTag, | |
| const std::string& bmiBoardSerialNumber, const std::string& bmiFamily, | |
| const std::string& bmiProductName, const std::string& bmiSKU, | |
| const std::string& bmiSystemSerialNumber, const std::string& bmiUUID) { | |
| BMCWEB_LOG_DEBUG << "Set BM Instance properties"; | |
| std::filesystem::path bmInstanceDirPath = "/run/bm-instance"; | |
| // Create parent directories if they don't exist | |
| std::error_code ec; | |
| if (!std::filesystem::exists(bmInstanceDirPath, ec)) { | |
| std::filesystem::create_directories(bmInstanceDirPath, ec); | |
| } | |
| if (ec) { | |
| if (!std::filesystem::exists(bmInstanceDirPath, ec)) { | |
| BMCWEB_LOG_DEBUG << "Failed to create base directory(" | |
| << bmInstanceDirPath << "): " << ec; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| } | |
| const std::string systemSuffix = getSystemSuffix(systemName); | |
| // Helper function to output the value to the file path | |
| auto processFile = [&systemSuffix](const std::filesystem::path& path, | |
| std::string_view val) -> bool { | |
| std::filesystem::path appendedPath = path; | |
| appendedPath.concat(systemSuffix); | |
| std::ofstream file(appendedPath); | |
| if (!file) { | |
| BMCWEB_LOG_DEBUG << "Failed to create: " << appendedPath.string(); | |
| return false; | |
| } | |
| file << val << '\n'; | |
| file.close(); | |
| return true; | |
| }; | |
| if (!bmiAssetTag.empty()) { | |
| if (!processFile(bmInstanceDirPath / "asset-tag", bmiAssetTag)) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| } | |
| if (!bmiBoardSerialNumber.empty()) { | |
| if (!processFile(bmInstanceDirPath / "board-serial-number", | |
| bmiBoardSerialNumber)) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| } | |
| if (!bmiFamily.empty()) { | |
| if (!processFile(bmInstanceDirPath / "family", bmiFamily)) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| } | |
| if (!bmiProductName.empty()) { | |
| if (!processFile(bmInstanceDirPath / "product-name", bmiProductName)) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| } | |
| if (!bmiSKU.empty()) { | |
| if (!processFile(bmInstanceDirPath / "sku", bmiSKU)) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| } | |
| if (!bmiSystemSerialNumber.empty()) { | |
| if (!processFile(bmInstanceDirPath / "system-serial-number", | |
| bmiSystemSerialNumber)) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| } | |
| if (!bmiUUID.empty()) { | |
| if (!processFile(bmInstanceDirPath / "uuid", bmiUUID)) { | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| } | |
| messages::success(aResp->res); | |
| BMCWEB_LOG_DEBUG << "EXIT: Set BM Instance properties"; | |
| } | |
| inline void handleComputerSystemHead( | |
| crow::App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| asyncResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/ComputerSystemCollection/" | |
| "ComputerSystemCollection.json>; rel=describedby"); | |
| } | |
| inline void handleGetSystemsCollection( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| asyncResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/" | |
| "ComputerSystemCollection.json>; rel=describedby"); | |
| asyncResp->res.jsonValue["@odata.type"] = | |
| "#ComputerSystemCollection.ComputerSystemCollection"; | |
| asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Systems"; | |
| asyncResp->res.jsonValue["Name"] = "Computer System Collection"; | |
| managedStore::ManagedObjectStoreContext context(asyncResp); | |
| managedStore::GetManagedObjectStore()->getSubTreePaths( | |
| "/", 0, systemInterfaces, context, | |
| [asyncResp, context](const boost::system::error_code& ec, | |
| const std::vector<std::string>& objects) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| if (system_utils::isHardcodedSingleHost(objects.size())) { | |
| nlohmann::json::object_t system; | |
| system["@odata.id"] = "/redfish/v1/Systems/system"; | |
| asyncResp->res.jsonValue["Members"].emplace_back(system); | |
| asyncResp->res.jsonValue["Members@odata.count"] = | |
| asyncResp->res.jsonValue["Members"].size(); | |
| return; | |
| } | |
| // Otherwise list all systems | |
| for (const auto& object : objects) { | |
| std::string systemName = object.substr(object.rfind('/')); | |
| nlohmann::json::object_t system; | |
| system["@odata.id"] = "/redfish/v1/Systems" + systemName; | |
| asyncResp->res.jsonValue["Members"].emplace_back(system); | |
| } | |
| asyncResp->res.jsonValue["Members@odata.count"] = | |
| asyncResp->res.jsonValue["Members"].size(); | |
| }); | |
| } | |
| /** | |
| * SystemsCollection derived class for delivering ComputerSystems Collection | |
| * Schema | |
| */ | |
| inline void requestRoutesSystemsCollection(App& app) { | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/") | |
| .privileges(redfish::privileges::headComputerSystemCollection) | |
| .methods(boost::beast::http::verb::head)( | |
| std::bind_front(handleComputerSystemHead, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/") | |
| .privileges(redfish::privileges::getComputerSystemCollection) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleGetSystemsCollection, std::ref(app))); | |
| } | |
| /** | |
| * Function transceives data with dbus directly. | |
| */ | |
| inline void doNMI(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| constexpr char const* serviceName = "xyz.openbmc_project.Control.Host.NMI"; | |
| constexpr char const* objectPath = "/xyz/openbmc_project/control/host0/nmi"; | |
| constexpr char const* interfaceName = "xyz.openbmc_project.Control.Host.NMI"; | |
| constexpr char const* method = "NMI"; | |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | |
| asyncResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [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 sendPowerSystemCommand( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& command, const std::string& resetType, | |
| const std::string& service, const std::string& path, | |
| const std::string& interface, const std::string& property) { | |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | |
| asyncResp->strand_, | |
| absl::AnyInvocable<void(const boost::system::error_code&)>( | |
| [asyncResp, resetType](const boost::system::error_code ec) { | |
| if (ec) { | |
| BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; | |
| if (ec.value() == boost::asio::error::invalid_argument) { | |
| messages::actionParameterNotSupported(asyncResp->res, resetType, | |
| "Reset"); | |
| } else { | |
| messages::internalError(asyncResp->res); | |
| } | |
| return; | |
| } | |
| messages::success(asyncResp->res); | |
| }), | |
| service, path, "org.freedesktop.DBus.Properties", "Set", interface, | |
| property, dbus::utility::DbusVariantType{command}); | |
| } | |
| struct SystemResetCommand { | |
| std::string command = ""; // NOLINT | |
| bool hostCommand = true; | |
| bool validCommand = true; | |
| }; | |
| inline SystemResetCommand GetSystemResetCommandFromInput( | |
| const std::string& resetType) { | |
| if ((resetType == "On") || (resetType == "ForceOn")) { | |
| return SystemResetCommand{ | |
| .command = "xyz.openbmc_project.State.Host.Transition.On", | |
| .hostCommand = true}; | |
| } | |
| if (resetType == "ForceOff") { | |
| return SystemResetCommand{ | |
| .command = "xyz.openbmc_project.State.Chassis.Transition.Off", | |
| .hostCommand = false}; | |
| } | |
| if (resetType == "ForceRestart") { | |
| return SystemResetCommand{ | |
| .command = "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot", | |
| .hostCommand = true}; | |
| } | |
| if (resetType == "GracefulShutdown") { | |
| return SystemResetCommand{ | |
| .command = "xyz.openbmc_project.State.Host.Transition.Off", | |
| .hostCommand = true}; | |
| } | |
| if (resetType == "GracefulRestart") { | |
| return SystemResetCommand{ | |
| .command = | |
| "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot", | |
| .hostCommand = true}; | |
| } | |
| if (resetType == "PowerCycle") { | |
| return SystemResetCommand{ | |
| .command = "xyz.openbmc_project.State.Host.Transition.Reboot", | |
| .hostCommand = true}; | |
| } | |
| // TODO(b/294130541) (b/335339881) | |
| // On the gsz platform (GracefulShutdown) does forced shutdown in | |
| // x86-power-control This second call path was needed for the bm use case | |
| // This is ONLY supported on "gsz" | |
| #ifdef BMCWEB_ENABLE_REALLY_GRACEFUL_SHUTDOWN | |
| if (resetType == "ReallyGracefulShutDownTechDebt") { | |
| return SystemResetCommand{ | |
| .command = | |
| "xyz.openbmc_project.State.Host.Transition.ReallyGracefulShutDown", | |
| .hostCommand = true}; | |
| } | |
| #endif // BMCWEB_ENABLE_REALLY_GRACEFUL_SHUTDOWN | |
| return SystemResetCommand{.validCommand = false}; | |
| } | |
| /* Have systemd run a curl command in a set amount of time | |
| * resetURI: Chassis or Systems based on where you want to reset | |
| * resetTimeSec: The amount of seconds to delay reset | |
| * resetOp: same as resetType variable here | |
| * asyncResp update the response | |
| */ | |
| void delayResetSystem(std::string_view systemName, int delayTimeSecs, | |
| std::string_view resetType, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp); | |
| /* | |
| * Handles System reset by creating the DBus call associated with the requested | |
| * reset. | |
| * | |
| * systemPath is object path of the related Item.System object to call the | |
| * ComputerSystem.Reset call on. This is needed to find its associated power | |
| * control objects. | |
| * | |
| * If systemPath is /xyz/openbmc_project/state/host1 (sample Item.System object) | |
| * Then host power state objects are associated with forward association | |
| * system_power and chassis power state objects are associated with forward | |
| * asociation chassis_power | |
| */ | |
| inline void sendSystemResetAction( | |
| const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemPath, const std::string& systemName) { | |
| std::string resetType; | |
| std::optional<int> delayTimeSec; | |
| if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType, | |
| "Delay", delayTimeSec)) { | |
| return; | |
| } | |
| if (resetType == "Nmi") { | |
| doNMI(asyncResp); | |
| return; | |
| } | |
| SystemResetCommand systemResetCommand = | |
| GetSystemResetCommandFromInput(resetType); | |
| if (!systemResetCommand.validCommand) { | |
| messages::actionParameterUnknown(asyncResp->res, "Reset", resetType); | |
| return; | |
| } | |
| // Get the command and host vs. chassis | |
| std::string command = systemResetCommand.command; | |
| bool hostCommand = systemResetCommand.hostCommand; | |
| if (delayTimeSec.has_value() && delayTimeSec.value() > 900) // 15 minutes | |
| { | |
| BMCWEB_LOG_DEBUG << "reset delay timer too large, >900"; | |
| messages::propertyValueOutOfRange(asyncResp->res, "900", "Delay"); | |
| return; | |
| } | |
| if (delayTimeSec.has_value() && delayTimeSec.value() <= 900 && | |
| (resetType == "On" || resetType == "ForceOn" || resetType == "ForceOff" || | |
| resetType == "ForceRestart" || resetType == "GracefulShutdown" || | |
| resetType == "PowerCycle")) { | |
| BMCWEB_LOG_DEBUG << "Seting delayed " << resetType; | |
| delayResetSystem(systemName, delayTimeSec.value(), resetType, asyncResp); | |
| return; | |
| } | |
| if (hostCommand) { | |
| // For single system servers, the power state object is hardcoded | |
| if (systemPath.empty()) { | |
| sendPowerSystemCommand( | |
| asyncResp, command, resetType, "xyz.openbmc_project.State.Host", | |
| "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host", | |
| "RequestedHostTransition"); | |
| return; | |
| } | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| managedStore::GetManagedObjectStore()->getAssociatedSubTree( | |
| sdbusplus::message::object_path(systemPath + "/host_power"), | |
| sdbusplus::message::object_path("/xyz/openbmc_project/state"), 0, | |
| std::array<std::string_view, 1>{"xyz.openbmc_project.State.Host"}, | |
| requestContext, | |
| [asyncResp, command, resetType]( | |
| const boost::system::error_code ec, | |
| const dbus::utility::MapperGetSubTreeResponse& objects) { | |
| if (ec || objects.empty()) { | |
| BMCWEB_LOG_ERROR | |
| << "Host power control not associated with system object"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| // There should only be one host power control object associated | |
| // with this system | |
| if (objects.size() > 1) { | |
| BMCWEB_LOG_ERROR << "Too many host power control objects " | |
| "associated with the system"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| // Map1: [key, val] -> [objectPath, Map2] | |
| // Map2: [key, val] -> [serviceName, list of interfaces] | |
| const std::string powerControlPath = objects[0].first; | |
| // This object path can have multiple services, we need the one that | |
| // contains "State.Host" | |
| for (const auto& serviceMapping : objects[0].second) { | |
| // Host 1 object is implemented by | |
| // xyz.openbmc_project.State.Host1 Cannot directly find | |
| // interface as the different host's have different services | |
| if (absl::StrContains(serviceMapping.first, | |
| "xyz.openbmc_project.State.Host")) { | |
| sendPowerSystemCommand(asyncResp, command, resetType, | |
| serviceMapping.first, powerControlPath, | |
| "xyz.openbmc_project.State.Host", | |
| "RequestedHostTransition"); | |
| return; | |
| } | |
| } | |
| BMCWEB_LOG_ERROR | |
| << "Could not find correct host Power Control Object"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| }); | |
| } else { | |
| // For single system servers, the power state object is hardcoded | |
| if (systemPath.empty()) { | |
| sendPowerSystemCommand( | |
| asyncResp, command, resetType, "xyz.openbmc_project.State.Chassis", | |
| "/xyz/openbmc_project/state/chassis0", | |
| "xyz.openbmc_project.State.Chassis", "RequestedPowerTransition"); | |
| return; | |
| } | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| managedStore::GetManagedObjectStore()->getAssociatedSubTree( | |
| sdbusplus::message::object_path(systemPath + "/chassis_power"), | |
| sdbusplus::message::object_path("/xyz/openbmc_project/state"), 0, | |
| std::array<std::string_view, 1>{"xyz.openbmc_project.State.Chassis"}, | |
| requestContext, | |
| [asyncResp, command, resetType]( | |
| const boost::system::error_code ec, | |
| const dbus::utility::MapperGetSubTreeResponse& objects) { | |
| if (ec || objects.empty()) { | |
| BMCWEB_LOG_ERROR | |
| << "Chassis power control not associated with system object"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| // There should only be one chassis power control object associated | |
| // with this system | |
| if (objects.size() > 1) { | |
| BMCWEB_LOG_ERROR << "Too many chassis power control objects " | |
| "associated with the system"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| // Map1: [key, val] -> [objectPath, Map2] | |
| // Map2: [key, val] -> [serviceName, list of interfaces] | |
| const std::string powerControlPath = objects[0].first; | |
| // This object path can have multiple services, we need the one that | |
| // contains "State.Host" | |
| for (const auto& serviceMapping : objects[0].second) { | |
| if (absl::StrContains(serviceMapping.first, | |
| "xyz.openbmc_project.State.Chassis")) { | |
| sendPowerSystemCommand(asyncResp, command, resetType, | |
| serviceMapping.first, powerControlPath, | |
| "xyz.openbmc_project.State.Chassis", | |
| "RequestedPowerTransition"); | |
| return; | |
| } | |
| } | |
| BMCWEB_LOG_ERROR | |
| << "Could not find correct chassis Power Control Object"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| }); | |
| } | |
| } | |
| inline void handlePostComputerSystemReset( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| managedStore::GetManagedObjectStore()->getSubTreePaths( | |
| "/", 0, systemInterfaces, requestContext, | |
| [req, asyncResp, systemName](const boost::system::error_code& ec, | |
| const std::vector<std::string>& objects) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| system_utils::findSystemPathAndTriggerCallback( | |
| asyncResp, systemName, objects, | |
| [req, systemName](const std::shared_ptr<bmcweb::AsyncResp>& resp, | |
| const std::string& objectPath) { | |
| sendSystemResetAction(req, resp, objectPath, systemName); | |
| }); | |
| }); | |
| } | |
| /** | |
| * SystemActionsReset class supports handle POST method for Reset action. | |
| * The class retrieves and sends data directly to D-Bus. | |
| */ | |
| inline void requestRoutesSystemActionsReset(App& app) { | |
| /** | |
| * Function handles POST method request. | |
| * Analyzes POST body message before sends Reset request data to D-Bus. | |
| */ | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Actions/ComputerSystem.Reset/") | |
| .privileges(redfish::privileges::postComputerSystem) | |
| .methods(boost::beast::http::verb::post)( | |
| std::bind_front(handlePostComputerSystemReset, std::ref(app))); | |
| ; | |
| } | |
| inline void handlePostSystemBootGuestOS( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| const std::string systemSuffix = getSystemSuffix(systemName); | |
| const std::string bmDriveCleaningDoneFlagPath = | |
| "/run/bm-drive-cleaning-done.flag" + systemSuffix; | |
| const std::string bmDriveCleaningDoneAckFlagPath = | |
| "/run/bm-drive-cleaning-done-ack.flag" + systemSuffix; | |
| std::error_code ec; | |
| if (std::filesystem::exists(bmDriveCleaningDoneFlagPath, ec) || | |
| std::filesystem::exists(bmDriveCleaningDoneAckFlagPath, ec)) { | |
| BMCWEB_LOG_DEBUG | |
| << "NERF was successfully notified to go into UEFI boot already"; | |
| messages::success(asyncResp->res); | |
| return; | |
| } | |
| if (!std::filesystem::exists(bmDriveCleaningDoneFlagPath, ec)) { | |
| std::ofstream ofs; | |
| ofs.open(bmDriveCleaningDoneFlagPath, std::ofstream::out); | |
| ofs.close(); | |
| BMCWEB_LOG_DEBUG << "Successfully created flag file: " | |
| << bmDriveCleaningDoneFlagPath; | |
| messages::success(asyncResp->res); | |
| return; | |
| } | |
| } | |
| /** | |
| * Notify BMC to let NERF boot to Guest OS | |
| */ | |
| inline void requestRoutesSystemBootGuestOS(App& app) { | |
| /** | |
| * Function handles POST method request. | |
| */ | |
| BMCWEB_ROUTE( | |
| app, | |
| "/redfish/v1/Systems/<str>/Actions/Oem/GoogleComputerSystem.BootGuestOS/") | |
| .privileges(redfish::privileges::postComputerSystem) | |
| .methods(boost::beast::http::verb::post)( | |
| std::bind_front(handlePostSystemBootGuestOS, std::ref(app))); | |
| } | |
| inline void handleGetSystemBootGuestOSInfo( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo"; | |
| asyncResp->res.jsonValue["@odata.id"] = | |
| "/redfish/v1/System/" + systemName + "/Oem/Google/BootGuestOSActionInfo"; | |
| asyncResp->res.jsonValue["Name"] = "Boot Guest OS"; | |
| asyncResp->res.jsonValue["Id"] = "BootGuestOS"; | |
| } | |
| /** | |
| * Action Info for notifying BMC to let NERF boot to Guest OS | |
| */ | |
| inline void requestRoutesSystemBootGuestOSInfo(App& app) { | |
| /** | |
| * Function handles POST method request. | |
| */ | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Systems/<str>/Oem/Google/BootGuestOSActionInfo/") | |
| .privileges(redfish::privileges::getActionInfo) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleGetSystemBootGuestOSInfo, std::ref(app))); | |
| } | |
| inline void handleComputerSystemCollectionHead( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| asyncResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/ComputerSystem/" | |
| "ComputerSystem.json>; rel=describedby"); | |
| } | |
| inline void afterPortRequest( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const boost::system::error_code& ec, | |
| const std::vector<std::tuple<std::string, std::string, bool>>& socketData) { | |
| if (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") { | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| getPortNumber(socketPath, requestContext, | |
| [asyncResp, protocolName]( | |
| const boost::system::error_code ec1, int portNumber) { | |
| if (ec1) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| nlohmann::json& dataJson1 = | |
| asyncResp->res.jsonValue["SerialConsole"]; | |
| dataJson1[protocolName]["Port"] = portNumber; | |
| }); | |
| } | |
| } | |
| } | |
| inline void handleComputerSystem( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemObjectPath, const std::string& systemName, | |
| bool multiHost) { | |
| // Handling both single-system and multi-system cases | |
| asyncResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/ComputerSystem/" | |
| "ComputerSystem.json>; rel=describedby"); | |
| asyncResp->res.jsonValue["@odata.type"] = | |
| "#ComputerSystem.v1_16_0.ComputerSystem"; | |
| asyncResp->res.jsonValue["Name"] = systemName; | |
| asyncResp->res.jsonValue["Id"] = systemName; | |
| asyncResp->res.jsonValue["SystemType"] = "Physical"; | |
| asyncResp->res.jsonValue["Description"] = "Computer System"; | |
| asyncResp->res.jsonValue["@odata.id"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName); | |
| asyncResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"]["target"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, | |
| "/Actions/ComputerSystem.Reset"); | |
| asyncResp->res | |
| .jsonValue["Actions"]["#ComputerSystem.Reset"]["@Redfish.ActionInfo"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, "/ResetActionInfo"); | |
| asyncResp->res.jsonValue["Processors"]["@odata.id"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, "/Processors"); | |
| asyncResp->res.jsonValue["Storage"]["@odata.id"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, "/Storage"); | |
| asyncResp->res.jsonValue["Bios"]["@odata.id"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, "/Bios"); | |
| getBMReady(asyncResp); | |
| if constexpr (enableGoogleBaremetal) { | |
| asyncResp->res.jsonValue["Actions"]["Oem"]["Google"] | |
| ["#GoogleComputerSystem.BootGuestOS"]["target"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, | |
| "/Actions/Oem/GoogleComputerSystem.BootGuestOS"); | |
| asyncResp->res | |
| .jsonValue["Actions"]["Oem"]["Google"] | |
| ["#GoogleComputerSystem.BootGuestOS"]["@Redfish.ActionInfo"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, | |
| "/Oem/Google/BootGuestOSActionInfo"); | |
| asyncResp->res | |
| .jsonValue["Oem"]["Google"]["BareMetalInstance"]["@odata.id"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, | |
| "/Oem/Google/BareMetalInstance"); | |
| getNERFStatus(asyncResp, systemName); | |
| } | |
| #ifdef ENABLE_REDFISH_CONFIGURE_IFS | |
| asyncResp->res | |
| .jsonValue["Actions"]["Oem"]["Google"] | |
| ["#GoogleComputerSystem.ResetCpuIfsPattern"]["target"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, | |
| "/Actions/Oem/GoogleComputerSystem.ResetCpuIfsPattern"); | |
| if (std::shared_ptr<milotic_tlbmc::SystemRegistry> registry = | |
| managedStore::GetManagedObjectStore()->GetSystemRegistry()) { | |
| absl::StatusOr<std::string> pattern = | |
| registry->GetBase64EncodedIfsPattern(systemName); | |
| if (!pattern.ok()) { | |
| BMCWEB_LOG_ERROR << "Failed to get Base64 IFS pattern: " | |
| << pattern.status(); | |
| messages::internalError(asyncResp->res); | |
| } else { | |
| asyncResp->res.jsonValue["Oem"]["Google"]["CpuIfsPatternBase64"] = | |
| std::move(*pattern); | |
| } | |
| } | |
| #endif | |
| #ifdef BMCWEB_ENABLE_REDFISH_SYSTEM_BOOTNUMBER | |
| asyncResp->res.jsonValue["Oem"]["Google"]["BootNumber"]["@odata.id"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, | |
| "/Oem/Google/BootNumber"); | |
| #endif | |
| asyncResp->res.jsonValue["LogServices"]["@odata.id"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, "/LogServices"); | |
| asyncResp->res.jsonValue["Memory"]["@odata.id"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, "/Memory"); | |
| asyncResp->res.jsonValue["Oem"]["Google"]["BootTime"]["@odata.id"] = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, "/Oem/Google/BootTime"); | |
| nlohmann::json::array_t managedBy; | |
| nlohmann::json& manager = managedBy.emplace_back(); | |
| manager["@odata.id"] = "/redfish/v1/Managers/bmc"; | |
| asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); | |
| getMainChassisId( | |
| asyncResp, [](const std::string& chassisId, | |
| const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { | |
| nlohmann::json::array_t chassisArray; | |
| nlohmann::json& chassis = chassisArray.emplace_back(); | |
| chassis["@odata.id"] = | |
| crow::utility::urlFromPieces("redfish", "v1", "Chassis", chassisId); | |
| aRsp->res.jsonValue["Links"]["Chassis"] = std::move(chassisArray); | |
| }); | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; | |
| managedStore::GetManagedObjectStore()->getAssociatedSubTree( | |
| sdbusplus::message::object_path(systemObjectPath + "/host_power"), | |
| sdbusplus::message::object_path("/xyz/openbmc_project/state"), 0, | |
| hostStateInterfaces, requestContext, | |
| [asyncResp, systemObjectPath, multiHost]( | |
| const boost::system::error_code& ec, | |
| const dbus::utility::MapperGetSubTreeResponse& objects) { | |
| if (ec) { | |
| BMCWEB_LOG_ERROR | |
| << "Host power control not associated with system object"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| // There should only be one host power control object associated with | |
| // this system | |
| if (objects.size() > 1) { | |
| BMCWEB_LOG_ERROR << "Too many host power control objects associated " | |
| "with the system"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| getHostState(asyncResp, systemObjectPath, objects); | |
| if (!multiHost) { | |
| getLastResetTime(asyncResp, systemObjectPath, objects); | |
| } | |
| }); | |
| // Handling last reset time for multi-system | |
| if (multiHost) { | |
| managedStore::GetManagedObjectStore()->getAssociatedSubTree( | |
| sdbusplus::message::object_path(systemObjectPath + "/chassis_power"), | |
| sdbusplus::message::object_path("/xyz/openbmc_project/state"), 0, | |
| chassisStateInterfaces, requestContext, | |
| [asyncResp, systemObjectPath]( | |
| const boost::system::error_code& ec, | |
| const dbus::utility::MapperGetSubTreeResponse& objects) { | |
| if (ec) { | |
| BMCWEB_LOG_ERROR | |
| << "Chassis power control not associated with system object"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| // There should only be one chassis power control object associated | |
| // with this system | |
| if (objects.size() > 1) { | |
| BMCWEB_LOG_ERROR << "Too many chassis power control objects " | |
| "associated with the system"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| getLastResetTime(asyncResp, systemObjectPath, objects); | |
| }); | |
| } | |
| getPCIeDeviceList(asyncResp, systemName, "PCIeDevices"); | |
| getBootProperties(asyncResp, systemName, multiHost); | |
| // Handling multi-system and single-system separately. In the future, the | |
| // goal is to not have to separate these two. However, until code is | |
| // refactored, there will be diffs. | |
| // Handle single-system code | |
| if (!multiHost) { | |
| asyncResp->res.jsonValue["ProcessorSummary"]["Count"] = 0; | |
| asyncResp->res.jsonValue["ProcessorSummary"]["Status"]["State"] = | |
| "Disabled"; | |
| asyncResp->res.jsonValue["MemorySummary"]["TotalSystemMemoryGiB"] = | |
| static_cast<uint64_t>(0); | |
| asyncResp->res.jsonValue["MemorySummary"]["Status"]["State"] = "Disabled"; | |
| asyncResp->res.jsonValue["FabricAdapters"]["@odata.id"] = | |
| "/redfish/v1/Systems/system/FabricAdapters"; | |
| asyncResp->res.jsonValue["Status"]["Health"] = "OK"; | |
| // Fill in SerialConsole info | |
| asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15; | |
| asyncResp->res.jsonValue["SerialConsole"]["IPMI"]["ServiceEnabled"] = true; | |
| // TODO (Gunnar): Should look for obmc-console-ssh@2200.service | |
| 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}, requestContext, | |
| std::bind_front(afterPortRequest, asyncResp)); | |
| auto health = std::make_shared<HealthPopulate>(asyncResp); | |
| #ifdef HEALTH_POPULATE | |
| constexpr std::array<std::string_view, 4> inventoryForSystems{ | |
| "xyz.openbmc_project.Inventory.Item.Dimm", | |
| "xyz.openbmc_project.Inventory.Item.Cpu", | |
| "xyz.openbmc_project.Inventory.Item.Drive", | |
| "xyz.openbmc_project.Inventory.Item.StorageController"}; | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| managedStore::GetManagedObjectStore()->getSubTreePaths( | |
| "/", 0, inventoryForSystems, requestContext, | |
| [health](const boost::system::error_code& ec, | |
| const std::vector<std::string>& resp) { | |
| if (ec) { | |
| // no inventory | |
| return; | |
| } | |
| health->inventory = resp; | |
| }); | |
| health->populate(); | |
| #endif | |
| getLocationIndicatorActive(asyncResp); | |
| // TODO (Gunnar): Remove IndicatorLED after enough time has | |
| // passed | |
| getIndicatorLedState(asyncResp); | |
| getComputerSystem(asyncResp, health, systemName); | |
| getBootProgress(asyncResp); | |
| getBootProgressLastStateTime(asyncResp); | |
| getBootProgressOemLastState(asyncResp); | |
| getHostWatchdogTimer(asyncResp); | |
| getPowerRestorePolicy(asyncResp); | |
| getAutomaticRetry(asyncResp); | |
| #ifdef BMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE | |
| getProvisioningStatus(asyncResp); | |
| #endif | |
| getTrustedModuleRequiredToBoot(asyncResp); | |
| getPowerMode(asyncResp); | |
| getIdlePowerSaver(asyncResp); | |
| } | |
| } | |
| inline void handleGetComputerSystemCollection( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| managedStore::GetManagedObjectStore()->getSubTreePaths( | |
| "/", 0, systemInterfaces, requestContext, | |
| [asyncResp, systemName](const boost::system::error_code& ec, | |
| const std::vector<std::string>& objects) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| system_utils::findSystemPathAndTriggerCallback( | |
| asyncResp, systemName, objects, | |
| [systemName](const std::shared_ptr<bmcweb::AsyncResp>& resp, | |
| const std::string& objectPath) { | |
| const bool multiHost = !objectPath.empty(); | |
| handleComputerSystem(resp, objectPath, systemName, multiHost); | |
| }); | |
| }); | |
| } | |
| inline void handlePatchComputerSystemCollection( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| std::optional<bool> locationIndicatorActive; | |
| std::optional<std::string> indicatorLed; | |
| std::optional<std::string> assetTag; | |
| std::optional<std::string> powerRestorePolicy; | |
| std::optional<std::string> powerMode; | |
| std::optional<bool> wdtEnable; | |
| std::optional<std::string> wdtTimeOutAction; | |
| std::optional<std::string> bootSource; | |
| std::optional<std::string> bootType; | |
| std::optional<std::string> bootEnable; | |
| std::optional<std::vector<std::string>> bootOrder; | |
| std::optional<std::string> bootNext; | |
| std::optional<std::string> bootAutomaticRetry; | |
| std::optional<bool> bootTrustedModuleRequired; | |
| std::optional<bool> ipsEnable; | |
| std::optional<uint8_t> ipsEnterUtil; | |
| std::optional<uint64_t> ipsEnterTime; | |
| std::optional<uint8_t> ipsExitUtil; | |
| std::optional<uint64_t> ipsExitTime; | |
| std::optional<std::string> cpuIfsPatternBase64; | |
| // 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/BootOrder", bootOrder, | |
| "Boot/BootNext", bootNext, | |
| "Boot/AutomaticRetryConfig", bootAutomaticRetry, | |
| "Boot/TrustedModuleRequiredToBoot", bootTrustedModuleRequired, | |
| "IdlePowerSaver/Enabled", ipsEnable, | |
| "IdlePowerSaver/EnterUtilizationPercent", ipsEnterUtil, | |
| "IdlePowerSaver/EnterDwellTimeSeconds", ipsEnterTime, | |
| "IdlePowerSaver/ExitUtilizationPercent", ipsExitUtil, | |
| "IdlePowerSaver/ExitDwellTimeSeconds", ipsExitTime | |
| #ifdef ENABLE_REDFISH_CONFIGURE_IFS | |
| , "Oem/Google/CpuIfsPatternBase64", cpuIfsPatternBase64 | |
| #endif | |
| )) { | |
| return; | |
| } | |
| // clang-format on | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [systemName, locationIndicatorActive = std::move(locationIndicatorActive), | |
| indicatorLed = std::move(indicatorLed), assetTag = std::move(assetTag), | |
| powerRestorePolicy = std::move(powerRestorePolicy), | |
| powerMode = std::move(powerMode), wdtEnable = std::move(wdtEnable), | |
| wdtTimeOutAction = std::move(wdtTimeOutAction), | |
| bootSource = std::move(bootSource), bootType = std::move(bootType), | |
| bootEnable = std::move(bootEnable), bootOrder = std::move(bootOrder), | |
| bootNext = std::move(bootNext), | |
| bootAutomaticRetry = std::move(bootAutomaticRetry), | |
| bootTrustedModuleRequired = std::move(bootTrustedModuleRequired), | |
| ipsEnable = std::move(ipsEnable), ipsEnterUtil = std::move(ipsEnterUtil), | |
| ipsEnterTime = std::move(ipsEnterTime), | |
| ipsExitUtil = std::move(ipsExitUtil), | |
| ipsExitTime = std::move(ipsExitTime), | |
| cpuIfsPatternBase64 = std::move(cpuIfsPatternBase64)]( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& objectPath) { | |
| const bool multiHost = !objectPath.empty(); | |
| asyncResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/ComputerSystem/" | |
| "ComputerSystem.json>; rel=describedby"); | |
| 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 || bootOrder || bootNext) { | |
| setBootProperties(asyncResp, bootSource, bootType, bootEnable, | |
| bootOrder, bootNext, systemName, multiHost); | |
| } | |
| if (bootAutomaticRetry) { | |
| setAutomaticRetry(asyncResp, *bootAutomaticRetry); | |
| } | |
| if (bootTrustedModuleRequired) { | |
| setTrustedModuleRequiredToBoot(asyncResp, *bootTrustedModuleRequired); | |
| } | |
| if (locationIndicatorActive) { | |
| setLocationIndicatorActive(asyncResp, *locationIndicatorActive); | |
| } | |
| 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); | |
| } | |
| #ifdef ENABLE_REDFISH_CONFIGURE_IFS | |
| if (cpuIfsPatternBase64) { | |
| std::shared_ptr<milotic_tlbmc::SystemRegistry> registry = | |
| managedStore::GetManagedObjectStore()->GetSystemRegistry(); | |
| if (!registry) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| absl::Status status = | |
| registry->SetIfsPattern(systemName, *cpuIfsPatternBase64); | |
| if (status.code() == absl::StatusCode::kInvalidArgument) { | |
| messages::propertyValueFormatError( | |
| asyncResp->res, *cpuIfsPatternBase64, | |
| "Oem/Google/CpuIfsPatternBase64"); | |
| return; | |
| } | |
| if (!status.ok()) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| } | |
| #endif | |
| }); | |
| } | |
| inline void handleGetBootNumber( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| asyncResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/GoogleBootNumber/" | |
| "GoogleBootNumber.json>; rel=describedby"); | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [systemName](const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemPath) { | |
| getBootNumber(asyncResp, systemName, systemPath); | |
| }); | |
| } | |
| inline void handleGetBareMetalInstance( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| asyncResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/GoogleBareMetalInstance/" | |
| "GoogleBareMetalInstance.json>; rel=describedby"); | |
| getBMInstance(asyncResp, systemName); | |
| } | |
| inline void handlePatchBareMetalInstance( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| asyncResp->res.addHeader(boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/GoogleBareMetalInstance/" | |
| "GoogleBareMetalInstance.json>; rel=describedby"); | |
| std::optional<std::string> bmiAssetTag; | |
| std::optional<std::string> bmiBoardSerialNumber; | |
| std::optional<std::string> bmiFamily; | |
| std::optional<std::string> bmiProductName; | |
| std::optional<std::string> bmiSKU; | |
| std::optional<std::string> bmiSystemSerialNumber; | |
| std::optional<std::string> bmiUUID; | |
| // clang-format off | |
| if (!json_util::readJsonPatch( | |
| req, asyncResp->res, | |
| "AssetTag", bmiAssetTag, | |
| "BoardSerialNumber", bmiBoardSerialNumber, | |
| "Family", bmiFamily, | |
| "ProductName", bmiProductName, | |
| "SKU", bmiSKU, | |
| "SystemSerialNumber", bmiSystemSerialNumber, | |
| "UUID", bmiUUID)) | |
| { | |
| return; | |
| } | |
| // clang-format on | |
| asyncResp->res.result(boost::beast::http::status::no_content); | |
| if (bmiAssetTag || bmiBoardSerialNumber || bmiFamily || bmiProductName || | |
| bmiSKU || bmiSystemSerialNumber || bmiUUID) { | |
| setBMInstance(asyncResp, systemName, bmiAssetTag.value_or(""), | |
| bmiBoardSerialNumber.value_or(""), bmiFamily.value_or(""), | |
| bmiProductName.value_or(""), bmiSKU.value_or(""), | |
| bmiSystemSerialNumber.value_or(""), bmiUUID.value_or("")); | |
| } | |
| } | |
| inline void handleGetBootOptionCollection( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [systemName](const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& /*objectPath*/) { | |
| std::shared_ptr<milotic_tlbmc::SystemRegistry> registry = | |
| managedStore::GetManagedObjectStore()->GetSystemRegistry(); | |
| if (!registry) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| absl::StatusOr<std::vector<gbmc::settings::BootOption>> bootOptions = | |
| registry->GetBootOptions(systemName); | |
| if (!bootOptions.ok()) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", systemName, "Boot", "BootOptions"); | |
| asyncResp->res.jsonValue["@odata.type"] = | |
| "#BootOptionCollection.BootOptionCollection"; | |
| asyncResp->res.jsonValue["Name"] = "Boot Option Collection"; | |
| asyncResp->res.jsonValue["Description"] = | |
| "Collection of UEFI Boot Options"; | |
| nlohmann::json::array_t members; | |
| for (const auto& bootOption : *bootOptions) { | |
| nlohmann::json::object_t member; | |
| member["@odata.id"] = crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", systemName, "Boot", "BootOptions", | |
| bootOption.boot_option_reference()); | |
| members.push_back(std::move(member)); | |
| } | |
| asyncResp->res.jsonValue["Members@odata.count"] = members.size(); | |
| asyncResp->res.jsonValue["Members"] = std::move(members); | |
| }); | |
| } | |
| inline void handlePostBootOptionCollection( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| std::string bootOptionReference; | |
| std::string displayName; | |
| std::optional<bool> bootOptionEnabled; | |
| std::optional<std::string> uefiDevicePath; | |
| if (!json_util::readJsonPatch(req, asyncResp->res, "BootOptionReference", | |
| bootOptionReference, "DisplayName", displayName, | |
| "BootOptionEnabled", bootOptionEnabled, | |
| "UefiDevicePath", uefiDevicePath)) { | |
| return; | |
| } | |
| if (!parseBootReference(bootOptionReference).has_value()) { | |
| messages::propertyValueFormatError(asyncResp->res, bootOptionReference, | |
| "BootOptionReference"); | |
| return; | |
| } | |
| if (displayName.empty()) { | |
| messages::propertyValueFormatError(asyncResp->res, displayName, | |
| "DisplayName"); | |
| return; | |
| } | |
| gbmc::settings::BootOption bootOption; | |
| bootOption.set_boot_option_reference(bootOptionReference); | |
| bootOption.set_display_name(displayName); | |
| bootOption.set_boot_option_enabled(bootOptionEnabled.value_or(true)); | |
| if (uefiDevicePath.has_value()) { | |
| bootOption.set_uefi_device_path(*uefiDevicePath); | |
| } | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [systemName, bootOption = std::move(bootOption), bootOptionReference]( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& /*objectPath*/) { | |
| std::shared_ptr<milotic_tlbmc::SystemRegistry> registry = | |
| managedStore::GetManagedObjectStore()->GetSystemRegistry(); | |
| if (!registry) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| absl::Status status = registry->AddBootOption(systemName, bootOption); | |
| if (absl::IsAlreadyExists(status)) { | |
| messages::resourceAlreadyExists(asyncResp->res, "BootOption", | |
| "BootOptionReference", | |
| bootOptionReference); | |
| asyncResp->res.result(boost::beast::http::status::conflict); | |
| return; | |
| } | |
| if (!status.ok()) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| asyncResp->res.addHeader(boost::beast::http::field::location, | |
| crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", systemName, | |
| "Boot", "BootOptions", bootOptionReference) | |
| .buffer()); | |
| messages::created(asyncResp->res); | |
| }); | |
| } | |
| inline void handleGetBootOption( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName, const std::string& bootOptionId) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [systemName, bootOptionId]( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& /*objectPath*/) { | |
| std::shared_ptr<milotic_tlbmc::SystemRegistry> registry = | |
| managedStore::GetManagedObjectStore()->GetSystemRegistry(); | |
| if (!registry) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| absl::StatusOr<gbmc::settings::BootOption> bootOption = | |
| registry->GetBootOption(systemName, bootOptionId); | |
| if (absl::IsNotFound(bootOption.status())) { | |
| messages::resourceNotFound(asyncResp->res, "BootOption", | |
| bootOptionId); | |
| return; | |
| } | |
| if (!bootOption.ok()) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces( | |
| "redfish", "v1", "Systems", systemName, "Boot", "BootOptions", | |
| bootOption->boot_option_reference()); | |
| asyncResp->res.jsonValue["@odata.type"] = | |
| "#BootOption.v1_0_4.BootOption"; | |
| asyncResp->res.jsonValue["Id"] = bootOption->boot_option_reference(); | |
| asyncResp->res.jsonValue["Name"] = "Boot Option"; | |
| asyncResp->res.jsonValue["Description"] = "UEFI Boot Option"; | |
| asyncResp->res.jsonValue["BootOptionReference"] = | |
| bootOption->boot_option_reference(); | |
| asyncResp->res.jsonValue["DisplayName"] = bootOption->display_name(); | |
| asyncResp->res.jsonValue["BootOptionEnabled"] = | |
| bootOption->boot_option_enabled(); | |
| if (bootOption->has_uefi_device_path() && | |
| !bootOption->uefi_device_path().empty()) { | |
| asyncResp->res.jsonValue["UefiDevicePath"] = | |
| bootOption->uefi_device_path(); | |
| } | |
| }); | |
| } | |
| inline void handlePatchBootOption( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName, const std::string& bootOptionId) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| bool bootOptionEnabled = false; | |
| if (!json_util::readJsonPatch(req, asyncResp->res, "BootOptionEnabled", | |
| bootOptionEnabled)) { | |
| return; | |
| } | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [systemName, bootOptionId, bootOptionEnabled]( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& /*objectPath*/) { | |
| std::shared_ptr<milotic_tlbmc::SystemRegistry> registry = | |
| managedStore::GetManagedObjectStore()->GetSystemRegistry(); | |
| if (!registry) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| absl::Status status = registry->UpdateBootOptionEnabled( | |
| systemName, bootOptionId, bootOptionEnabled); | |
| if (absl::IsNotFound(status)) { | |
| messages::resourceNotFound(asyncResp->res, "BootOption", | |
| bootOptionId); | |
| return; | |
| } | |
| if (!status.ok()) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| asyncResp->res.result(boost::beast::http::status::no_content); | |
| }); | |
| } | |
| inline void handleDeleteBootOption( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName, const std::string& bootOptionId) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [systemName, bootOptionId]( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& /*objectPath*/) { | |
| std::shared_ptr<milotic_tlbmc::SystemRegistry> registry = | |
| managedStore::GetManagedObjectStore()->GetSystemRegistry(); | |
| if (!registry) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| absl::Status status = | |
| registry->DeleteBootOption(systemName, bootOptionId); | |
| if (absl::IsNotFound(status)) { | |
| messages::resourceNotFound(asyncResp->res, "BootOption", | |
| bootOptionId); | |
| return; | |
| } | |
| if (!status.ok()) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| asyncResp->res.result(boost::beast::http::status::no_content); | |
| }); | |
| } | |
| /** | |
| * Systems derived class for delivering Computer Systems Schema. | |
| */ | |
| inline void requestRoutesSystems(App& app) { | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/") | |
| .privileges(redfish::privileges::headComputerSystem) | |
| .methods(boost::beast::http::verb::head)( | |
| std::bind_front(handleComputerSystemCollectionHead, std::ref(app))); | |
| /** | |
| * Functions triggers appropriate requests on DBus | |
| */ | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/") | |
| .privileges(redfish::privileges::getComputerSystem) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleGetComputerSystemCollection, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/") | |
| .privileges(redfish::privileges::patchComputerSystem) | |
| .methods(boost::beast::http::verb::patch)( | |
| std::bind_front(handlePatchComputerSystemCollection, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Oem/Google/BareMetalInstance/") | |
| .privileges(redfish::privileges::getComputerSystem) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleGetBareMetalInstance, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Oem/Google/BareMetalInstance/") | |
| .privileges(redfish::privileges::patchComputerSystem) | |
| .methods(boost::beast::http::verb::patch)( | |
| std::bind_front(handlePatchBareMetalInstance, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Oem/Google/BootNumber/") | |
| .privileges(redfish::privileges::getComputerSystem) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleGetBootNumber, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Boot/BootOptions/") | |
| .privileges(redfish::privileges::getBootOptionCollection) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleGetBootOptionCollection, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Boot/BootOptions/") | |
| .privileges(redfish::privileges::postBootOptionCollection) | |
| .methods(boost::beast::http::verb::post)( | |
| std::bind_front(handlePostBootOptionCollection, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Boot/BootOptions/<str>/") | |
| .privileges(redfish::privileges::getBootOption) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleGetBootOption, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Boot/BootOptions/<str>/") | |
| .privileges(redfish::privileges::patchBootOption) | |
| .methods(boost::beast::http::verb::patch)( | |
| std::bind_front(handlePatchBootOption, std::ref(app))); | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Boot/BootOptions/<str>/") | |
| .privileges(redfish::privileges::deleteBootOption) | |
| .methods(boost::beast::http::verb::delete_)( | |
| std::bind_front(handleDeleteBootOption, std::ref(app))); | |
| #ifdef ENABLE_REDFISH_CONFIGURE_IFS | |
| BMCWEB_ROUTE(app, | |
| "/redfish/v1/Systems/<str>/Actions/Oem/" | |
| "GoogleComputerSystem.ResetCpuIfsPattern") | |
| .privileges(redfish::privileges::postComputerSystem) | |
| .methods(boost::beast::http::verb::post)( | |
| [&app](const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| redfish::system_utils::getSystemInformation( | |
| asyncResp, systemName, | |
| [systemName]( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& /*objectPath*/) { | |
| std::shared_ptr<milotic_tlbmc::SystemRegistry> registry = | |
| managedStore::GetManagedObjectStore() | |
| ->GetSystemRegistry(); | |
| if (!registry) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| absl::Status status = registry->ResetIfsPattern(systemName); | |
| if (!status.ok()) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| messages::success(asyncResp->res); | |
| }); | |
| }); | |
| #endif | |
| } | |
| inline void handleSystemCollectionResetActionHead( | |
| crow::App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| asyncResp->res.addHeader( | |
| boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/ActionInfo/ActionInfo.json>; rel=describedby"); | |
| } | |
| inline void createResetActionInfoResponse( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| asyncResp->res.addHeader( | |
| boost::beast::http::field::link, | |
| "</redfish/v1/JsonSchemas/ActionInfo/ActionInfo.json>; rel=describedby"); | |
| asyncResp->res.jsonValue["@odata.id"] = | |
| "/redfish/v1/Systems/" + systemName + "/ResetActionInfo"; | |
| asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo"; | |
| asyncResp->res.jsonValue["Name"] = "Reset Action Info"; | |
| asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; | |
| nlohmann::json::array_t parameters; | |
| nlohmann::json::object_t parameter; | |
| parameter["Name"] = "ResetType"; | |
| parameter["Required"] = true; | |
| parameter["DataType"] = "String"; | |
| parameter["Delay"] = "Number"; | |
| nlohmann::json::array_t allowableValues; | |
| allowableValues.emplace_back("On"); | |
| allowableValues.emplace_back("ForceOff"); | |
| allowableValues.emplace_back("ForceOn"); | |
| allowableValues.emplace_back("ForceRestart"); | |
| allowableValues.emplace_back("GracefulRestart"); | |
| allowableValues.emplace_back("GracefulShutdown"); | |
| allowableValues.emplace_back("PowerCycle"); | |
| allowableValues.emplace_back("Nmi"); | |
| #ifdef BMCWEB_ENABLE_REALLY_GRACEFUL_SHUTDOWN | |
| allowableValues.emplace_back("ReallyGracefulShutDownTechDebt"); | |
| #endif // BMCWEB_ENABLE_REALLY_GRACEFUL_SHUTDOWN | |
| parameter["AllowableValues"] = std::move(allowableValues); | |
| parameters.emplace_back(std::move(parameter)); | |
| nlohmann::json::object_t delayParameter; | |
| delayParameter["Name"] = "Delay"; | |
| delayParameter["Required"] = false; | |
| delayParameter["DataType"] = "Number"; | |
| parameters.emplace_back(std::move(delayParameter)); | |
| asyncResp->res.jsonValue["Parameters"] = std::move(parameters); | |
| } | |
| inline void handleGetSystemResetActionInfo( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| managedStore::GetManagedObjectStore()->getSubTreePaths( | |
| "/", 0, systemInterfaces, requestContext, | |
| [asyncResp, systemName](const boost::system::error_code& ec, | |
| const std::vector<std::string>& objects) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error"; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| system_utils::findSystemPathAndTriggerCallback( | |
| asyncResp, systemName, objects, | |
| [systemName](const std::shared_ptr<bmcweb::AsyncResp>& resp, | |
| const std::string& /*objectPath*/) { | |
| createResetActionInfoResponse(resp, systemName); | |
| }); | |
| }); | |
| } | |
| /** | |
| * SystemResetActionInfo derived class for delivering Computer Systems | |
| * ResetType AllowableValues using ResetInfo schema. | |
| */ | |
| inline void requestRoutesSystemResetActionInfo(App& app) { | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/ResetActionInfo/") | |
| .privileges(redfish::privileges::headActionInfo) | |
| .methods(boost::beast::http::verb::head)(std::bind_front( | |
| handleSystemCollectionResetActionHead, std::ref(app))); | |
| /** | |
| * Functions triggers appropriate requests on DBus | |
| */ | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/ResetActionInfo/") | |
| .privileges(redfish::privileges::getActionInfo) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleGetSystemResetActionInfo, std::ref(app))); | |
| } | |
| inline void handleHostBootTimeDataGet( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& systemName) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| #ifdef BMCWEB_ENABLE_REDFISH_BOOT_TIME | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| managedStore::GetManagedObjectStore()->getSubTreePaths( | |
| "/", 0, systemInterfaces, requestContext, | |
| [asyncResp, systemName](const boost::system::error_code& ec, | |
| const std::vector<std::string>& objects) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "[handleHostBootTimeDataGet] failed to get " | |
| "number of systems from dbus."; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| auto status = | |
| boottime::PopulateHostData(asyncResp, systemName, objects); | |
| if (!status.ok()) { | |
| BMCWEB_LOG_ERROR << status.message(); | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| }); | |
| #else | |
| std::string oDataId = | |
| absl::StrCat("/redfish/v1/Systems/", systemName, "/Oem/Google/BootTime"); | |
| boottime::PopulateDisabledResponse(asyncResp, oDataId); | |
| #endif | |
| } | |
| inline void requestRoutesHostBootTime(App& app) { | |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Oem/Google/BootTime/") | |
| .privileges(redfish::privileges::getComputerSystemCollection) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleHostBootTimeDataGet, std::ref(app))); | |
| } | |
| } // namespace redfish | |
| #endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_SYSTEMS_H_ |