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