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