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 {
readKeyword(const std::string & i_vpdPath,const std::string & i_recordName,const std::string & i_keywordName,const bool i_onHardware,const std::string & i_fileToSave)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
dumpObject(std::string i_fruPath) const91 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
getFruProperties(const std::string & i_objectPath) const123 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>
getInventoryPropertyJson(const std::string & i_objectPath,const std::string & i_interface,const std::string & i_propertyName) const216 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
fixSystemVpd() const265 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
writeKeyword(std::string i_vpdPath,const std::string & i_recordName,const std::string & i_keywordName,const std::string & i_keywordValue,const bool i_onHardware)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
getBackupRestoreCfgJsonObj() const365 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
cleanSystemVpd(bool i_syncBiosAttributes) const393 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
fetchKeywordInfo(nlohmann::json & io_parsedJsonObj) const494 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
getFruTypeProperty(const std::string & i_objectPath) const631 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
isFruPresent(const std::string & i_objectPath) const658 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
printFixSystemVpdOption(const types::UserOption & i_option) const684 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
dumpInventory(bool i_dumpTable) const730 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
printSystemVpd(const nlohmann::json & i_parsedJsonObj) const845 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
updateAllKeywords(const nlohmann::json & i_parsedJsonObj,bool i_useBackupData) const910 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
handleMoreOption(const nlohmann::json & i_parsedJsonObj) const1006 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