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