blob: d7babc9e533543b7507b14fca22daa6795d76af9 [file] [log] [blame]
#ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_SW_UTILS_H_
#define THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_SW_UTILS_H_
#pragma once
#include <algorithm>
#include <array>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "boost/system/error_code.hpp" // NOLINT
#include "logging.hpp"
#include "utility.hpp"
#include "async_resp.hpp"
#include "dbus_utility.hpp"
#include "error_messages.hpp"
#include "generated/enums/resource.hpp"
#include "dbus_utils.hpp"
#include <nlohmann/json.hpp>
#include "managed_store.hpp"
#include "managed_store_types.hpp"
#include "sdbusplus/message/native_types.hpp"
#include "sdbusplus/unpack_properties.hpp"
#ifdef UNIT_TEST_BUILD
#include "test/g3/mock_managed_store.hpp" // NOLINT
#endif
namespace redfish {
namespace sw_util {
/* @brief String that indicates a bios software instance */
constexpr const char* biosPurpose =
"xyz.openbmc_project.Software.Version.VersionPurpose.Host";
/* @brief String that indicates a BMC software instance */
constexpr const char* bmcPurpose =
"xyz.openbmc_project.Software.Version.VersionPurpose.BMC";
/* @brief String that indicates other firmware instance */
constexpr const char* otherPurpose =
"xyz.openbmc_project.Software.Version.VersionPurpose.Other";
/**
* @brief Populate the running software version and image links
*
* @param[i,o] aResp Async response object
* @param[i] swVersionPurpose Indicates what target to look for
* @param[i] activeVersionPropName Index in aResp->res.jsonValue to write
* the running software version to
* @param[i] populateLinkToImages Populate aResp->res "Links"
* "ActiveSoftwareImage" with a link to the running software image and
* "SoftwareImages" with a link to the all its software images
*
* @return void
*/
inline void populateSoftwareInformation(
const std::shared_ptr<bmcweb::AsyncResp>& aResp,
const std::string& swVersionPurpose,
const std::string& activeVersionPropName, const bool populateLinkToImages) {
// Used later to determine running (known on Redfish as active) Sw images
managedStore::ManagedObjectStoreContext requestContext(aResp);
dbus_utils::getAssociationEndPoints(
"/xyz/openbmc_project/software/functional", requestContext,
[aResp, swVersionPurpose, activeVersionPropName, populateLinkToImages](
const boost::system::error_code& ec,
const dbus::utility::MapperEndPoints& functionalSw) {
BMCWEB_LOG_DEBUG << "populateSoftwareInformation enter";
if (ec) {
BMCWEB_LOG_ERROR << "error_code = " << ec;
BMCWEB_LOG_ERROR << "error msg = " << ec.message();
messages::internalError(aResp->res);
return;
}
if (functionalSw.empty()) {
// Could keep going and try to populate SoftwareImages but
// something is seriously wrong, so just fail
BMCWEB_LOG_ERROR << "Zero functional software in system";
messages::internalError(aResp->res);
return;
}
std::vector<std::string> functionalSwIds;
// example functionalSw:
// v as 2 "/xyz/openbmc_project/software/ace821ef"
// "/xyz/openbmc_project/software/230fb078"
for (const auto& sw : functionalSw) {
sdbusplus::message::object_path path(sw);
std::string leaf = path.filename();
if (leaf.empty()) {
continue;
}
functionalSwIds.push_back(leaf);
}
constexpr std::array<std::string_view, 1> interfaces = {
"xyz.openbmc_project.Software.Version"};
managedStore::ManagedObjectStoreContext requestContext(aResp);
managedStore::GetManagedObjectStore()->getSubTree(
"/xyz/openbmc_project/software", 0, interfaces, requestContext,
[aResp, swVersionPurpose, activeVersionPropName,
populateLinkToImages, functionalSwIds](
const boost::system::error_code& ec2,
const dbus::utility::MapperGetSubTreeResponse& subtree) {
if (ec2) {
BMCWEB_LOG_ERROR << "error_code = " << ec2;
BMCWEB_LOG_ERROR << "error msg = " << ec2.message();
messages::internalError(aResp->res);
return;
}
BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " images";
for (const std::pair<std::string,
std::vector<std::pair<
std::string, std::vector<std::string>>>>&
obj : subtree) {
sdbusplus::message::object_path path(obj.first);
std::string swId = path.filename();
if (swId.empty()) {
messages::internalError(aResp->res);
BMCWEB_LOG_ERROR << "Invalid software ID";
return;
}
bool runningImage = false;
// Look at Ids from
// /xyz/openbmc_project/software/functional
// to determine if this is a running image
if (std::find(functionalSwIds.begin(), functionalSwIds.end(),
swId) != functionalSwIds.end()) {
runningImage = true;
}
// Now grab its version info
managedStore::ManagedObjectStoreContext context(aResp);
managedStore::GetManagedObjectStore()->getAllProperties(
obj.second[0].first, obj.first,
"xyz.openbmc_project.Software.Version", context,
[aResp, swId, runningImage, swVersionPurpose,
activeVersionPropName, populateLinkToImages](
const boost::system::error_code& ec3,
const dbus::utility::DBusPropertiesMap&
propertiesList) {
if (ec3) {
BMCWEB_LOG_ERROR << "error_code = " << ec3;
BMCWEB_LOG_ERROR << "error msg = " << ec3.message();
// Have seen the code update app delete the D-Bus
// object, during code update, between the call to
// mapper and here. Just leave these properties off if
// resource not found.
if (ec3.value() == EBADR) {
return;
}
messages::internalError(aResp->res);
return;
}
// example propertiesList
// a{sv} 2 "Version" s
// "IBM-witherspoon-OP9-v2.0.10-2.22" "Purpose"
// s
// "xyz.openbmc_project.Software.Version.VersionPurpose.Host"
const std::string* version = nullptr;
const std::string* swInvPurpose = nullptr;
const bool success = sdbusplus::unpackPropertiesNoThrow(
dbus_utils::UnpackErrorPrinter(), propertiesList,
"Purpose", swInvPurpose, "Version", version);
if (!success) {
messages::internalError(aResp->res);
return;
}
if (version == nullptr || version->empty()) {
messages::internalError(aResp->res);
return;
}
if (swInvPurpose == nullptr ||
*swInvPurpose != swVersionPurpose) {
// Not purpose we're looking for
return;
}
BMCWEB_LOG_DEBUG << "Image ID: " << swId;
BMCWEB_LOG_DEBUG << "Running image: " << runningImage;
BMCWEB_LOG_DEBUG << "Image purpose: " << *swInvPurpose;
if (populateLinkToImages) {
nlohmann::json& softwareImageMembers =
aResp->res.jsonValue["Links"]["SoftwareImages"];
// Firmware images are at
// /redfish/v1/UpdateService/FirmwareInventory/<Id>
// e.g. .../FirmwareInventory/82d3ec86
nlohmann::json::object_t member;
member["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "UpdateService",
"FirmwareInventory", swId);
softwareImageMembers.push_back(std::move(member));
aResp->res
.jsonValue["Links"]["SoftwareImages@odata.count"] =
softwareImageMembers.size();
if (runningImage) {
nlohmann::json::object_t runningMember;
runningMember["@odata.id"] =
crow::utility::urlFromPieces(
"redfish", "v1", "UpdateService",
"FirmwareInventory", swId);
// Create the link to the running image
aResp->res.jsonValue["Links"]["ActiveSoftwareImage"] =
std::move(runningMember);
}
}
if (!activeVersionPropName.empty() && runningImage) {
aResp->res.jsonValue[activeVersionPropName] = *version;
}
});
}
});
});
}
/**
* @brief Translate input swState to Redfish state
*
* This function will return the corresponding Redfish state
*
* @param[i] swState The OpenBMC software state
*
* @return The corresponding Redfish state
*/
inline resource::State getRedfishSwState(const std::string& swState) {
if (swState == "xyz.openbmc_project.Software.Activation.Activations.Active") {
return resource::State::Enabled;
}
if (swState ==
"xyz.openbmc_project.Software.Activation."
"Activations.Activating") {
return resource::State::Updating;
}
if (swState ==
"xyz.openbmc_project.Software.Activation."
"Activations.StandbySpare") {
return resource::State::StandbySpare;
}
BMCWEB_LOG_DEBUG << "Default sw state " << swState << " to Disabled";
return resource::State::Disabled;
}
/**
* @brief Translate input swState to Redfish health state
*
* This function will return the corresponding Redfish health state
*
* @param[i] swState The OpenBMC software state
*
* @return The corresponding Redfish health state
*/
inline std::string getRedfishSwHealth(const std::string& swState) {
if ((swState ==
"xyz.openbmc_project.Software.Activation.Activations.Active") ||
(swState == "xyz.openbmc_project.Software.Activation.Activations."
"Activating") ||
(swState ==
"xyz.openbmc_project.Software.Activation.Activations.Ready")) {
return "OK";
}
BMCWEB_LOG_DEBUG << "Sw state " << swState << " to Warning";
return "Warning";
}
/**
* @brief Put status of input swId into json response
*
* This function will put the appropriate Redfish state of the input
* software id to ["Status"]["State"] within the json response
*
* @param[i,o] aResp Async response object
* @param[i] swId The software ID to get status for
* @param[i] dbusSvc The dbus service implementing the software object
*
* @return void
*/
inline void getSwStatus(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::shared_ptr<std::string>& swId,
const std::string& dbusSvc) {
BMCWEB_LOG_DEBUG << "getSwStatus: swId " << *swId << " svc " << dbusSvc;
managedStore::ManagedObjectStoreContext context(asyncResp);
managedStore::GetManagedObjectStore()->getAllProperties(
dbusSvc, "/xyz/openbmc_project/software/" + *swId,
"xyz.openbmc_project.Software.Activation", context,
[asyncResp, swId](
const boost::system::error_code& errorCode,
const dbus::utility::DBusPropertiesMap& propertiesList) {
if (errorCode) {
// not all swtypes are updateable, this is ok
asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
return;
}
const std::string* swInvActivation = nullptr;
const bool success = sdbusplus::unpackPropertiesNoThrow(
dbus_utils::UnpackErrorPrinter(), propertiesList, "Activation",
swInvActivation);
if (!success) {
messages::internalError(asyncResp->res);
return;
}
if (swInvActivation == nullptr) {
messages::internalError(asyncResp->res);
return;
}
BMCWEB_LOG_DEBUG << "getSwStatus: Activation " << *swInvActivation;
asyncResp->res.jsonValue["Status"]["State"] =
getRedfishSwState(*swInvActivation);
asyncResp->res.jsonValue["Status"]["Health"] =
getRedfishSwHealth(*swInvActivation);
});
}
/**
* @brief Updates programmable status of input swId into json response
*
* This function checks whether software inventory component
* can be programmable or not and fill's the "Updatable"
* Property.
*
* @param[i,o] asyncResp Async response object
* @param[i] swId The software ID
*/
inline void getSwUpdatableStatus(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::shared_ptr<std::string>& swId) {
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
dbus_utils::getAssociationEndPoints(
"/xyz/openbmc_project/software/updateable", requestContext,
[asyncResp, swId](const boost::system::error_code& ec,
const dbus::utility::MapperEndPoints& objPaths) {
if (ec) {
BMCWEB_LOG_DEBUG << " error_code = " << ec
<< " error msg = " << ec.message();
// System can exist with no updateable software,
// so don't throw error here.
return;
}
std::string reqSwObjPath = "/xyz/openbmc_project/software/" + *swId;
if (std::find(objPaths.begin(), objPaths.end(), reqSwObjPath) !=
objPaths.end()) {
asyncResp->res.jsonValue["Updateable"] = true;
return;
}
});
}
/**
* @brief Updates programmable settings of input swId into json response
*
* This function checks whether firmware inventory component's setting from
* D-bus. It only looks like the `WriteProtected` property to see if the
* firmware is write protect.
*
* @param[i,o] asyncResp Async response object
* @param[i] service Service handling the firmware
* @param[i] path D-bus path of the firmware component
*/
inline void getSoftwareSettings(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& service, const std::string& path) {
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
dbus_utils::getProperty<bool>(
service, path, "xyz.openbmc_project.Software.Settings", "WriteProtected",
requestContext,
[asyncResp](const boost::system::error_code ec, bool writeProtected) {
if (ec) {
// System can exist with no updateable/writable firmware,
// so don't throw error here.
BMCWEB_LOG_DEBUG << " getFirmwareSettings error_code = " << ec
<< " error msg = " << ec.message();
return;
}
asyncResp->res.jsonValue["WriteProtected"] = writeProtected;
});
}
} // namespace sw_util
} // namespace redfish
#endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_SW_UTILS_H_