blob: c05f8cbb058b8d7d4973fcff3b499a0483aaa298 [file] [log] [blame]
#pragma once
#include <async_resp.hpp>
#include <dbus_utility.hpp>
#include <sdbusplus/asio/property.hpp>
#include <algorithm>
#include <string>
#include <vector>
namespace redfish
{
namespace fw_util
{
/* @brief String that indicates a bios firmware instance */
constexpr const char* biosPurpose =
"xyz.openbmc_project.Software.Version.VersionPurpose.Host";
/* @brief String that indicates a BMC firmware instance */
constexpr const char* bmcPurpose =
"xyz.openbmc_project.Software.Version.VersionPurpose.BMC";
/**
* @brief Populate the running firmware version and image links
*
* @param[i,o] aResp Async response object
* @param[i] fwVersionPurpose Indicates what target to look for
* @param[i] activeVersionPropName Index in aResp->res.jsonValue to write
* the running firmware version to
* @param[i] populateLinkToImages Populate aResp->res "Links"
* "ActiveSoftwareImage" with a link to the running firmware image and
* "SoftwareImages" with a link to the all its firmware images
*
* @return void
*/
inline void
populateFirmwareInformation(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
const std::string& fwVersionPurpose,
const std::string& activeVersionPropName,
const bool populateLinkToImages)
{
// Used later to determine running (known on Redfish as active) FW images
sdbusplus::asio::getProperty<std::vector<std::string>>(
*crow::connections::systemBus, "xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/software/functional",
"xyz.openbmc_project.Association", "endpoints",
[aResp, fwVersionPurpose, activeVersionPropName,
populateLinkToImages](const boost::system::error_code ec,
const std::vector<std::string>& functionalFw) {
BMCWEB_LOG_DEBUG << "populateFirmwareInformation enter";
if (ec)
{
BMCWEB_LOG_ERROR << "error_code = " << ec;
BMCWEB_LOG_ERROR << "error msg = " << ec.message();
messages::internalError(aResp->res);
return;
}
if (functionalFw.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> functionalFwIds;
// example functionalFw:
// v as 2 "/xyz/openbmc_project/software/ace821ef"
// "/xyz/openbmc_project/software/230fb078"
for (const auto& fw : functionalFw)
{
sdbusplus::message::object_path path(fw);
std::string leaf = path.filename();
if (leaf.empty())
{
continue;
}
functionalFwIds.push_back(leaf);
}
crow::connections::systemBus->async_method_call(
[aResp, fwVersionPurpose, activeVersionPropName,
populateLinkToImages, functionalFwIds](
const boost::system::error_code ec2,
const std::vector<
std::pair<std::string,
std::vector<std::pair<
std::string, std::vector<std::string>>>>>&
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 firmware 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(functionalFwIds.begin(),
functionalFwIds.end(),
swId) != functionalFwIds.end())
{
runningImage = true;
}
// Now grab its version info
crow::connections::systemBus->async_method_call(
[aResp, swId, runningImage, fwVersionPurpose,
activeVersionPropName, populateLinkToImages](
const boost::system::error_code ec3,
const boost::container::flat_map<
std::string,
dbus::utility::DbusVariantType>&
propertiesList) {
if (ec3)
{
BMCWEB_LOG_ERROR << "error_code = " << ec3;
BMCWEB_LOG_ERROR << "error msg = "
<< ec3.message();
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"
boost::container::flat_map<
std::string,
dbus::utility::DbusVariantType>::
const_iterator it =
propertiesList.find("Purpose");
if (it == propertiesList.end())
{
BMCWEB_LOG_ERROR
<< "Can't find property \"Purpose\"!";
messages::internalError(aResp->res);
return;
}
const std::string* swInvPurpose =
std::get_if<std::string>(&it->second);
if (swInvPurpose == nullptr)
{
BMCWEB_LOG_ERROR << "wrong types for "
"property \"Purpose\"!";
messages::internalError(aResp->res);
return;
}
BMCWEB_LOG_DEBUG << "Image ID: " << swId;
BMCWEB_LOG_DEBUG << "Image purpose: "
<< *swInvPurpose;
BMCWEB_LOG_DEBUG << "Running image: "
<< runningImage;
if (*swInvPurpose != fwVersionPurpose)
{
// Not purpose we're looking for
return;
}
if (populateLinkToImages)
{
nlohmann::json& softwareImageMembers =
aResp->res.jsonValue["Links"]
["SoftwareImages"];
// Firmware images are at
// /redfish/v1/UpdateService/FirmwareInventory/<Id>
// e.g. .../FirmwareInventory/82d3ec86
softwareImageMembers.push_back(
{{"@odata.id",
"/redfish/v1/UpdateService/"
"FirmwareInventory/" +
swId}});
aResp->res.jsonValue
["Links"]
["SoftwareImages@odata.count"] =
softwareImageMembers.size();
if (runningImage)
{
// Create the link to the running image
aResp->res
.jsonValue["Links"]
["ActiveSoftwareImage"] =
{{"@odata.id",
"/redfish/v1/UpdateService/"
"FirmwareInventory/" +
swId}};
}
}
if (!activeVersionPropName.empty() &&
runningImage)
{
it = propertiesList.find("Version");
if (it == propertiesList.end())
{
BMCWEB_LOG_ERROR
<< "Can't find property "
"\"Version\"!";
messages::internalError(aResp->res);
return;
}
const std::string* version =
std::get_if<std::string>(&it->second);
if (version == nullptr)
{
BMCWEB_LOG_ERROR
<< "Error getting fw version";
messages::internalError(aResp->res);
return;
}
aResp->res
.jsonValue[activeVersionPropName] =
*version;
}
},
obj.second[0].first, obj.first,
"org.freedesktop.DBus.Properties", "GetAll",
"xyz.openbmc_project.Software.Version");
}
},
"xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTree",
"/xyz/openbmc_project/software", static_cast<int32_t>(0),
std::array<const char*, 1>{
"xyz.openbmc_project.Software.Version"});
});
}
/**
* @brief Translate input fwState to Redfish state
*
* This function will return the corresponding Redfish state
*
* @param[i] fwState The OpenBMC firmware state
*
* @return The corresponding Redfish state
*/
inline std::string getRedfishFWState(const std::string& fwState)
{
if (fwState == "xyz.openbmc_project.Software.Activation.Activations.Active")
{
return "Enabled";
}
if (fwState == "xyz.openbmc_project.Software.Activation."
"Activations.Activating")
{
return "Updating";
}
if (fwState == "xyz.openbmc_project.Software.Activation."
"Activations.StandbySpare")
{
return "StandbySpare";
}
BMCWEB_LOG_DEBUG << "Default fw state " << fwState << " to Disabled";
return "Disabled";
}
/**
* @brief Translate input fwState to Redfish health state
*
* This function will return the corresponding Redfish health state
*
* @param[i] fwState The OpenBMC firmware state
*
* @return The corresponding Redfish health state
*/
inline std::string getRedfishFWHealth(const std::string& fwState)
{
if ((fwState ==
"xyz.openbmc_project.Software.Activation.Activations.Active") ||
(fwState == "xyz.openbmc_project.Software.Activation.Activations."
"Activating") ||
(fwState ==
"xyz.openbmc_project.Software.Activation.Activations.Ready"))
{
return "OK";
}
BMCWEB_LOG_DEBUG << "FW state " << fwState << " to Warning";
return "Warning";
}
/**
* @brief Put status of input swId into json response
*
* This function will put the appropriate Redfish state of the input
* firmware 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 getFwStatus(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::shared_ptr<std::string>& swId,
const std::string& dbusSvc)
{
BMCWEB_LOG_DEBUG << "getFwStatus: swId " << *swId << " svc " << dbusSvc;
crow::connections::systemBus->async_method_call(
[asyncResp, swId](
const boost::system::error_code errorCode,
const boost::container::flat_map<
std::string, dbus::utility::DbusVariantType>& propertiesList) {
if (errorCode)
{
// not all fwtypes are updateable, this is ok
asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
return;
}
boost::container::flat_map<
std::string, dbus::utility::DbusVariantType>::const_iterator
it = propertiesList.find("Activation");
if (it == propertiesList.end())
{
BMCWEB_LOG_DEBUG << "Can't find property \"Activation\"!";
messages::propertyMissing(asyncResp->res, "Activation");
return;
}
const std::string* swInvActivation =
std::get_if<std::string>(&it->second);
if (swInvActivation == nullptr)
{
BMCWEB_LOG_DEBUG << "wrong types for property\"Activation\"!";
messages::propertyValueTypeError(asyncResp->res, "",
"Activation");
return;
}
BMCWEB_LOG_DEBUG << "getFwStatus: Activation " << *swInvActivation;
asyncResp->res.jsonValue["Status"]["State"] =
getRedfishFWState(*swInvActivation);
asyncResp->res.jsonValue["Status"]["Health"] =
getRedfishFWHealth(*swInvActivation);
},
dbusSvc, "/xyz/openbmc_project/software/" + *swId,
"org.freedesktop.DBus.Properties", "GetAll",
"xyz.openbmc_project.Software.Activation");
}
/**
* @brief Updates programmable status of input swId into json response
*
* This function checks whether firmware inventory component
* can be programmable or not and fill's the "Updateable"
* Property.
*
* @param[i,o] asyncResp Async response object
* @param[i] fwId The firmware ID
*/
inline void
getFwUpdateableStatus(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::shared_ptr<std::string>& fwId)
{
sdbusplus::asio::getProperty<std::vector<std::string>>(
*crow::connections::systemBus, "xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/software/updateable",
"xyz.openbmc_project.Association", "endpoints",
[asyncResp, fwId](const boost::system::error_code ec,
const std::vector<std::string>& objPaths) {
if (ec)
{
BMCWEB_LOG_DEBUG << " error_code = " << ec
<< " error msg = " << ec.message();
// System can exist with no updateable firmware,
// so don't throw error here.
return;
}
std::string reqFwObjPath = "/xyz/openbmc_project/software/" + *fwId;
if (std::find(objPaths.begin(), objPaths.end(), reqFwObjPath) !=
objPaths.end())
{
asyncResp->res.jsonValue["Updateable"] = true;
return;
}
});
}
} // namespace fw_util
} // namespace redfish