1eee0013eSWilly Tu #pragma once 23ccb3adbSEd Tanous #include "async_resp.hpp" 33ccb3adbSEd Tanous #include "dbus_utility.hpp" 43ccb3adbSEd Tanous #include "error_messages.hpp" 53ccb3adbSEd Tanous #include "generated/enums/resource.hpp" 6eddfc437SWilly Tu #include "http/utility.hpp" 73ccb3adbSEd Tanous #include "utils/dbus_utils.hpp" 83ccb3adbSEd Tanous 9e99073f5SGeorge Liu #include <boost/system/error_code.hpp> 10ef4c65b7SEd Tanous #include <boost/url/format.hpp> 11eee0013eSWilly Tu #include <sdbusplus/asio/property.hpp> 12d1bde9e5SKrzysztof Grobelny #include <sdbusplus/unpack_properties.hpp> 13eee0013eSWilly Tu 14eee0013eSWilly Tu #include <algorithm> 15e99073f5SGeorge Liu #include <array> 16*3544d2a7SEd Tanous #include <ranges> 17eee0013eSWilly Tu #include <string> 18e99073f5SGeorge Liu #include <string_view> 19eee0013eSWilly Tu #include <vector> 20eee0013eSWilly Tu 21eee0013eSWilly Tu namespace redfish 22eee0013eSWilly Tu { 23eee0013eSWilly Tu namespace sw_util 24eee0013eSWilly Tu { 25eee0013eSWilly Tu /* @brief String that indicates a bios software instance */ 26eee0013eSWilly Tu constexpr const char* biosPurpose = 27eee0013eSWilly Tu "xyz.openbmc_project.Software.Version.VersionPurpose.Host"; 28eee0013eSWilly Tu 29eee0013eSWilly Tu /* @brief String that indicates a BMC software instance */ 30eee0013eSWilly Tu constexpr const char* bmcPurpose = 31eee0013eSWilly Tu "xyz.openbmc_project.Software.Version.VersionPurpose.BMC"; 32eee0013eSWilly Tu 33eee0013eSWilly Tu /** 34eee0013eSWilly Tu * @brief Populate the running software version and image links 35eee0013eSWilly Tu * 36ac106bf6SEd Tanous * @param[i,o] asyncResp Async response object 37eee0013eSWilly Tu * @param[i] swVersionPurpose Indicates what target to look for 38ac106bf6SEd Tanous * @param[i] activeVersionPropName Index in asyncResp->res.jsonValue to write 39eee0013eSWilly Tu * the running software version to 40ac106bf6SEd Tanous * @param[i] populateLinkToImages Populate asyncResp->res "Links" 41eee0013eSWilly Tu * "ActiveSoftwareImage" with a link to the running software image and 42eee0013eSWilly Tu * "SoftwareImages" with a link to the all its software images 43eee0013eSWilly Tu * 44eee0013eSWilly Tu * @return void 45eee0013eSWilly Tu */ 46ac106bf6SEd Tanous inline void populateSoftwareInformation( 47ac106bf6SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 48eee0013eSWilly Tu const std::string& swVersionPurpose, 49ac106bf6SEd Tanous const std::string& activeVersionPropName, const bool populateLinkToImages) 50eee0013eSWilly Tu { 51eee0013eSWilly Tu // Used later to determine running (known on Redfish as active) Sw images 52a4eb761aSGeorge Liu dbus::utility::getAssociationEndPoints( 53eee0013eSWilly Tu "/xyz/openbmc_project/software/functional", 54ac106bf6SEd Tanous [asyncResp, swVersionPurpose, activeVersionPropName, 55ac106bf6SEd Tanous populateLinkToImages]( 56a4eb761aSGeorge Liu const boost::system::error_code& ec, 57a4eb761aSGeorge Liu const dbus::utility::MapperEndPoints& functionalSw) { 5862598e31SEd Tanous BMCWEB_LOG_DEBUG("populateSoftwareInformation enter"); 59eee0013eSWilly Tu if (ec) 60eee0013eSWilly Tu { 6162598e31SEd Tanous BMCWEB_LOG_ERROR("error_code = {}", ec); 6262598e31SEd Tanous BMCWEB_LOG_ERROR("error msg = {}", ec.message()); 63ac106bf6SEd Tanous messages::internalError(asyncResp->res); 64eee0013eSWilly Tu return; 65eee0013eSWilly Tu } 66eee0013eSWilly Tu 67eee0013eSWilly Tu if (functionalSw.empty()) 68eee0013eSWilly Tu { 69eee0013eSWilly Tu // Could keep going and try to populate SoftwareImages but 70eee0013eSWilly Tu // something is seriously wrong, so just fail 7162598e31SEd Tanous BMCWEB_LOG_ERROR("Zero functional software in system"); 72ac106bf6SEd Tanous messages::internalError(asyncResp->res); 73eee0013eSWilly Tu return; 74eee0013eSWilly Tu } 75eee0013eSWilly Tu 76eee0013eSWilly Tu std::vector<std::string> functionalSwIds; 77eee0013eSWilly Tu // example functionalSw: 78eee0013eSWilly Tu // v as 2 "/xyz/openbmc_project/software/ace821ef" 79eee0013eSWilly Tu // "/xyz/openbmc_project/software/230fb078" 80c6830d5fSGunnar Mills for (const auto& sw : functionalSw) 81eee0013eSWilly Tu { 82c6830d5fSGunnar Mills sdbusplus::message::object_path path(sw); 83eee0013eSWilly Tu std::string leaf = path.filename(); 84eee0013eSWilly Tu if (leaf.empty()) 85eee0013eSWilly Tu { 86eee0013eSWilly Tu continue; 87eee0013eSWilly Tu } 88eee0013eSWilly Tu 89eee0013eSWilly Tu functionalSwIds.push_back(leaf); 90eee0013eSWilly Tu } 91eee0013eSWilly Tu 92e99073f5SGeorge Liu constexpr std::array<std::string_view, 1> interfaces = { 93e99073f5SGeorge Liu "xyz.openbmc_project.Software.Version"}; 94e99073f5SGeorge Liu dbus::utility::getSubTree( 95e99073f5SGeorge Liu "/xyz/openbmc_project/software", 0, interfaces, 96ac106bf6SEd Tanous [asyncResp, swVersionPurpose, activeVersionPropName, 97eee0013eSWilly Tu populateLinkToImages, functionalSwIds]( 98e99073f5SGeorge Liu const boost::system::error_code& ec2, 99eee0013eSWilly Tu const dbus::utility::MapperGetSubTreeResponse& subtree) { 100eee0013eSWilly Tu if (ec2) 101eee0013eSWilly Tu { 10262598e31SEd Tanous BMCWEB_LOG_ERROR("error_code = {}", ec2); 10362598e31SEd Tanous BMCWEB_LOG_ERROR("error msg = {}", ec2.message()); 104ac106bf6SEd Tanous messages::internalError(asyncResp->res); 105eee0013eSWilly Tu return; 106eee0013eSWilly Tu } 107eee0013eSWilly Tu 10862598e31SEd Tanous BMCWEB_LOG_DEBUG("Found {} images", subtree.size()); 109eee0013eSWilly Tu 110eee0013eSWilly Tu for (const std::pair<std::string, 111eee0013eSWilly Tu std::vector<std::pair< 112eee0013eSWilly Tu std::string, std::vector<std::string>>>>& 113eee0013eSWilly Tu obj : subtree) 114eee0013eSWilly Tu { 115eee0013eSWilly Tu sdbusplus::message::object_path path(obj.first); 116eee0013eSWilly Tu std::string swId = path.filename(); 117eee0013eSWilly Tu if (swId.empty()) 118eee0013eSWilly Tu { 119ac106bf6SEd Tanous messages::internalError(asyncResp->res); 12062598e31SEd Tanous BMCWEB_LOG_ERROR("Invalid software ID"); 121eee0013eSWilly Tu 122eee0013eSWilly Tu return; 123eee0013eSWilly Tu } 124eee0013eSWilly Tu 125eee0013eSWilly Tu bool runningImage = false; 126eee0013eSWilly Tu // Look at Ids from 127eee0013eSWilly Tu // /xyz/openbmc_project/software/functional 128eee0013eSWilly Tu // to determine if this is a running image 129*3544d2a7SEd Tanous if (std::ranges::find(functionalSwIds, swId) != 130*3544d2a7SEd Tanous functionalSwIds.end()) 131eee0013eSWilly Tu { 132eee0013eSWilly Tu runningImage = true; 133eee0013eSWilly Tu } 134eee0013eSWilly Tu 135eee0013eSWilly Tu // Now grab its version info 136d1bde9e5SKrzysztof Grobelny sdbusplus::asio::getAllProperties( 137d1bde9e5SKrzysztof Grobelny *crow::connections::systemBus, obj.second[0].first, 138d1bde9e5SKrzysztof Grobelny obj.first, "xyz.openbmc_project.Software.Version", 139ac106bf6SEd Tanous [asyncResp, swId, runningImage, swVersionPurpose, 140eee0013eSWilly Tu activeVersionPropName, populateLinkToImages]( 1415e7e2dc5SEd Tanous const boost::system::error_code& ec3, 142eee0013eSWilly Tu const dbus::utility::DBusPropertiesMap& 143eee0013eSWilly Tu propertiesList) { 144eee0013eSWilly Tu if (ec3) 145eee0013eSWilly Tu { 14662598e31SEd Tanous BMCWEB_LOG_ERROR("error_code = {}", ec3); 14762598e31SEd Tanous BMCWEB_LOG_ERROR("error msg = {}", ec3.message()); 148eee0013eSWilly Tu // Have seen the code update app delete the D-Bus 149eee0013eSWilly Tu // object, during code update, between the call to 150eee0013eSWilly Tu // mapper and here. Just leave these properties off if 151eee0013eSWilly Tu // resource not found. 152eee0013eSWilly Tu if (ec3.value() == EBADR) 153eee0013eSWilly Tu { 154eee0013eSWilly Tu return; 155eee0013eSWilly Tu } 156ac106bf6SEd Tanous messages::internalError(asyncResp->res); 157eee0013eSWilly Tu return; 158eee0013eSWilly Tu } 159eee0013eSWilly Tu // example propertiesList 160eee0013eSWilly Tu // a{sv} 2 "Version" s 161eee0013eSWilly Tu // "IBM-witherspoon-OP9-v2.0.10-2.22" "Purpose" 162eee0013eSWilly Tu // s 163eee0013eSWilly Tu // "xyz.openbmc_project.Software.Version.VersionPurpose.Host" 164d1bde9e5SKrzysztof Grobelny const std::string* version = nullptr; 165d1bde9e5SKrzysztof Grobelny const std::string* swInvPurpose = nullptr; 166d1bde9e5SKrzysztof Grobelny 167d1bde9e5SKrzysztof Grobelny const bool success = sdbusplus::unpackPropertiesNoThrow( 168d1bde9e5SKrzysztof Grobelny dbus_utils::UnpackErrorPrinter(), propertiesList, 169d1bde9e5SKrzysztof Grobelny "Purpose", swInvPurpose, "Version", version); 170d1bde9e5SKrzysztof Grobelny 171d1bde9e5SKrzysztof Grobelny if (!success) 172eee0013eSWilly Tu { 173ac106bf6SEd Tanous messages::internalError(asyncResp->res); 174eee0013eSWilly Tu return; 175eee0013eSWilly Tu } 176eee0013eSWilly Tu 177d1bde9e5SKrzysztof Grobelny if (version == nullptr || version->empty()) 178eee0013eSWilly Tu { 179ac106bf6SEd Tanous messages::internalError(asyncResp->res); 180eee0013eSWilly Tu return; 181eee0013eSWilly Tu } 182d1bde9e5SKrzysztof Grobelny if (swInvPurpose == nullptr || 183d1bde9e5SKrzysztof Grobelny *swInvPurpose != swVersionPurpose) 184eee0013eSWilly Tu { 185eee0013eSWilly Tu // Not purpose we're looking for 186eee0013eSWilly Tu return; 187eee0013eSWilly Tu } 188eee0013eSWilly Tu 18962598e31SEd Tanous BMCWEB_LOG_DEBUG("Image ID: {}", swId); 19062598e31SEd Tanous BMCWEB_LOG_DEBUG("Running image: {}", runningImage); 19162598e31SEd Tanous BMCWEB_LOG_DEBUG("Image purpose: {}", *swInvPurpose); 192d1bde9e5SKrzysztof Grobelny 193eee0013eSWilly Tu if (populateLinkToImages) 194eee0013eSWilly Tu { 195eee0013eSWilly Tu nlohmann::json& softwareImageMembers = 196ac106bf6SEd Tanous asyncResp->res.jsonValue["Links"]["SoftwareImages"]; 197eee0013eSWilly Tu // Firmware images are at 198eee0013eSWilly Tu // /redfish/v1/UpdateService/FirmwareInventory/<Id> 199eee0013eSWilly Tu // e.g. .../FirmwareInventory/82d3ec86 200613dabeaSEd Tanous nlohmann::json::object_t member; 201ef4c65b7SEd Tanous member["@odata.id"] = boost::urls::format( 202ef4c65b7SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory/{}", 203ef4c65b7SEd Tanous swId); 204b2ba3072SPatrick Williams softwareImageMembers.emplace_back(std::move(member)); 205ac106bf6SEd Tanous asyncResp->res 206eee0013eSWilly Tu .jsonValue["Links"]["SoftwareImages@odata.count"] = 207eee0013eSWilly Tu softwareImageMembers.size(); 208eee0013eSWilly Tu 209eee0013eSWilly Tu if (runningImage) 210eee0013eSWilly Tu { 211613dabeaSEd Tanous nlohmann::json::object_t runningMember; 212ef4c65b7SEd Tanous runningMember["@odata.id"] = boost::urls::format( 213ef4c65b7SEd Tanous "/redfish/v1/UpdateService/FirmwareInventory/{}", 214ef4c65b7SEd Tanous swId); 215eee0013eSWilly Tu // Create the link to the running image 216ac106bf6SEd Tanous asyncResp->res 217613dabeaSEd Tanous .jsonValue["Links"]["ActiveSoftwareImage"] = 218613dabeaSEd Tanous std::move(runningMember); 219eee0013eSWilly Tu } 220eee0013eSWilly Tu } 221eee0013eSWilly Tu if (!activeVersionPropName.empty() && runningImage) 222eee0013eSWilly Tu { 223ac106bf6SEd Tanous asyncResp->res.jsonValue[activeVersionPropName] = 224ac106bf6SEd Tanous *version; 225eee0013eSWilly Tu } 226d1bde9e5SKrzysztof Grobelny }); 227eee0013eSWilly Tu } 228e99073f5SGeorge Liu }); 229eee0013eSWilly Tu }); 230eee0013eSWilly Tu } 231eee0013eSWilly Tu 232eee0013eSWilly Tu /** 233eee0013eSWilly Tu * @brief Translate input swState to Redfish state 234eee0013eSWilly Tu * 235eee0013eSWilly Tu * This function will return the corresponding Redfish state 236eee0013eSWilly Tu * 237eee0013eSWilly Tu * @param[i] swState The OpenBMC software state 238eee0013eSWilly Tu * 239eee0013eSWilly Tu * @return The corresponding Redfish state 240eee0013eSWilly Tu */ 2410ec8b83dSEd Tanous inline resource::State getRedfishSwState(const std::string& swState) 242eee0013eSWilly Tu { 243eee0013eSWilly Tu if (swState == "xyz.openbmc_project.Software.Activation.Activations.Active") 244eee0013eSWilly Tu { 2450ec8b83dSEd Tanous return resource::State::Enabled; 246eee0013eSWilly Tu } 247eee0013eSWilly Tu if (swState == "xyz.openbmc_project.Software.Activation." 248eee0013eSWilly Tu "Activations.Activating") 249eee0013eSWilly Tu { 2500ec8b83dSEd Tanous return resource::State::Updating; 251eee0013eSWilly Tu } 252eee0013eSWilly Tu if (swState == "xyz.openbmc_project.Software.Activation." 253eee0013eSWilly Tu "Activations.StandbySpare") 254eee0013eSWilly Tu { 2550ec8b83dSEd Tanous return resource::State::StandbySpare; 256eee0013eSWilly Tu } 25762598e31SEd Tanous BMCWEB_LOG_DEBUG("Default sw state {} to Disabled", swState); 2580ec8b83dSEd Tanous return resource::State::Disabled; 259eee0013eSWilly Tu } 260eee0013eSWilly Tu 261eee0013eSWilly Tu /** 262eee0013eSWilly Tu * @brief Translate input swState to Redfish health state 263eee0013eSWilly Tu * 264eee0013eSWilly Tu * This function will return the corresponding Redfish health state 265eee0013eSWilly Tu * 266eee0013eSWilly Tu * @param[i] swState The OpenBMC software state 267eee0013eSWilly Tu * 268eee0013eSWilly Tu * @return The corresponding Redfish health state 269eee0013eSWilly Tu */ 270eee0013eSWilly Tu inline std::string getRedfishSwHealth(const std::string& swState) 271eee0013eSWilly Tu { 272eee0013eSWilly Tu if ((swState == 273eee0013eSWilly Tu "xyz.openbmc_project.Software.Activation.Activations.Active") || 274eee0013eSWilly Tu (swState == "xyz.openbmc_project.Software.Activation.Activations." 275eee0013eSWilly Tu "Activating") || 276eee0013eSWilly Tu (swState == 277eee0013eSWilly Tu "xyz.openbmc_project.Software.Activation.Activations.Ready")) 278eee0013eSWilly Tu { 279eee0013eSWilly Tu return "OK"; 280eee0013eSWilly Tu } 28162598e31SEd Tanous BMCWEB_LOG_DEBUG("Sw state {} to Warning", swState); 282eee0013eSWilly Tu return "Warning"; 283eee0013eSWilly Tu } 284eee0013eSWilly Tu 285eee0013eSWilly Tu /** 286eee0013eSWilly Tu * @brief Put status of input swId into json response 287eee0013eSWilly Tu * 288eee0013eSWilly Tu * This function will put the appropriate Redfish state of the input 289eee0013eSWilly Tu * software id to ["Status"]["State"] within the json response 290eee0013eSWilly Tu * 291ac106bf6SEd Tanous * @param[i,o] asyncResp Async response object 292eee0013eSWilly Tu * @param[i] swId The software ID to get status for 293eee0013eSWilly Tu * @param[i] dbusSvc The dbus service implementing the software object 294eee0013eSWilly Tu * 295eee0013eSWilly Tu * @return void 296eee0013eSWilly Tu */ 297eee0013eSWilly Tu inline void getSwStatus(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 298eee0013eSWilly Tu const std::shared_ptr<std::string>& swId, 299eee0013eSWilly Tu const std::string& dbusSvc) 300eee0013eSWilly Tu { 30162598e31SEd Tanous BMCWEB_LOG_DEBUG("getSwStatus: swId {} svc {}", *swId, dbusSvc); 302eee0013eSWilly Tu 303d1bde9e5SKrzysztof Grobelny sdbusplus::asio::getAllProperties( 304d1bde9e5SKrzysztof Grobelny *crow::connections::systemBus, dbusSvc, 305d1bde9e5SKrzysztof Grobelny "/xyz/openbmc_project/software/" + *swId, 306d1bde9e5SKrzysztof Grobelny "xyz.openbmc_project.Software.Activation", 307eee0013eSWilly Tu [asyncResp, 3088b24275dSEd Tanous swId](const boost::system::error_code& ec, 309eee0013eSWilly Tu const dbus::utility::DBusPropertiesMap& propertiesList) { 3108b24275dSEd Tanous if (ec) 311eee0013eSWilly Tu { 312eee0013eSWilly Tu // not all swtypes are updateable, this is ok 313eee0013eSWilly Tu asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 314eee0013eSWilly Tu return; 315eee0013eSWilly Tu } 316d1bde9e5SKrzysztof Grobelny 317eee0013eSWilly Tu const std::string* swInvActivation = nullptr; 318d1bde9e5SKrzysztof Grobelny 319d1bde9e5SKrzysztof Grobelny const bool success = sdbusplus::unpackPropertiesNoThrow( 320d1bde9e5SKrzysztof Grobelny dbus_utils::UnpackErrorPrinter(), propertiesList, "Activation", 321d1bde9e5SKrzysztof Grobelny swInvActivation); 322d1bde9e5SKrzysztof Grobelny 323d1bde9e5SKrzysztof Grobelny if (!success) 324eee0013eSWilly Tu { 325d1bde9e5SKrzysztof Grobelny messages::internalError(asyncResp->res); 326d1bde9e5SKrzysztof Grobelny return; 327eee0013eSWilly Tu } 328eee0013eSWilly Tu 329eee0013eSWilly Tu if (swInvActivation == nullptr) 330eee0013eSWilly Tu { 331eee0013eSWilly Tu messages::internalError(asyncResp->res); 332eee0013eSWilly Tu return; 333eee0013eSWilly Tu } 334d1bde9e5SKrzysztof Grobelny 33562598e31SEd Tanous BMCWEB_LOG_DEBUG("getSwStatus: Activation {}", *swInvActivation); 336eee0013eSWilly Tu asyncResp->res.jsonValue["Status"]["State"] = 337eee0013eSWilly Tu getRedfishSwState(*swInvActivation); 338eee0013eSWilly Tu asyncResp->res.jsonValue["Status"]["Health"] = 339eee0013eSWilly Tu getRedfishSwHealth(*swInvActivation); 340d1bde9e5SKrzysztof Grobelny }); 341eee0013eSWilly Tu } 342eee0013eSWilly Tu 343eee0013eSWilly Tu /** 344eee0013eSWilly Tu * @brief Updates programmable status of input swId into json response 345eee0013eSWilly Tu * 346eee0013eSWilly Tu * This function checks whether software inventory component 347eee0013eSWilly Tu * can be programmable or not and fill's the "Updatable" 348eee0013eSWilly Tu * Property. 349eee0013eSWilly Tu * 350eee0013eSWilly Tu * @param[i,o] asyncResp Async response object 351eee0013eSWilly Tu * @param[i] swId The software ID 352eee0013eSWilly Tu */ 353eee0013eSWilly Tu inline void 354eee0013eSWilly Tu getSwUpdatableStatus(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 355eee0013eSWilly Tu const std::shared_ptr<std::string>& swId) 356eee0013eSWilly Tu { 3576c3e9451SGeorge Liu dbus::utility::getAssociationEndPoints( 358eee0013eSWilly Tu "/xyz/openbmc_project/software/updateable", 3595e7e2dc5SEd Tanous [asyncResp, swId](const boost::system::error_code& ec, 3606c3e9451SGeorge Liu const dbus::utility::MapperEndPoints& objPaths) { 361eee0013eSWilly Tu if (ec) 362eee0013eSWilly Tu { 36362598e31SEd Tanous BMCWEB_LOG_DEBUG(" error_code = {} error msg = {}", ec, 36462598e31SEd Tanous ec.message()); 365eee0013eSWilly Tu // System can exist with no updateable software, 366eee0013eSWilly Tu // so don't throw error here. 367eee0013eSWilly Tu return; 368eee0013eSWilly Tu } 369eee0013eSWilly Tu std::string reqSwObjPath = "/xyz/openbmc_project/software/" + *swId; 370eee0013eSWilly Tu 371*3544d2a7SEd Tanous if (std::ranges::find(objPaths, reqSwObjPath) != objPaths.end()) 372eee0013eSWilly Tu { 37383edb08fSGunnar Mills asyncResp->res.jsonValue["Updateable"] = true; 374eee0013eSWilly Tu return; 375eee0013eSWilly Tu } 376eee0013eSWilly Tu }); 377eee0013eSWilly Tu } 378eee0013eSWilly Tu 379eee0013eSWilly Tu } // namespace sw_util 380eee0013eSWilly Tu } // namespace redfish 381