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