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