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