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