blob: 6f0d96d2887471798a0c2fecea2512a00b0506c0 [file] [log] [blame]
/*
// Copyright (c) 2018 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#pragma once
#include "bm_config.h"
#include "bmcweb_config.h"
#include "app.hpp"
#include "dbus_utility.hpp"
#include "health.hpp"
#include "hypervisor_system.hpp"
#include "led.hpp"
#include "managed_store.hpp"
#include "pcie.hpp"
#include "query.hpp"
#include "redfish_util.hpp"
#include "registries/privilege_registry.hpp"
#include "utils/boot_time_utils.hpp"
#include "utils/dbus_utils.hpp"
#include "utils/json_utils.hpp"
#include "utils/sw_utils.hpp"
#include "utils/time_utils.hpp"
#include <boost/container/flat_map.hpp>
#include <boost/system/error_code.hpp>
#include <sdbusplus/asio/property.hpp>
#include <sdbusplus/message/types.hpp>
#include <sdbusplus/unpack_properties.hpp>
#include <array>
#include <filesystem>
#include <iomanip>
#include <string_view>
#include <variant>
#ifdef UNIT_TEST_BUILD
#include "test/g3/mock_managed_store.hpp" // NOLINT
#endif
namespace redfish
{
const static 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"}};
/**
* @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)
{
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](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
for (const auto& connection : connectionNames)
{
for (const auto& interfaceName : connection.second)
{
if (interfaceName ==
"xyz.openbmc_project.Inventory.Item.Dimm")
{
BMCWEB_LOG_DEBUG
<< "Found Dimm, now get its properties.";
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](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);
});
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;
});
}
}
break;
}
}
});
}
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";
messages::internalError(aResp->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, "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 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)
{
BMCWEB_LOG_DEBUG << "Get boot information.";
getBootOverrideSource(aResp);
getBootOverrideType(aResp);
getBootOverrideEnable(aResp);
}
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";
messages::internalError(aResp->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, "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_,
[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_,
[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_,
[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_,
[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_,
[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_,
[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 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.
*
* @return Integer error code.
*/
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)
{
BMCWEB_LOG_DEBUG << "Set boot information.";
setBootModeOrSource(aResp, bootSource);
setBootType(aResp, bootType);
setBootEnable(aResp, bootEnable);
}
/**
* @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_,
[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_,
[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_,
[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_,
[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_,
[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_,
[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";
}
/**
* @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_,
[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_,
[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_,
[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_,
[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_,
[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 there are less than 2 Item.Systems assume singlehost
if (objects.size() < 2)
{
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();
});
dbus_utils::getProperty<std::string>(
"xyz.openbmc_project.Settings",
"/xyz/openbmc_project/network/hypervisor",
"xyz.openbmc_project.Network.SystemConfiguration", "HostName", context,
[asyncResp](const boost::system::error_code& ec2,
const std::string& /*hostName*/) {
if (!ec2)
{
BMCWEB_LOG_DEBUG << "Hypervisor is available";
nlohmann::json::object_t hypervisor;
hypervisor["@odata.id"] = "/redfish/v1/Systems/hypervisor";
asyncResp->res.jsonValue["Members"].push_back(
std::move(hypervisor));
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_,
[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_,
[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"
if (resetType == "ReallyGracefulShutDownTechDebt")
{
return SystemResetCommand {
.command = "xyz.openbmc_project.State.Host.Transition.ReallyGracefulShutDown",
.hostCommand = true
};
}
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;
}
// If there are less than 2 Item.Systems assume singlehost
if (objects.size() < 2)
{
if (systemName != "system")
{
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}
sendSystemResetAction(req, asyncResp, "", systemName);
return;
}
// Otherwise list all systems
for (const auto& object : objects)
{
std::string objectName = object.substr(object.rfind('/') + 1);
// Found system object
if (systemName == objectName)
{
sendSystemResetAction(req, asyncResp, object, systemName);
return;
}
}
// Could not find system object
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
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"] = "/redfish/v1/Systems/" + systemName;
asyncResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"]["target"] =
"/redfish/v1/Systems/" + systemName + "/Actions/ComputerSystem.Reset";
asyncResp->res
.jsonValue["Actions"]["#ComputerSystem.Reset"]["@Redfish.ActionInfo"] =
"/redfish/v1/Systems/" + systemName + "/ResetActionInfo";
asyncResp->res.jsonValue["Processors"]["@odata.id"] =
"/redfish/v1/Systems/" + systemName + "/Processors";
asyncResp->res.jsonValue["Storage"]["@odata.id"] =
"/redfish/v1/Systems/" + systemName + "/Storage";
getBMReady(asyncResp);
if constexpr (enableGoogleBaremetal)
{
asyncResp->res
.jsonValue["Actions"]["Oem"]["Google"]
["#GoogleComputerSystem.BootGuestOS"]["target"] =
"/redfish/v1/Systems/" + systemName +
"/Actions/Oem/GoogleComputerSystem.BootGuestOS";
asyncResp->res.jsonValue["Actions"]["Oem"]["Google"]
["#GoogleComputerSystem.BootGuestOS"]
["@Redfish.ActionInfo"] =
"/redfish/v1/Systems/" + systemName +
"/Oem/Google/BootGuestOSActionInfo";
asyncResp->res
.jsonValue["Oem"]["Google"]["BareMetalInstance"]["@odata.id"] =
"/redfish/v1/Systems/" + systemName +
"/Oem/Google/BareMetalInstance";
getNERFStatus(asyncResp, systemName);
}
asyncResp->res.jsonValue["LogServices"]["@odata.id"] =
"/redfish/v1/Systems/" + systemName + "/LogServices";
asyncResp->res.jsonValue["Memory"]["@odata.id"] =
"/redfish/v1/Systems/" + systemName + "/Memory";
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](
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;
}
getHostState(asyncResp, systemObjectPath, objects);
getLastResetTime(asyncResp, systemObjectPath, objects);
});
getPCIeDeviceList(asyncResp, systemName, "PCIeDevices");
// 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"] =
uint64_t(0);
asyncResp->res.jsonValue["MemorySummary"]["Status"]["State"] =
"Disabled";
asyncResp->res.jsonValue["FabricAdapters"]["@odata.id"] =
"/redfish/v1/Systems/system/FabricAdapters";
asyncResp->res.jsonValue["Bios"]["@odata.id"] =
"/redfish/v1/Systems/system/Bios";
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));
#ifdef BMCWEB_ENABLE_KVM
// Fill in GraphicalConsole info
asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true;
asyncResp->res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] =
4;
asyncResp->res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] =
nlohmann::json::array_t({"KVMIP"});
#endif // BMCWEB_ENABLE_KVM
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);
getBootProperties(asyncResp);
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);
// Enabled boot time only for single host
#ifdef BMCWEB_ENABLE_REDFISH_BOOT_TIME
asyncResp->res.jsonValue["Oem"]["Google"]["BootTime"]["@odata.id"] =
"/redfish/v1/Systems/system/Oem/Google/BootTime";
#endif
}
}
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;
}
if (systemName == "hypervisor")
{
handleHypervisorSystemGet(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;
}
// If there are less than 2 Item.Systems assume singlehost
if (objects.size() < 2)
{
if (systemName != "system")
{
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}
handleComputerSystem(asyncResp, "", systemName, false);
return;
}
// Otherwise find system
for (const auto& object : objects)
{
std::string objectName = object.substr(object.rfind('/') + 1);
// Found system object
if (systemName == objectName)
{
handleComputerSystem(asyncResp, object, systemName, true);
return;
}
}
// Could not find system object
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
});
}
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;
}
if (systemName != "system")
{
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}
asyncResp->res.addHeader(
boost::beast::http::field::link,
"</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby");
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::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;
// clang-format off
if (!json_util::readJsonPatch(
req, asyncResp->res,
"IndicatorLED", indicatorLed,
"LocationIndicatorActive", locationIndicatorActive,
"AssetTag", assetTag,
"PowerRestorePolicy", powerRestorePolicy,
"PowerMode", powerMode,
"HostWatchdogTimer/FunctionEnabled", wdtEnable,
"HostWatchdogTimer/TimeoutAction", wdtTimeOutAction,
"Boot/BootSourceOverrideTarget", bootSource,
"Boot/BootSourceOverrideMode", bootType,
"Boot/BootSourceOverrideEnabled", bootEnable,
"Boot/AutomaticRetryConfig", bootAutomaticRetry,
"Boot/TrustedModuleRequiredToBoot", bootTrustedModuleRequired,
"IdlePowerSaver/Enabled", ipsEnable,
"IdlePowerSaver/EnterUtilizationPercent", ipsEnterUtil,
"IdlePowerSaver/EnterDwellTimeSeconds", ipsEnterTime,
"IdlePowerSaver/ExitUtilizationPercent", ipsExitUtil,
"IdlePowerSaver/ExitDwellTimeSeconds", ipsExitTime))
{
return;
}
// clang-format on
asyncResp->res.result(boost::beast::http::status::no_content);
if (assetTag)
{
setAssetTag(asyncResp, *assetTag);
}
if (wdtEnable || wdtTimeOutAction)
{
setWDTProperties(asyncResp, wdtEnable, wdtTimeOutAction);
}
if (bootSource || bootType || bootEnable)
{
setBootProperties(asyncResp, bootSource, bootType, bootEnable);
}
if (bootAutomaticRetry)
{
setAutomaticRetry(asyncResp, *bootAutomaticRetry);
}
if (bootTrustedModuleRequired)
{
setTrustedModuleRequiredToBoot(asyncResp, *bootTrustedModuleRequired);
}
if (locationIndicatorActive)
{
setLocationIndicatorActive(asyncResp, *locationIndicatorActive);
}
// TODO (Gunnar): Remove IndicatorLED after enough time has
// passed
if (indicatorLed)
{
setIndicatorLedState(asyncResp, *indicatorLed);
asyncResp->res.addHeader(boost::beast::http::field::warning,
"299 - \"IndicatorLED is deprecated. Use "
"LocationIndicatorActive instead.\"");
}
if (powerRestorePolicy)
{
setPowerRestorePolicy(asyncResp, *powerRestorePolicy);
}
if (powerMode)
{
setPowerMode(asyncResp, *powerMode);
}
if (ipsEnable || ipsEnterUtil || ipsEnterTime || ipsExitUtil || ipsExitTime)
{
setIdlePowerSaver(asyncResp, ipsEnable, ipsEnterUtil, ipsEnterTime,
ipsExitUtil, ipsExitTime);
}
}
inline void 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(""));
}
}
/**
* 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)));
}
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");
allowableValues.emplace_back("ReallyGracefulShutDownTechDebt");
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;
}
if (systemName == "hypervisor")
{
handleHypervisorResetActionGet(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;
}
// If there are less than 2 Item.Systems assume singlehost
if (objects.size() < 2)
{
if (systemName != "system")
{
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}
createResetActionInfoResponse(asyncResp, systemName);
return;
}
// Otherwise list all systems
for (const auto& object : objects)
{
std::string objectName = object.substr(object.rfind('/') + 1);
// Found system object
if (systemName == objectName)
{
createResetActionInfoResponse(asyncResp, systemName);
return;
}
}
// Could not find system object
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
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;
}
// only for single host
if (systemName != "system")
{
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}
boot_time_utils::PopulateDefaultBootTimeProperties(asyncResp);
asyncResp->res.jsonValue["@odata.id"] =
"/redfish/v1/Systems/" + systemName + "/Oem/Google/BootTime";
std::shared_ptr<std::vector<boot_time_utils::Checkpoint>> hostCheckpoints =
std::make_shared<std::vector<boot_time_utils::Checkpoint>>();
std::shared_ptr<std::vector<boot_time_utils::Duration>> hostDurations =
std::make_shared<std::vector<boot_time_utils::Duration>>();
std::shared_ptr<std::vector<boot_time_utils::Checkpoint>> bmcCheckpoints =
std::make_shared<std::vector<boot_time_utils::Checkpoint>>();
std::shared_ptr<bool> hostIsRebooting = std::make_shared<bool>(false);
std::shared_ptr<int> waitingForLoadingData = std::make_shared<int>(4);
std::shared_ptr<bool> errorOccurred = std::make_shared<bool>(false);
std::function<void()> dbusSuccessCallback =
[asyncResp, hostCheckpoints, hostDurations, bmcCheckpoints,
hostIsRebooting, waitingForLoadingData, errorOccurred]() {
--(*waitingForLoadingData);
if (*waitingForLoadingData == 0 && !*errorOccurred)
{
boot_time_utils::GetHostBreakdownStages(
asyncResp, hostCheckpoints, bmcCheckpoints, hostDurations);
boot_time_utils::PopulateDurationInResponse(asyncResp,
hostDurations);
boot_time_utils::GetHostRebootFlow(asyncResp, hostCheckpoints,
bmcCheckpoints);
boot_time_utils::PopulateTotalTimeInResponse(
asyncResp, hostCheckpoints, hostDurations);
boot_time_utils::PopulateStatPowerSourceInResponse(asyncResp,
hostCheckpoints);
boot_time_utils::PopulateStatIsRebootingInResponse(asyncResp,
hostIsRebooting);
}
};
std::function<void()> dbusErrCallback = [errorOccurred,
waitingForLoadingData]() {
--(*waitingForLoadingData);
*errorOccurred = true;
};
boot_time_utils::GetBootTimeCheckpoints(asyncResp, "host0", hostCheckpoints,
dbusSuccessCallback,
dbusErrCallback);
boot_time_utils::GetBootTimeDurations(asyncResp, "host0", hostDurations,
dbusSuccessCallback, dbusErrCallback);
boot_time_utils::GetBootTimeCheckpoints(
asyncResp, "bmc", bmcCheckpoints, dbusSuccessCallback, dbusErrCallback);
boot_time_utils::GetIsRebooting(asyncResp, "host0", hostIsRebooting,
dbusSuccessCallback, dbusErrCallback);
}
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