1 #include "prime_inventory.hpp" 2 3 #include "event_logger.hpp" 4 #include "exceptions.hpp" 5 #include "utility/common_utility.hpp" 6 #include "utility/dbus_utility.hpp" 7 #include "utility/json_utility.hpp" 8 #include "utility/vpd_specific_utility.hpp" 9 10 #include <string> 11 #include <vector> 12 13 PrimeInventory::PrimeInventory() 14 { 15 try 16 { 17 uint16_t l_errCode = 0; 18 m_sysCfgJsonObj = 19 vpd::jsonUtility::getParsedJson(INVENTORY_JSON_SYM_LINK, l_errCode); 20 21 if (l_errCode) 22 { 23 throw std::runtime_error( 24 "JSON parsing failed for file [ " + 25 std::string(INVENTORY_JSON_SYM_LINK) + 26 " ], error : " + vpd::commonUtility::getErrCodeMsg(l_errCode)); 27 } 28 29 // check for mandatory fields at this point itself. 30 if (!m_sysCfgJsonObj.contains("frus")) 31 { 32 throw std::runtime_error( 33 "Mandatory tag(s) missing from JSON file [" + 34 std::string(INVENTORY_JSON_SYM_LINK) + "]"); 35 } 36 37 m_logger = vpd::Logger::getLoggerInstance(); 38 } 39 catch (const std::exception& l_ex) 40 { 41 vpd::EventLogger::createSyncPel( 42 vpd::types::ErrorType::JsonFailure, 43 vpd::types::SeverityType::Critical, __FILE__, __FUNCTION__, 0, 44 "Prime inventory failed, reason: " + std::string(l_ex.what()), 45 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 46 47 throw; 48 } 49 } 50 51 bool PrimeInventory::isPrimingRequired() const noexcept 52 { 53 try 54 { 55 // get all object paths under PIM 56 const auto l_objectPaths = vpd::dbusUtility::GetSubTreePaths( 57 vpd::constants::systemInvPath, 0, 58 std::vector<std::string>{vpd::constants::vpdCollectionInterface}); 59 60 const nlohmann::json& l_listOfFrus = 61 m_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>(); 62 63 size_t l_invPathCount = 0; 64 65 for (const auto& l_itemFRUS : l_listOfFrus.items()) 66 { 67 for (const auto& l_Fru : l_itemFRUS.value()) 68 { 69 if (l_Fru.contains("ccin") || (l_Fru.contains("noprime") && 70 l_Fru.value("noprime", false))) 71 { 72 continue; 73 } 74 75 l_invPathCount += 1; 76 } 77 } 78 return (l_objectPaths.size() < l_invPathCount); 79 } 80 catch (const std::exception& l_ex) 81 { 82 m_logger->logMessage( 83 "Error while checking is priming required or not, error: " + 84 std::string(l_ex.what())); 85 } 86 87 // In case of any error, perform priming, as it's unclear whether priming is 88 // required. 89 return true; 90 } 91 92 void PrimeInventory::primeSystemBlueprint() const noexcept 93 { 94 try 95 { 96 if (m_sysCfgJsonObj.empty() || !isPrimingRequired()) 97 { 98 return; 99 } 100 101 const nlohmann::json& l_listOfFrus = 102 m_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>(); 103 104 vpd::types::ObjectMap l_objectInterfaceMap; 105 for (const auto& l_itemFRUS : l_listOfFrus.items()) 106 { 107 const std::string& l_vpdFilePath = l_itemFRUS.key(); 108 109 if (l_vpdFilePath == SYSTEM_VPD_FILE_PATH) 110 { 111 continue; 112 } 113 114 // Prime the inventry for FRUs 115 for (const auto& l_Fru : m_sysCfgJsonObj["frus"][l_vpdFilePath]) 116 { 117 if (!primeInventory(l_objectInterfaceMap, l_Fru)) 118 { 119 m_logger->logMessage( 120 "Priming of inventory failed for FRU " + 121 std::string(l_Fru["inventoryPath"])); 122 } 123 } 124 } 125 126 // Notify PIM 127 if (!l_objectInterfaceMap.empty()) 128 { 129 if (!vpd::dbusUtility::callPIM(move(l_objectInterfaceMap))) 130 { 131 m_logger->logMessage( 132 "Call to PIM failed while priming inventory"); 133 } 134 } 135 else 136 { 137 m_logger->logMessage("Priming inventory failed"); 138 } 139 } 140 catch (const std::exception& l_ex) 141 { 142 m_logger->logMessage("Prime system inventory failed, reason: " + 143 std::string(l_ex.what())); 144 } 145 } 146 147 bool PrimeInventory::primeInventory( 148 vpd::types::ObjectMap& o_objectInterfaceMap, 149 const nlohmann::json& i_fruJsonObj) const noexcept 150 { 151 if (i_fruJsonObj.empty()) 152 { 153 m_logger->logMessage("Empty FRU JSON given"); 154 return false; 155 } 156 157 vpd::types::InterfaceMap l_interfaces; 158 sdbusplus::message::object_path l_fruObjectPath( 159 i_fruJsonObj["inventoryPath"]); 160 161 if (i_fruJsonObj.contains("ccin")) 162 { 163 return true; 164 } 165 166 if (i_fruJsonObj.contains("noprime") && 167 i_fruJsonObj.value("noprime", false)) 168 { 169 return true; 170 } 171 172 // Reset data under PIM for this FRU only if the FRU is not synthesized 173 // and we handle it's Present property. 174 if (isPresentPropertyHandlingRequired(i_fruJsonObj)) 175 { 176 // Clear data under PIM if already exists. 177 uint16_t l_errCode = 0; 178 vpd::vpdSpecificUtility::resetDataUnderPIM( 179 std::string(i_fruJsonObj["inventoryPath"]), l_interfaces, 180 l_errCode); 181 182 if (l_errCode) 183 { 184 m_logger->logMessage( 185 "Failed to reset data under PIM for path [" + 186 std::string(i_fruJsonObj["inventoryPath"]) + 187 "], error : " + vpd::commonUtility::getErrCodeMsg(l_errCode)); 188 } 189 } 190 191 // Add extra interfaces mentioned in the Json config file 192 if (i_fruJsonObj.contains("extraInterfaces")) 193 { 194 populateInterfaces(i_fruJsonObj["extraInterfaces"], l_interfaces, 195 std::monostate{}); 196 } 197 198 vpd::types::PropertyMap l_propertyValueMap; 199 200 // Update Present property for this FRU only if we handle Present 201 // property for the FRU. 202 if (isPresentPropertyHandlingRequired(i_fruJsonObj)) 203 { 204 l_propertyValueMap.emplace("Present", false); 205 206 // TODO: Present based on file will be taken care in future. 207 // By default present is set to false for FRU at the time of 208 // priming. Once collection goes through, it will be set to true in 209 // that flow. 210 /*if (std::filesystem::exists(i_vpdFilePath)) 211 { 212 l_propertyValueMap["Present"] = true; 213 }*/ 214 } 215 216 uint16_t l_errCode = 0; 217 218 vpd::vpdSpecificUtility::insertOrMerge( 219 l_interfaces, "xyz.openbmc_project.Inventory.Item", 220 move(l_propertyValueMap), l_errCode); 221 222 if (l_errCode) 223 { 224 m_logger->logMessage("Failed to insert value into map, error : " + 225 vpd::commonUtility::getErrCodeMsg(l_errCode)); 226 } 227 228 if (i_fruJsonObj.value("inherit", true) && 229 m_sysCfgJsonObj.contains("commonInterfaces")) 230 { 231 populateInterfaces(m_sysCfgJsonObj["commonInterfaces"], l_interfaces, 232 std::monostate{}); 233 } 234 235 processFunctionalProperty(i_fruJsonObj["inventoryPath"], l_interfaces); 236 processEnabledProperty(i_fruJsonObj["inventoryPath"], l_interfaces); 237 238 // Emplace the default state of FRU VPD collection 239 vpd::types::PropertyMap l_fruCollectionProperty = { 240 {"Status", vpd::constants::vpdCollectionNotStarted}}; 241 242 l_errCode = 0; 243 vpd::vpdSpecificUtility::insertOrMerge( 244 l_interfaces, vpd::constants::vpdCollectionInterface, 245 std::move(l_fruCollectionProperty), l_errCode); 246 247 if (l_errCode) 248 { 249 m_logger->logMessage("Failed to insert value into map, error : " + 250 vpd::commonUtility::getErrCodeMsg(l_errCode)); 251 } 252 253 o_objectInterfaceMap.emplace(std::move(l_fruObjectPath), 254 std::move(l_interfaces)); 255 256 return true; 257 } 258 259 void PrimeInventory::populateInterfaces( 260 const nlohmann::json& i_interfaceJson, 261 vpd::types::InterfaceMap& io_interfaceMap, 262 const vpd::types::VPDMapVariant& i_parsedVpdMap) const noexcept 263 { 264 if (i_interfaceJson.empty()) 265 { 266 return; 267 } 268 269 for (const auto& l_interfacesPropPair : i_interfaceJson.items()) 270 { 271 const std::string& l_interface = l_interfacesPropPair.key(); 272 vpd::types::PropertyMap l_propertyMap; 273 uint16_t l_errCode = 0; 274 275 for (const auto& l_propValuePair : l_interfacesPropPair.value().items()) 276 { 277 const std::string l_property = l_propValuePair.key(); 278 279 if (l_propValuePair.value().is_boolean()) 280 { 281 l_propertyMap.emplace(l_property, 282 l_propValuePair.value().get<bool>()); 283 } 284 else if (l_propValuePair.value().is_string()) 285 { 286 if (l_property.compare("LocationCode") == 0 && 287 l_interface.compare("com.ibm.ipzvpd.Location") == 0) 288 { 289 std::string l_value = 290 vpd::vpdSpecificUtility::getExpandedLocationCode( 291 l_propValuePair.value().get<std::string>(), 292 i_parsedVpdMap); 293 l_propertyMap.emplace(l_property, l_value); 294 295 auto l_locCodeProperty = l_propertyMap; 296 vpd::vpdSpecificUtility::insertOrMerge( 297 io_interfaceMap, 298 std::string(vpd::constants::xyzLocationCodeInf), 299 move(l_locCodeProperty), l_errCode); 300 301 if (l_errCode) 302 { 303 m_logger->logMessage( 304 "Failed to insert value into map, error : " + 305 vpd::commonUtility::getErrCodeMsg(l_errCode)); 306 } 307 } 308 else 309 { 310 l_propertyMap.emplace( 311 l_property, l_propValuePair.value().get<std::string>()); 312 } 313 } 314 else if (l_propValuePair.value().is_array()) 315 { 316 try 317 { 318 l_propertyMap.emplace(l_property, 319 l_propValuePair.value() 320 .get<vpd::types::BinaryVector>()); 321 } 322 catch (const nlohmann::detail::type_error& e) 323 { 324 std::cerr << "Type exception: " << e.what() << "\n"; 325 } 326 } 327 else if (l_propValuePair.value().is_number()) 328 { 329 // For now assume the value is a size_t. In the future it would 330 // be nice to come up with a way to get the type from the JSON. 331 l_propertyMap.emplace(l_property, 332 l_propValuePair.value().get<size_t>()); 333 } 334 else if (l_propValuePair.value().is_object()) 335 { 336 const std::string& l_record = 337 l_propValuePair.value().value("recordName", ""); 338 const std::string& l_keyword = 339 l_propValuePair.value().value("keywordName", ""); 340 const std::string& l_encoding = 341 l_propValuePair.value().value("encoding", ""); 342 343 uint16_t l_errCode = 0; 344 345 if (auto l_ipzVpdMap = 346 std::get_if<vpd::types::IPZVpdMap>(&i_parsedVpdMap)) 347 { 348 if (!l_record.empty() && !l_keyword.empty() && 349 (*l_ipzVpdMap).count(l_record) && 350 (*l_ipzVpdMap).at(l_record).count(l_keyword)) 351 { 352 auto l_encoded = vpd::vpdSpecificUtility::encodeKeyword( 353 ((*l_ipzVpdMap).at(l_record).at(l_keyword)), 354 l_encoding, l_errCode); 355 356 if (l_errCode) 357 { 358 m_logger->logMessage( 359 "Failed to get encoded keyword value for : " + 360 l_keyword + ", error : " + 361 vpd::commonUtility::getErrCodeMsg(l_errCode)); 362 } 363 364 l_propertyMap.emplace(l_property, l_encoded); 365 } 366 } 367 else if (auto l_kwdVpdMap = 368 std::get_if<vpd::types::KeywordVpdMap>( 369 &i_parsedVpdMap)) 370 { 371 if (!l_keyword.empty() && (*l_kwdVpdMap).count(l_keyword)) 372 { 373 if (auto l_kwValue = 374 std::get_if<vpd::types::BinaryVector>( 375 &(*l_kwdVpdMap).at(l_keyword))) 376 { 377 auto l_encodedValue = 378 vpd::vpdSpecificUtility::encodeKeyword( 379 std::string((*l_kwValue).begin(), 380 (*l_kwValue).end()), 381 l_encoding, l_errCode); 382 383 if (l_errCode) 384 { 385 m_logger->logMessage( 386 "Failed to get encoded keyword value for : " + 387 l_keyword + ", error : " + 388 vpd::commonUtility::getErrCodeMsg( 389 l_errCode)); 390 } 391 392 l_propertyMap.emplace(l_property, l_encodedValue); 393 } 394 else if (auto l_kwValue = std::get_if<std::string>( 395 &(*l_kwdVpdMap).at(l_keyword))) 396 { 397 auto l_encodedValue = 398 vpd::vpdSpecificUtility::encodeKeyword( 399 std::string((*l_kwValue).begin(), 400 (*l_kwValue).end()), 401 l_encoding, l_errCode); 402 403 if (l_errCode) 404 { 405 m_logger->logMessage( 406 "Failed to get encoded keyword value for : " + 407 l_keyword + ", error : " + 408 vpd::commonUtility::getErrCodeMsg( 409 l_errCode)); 410 } 411 412 l_propertyMap.emplace(l_property, l_encodedValue); 413 } 414 else if (auto l_uintValue = std::get_if<size_t>( 415 &(*l_kwdVpdMap).at(l_keyword))) 416 { 417 l_propertyMap.emplace(l_property, *l_uintValue); 418 } 419 else 420 { 421 m_logger->logMessage( 422 "Unknown keyword found, Keywrod = " + 423 l_keyword); 424 } 425 } 426 } 427 } 428 } 429 l_errCode = 0; 430 vpd::vpdSpecificUtility::insertOrMerge(io_interfaceMap, l_interface, 431 move(l_propertyMap), l_errCode); 432 433 if (l_errCode) 434 { 435 m_logger->logMessage("Failed to insert value into map, error : " + 436 vpd::commonUtility::getErrCodeMsg(l_errCode)); 437 } 438 } 439 } 440 441 void PrimeInventory::processFunctionalProperty( 442 const std::string& i_inventoryObjPath, 443 vpd::types::InterfaceMap& io_interfaces) const noexcept 444 { 445 if (!vpd::dbusUtility::isChassisPowerOn()) 446 { 447 std::vector<std::string> l_operationalStatusInf{ 448 vpd::constants::operationalStatusInf}; 449 450 auto l_mapperObjectMap = vpd::dbusUtility::getObjectMap( 451 i_inventoryObjPath, l_operationalStatusInf); 452 453 // If the object has been found. Check if it is under PIM. 454 if (l_mapperObjectMap.size() != 0) 455 { 456 for (const auto& [l_serviceName, l_interfaceLsit] : 457 l_mapperObjectMap) 458 { 459 if (l_serviceName == vpd::constants::pimServiceName) 460 { 461 // The object is already under PIM. No need to process 462 // again. Retain the old value. 463 return; 464 } 465 } 466 } 467 468 // Implies value is not there in D-Bus. Populate it with default 469 // value "true". 470 uint16_t l_errCode = 0; 471 vpd::types::PropertyMap l_functionalProp; 472 l_functionalProp.emplace("Functional", true); 473 vpd::vpdSpecificUtility::insertOrMerge( 474 io_interfaces, vpd::constants::operationalStatusInf, 475 move(l_functionalProp), l_errCode); 476 477 if (l_errCode) 478 { 479 m_logger->logMessage("Failed to insert value into map, error : " + 480 vpd::commonUtility::getErrCodeMsg(l_errCode)); 481 } 482 } 483 484 // if chassis is power on. Functional property should be there on D-Bus. 485 // Don't process. 486 return; 487 } 488 489 void PrimeInventory::processEnabledProperty( 490 const std::string& i_inventoryObjPath, 491 vpd::types::InterfaceMap& io_interfaces) const noexcept 492 { 493 if (!vpd::dbusUtility::isChassisPowerOn()) 494 { 495 std::vector<std::string> l_enableInf{vpd::constants::enableInf}; 496 497 auto l_mapperObjectMap = 498 vpd::dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf); 499 500 // If the object has been found. Check if it is under PIM. 501 if (l_mapperObjectMap.size() != 0) 502 { 503 for (const auto& [l_serviceName, l_interfaceLsit] : 504 l_mapperObjectMap) 505 { 506 if (l_serviceName == vpd::constants::pimServiceName) 507 { 508 // The object is already under PIM. No need to process 509 // again. Retain the old value. 510 return; 511 } 512 } 513 } 514 515 // Implies value is not there in D-Bus. Populate it with default 516 // value "true". 517 uint16_t l_errCode = 0; 518 vpd::types::PropertyMap l_enabledProp; 519 l_enabledProp.emplace("Enabled", true); 520 vpd::vpdSpecificUtility::insertOrMerge( 521 io_interfaces, vpd::constants::enableInf, move(l_enabledProp), 522 l_errCode); 523 524 if (l_errCode) 525 { 526 m_logger->logMessage("Failed to insert value into map, error : " + 527 vpd::commonUtility::getErrCodeMsg(l_errCode)); 528 } 529 } 530 531 // if chassis is power on. Enabled property should be there on D-Bus. 532 // Don't process. 533 return; 534 } 535