1 #include "prime_inventory.hpp" 2 3 #include "exceptions.hpp" 4 #include "utility/common_utility.hpp" 5 #include "utility/dbus_utility.hpp" 6 #include "utility/event_logger_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 vpd::vpdSpecificUtility::insertOrMerge( 243 l_interfaces, vpd::constants::vpdCollectionInterface, 244 std::move(l_fruCollectionProperty), l_errCode); 245 246 if (l_errCode) 247 { 248 m_logger->logMessage("Failed to insert value into map, error : " + 249 vpd::commonUtility::getErrCodeMsg(l_errCode)); 250 } 251 252 o_objectInterfaceMap.emplace(std::move(l_fruObjectPath), 253 std::move(l_interfaces)); 254 255 return true; 256 } 257 258 void PrimeInventory::populateInterfaces( 259 const nlohmann::json& i_interfaceJson, 260 vpd::types::InterfaceMap& io_interfaceMap, 261 const vpd::types::VPDMapVariant& i_parsedVpdMap) const noexcept 262 { 263 if (i_interfaceJson.empty()) 264 { 265 return; 266 } 267 268 for (const auto& l_interfacesPropPair : i_interfaceJson.items()) 269 { 270 const std::string& l_interface = l_interfacesPropPair.key(); 271 vpd::types::PropertyMap l_propertyMap; 272 uint16_t l_errCode = 0; 273 274 for (const auto& l_propValuePair : l_interfacesPropPair.value().items()) 275 { 276 const std::string l_property = l_propValuePair.key(); 277 278 if (l_propValuePair.value().is_boolean()) 279 { 280 l_propertyMap.emplace(l_property, 281 l_propValuePair.value().get<bool>()); 282 } 283 else if (l_propValuePair.value().is_string()) 284 { 285 if (l_property.compare("LocationCode") == 0 && 286 l_interface.compare("com.ibm.ipzvpd.Location") == 0) 287 { 288 std::string l_value = 289 vpd::vpdSpecificUtility::getExpandedLocationCode( 290 l_propValuePair.value().get<std::string>(), 291 i_parsedVpdMap, l_errCode); 292 293 if (l_errCode) 294 { 295 m_logger->logMessage( 296 "Failed to get expanded location code for location code - " + 297 l_propValuePair.value().get<std::string>() + 298 " ,error : " + 299 vpd::commonUtility::getErrCodeMsg(l_errCode)); 300 } 301 302 l_propertyMap.emplace(l_property, l_value); 303 304 auto l_locCodeProperty = l_propertyMap; 305 vpd::vpdSpecificUtility::insertOrMerge( 306 io_interfaceMap, 307 std::string(vpd::constants::xyzLocationCodeInf), 308 move(l_locCodeProperty), l_errCode); 309 310 if (l_errCode) 311 { 312 m_logger->logMessage( 313 "Failed to insert value into map, error : " + 314 vpd::commonUtility::getErrCodeMsg(l_errCode)); 315 } 316 } 317 else 318 { 319 l_propertyMap.emplace( 320 l_property, l_propValuePair.value().get<std::string>()); 321 } 322 } 323 else if (l_propValuePair.value().is_array()) 324 { 325 try 326 { 327 l_propertyMap.emplace(l_property, 328 l_propValuePair.value() 329 .get<vpd::types::BinaryVector>()); 330 } 331 catch (const nlohmann::detail::type_error& e) 332 { 333 std::cerr << "Type exception: " << e.what() << "\n"; 334 } 335 } 336 else if (l_propValuePair.value().is_number()) 337 { 338 // For now assume the value is a size_t. In the future it would 339 // be nice to come up with a way to get the type from the JSON. 340 l_propertyMap.emplace(l_property, 341 l_propValuePair.value().get<size_t>()); 342 } 343 else if (l_propValuePair.value().is_object()) 344 { 345 const std::string& l_record = 346 l_propValuePair.value().value("recordName", ""); 347 const std::string& l_keyword = 348 l_propValuePair.value().value("keywordName", ""); 349 const std::string& l_encoding = 350 l_propValuePair.value().value("encoding", ""); 351 352 if (auto l_ipzVpdMap = 353 std::get_if<vpd::types::IPZVpdMap>(&i_parsedVpdMap)) 354 { 355 if (!l_record.empty() && !l_keyword.empty() && 356 (*l_ipzVpdMap).count(l_record) && 357 (*l_ipzVpdMap).at(l_record).count(l_keyword)) 358 { 359 auto l_encoded = vpd::vpdSpecificUtility::encodeKeyword( 360 ((*l_ipzVpdMap).at(l_record).at(l_keyword)), 361 l_encoding, l_errCode); 362 363 if (l_errCode) 364 { 365 m_logger->logMessage( 366 "Failed to get encoded keyword value for : " + 367 l_keyword + ", error : " + 368 vpd::commonUtility::getErrCodeMsg(l_errCode)); 369 } 370 371 l_propertyMap.emplace(l_property, l_encoded); 372 } 373 } 374 else if (auto l_kwdVpdMap = 375 std::get_if<vpd::types::KeywordVpdMap>( 376 &i_parsedVpdMap)) 377 { 378 if (!l_keyword.empty() && (*l_kwdVpdMap).count(l_keyword)) 379 { 380 if (auto l_kwValue = 381 std::get_if<vpd::types::BinaryVector>( 382 &(*l_kwdVpdMap).at(l_keyword))) 383 { 384 auto l_encodedValue = 385 vpd::vpdSpecificUtility::encodeKeyword( 386 std::string((*l_kwValue).begin(), 387 (*l_kwValue).end()), 388 l_encoding, l_errCode); 389 390 if (l_errCode) 391 { 392 m_logger->logMessage( 393 "Failed to get encoded keyword value for : " + 394 l_keyword + ", error : " + 395 vpd::commonUtility::getErrCodeMsg( 396 l_errCode)); 397 } 398 399 l_propertyMap.emplace(l_property, l_encodedValue); 400 } 401 else if (auto l_kwValue = std::get_if<std::string>( 402 &(*l_kwdVpdMap).at(l_keyword))) 403 { 404 auto l_encodedValue = 405 vpd::vpdSpecificUtility::encodeKeyword( 406 std::string((*l_kwValue).begin(), 407 (*l_kwValue).end()), 408 l_encoding, l_errCode); 409 410 if (l_errCode) 411 { 412 m_logger->logMessage( 413 "Failed to get encoded keyword value for : " + 414 l_keyword + ", error : " + 415 vpd::commonUtility::getErrCodeMsg( 416 l_errCode)); 417 } 418 419 l_propertyMap.emplace(l_property, l_encodedValue); 420 } 421 else if (auto l_uintValue = std::get_if<size_t>( 422 &(*l_kwdVpdMap).at(l_keyword))) 423 { 424 l_propertyMap.emplace(l_property, *l_uintValue); 425 } 426 else 427 { 428 m_logger->logMessage( 429 "Unknown keyword found, Keywrod = " + 430 l_keyword); 431 } 432 } 433 } 434 } 435 } 436 vpd::vpdSpecificUtility::insertOrMerge(io_interfaceMap, l_interface, 437 move(l_propertyMap), l_errCode); 438 439 if (l_errCode) 440 { 441 m_logger->logMessage("Failed to insert value into map, error : " + 442 vpd::commonUtility::getErrCodeMsg(l_errCode)); 443 } 444 } 445 } 446 447 void PrimeInventory::processFunctionalProperty( 448 const std::string& i_inventoryObjPath, 449 vpd::types::InterfaceMap& io_interfaces) const noexcept 450 { 451 if (!vpd::dbusUtility::isChassisPowerOn()) 452 { 453 std::vector<std::string> l_operationalStatusInf{ 454 vpd::constants::operationalStatusInf}; 455 456 auto l_mapperObjectMap = vpd::dbusUtility::getObjectMap( 457 i_inventoryObjPath, l_operationalStatusInf); 458 459 // If the object has been found. Check if it is under PIM. 460 if (l_mapperObjectMap.size() != 0) 461 { 462 for (const auto& [l_serviceName, l_interfaceLsit] : 463 l_mapperObjectMap) 464 { 465 if (l_serviceName == vpd::constants::pimServiceName) 466 { 467 // The object is already under PIM. No need to process 468 // again. Retain the old value. 469 return; 470 } 471 } 472 } 473 474 // Implies value is not there in D-Bus. Populate it with default 475 // value "true". 476 uint16_t l_errCode = 0; 477 vpd::types::PropertyMap l_functionalProp; 478 l_functionalProp.emplace("Functional", true); 479 vpd::vpdSpecificUtility::insertOrMerge( 480 io_interfaces, vpd::constants::operationalStatusInf, 481 move(l_functionalProp), l_errCode); 482 483 if (l_errCode) 484 { 485 m_logger->logMessage("Failed to insert value into map, error : " + 486 vpd::commonUtility::getErrCodeMsg(l_errCode)); 487 } 488 } 489 490 // if chassis is power on. Functional property should be there on D-Bus. 491 // Don't process. 492 return; 493 } 494 495 void PrimeInventory::processEnabledProperty( 496 const std::string& i_inventoryObjPath, 497 vpd::types::InterfaceMap& io_interfaces) const noexcept 498 { 499 if (!vpd::dbusUtility::isChassisPowerOn()) 500 { 501 std::vector<std::string> l_enableInf{vpd::constants::enableInf}; 502 503 auto l_mapperObjectMap = 504 vpd::dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf); 505 506 // If the object has been found. Check if it is under PIM. 507 if (l_mapperObjectMap.size() != 0) 508 { 509 for (const auto& [l_serviceName, l_interfaceLsit] : 510 l_mapperObjectMap) 511 { 512 if (l_serviceName == vpd::constants::pimServiceName) 513 { 514 // The object is already under PIM. No need to process 515 // again. Retain the old value. 516 return; 517 } 518 } 519 } 520 521 // Implies value is not there in D-Bus. Populate it with default 522 // value "true". 523 uint16_t l_errCode = 0; 524 vpd::types::PropertyMap l_enabledProp; 525 l_enabledProp.emplace("Enabled", true); 526 vpd::vpdSpecificUtility::insertOrMerge( 527 io_interfaces, vpd::constants::enableInf, move(l_enabledProp), 528 l_errCode); 529 530 if (l_errCode) 531 { 532 m_logger->logMessage("Failed to insert value into map, error : " + 533 vpd::commonUtility::getErrCodeMsg(l_errCode)); 534 } 535 } 536 537 // if chassis is power on. Enabled property should be there on D-Bus. 538 // Don't process. 539 return; 540 } 541