1 #include "config.h" 2 3 #include "vpd_tool.hpp" 4 5 #include "tool_constants.hpp" 6 #include "tool_types.hpp" 7 #include "tool_utils.hpp" 8 9 #include <iostream> 10 #include <regex> 11 #include <tuple> 12 namespace vpd 13 { 14 int VpdTool::readKeyword( 15 const std::string& i_vpdPath, const std::string& i_recordName, 16 const std::string& i_keywordName, const bool i_onHardware, 17 const std::string& i_fileToSave) 18 { 19 int l_rc = constants::FAILURE; 20 try 21 { 22 types::DbusVariantType l_keywordValue; 23 if (i_onHardware) 24 { 25 l_keywordValue = utils::readKeywordFromHardware( 26 i_vpdPath, std::make_tuple(i_recordName, i_keywordName)); 27 } 28 else 29 { 30 std::string l_inventoryObjectPath( 31 constants::baseInventoryPath + i_vpdPath); 32 33 l_keywordValue = utils::readDbusProperty( 34 constants::inventoryManagerService, l_inventoryObjectPath, 35 constants::ipzVpdInfPrefix + i_recordName, i_keywordName); 36 } 37 38 if (const auto l_value = 39 std::get_if<types::BinaryVector>(&l_keywordValue); 40 l_value && !l_value->empty()) 41 { 42 // ToDo: Print value in both ASCII and hex formats 43 const std::string& l_keywordStrValue = 44 utils::getPrintableValue(*l_value); 45 46 if (i_fileToSave.empty()) 47 { 48 utils::displayOnConsole(i_vpdPath, i_keywordName, 49 l_keywordStrValue); 50 l_rc = constants::SUCCESS; 51 } 52 else 53 { 54 if (utils::saveToFile(i_fileToSave, l_keywordStrValue)) 55 { 56 std::cout 57 << "Value read is saved on the file: " << i_fileToSave 58 << std::endl; 59 l_rc = constants::SUCCESS; 60 } 61 else 62 { 63 std::cerr 64 << "Error while saving the read value on the file: " 65 << i_fileToSave 66 << "\nDisplaying the read value on console" 67 << std::endl; 68 utils::displayOnConsole(i_vpdPath, i_keywordName, 69 l_keywordStrValue); 70 } 71 } 72 } 73 else 74 { 75 // TODO: Enable logging when verbose is enabled. 76 // std::cout << "Invalid data type or empty data received." << 77 // std::endl; 78 } 79 } 80 catch (const std::exception& l_ex) 81 { 82 // TODO: Enable logging when verbose is enabled. 83 /*std::cerr << "Read keyword's value for path: " << i_vpdPath 84 << ", Record: " << i_recordName 85 << ", Keyword: " << i_keywordName 86 << " is failed, exception: " << l_ex.what() << std::endl;*/ 87 } 88 return l_rc; 89 } 90 91 int VpdTool::dumpObject(std::string i_fruPath) const noexcept 92 { 93 int l_rc{constants::FAILURE}; 94 try 95 { 96 // ToDo: For PFuture system take only full path from the user. 97 i_fruPath = constants::baseInventoryPath + i_fruPath; 98 99 nlohmann::json l_resultJsonArray = nlohmann::json::array({}); 100 const nlohmann::json l_fruJson = getFruProperties(i_fruPath); 101 if (!l_fruJson.empty()) 102 { 103 l_resultJsonArray += l_fruJson; 104 105 utils::printJson(l_resultJsonArray); 106 } 107 else 108 { 109 std::cout << "FRU [" << i_fruPath 110 << "] is not present in the system" << std::endl; 111 } 112 l_rc = constants::SUCCESS; 113 } 114 catch (std::exception& l_ex) 115 { 116 // TODO: Enable logging when verbose is enabled. 117 std::cerr << "Dump Object failed for FRU [" << i_fruPath 118 << "], Error: " << l_ex.what() << std::endl; 119 } 120 return l_rc; 121 } 122 123 nlohmann::json VpdTool::getFruProperties(const std::string& i_objectPath) const 124 { 125 // check if FRU is present in the system 126 if (!isFruPresent(i_objectPath)) 127 { 128 return nlohmann::json::object_t(); 129 } 130 131 nlohmann::json l_fruJson = nlohmann::json::object_t({}); 132 133 // need to trim out the base inventory path in the FRU JSON. 134 const std::string l_displayObjectPath = 135 (i_objectPath.find(constants::baseInventoryPath) == std::string::npos) 136 ? i_objectPath 137 : i_objectPath.substr(strlen(constants::baseInventoryPath)); 138 139 l_fruJson.emplace(l_displayObjectPath, nlohmann::json::object_t({})); 140 141 auto& l_fruObject = l_fruJson[l_displayObjectPath]; 142 143 const auto l_prettyNameInJson = getInventoryPropertyJson<std::string>( 144 i_objectPath, constants::inventoryItemInf, "PrettyName"); 145 if (!l_prettyNameInJson.empty()) 146 { 147 l_fruObject.insert(l_prettyNameInJson.cbegin(), 148 l_prettyNameInJson.cend()); 149 } 150 151 const auto l_locationCodeInJson = getInventoryPropertyJson<std::string>( 152 i_objectPath, constants::locationCodeInf, "LocationCode"); 153 if (!l_locationCodeInJson.empty()) 154 { 155 l_fruObject.insert(l_locationCodeInJson.cbegin(), 156 l_locationCodeInJson.cend()); 157 } 158 159 // Get the properties under VINI interface. 160 161 nlohmann::json l_viniPropertiesInJson = nlohmann::json::object({}); 162 163 auto l_readViniKeyWord = [i_objectPath, &l_viniPropertiesInJson, 164 this](const std::string& i_keyWord) { 165 const nlohmann::json l_keyWordJson = 166 getInventoryPropertyJson<vpd::types::BinaryVector>( 167 i_objectPath, constants::kwdVpdInf, i_keyWord); 168 l_viniPropertiesInJson.insert(l_keyWordJson.cbegin(), 169 l_keyWordJson.cend()); 170 }; 171 172 const std::vector<std::string> l_viniKeywords = {"SN", "PN", "CC", "FN", 173 "DR"}; 174 175 std::for_each(l_viniKeywords.cbegin(), l_viniKeywords.cend(), 176 l_readViniKeyWord); 177 178 if (!l_viniPropertiesInJson.empty()) 179 { 180 l_fruObject.insert(l_viniPropertiesInJson.cbegin(), 181 l_viniPropertiesInJson.cend()); 182 } 183 // if a FRU doesn't have VINI properties, we need to get the properties from 184 // Decorator.Asset interface 185 else 186 { 187 // Get properties under Decorator.Asset interface 188 const auto l_decoratorAssetPropertiesMap = 189 utils::getPropertyMap(constants::inventoryManagerService, 190 i_objectPath, constants::assetInf); 191 192 for (const auto& l_aProperty : l_decoratorAssetPropertiesMap) 193 { 194 if (const auto l_propertyValueStr = 195 std::get_if<std::string>(&l_aProperty.second)) 196 { 197 l_fruObject.emplace(l_aProperty.first, *l_propertyValueStr); 198 } 199 } 200 } 201 202 const auto l_typePropertyJson = getFruTypeProperty(i_objectPath); 203 if (!l_typePropertyJson.empty()) 204 { 205 l_fruObject.insert(l_typePropertyJson.cbegin(), 206 l_typePropertyJson.cend()); 207 } 208 209 // insert FRU "TYPE" 210 l_fruObject.emplace("TYPE", "FRU"); 211 212 return l_fruJson; 213 } 214 215 template <typename PropertyType> 216 nlohmann::json VpdTool::getInventoryPropertyJson( 217 const std::string& i_objectPath, const std::string& i_interface, 218 const std::string& i_propertyName) const noexcept 219 { 220 nlohmann::json l_resultInJson = nlohmann::json::object({}); 221 try 222 { 223 types::DbusVariantType l_keyWordValue; 224 225 l_keyWordValue = 226 utils::readDbusProperty(constants::inventoryManagerService, 227 i_objectPath, i_interface, i_propertyName); 228 229 if (const auto l_value = std::get_if<PropertyType>(&l_keyWordValue)) 230 { 231 if constexpr (std::is_same<PropertyType, std::string>::value) 232 { 233 l_resultInJson.emplace(i_propertyName, *l_value); 234 } 235 else if constexpr (std::is_same<PropertyType, bool>::value) 236 { 237 l_resultInJson.emplace(i_propertyName, 238 *l_value ? "true" : "false"); 239 } 240 else if constexpr (std::is_same<PropertyType, 241 types::BinaryVector>::value) 242 { 243 const std::string& l_keywordStrValue = 244 vpd::utils::getPrintableValue(*l_value); 245 246 l_resultInJson.emplace(i_propertyName, l_keywordStrValue); 247 } 248 } 249 else 250 { 251 // TODO: Enable logging when verbose is enabled. 252 // std::cout << "Invalid data type received." << std::endl; 253 } 254 } 255 catch (const std::exception& l_ex) 256 { 257 // TODO: Enable logging when verbose is enabled. 258 /*std::cerr << "Read " << i_propertyName << " value for FRU path: " << 259 i_objectPath 260 << ", failed with exception: " << l_ex.what() << std::endl;*/ 261 } 262 return l_resultInJson; 263 } 264 265 int VpdTool::fixSystemVpd() const noexcept 266 { 267 int l_rc = constants::FAILURE; 268 269 nlohmann::json l_backupRestoreCfgJsonObj = getBackupRestoreCfgJsonObj(); 270 if (!fetchKeywordInfo(l_backupRestoreCfgJsonObj)) 271 { 272 return l_rc; 273 } 274 275 printSystemVpd(l_backupRestoreCfgJsonObj); 276 277 do 278 { 279 printFixSystemVpdOption(types::UserOption::UseBackupDataForAll); 280 printFixSystemVpdOption( 281 types::UserOption::UseSystemBackplaneDataForAll); 282 printFixSystemVpdOption(types::UserOption::MoreOptions); 283 printFixSystemVpdOption(types::UserOption::Exit); 284 285 int l_userSelectedOption = types::UserOption::Exit; 286 std::cin >> l_userSelectedOption; 287 288 std::cout << std::endl << std::string(191, '=') << std::endl; 289 290 if (types::UserOption::UseBackupDataForAll == l_userSelectedOption) 291 { 292 l_rc = updateAllKeywords(l_backupRestoreCfgJsonObj, true); 293 break; 294 } 295 else if (types::UserOption::UseSystemBackplaneDataForAll == 296 l_userSelectedOption) 297 { 298 l_rc = updateAllKeywords(l_backupRestoreCfgJsonObj, false); 299 break; 300 } 301 else if (types::UserOption::MoreOptions == l_userSelectedOption) 302 { 303 l_rc = handleMoreOption(l_backupRestoreCfgJsonObj); 304 break; 305 } 306 else if (types::UserOption::Exit == l_userSelectedOption) 307 { 308 std::cout << "Exit successfully" << std::endl; 309 break; 310 } 311 else 312 { 313 std::cout << "Provide a valid option. Retry." << std::endl; 314 } 315 } while (true); 316 317 return l_rc; 318 } 319 320 int VpdTool::writeKeyword( 321 std::string i_vpdPath, const std::string& i_recordName, 322 const std::string& i_keywordName, const std::string& i_keywordValue, 323 const bool i_onHardware) noexcept 324 { 325 int l_rc = constants::FAILURE; 326 try 327 { 328 if (i_vpdPath.empty() || i_recordName.empty() || 329 i_keywordName.empty() || i_keywordValue.empty()) 330 { 331 throw std::runtime_error("Received input is empty."); 332 } 333 334 auto l_paramsToWrite = 335 std::make_tuple(i_recordName, i_keywordName, 336 utils::convertToBinary(i_keywordValue)); 337 338 if (i_onHardware) 339 { 340 l_rc = utils::writeKeywordOnHardware(i_vpdPath, l_paramsToWrite); 341 } 342 else 343 { 344 i_vpdPath = constants::baseInventoryPath + i_vpdPath; 345 l_rc = utils::writeKeyword(i_vpdPath, l_paramsToWrite); 346 } 347 348 if (l_rc > 0) 349 { 350 std::cout << "Data updated successfully " << std::endl; 351 l_rc = constants::SUCCESS; 352 } 353 } 354 catch (const std::exception& l_ex) 355 { 356 // TODO: Enable log when verbose is enabled. 357 std::cerr << "Write keyword's value for path: " << i_vpdPath 358 << ", Record: " << i_recordName 359 << ", Keyword: " << i_keywordName 360 << " is failed. Exception: " << l_ex.what() << std::endl; 361 } 362 return l_rc; 363 } 364 365 nlohmann::json VpdTool::getBackupRestoreCfgJsonObj() const noexcept 366 { 367 nlohmann::json l_parsedBackupRestoreJson{}; 368 try 369 { 370 nlohmann::json l_parsedSystemJson = 371 utils::getParsedJson(INVENTORY_JSON_SYM_LINK); 372 373 // check for mandatory fields at this point itself. 374 if (!l_parsedSystemJson.contains("backupRestoreConfigPath")) 375 { 376 throw std::runtime_error( 377 "backupRestoreConfigPath tag is missing from system config JSON : " + 378 std::string(INVENTORY_JSON_SYM_LINK)); 379 } 380 381 l_parsedBackupRestoreJson = 382 utils::getParsedJson(l_parsedSystemJson["backupRestoreConfigPath"]); 383 } 384 catch (const std::exception& l_ex) 385 { 386 // TODO: Enable logging when verbose is enabled. 387 std::cerr << l_ex.what() << std::endl; 388 } 389 390 return l_parsedBackupRestoreJson; 391 } 392 393 int VpdTool::cleanSystemVpd(bool i_syncBiosAttributes) const noexcept 394 { 395 try 396 { 397 (void)i_syncBiosAttributes; 398 399 // get the keyword map from backup_restore json 400 // iterate through the keyword map get default value of 401 // l_keywordName. 402 // use writeKeyword API to update default value on hardware, 403 // backup and D - Bus. 404 const nlohmann::json l_parsedBackupRestoreJson = 405 getBackupRestoreCfgJsonObj(); 406 407 // check for mandatory tags 408 if (l_parsedBackupRestoreJson.contains("source") && 409 l_parsedBackupRestoreJson.contains("backupMap") && 410 l_parsedBackupRestoreJson["source"].contains("hardwarePath") && 411 l_parsedBackupRestoreJson["backupMap"].is_array()) 412 { 413 // get the source hardware path 414 const auto& l_hardwarePath = 415 l_parsedBackupRestoreJson["source"]["hardwarePath"]; 416 417 // iterate through the backup map 418 for (const auto& l_aRecordKwInfo : 419 l_parsedBackupRestoreJson["backupMap"]) 420 { 421 // check if Manufacturing Reset is required for this entry 422 const bool l_isMfgCleanRequired = 423 l_aRecordKwInfo.value("isManufactureResetRequired", false); 424 425 if (l_isMfgCleanRequired) 426 { 427 // get the Record name and Keyword name 428 const std::string& l_srcRecordName = 429 l_aRecordKwInfo.value("sourceRecord", ""); 430 const std::string& l_srcKeywordName = 431 l_aRecordKwInfo.value("sourceKeyword", ""); 432 433 // validate the Record name, Keyword name and the 434 // defaultValue 435 if (!l_srcRecordName.empty() && !l_srcKeywordName.empty() && 436 l_aRecordKwInfo.contains("defaultValue") && 437 l_aRecordKwInfo["defaultValue"].is_array()) 438 { 439 const types::BinaryVector l_defaultBinaryValue = 440 l_aRecordKwInfo["defaultValue"] 441 .get<types::BinaryVector>(); 442 443 // update the Keyword with default value, use D-Bus 444 // method UpdateKeyword exposed by vpd-manager. 445 // Note: writing to all paths (Primary EEPROM path, 446 // Secondary EEPROM path, D-Bus cache and Backup path) 447 // is the responsibility of vpd-manager's UpdateKeyword 448 // API 449 if (constants::FAILURE == 450 utils::writeKeyword( 451 l_hardwarePath, 452 std::make_tuple(l_srcRecordName, 453 l_srcKeywordName, 454 l_defaultBinaryValue))) 455 { 456 // TODO: Enable logging when verbose 457 // is enabled. 458 std::cerr << "Failed to update " << l_srcRecordName 459 << ":" << l_srcKeywordName << std::endl; 460 } 461 } 462 else 463 { 464 std::cerr 465 << "Unrecognized Entry Record [" << l_srcRecordName 466 << "] Keyword [" << l_srcKeywordName 467 << "] in Backup Restore JSON backup map" 468 << std::endl; 469 } 470 } // mfgClean required check 471 } // keyword list loop 472 } 473 else // backupRestoreJson is not valid 474 { 475 std::cerr << "Backup Restore JSON is not valid" << std::endl; 476 } 477 478 // success/failure message 479 std::cout << "The critical keywords from system backplane VPD has " 480 "been reset successfully." 481 << std::endl; 482 483 } // try block end 484 catch (const std::exception& l_ex) 485 { 486 // TODO: Enable logging when verbose is enabled. 487 std::cerr 488 << "Manufacturing reset on system vpd keywords is unsuccessful. Error : " 489 << l_ex.what() << std::endl; 490 } 491 return constants::SUCCESS; 492 } 493 494 bool VpdTool::fetchKeywordInfo(nlohmann::json& io_parsedJsonObj) const noexcept 495 { 496 bool l_returnValue = false; 497 try 498 { 499 if (io_parsedJsonObj.empty() || !io_parsedJsonObj.contains("source") || 500 !io_parsedJsonObj.contains("destination") || 501 !io_parsedJsonObj.contains("backupMap")) 502 { 503 throw std::runtime_error("Invalid JSON"); 504 } 505 506 std::string l_srcVpdPath; 507 std::string l_dstVpdPath; 508 509 bool l_isSourceOnHardware = false; 510 if (l_srcVpdPath = io_parsedJsonObj["source"].value("hardwarePath", ""); 511 !l_srcVpdPath.empty()) 512 { 513 l_isSourceOnHardware = true; 514 } 515 else if (l_srcVpdPath = 516 io_parsedJsonObj["source"].value("inventoryPath", ""); 517 l_srcVpdPath.empty()) 518 { 519 throw std::runtime_error("Source path is empty in JSON"); 520 } 521 522 bool l_isDestinationOnHardware = false; 523 if (l_dstVpdPath = 524 io_parsedJsonObj["destination"].value("hardwarePath", ""); 525 !l_dstVpdPath.empty()) 526 { 527 l_isDestinationOnHardware = true; 528 } 529 else if (l_dstVpdPath = 530 io_parsedJsonObj["destination"].value("inventoryPath", ""); 531 l_dstVpdPath.empty()) 532 { 533 throw std::runtime_error("Destination path is empty in JSON"); 534 } 535 536 for (auto& l_aRecordKwInfo : io_parsedJsonObj["backupMap"]) 537 { 538 const std::string& l_srcRecordName = 539 l_aRecordKwInfo.value("sourceRecord", ""); 540 const std::string& l_srcKeywordName = 541 l_aRecordKwInfo.value("sourceKeyword", ""); 542 const std::string& l_dstRecordName = 543 l_aRecordKwInfo.value("destinationRecord", ""); 544 const std::string& l_dstKeywordName = 545 l_aRecordKwInfo.value("destinationKeyword", ""); 546 547 if (l_srcRecordName.empty() || l_dstRecordName.empty() || 548 l_srcKeywordName.empty() || l_dstKeywordName.empty()) 549 { 550 // TODO: Enable logging when verbose is enabled. 551 std::cout << "Record or keyword not found in the JSON." 552 << std::endl; 553 continue; 554 } 555 556 types::DbusVariantType l_srcKeywordVariant; 557 if (l_isSourceOnHardware) 558 { 559 l_srcKeywordVariant = utils::readKeywordFromHardware( 560 l_srcVpdPath, 561 std::make_tuple(l_srcRecordName, l_srcKeywordName)); 562 } 563 else 564 { 565 l_srcKeywordVariant = utils::readDbusProperty( 566 constants::inventoryManagerService, l_srcVpdPath, 567 constants::ipzVpdInfPrefix + l_srcRecordName, 568 l_srcKeywordName); 569 } 570 571 if (auto l_srcKeywordValue = 572 std::get_if<types::BinaryVector>(&l_srcKeywordVariant); 573 l_srcKeywordValue && !l_srcKeywordValue->empty()) 574 { 575 l_aRecordKwInfo["sourcekeywordValue"] = *l_srcKeywordValue; 576 } 577 else 578 { 579 // TODO: Enable logging when verbose is enabled. 580 std::cout 581 << "Invalid data type or empty data received, for source record: " 582 << l_srcRecordName << ", keyword: " << l_srcKeywordName 583 << std::endl; 584 continue; 585 } 586 587 types::DbusVariantType l_dstKeywordVariant; 588 if (l_isDestinationOnHardware) 589 { 590 l_dstKeywordVariant = utils::readKeywordFromHardware( 591 l_dstVpdPath, 592 std::make_tuple(l_dstRecordName, l_dstKeywordName)); 593 } 594 else 595 { 596 l_dstKeywordVariant = utils::readDbusProperty( 597 constants::inventoryManagerService, l_dstVpdPath, 598 constants::ipzVpdInfPrefix + l_dstRecordName, 599 l_dstKeywordName); 600 } 601 602 if (auto l_dstKeywordValue = 603 std::get_if<types::BinaryVector>(&l_dstKeywordVariant); 604 l_dstKeywordValue && !l_dstKeywordValue->empty()) 605 { 606 l_aRecordKwInfo["destinationkeywordValue"] = *l_dstKeywordValue; 607 } 608 else 609 { 610 // TODO: Enable logging when verbose is enabled. 611 std::cout 612 << "Invalid data type or empty data received, for destination record: " 613 << l_dstRecordName << ", keyword: " << l_dstKeywordName 614 << std::endl; 615 continue; 616 } 617 } 618 619 l_returnValue = true; 620 } 621 catch (const std::exception& l_ex) 622 { 623 // TODO: Enable logging when verbose is enabled. 624 std::cerr << l_ex.what() << std::endl; 625 } 626 627 return l_returnValue; 628 } 629 630 nlohmann::json 631 VpdTool::getFruTypeProperty(const std::string& i_objectPath) const noexcept 632 { 633 nlohmann::json l_resultInJson = nlohmann::json::object({}); 634 std::vector<std::string> l_pimInfList; 635 636 auto l_serviceInfMap = utils::GetServiceInterfacesForObject( 637 i_objectPath, std::vector<std::string>{constants::inventoryItemInf}); 638 if (l_serviceInfMap.contains(constants::inventoryManagerService)) 639 { 640 l_pimInfList = l_serviceInfMap[constants::inventoryManagerService]; 641 642 // iterate through the list and find 643 // "xyz.openbmc_project.Inventory.Item.*" 644 for (const auto& l_interface : l_pimInfList) 645 { 646 if (l_interface.find(constants::inventoryItemInf) != 647 std::string::npos && 648 l_interface.length() > 649 std::string(constants::inventoryItemInf).length()) 650 { 651 l_resultInJson.emplace("type", l_interface); 652 } 653 } 654 } 655 return l_resultInJson; 656 } 657 658 bool VpdTool::isFruPresent(const std::string& i_objectPath) const noexcept 659 { 660 bool l_returnValue{false}; 661 try 662 { 663 types::DbusVariantType l_keyWordValue; 664 665 l_keyWordValue = utils::readDbusProperty( 666 constants::inventoryManagerService, i_objectPath, 667 constants::inventoryItemInf, "Present"); 668 669 if (const auto l_value = std::get_if<bool>(&l_keyWordValue)) 670 { 671 l_returnValue = *l_value; 672 } 673 } 674 catch (const std::runtime_error& l_ex) 675 { 676 // TODO: Enable logging when verbose is enabled. 677 // std::cerr << "Failed to check \"Present\" property for FRU " 678 // << i_objectPath << " Error: " << l_ex.what() << 679 // std::endl; 680 } 681 return l_returnValue; 682 } 683 684 void VpdTool::printFixSystemVpdOption( 685 const types::UserOption& i_option) const noexcept 686 { 687 switch (i_option) 688 { 689 case types::UserOption::Exit: 690 std::cout << "Enter 0 => To exit successfully : "; 691 break; 692 case types::UserOption::UseBackupDataForAll: 693 std::cout << "Enter 1 => If you choose the data on backup for all " 694 "mismatching record-keyword pairs" 695 << std::endl; 696 break; 697 case types::UserOption::UseSystemBackplaneDataForAll: 698 std::cout << "Enter 2 => If you choose the data on primary for all " 699 "mismatching record-keyword pairs" 700 << std::endl; 701 break; 702 case types::UserOption::MoreOptions: 703 std::cout << "Enter 3 => If you wish to explore more options" 704 << std::endl; 705 break; 706 case types::UserOption::UseBackupDataForCurrent: 707 std::cout << "Enter 4 => If you choose the data on backup as the " 708 "right value" 709 << std::endl; 710 break; 711 case types::UserOption::UseSystemBackplaneDataForCurrent: 712 std::cout << "Enter 5 => If you choose the data on primary as the " 713 "right value" 714 << std::endl; 715 break; 716 case types::UserOption::NewValueOnBoth: 717 std::cout 718 << "Enter 6 => If you wish to enter a new value to update " 719 "both on backup and primary" 720 << std::endl; 721 break; 722 case types::UserOption::SkipCurrent: 723 std::cout << "Enter 7 => If you wish to skip the above " 724 "record-keyword pair" 725 << std::endl; 726 break; 727 } 728 } 729 730 int VpdTool::dumpInventory(bool i_dumpTable) const noexcept 731 { 732 int l_rc{constants::FAILURE}; 733 734 try 735 { 736 // get all object paths under PIM 737 const auto l_objectPaths = utils::GetSubTreePaths( 738 constants::baseInventoryPath, 0, 739 std::vector<std::string>{constants::inventoryItemInf}); 740 741 if (!l_objectPaths.empty()) 742 { 743 nlohmann::json l_resultInJson = nlohmann::json::array({}); 744 745 std::for_each(l_objectPaths.begin(), l_objectPaths.end(), 746 [&](const auto& l_objectPath) { 747 const auto l_fruJson = 748 getFruProperties(l_objectPath); 749 if (!l_fruJson.empty()) 750 { 751 if (l_resultInJson.empty()) 752 { 753 l_resultInJson += l_fruJson; 754 } 755 else 756 { 757 l_resultInJson.at(0).insert( 758 l_fruJson.cbegin(), l_fruJson.cend()); 759 } 760 } 761 }); 762 763 if (i_dumpTable) 764 { 765 // create Table object 766 utils::Table l_inventoryTable{}; 767 768 // columns to be populated in the Inventory table 769 const std::vector<types::TableColumnNameSizePair> 770 l_tableColumns = { 771 {"FRU", 100}, {"CC", 6}, {"DR", 20}, 772 {"LocationCode", 32}, {"PN", 8}, {"PrettyName", 80}, 773 {"SubModel", 10}, {"SN", 15}, {"type", 60}}; 774 775 types::TableInputData l_tableData; 776 777 // First prepare the Table Columns 778 for (const auto& l_column : l_tableColumns) 779 { 780 if (constants::FAILURE == 781 l_inventoryTable.AddColumn(l_column.first, 782 l_column.second)) 783 { 784 // TODO: Enable logging when verbose is enabled. 785 std::cerr << "Failed to add column " << l_column.first 786 << " in Inventory Table." << std::endl; 787 } 788 } 789 790 // iterate through the json array 791 for (const auto& l_fruEntry : l_resultInJson[0].items()) 792 { 793 // if object path ends in "unit([0-9][0-9]?)", skip adding 794 // the object path in the table 795 if (std::regex_search(l_fruEntry.key(), 796 std::regex("unit([0-9][0-9]?)"))) 797 { 798 continue; 799 } 800 801 std::vector<std::string> l_row; 802 for (const auto& l_column : l_tableColumns) 803 { 804 const auto& l_fruJson = l_fruEntry.value(); 805 806 if (l_column.first == "FRU") 807 { 808 l_row.push_back(l_fruEntry.key()); 809 } 810 else 811 { 812 if (l_fruJson.contains(l_column.first)) 813 { 814 l_row.push_back(l_fruJson[l_column.first]); 815 } 816 else 817 { 818 l_row.push_back(""); 819 } 820 } 821 } 822 823 l_tableData.push_back(l_row); 824 } 825 826 l_rc = l_inventoryTable.Print(l_tableData); 827 } 828 else 829 { 830 // print JSON to console 831 utils::printJson(l_resultInJson); 832 l_rc = constants::SUCCESS; 833 } 834 } 835 } 836 catch (const std::exception& l_ex) 837 { 838 // TODO: Enable logging when verbose is enabled. 839 std::cerr << "Dump inventory failed. Error: " << l_ex.what() 840 << std::endl; 841 } 842 return l_rc; 843 } 844 845 void VpdTool::printSystemVpd( 846 const nlohmann::json& i_parsedJsonObj) const noexcept 847 { 848 if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("backupMap")) 849 { 850 // TODO: Enable logging when verbose is enabled. 851 std::cerr << "Invalid JSON to print system VPD" << std::endl; 852 } 853 854 std::string l_outline(191, '='); 855 std::cout << "\nRestorable record-keyword pairs and their data on backup & " 856 "primary.\n\n" 857 << l_outline << std::endl; 858 859 std::cout << std::left << std::setw(6) << "S.No" << std::left 860 << std::setw(8) << "Record" << std::left << std::setw(9) 861 << "Keyword" << std::left << std::setw(75) << "Data On Backup" 862 << std::left << std::setw(75) << "Data On Primary" << std::left 863 << std::setw(14) << "Data Mismatch\n" 864 << l_outline << std::endl; 865 866 uint8_t l_slNum = 0; 867 868 for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"]) 869 { 870 if (l_aRecordKwInfo.contains("sourceRecord") || 871 l_aRecordKwInfo.contains("sourceKeyword") || 872 l_aRecordKwInfo.contains("destinationkeywordValue") || 873 l_aRecordKwInfo.contains("sourcekeywordValue")) 874 { 875 std::string l_mismatchFound{ 876 (l_aRecordKwInfo["destinationkeywordValue"] != 877 l_aRecordKwInfo["sourcekeywordValue"]) 878 ? "YES" 879 : "NO"}; 880 881 std::string l_splitLine(191, '-'); 882 883 try 884 { 885 std::cout << std::left << std::setw(6) 886 << static_cast<int>(++l_slNum) << std::left 887 << std::setw(8) 888 << l_aRecordKwInfo.value("sourceRecord", "") 889 << std::left << std::setw(9) 890 << l_aRecordKwInfo.value("sourceKeyword", "") 891 << std::left << std::setw(75) << std::setfill(' ') 892 << utils::getPrintableValue( 893 l_aRecordKwInfo["destinationkeywordValue"]) 894 << std::left << std::setw(75) << std::setfill(' ') 895 << utils::getPrintableValue( 896 l_aRecordKwInfo["sourcekeywordValue"]) 897 << std::left << std::setw(14) << l_mismatchFound 898 << '\n' 899 << l_splitLine << std::endl; 900 } 901 catch (const std::exception& l_ex) 902 { 903 // TODO: Enable logging when verbose is enabled. 904 std::cerr << l_ex.what() << std::endl; 905 } 906 } 907 } 908 } 909 910 int VpdTool::updateAllKeywords(const nlohmann::json& i_parsedJsonObj, 911 bool i_useBackupData) const noexcept 912 { 913 int l_rc = constants::FAILURE; 914 915 if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("source") || 916 !i_parsedJsonObj.contains("backupMap")) 917 { 918 // TODO: Enable logging when verbose is enabled. 919 std::cerr << "Invalid JSON" << std::endl; 920 return l_rc; 921 } 922 923 std::string l_srcVpdPath; 924 if (auto l_vpdPath = i_parsedJsonObj["source"].value("hardwarePath", ""); 925 !l_vpdPath.empty()) 926 { 927 l_srcVpdPath = l_vpdPath; 928 } 929 else if (auto l_vpdPath = 930 i_parsedJsonObj["source"].value("inventoryPath", ""); 931 !l_vpdPath.empty()) 932 { 933 l_srcVpdPath = l_vpdPath; 934 } 935 else 936 { 937 // TODO: Enable logging when verbose is enabled. 938 std::cerr << "source path information is missing in JSON" << std::endl; 939 return l_rc; 940 } 941 942 bool l_anyMismatchFound = false; 943 for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"]) 944 { 945 if (!l_aRecordKwInfo.contains("sourceRecord") || 946 !l_aRecordKwInfo.contains("sourceKeyword") || 947 !l_aRecordKwInfo.contains("destinationkeywordValue") || 948 !l_aRecordKwInfo.contains("sourcekeywordValue")) 949 { 950 // TODO: Enable logging when verbose is enabled. 951 std::cerr << "Missing required information in the JSON" 952 << std::endl; 953 continue; 954 } 955 956 if (l_aRecordKwInfo["sourcekeywordValue"] != 957 l_aRecordKwInfo["destinationkeywordValue"]) 958 { 959 l_anyMismatchFound = true; 960 961 auto l_keywordValue = 962 i_useBackupData ? l_aRecordKwInfo["destinationkeywordValue"] 963 : l_aRecordKwInfo["sourcekeywordValue"]; 964 965 auto l_paramsToWrite = std::make_tuple( 966 l_aRecordKwInfo["sourceRecord"], 967 l_aRecordKwInfo["sourceKeyword"], l_keywordValue); 968 969 try 970 { 971 l_rc = utils::writeKeyword(l_srcVpdPath, l_paramsToWrite); 972 if (l_rc > 0) 973 { 974 l_rc = constants::SUCCESS; 975 } 976 } 977 catch (const std::exception& l_ex) 978 { 979 // TODO: Enable logging when verbose is enabled. 980 std::cerr << "write keyword failed for record: " 981 << l_aRecordKwInfo["sourceRecord"] 982 << ", keyword: " << l_aRecordKwInfo["sourceKeyword"] 983 << ", error: " << l_ex.what() << std::ends; 984 } 985 } 986 } 987 988 std::string l_dataUsed = 989 (i_useBackupData ? "data from backup" : "data from primary VPD"); 990 if (l_anyMismatchFound) 991 { 992 std::cout << "Data updated successfully for all mismatching " 993 "record-keyword pairs by choosing their corresponding " 994 << l_dataUsed << ". Exit successfully." << std::endl; 995 } 996 else 997 { 998 std::cout << "No mismatch found for any of the above mentioned " 999 "record-keyword pair. Exit successfully." 1000 << std::endl; 1001 } 1002 1003 return l_rc; 1004 } 1005 1006 int VpdTool::handleMoreOption( 1007 const nlohmann::json& i_parsedJsonObj) const noexcept 1008 { 1009 int l_rc = constants::FAILURE; 1010 1011 try 1012 { 1013 if (i_parsedJsonObj.empty() || !i_parsedJsonObj.contains("backupMap")) 1014 { 1015 throw std::runtime_error("Invalid JSON"); 1016 } 1017 1018 std::string l_srcVpdPath; 1019 1020 if (auto l_vpdPath = 1021 i_parsedJsonObj["source"].value("hardwarePath", ""); 1022 !l_vpdPath.empty()) 1023 { 1024 l_srcVpdPath = l_vpdPath; 1025 } 1026 else if (auto l_vpdPath = 1027 i_parsedJsonObj["source"].value("inventoryPath", ""); 1028 !l_vpdPath.empty()) 1029 { 1030 l_srcVpdPath = l_vpdPath; 1031 } 1032 else 1033 { 1034 throw std::runtime_error( 1035 "source path information is missing in JSON"); 1036 } 1037 1038 auto updateKeywordValue = 1039 [](std::string io_vpdPath, const std::string& i_recordName, 1040 const std::string& i_keywordName, 1041 const types::BinaryVector& i_keywordValue) -> int { 1042 int l_rc = constants::FAILURE; 1043 1044 try 1045 { 1046 auto l_paramsToWrite = std::make_tuple( 1047 i_recordName, i_keywordName, i_keywordValue); 1048 l_rc = utils::writeKeyword(io_vpdPath, l_paramsToWrite); 1049 1050 if (l_rc > 0) 1051 { 1052 std::cout << std::endl 1053 << "Data updated successfully." << std::endl; 1054 } 1055 } 1056 catch (const std::exception& l_ex) 1057 { 1058 // TODO: Enable log when verbose is enabled. 1059 std::cerr << l_ex.what() << std::endl; 1060 } 1061 return l_rc; 1062 }; 1063 1064 do 1065 { 1066 int l_slNum = 0; 1067 bool l_exit = false; 1068 1069 for (const auto& l_aRecordKwInfo : i_parsedJsonObj["backupMap"]) 1070 { 1071 if (!l_aRecordKwInfo.contains("sourceRecord") || 1072 !l_aRecordKwInfo.contains("sourceKeyword") || 1073 !l_aRecordKwInfo.contains("destinationkeywordValue") || 1074 !l_aRecordKwInfo.contains("sourcekeywordValue")) 1075 { 1076 // TODO: Enable logging when verbose is enabled. 1077 std::cerr 1078 << "Source or destination information is missing in the JSON." 1079 << std::endl; 1080 continue; 1081 } 1082 1083 const std::string l_mismatchFound{ 1084 (l_aRecordKwInfo["sourcekeywordValue"] != 1085 l_aRecordKwInfo["destinationkeywordValue"]) 1086 ? "YES" 1087 : "NO"}; 1088 1089 std::cout << std::endl 1090 << std::left << std::setw(6) << "S.No" << std::left 1091 << std::setw(8) << "Record" << std::left 1092 << std::setw(9) << "Keyword" << std::left 1093 << std::setw(75) << std::setfill(' ') << "Backup Data" 1094 << std::left << std::setw(75) << std::setfill(' ') 1095 << "Primary Data" << std::left << std::setw(14) 1096 << "Data Mismatch" << std::endl; 1097 1098 std::cout << std::left << std::setw(6) 1099 << static_cast<int>(++l_slNum) << std::left 1100 << std::setw(8) 1101 << l_aRecordKwInfo.value("sourceRecord", "") 1102 << std::left << std::setw(9) 1103 << l_aRecordKwInfo.value("sourceKeyword", "") 1104 << std::left << std::setw(75) << std::setfill(' ') 1105 << utils::getPrintableValue( 1106 l_aRecordKwInfo["destinationkeywordValue"]) 1107 << std::left << std::setw(75) << std::setfill(' ') 1108 << utils::getPrintableValue( 1109 l_aRecordKwInfo["sourcekeywordValue"]) 1110 << std::left << std::setw(14) << l_mismatchFound 1111 << std::endl; 1112 1113 std::cout << std::string(191, '=') << std::endl; 1114 1115 if (constants::STR_CMP_SUCCESS == 1116 l_mismatchFound.compare("YES")) 1117 { 1118 printFixSystemVpdOption( 1119 types::UserOption::UseBackupDataForCurrent); 1120 printFixSystemVpdOption( 1121 types::UserOption::UseSystemBackplaneDataForCurrent); 1122 printFixSystemVpdOption(types::UserOption::NewValueOnBoth); 1123 printFixSystemVpdOption(types::UserOption::SkipCurrent); 1124 printFixSystemVpdOption(types::UserOption::Exit); 1125 } 1126 else 1127 { 1128 std::cout << "No mismatch found." << std::endl << std::endl; 1129 printFixSystemVpdOption(types::UserOption::NewValueOnBoth); 1130 printFixSystemVpdOption(types::UserOption::SkipCurrent); 1131 printFixSystemVpdOption(types::UserOption::Exit); 1132 } 1133 1134 int l_userSelectedOption = types::UserOption::Exit; 1135 std::cin >> l_userSelectedOption; 1136 1137 if (types::UserOption::UseBackupDataForCurrent == 1138 l_userSelectedOption) 1139 { 1140 l_rc = updateKeywordValue( 1141 l_srcVpdPath, l_aRecordKwInfo["sourceRecord"], 1142 l_aRecordKwInfo["sourceKeyword"], 1143 l_aRecordKwInfo["destinationkeywordValue"]); 1144 } 1145 else if (types::UserOption::UseSystemBackplaneDataForCurrent == 1146 l_userSelectedOption) 1147 { 1148 l_rc = updateKeywordValue( 1149 l_srcVpdPath, l_aRecordKwInfo["sourceRecord"], 1150 l_aRecordKwInfo["sourceKeyword"], 1151 l_aRecordKwInfo["sourcekeywordValue"]); 1152 } 1153 else if (types::UserOption::NewValueOnBoth == 1154 l_userSelectedOption) 1155 { 1156 std::string l_newValue; 1157 std::cout 1158 << std::endl 1159 << "Enter the new value to update on both " 1160 "primary & backup. Value should be in ASCII or " 1161 "in HEX(prefixed with 0x) : "; 1162 std::cin >> l_newValue; 1163 std::cout << std::endl 1164 << std::string(191, '=') << std::endl; 1165 1166 try 1167 { 1168 l_rc = updateKeywordValue( 1169 l_srcVpdPath, l_aRecordKwInfo["sourceRecord"], 1170 l_aRecordKwInfo["sourceKeyword"], 1171 utils::convertToBinary(l_newValue)); 1172 } 1173 catch (const std::exception& l_ex) 1174 { 1175 // TODO: Enable logging when verbose is enabled. 1176 std::cerr << l_ex.what() << std::endl; 1177 } 1178 } 1179 else if (types::UserOption::SkipCurrent == l_userSelectedOption) 1180 { 1181 std::cout << std::endl 1182 << "Skipped the above record-keyword pair. " 1183 "Continue to the next available pair." 1184 << std::endl; 1185 } 1186 else if (types::UserOption::Exit == l_userSelectedOption) 1187 { 1188 std::cout << "Exit successfully" << std::endl; 1189 l_exit = true; 1190 break; 1191 } 1192 else 1193 { 1194 std::cout << "Provide a valid option. Retrying for the " 1195 "current record-keyword pair" 1196 << std::endl; 1197 } 1198 } 1199 if (l_exit) 1200 { 1201 l_rc = constants::SUCCESS; 1202 break; 1203 } 1204 } while (true); 1205 } 1206 catch (const std::exception& l_ex) 1207 { 1208 // TODO: Enable logging when verbose is enabled. 1209 std::cerr << l_ex.what() << std::endl; 1210 } 1211 1212 return l_rc; 1213 } 1214 1215 } // namespace vpd 1216