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