xref: /openbmc/openpower-vpd-parser/vpd-manager/include/utility/json_utility.hpp (revision 393c0fade4690d37e7dd7227730b2bcaef9214db)
1 #pragma once
2 
3 #include "event_logger.hpp"
4 #include "exceptions.hpp"
5 #include "logger.hpp"
6 #include "types.hpp"
7 
8 #include <gpiod.hpp>
9 #include <nlohmann/json.hpp>
10 #include <utility/common_utility.hpp>
11 
12 #include <fstream>
13 #include <type_traits>
14 #include <unordered_map>
15 
16 namespace vpd
17 {
18 namespace jsonUtility
19 {
20 
21 // forward declaration of API for function map.
22 bool processSystemCmdTag(const nlohmann::json& i_parsedConfigJson,
23                          const std::string& i_vpdFilePath,
24                          const std::string& i_baseAction,
25                          const std::string& i_flagToProcess);
26 
27 // forward declaration of API for function map.
28 bool processGpioPresenceTag(
29     const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
30     const std::string& i_baseAction, const std::string& i_flagToProcess);
31 
32 // forward declaration of API for function map.
33 bool procesSetGpioTag(const nlohmann::json& i_parsedConfigJson,
34                       const std::string& i_vpdFilePath,
35                       const std::string& i_baseAction,
36                       const std::string& i_flagToProcess);
37 
38 // Function pointers to process tags from config JSON.
39 typedef bool (*functionPtr)(
40     const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
41     const std::string& i_baseAction, const std::string& i_flagToProcess);
42 
43 inline std::unordered_map<std::string, functionPtr> funcionMap{
44     {"gpioPresence", processGpioPresenceTag},
45     {"setGpio", procesSetGpioTag},
46     {"systemCmd", processSystemCmdTag}};
47 
48 /**
49  * @brief API to read VPD offset from JSON file.
50  *
51  * @param[in] i_sysCfgJsonObj - Parsed system config JSON object.
52  * @param[in] i_vpdFilePath - VPD file path.
53  * @return VPD offset if found in JSON, 0 otherwise.
54  */
55 inline size_t getVPDOffset(const nlohmann::json& i_sysCfgJsonObj,
56                            const std::string& i_vpdFilePath)
57 {
58     if (i_vpdFilePath.empty() || (i_sysCfgJsonObj.empty()) ||
59         (!i_sysCfgJsonObj.contains("frus")))
60     {
61         return 0;
62     }
63 
64     if (i_sysCfgJsonObj["frus"].contains(i_vpdFilePath))
65     {
66         return i_sysCfgJsonObj["frus"][i_vpdFilePath].at(0).value("offset", 0);
67     }
68 
69     const nlohmann::json& l_fruList =
70         i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
71 
72     for (const auto& l_fru : l_fruList.items())
73     {
74         const auto l_fruPath = l_fru.key();
75 
76         // check if given path is redundant FRU path
77         if (i_vpdFilePath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
78                                  "redundantEeprom", ""))
79         {
80             // Return the offset of redundant EEPROM taken from JSON.
81             return i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("offset", 0);
82         }
83     }
84 
85     return 0;
86 }
87 
88 /**
89  * @brief API to parse respective JSON.
90  *
91  * @param[in] pathToJson - Path to JSON.
92  * @return on success parsed JSON. On failure empty JSON object.
93  *
94  * Note: Caller has to handle it in case an empty JSON object is received.
95  */
96 inline nlohmann::json getParsedJson(const std::string& pathToJson) noexcept
97 {
98     try
99     {
100         if (pathToJson.empty())
101         {
102             throw std::runtime_error("Path to JSON is missing");
103         }
104 
105         if (!std::filesystem::exists(pathToJson) ||
106             std::filesystem::is_empty(pathToJson))
107         {
108             throw std::runtime_error(
109                 "File does not exist or empty file: [" + pathToJson + "]");
110         }
111 
112         std::ifstream l_jsonFile(pathToJson);
113         if (!l_jsonFile)
114         {
115             throw std::runtime_error(
116                 "Failed to access Json path = " + pathToJson);
117         }
118 
119         return nlohmann::json::parse(l_jsonFile);
120     }
121     catch (const std::exception& l_ex)
122     {
123         logging::logMessage(
124             "Failed to parse JSON file, error: " + std::string(l_ex.what()));
125     }
126 
127     return nlohmann::json{};
128 }
129 
130 /**
131  * @brief Get inventory object path from system config JSON.
132  *
133  * Given either D-bus inventory path/FRU EEPROM path/redundant EEPROM path,
134  * this API returns D-bus inventory path if present in JSON.
135  *
136  * @param[in] i_sysCfgJsonObj - System config JSON object
137  * @param[in] i_vpdPath - Path to where VPD is stored.
138  *
139  * @return On success a valid path is returned, on failure an empty string is
140  * returned.
141  *
142  * Note: Caller has to handle it in case an empty string is received.
143  */
144 inline std::string getInventoryObjPathFromJson(
145     const nlohmann::json& i_sysCfgJsonObj,
146     const std::string& i_vpdPath) noexcept
147 {
148     try
149     {
150         if (i_vpdPath.empty())
151         {
152             throw std::runtime_error("Path parameter is empty.");
153         }
154 
155         if (!i_sysCfgJsonObj.contains("frus"))
156         {
157             throw std::runtime_error("Missing frus tag in system config JSON.");
158         }
159 
160         // check if given path is FRU path
161         if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
162         {
163             return i_sysCfgJsonObj["frus"][i_vpdPath].at(0).value(
164                 "inventoryPath", "");
165         }
166 
167         const nlohmann::json& l_fruList =
168             i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
169 
170         for (const auto& l_fru : l_fruList.items())
171         {
172             const auto l_fruPath = l_fru.key();
173             const auto l_invObjPath =
174                 i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("inventoryPath",
175                                                                "");
176 
177             // check if given path is redundant FRU path or inventory path
178             if (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
179                                  "redundantEeprom", "") ||
180                 (i_vpdPath == l_invObjPath))
181             {
182                 return l_invObjPath;
183             }
184         }
185     }
186     catch (const std::exception& l_ex)
187     {
188         logging::logMessage(
189             "Failed to get inventory object path from json, error: " +
190             std::string(l_ex.what()));
191     }
192 
193     return std::string();
194 }
195 
196 /**
197  * @brief Process "PostFailAction" defined in config JSON.
198  *
199  * In case there is some error in the processing of "preAction" execution and a
200  * set of procedure needs to be done as a part of post fail action. This base
201  * action can be defined in the config JSON for that FRU and it will be handled
202  * under this API.
203  *
204  * @param[in] i_parsedConfigJson - config JSON
205  * @param[in] i_vpdFilePath - EEPROM file path
206  * @param[in] i_flagToProcess - To identify which flag(s) needs to be processed
207  * under PostFailAction tag of config JSON.
208  * @return - success or failure
209  */
210 inline bool executePostFailAction(const nlohmann::json& i_parsedConfigJson,
211                                   const std::string& i_vpdFilePath,
212                                   const std::string& i_flagToProcess)
213 {
214     try
215     {
216         if (i_parsedConfigJson.empty() || i_vpdFilePath.empty() ||
217             i_flagToProcess.empty())
218         {
219             throw std::runtime_error(
220                 "Invalid parameters. Abort processing for post fail action");
221         }
222 
223         if (!(i_parsedConfigJson["frus"][i_vpdFilePath].at(0))["postFailAction"]
224                  .contains(i_flagToProcess))
225         {
226             throw std::runtime_error(
227                 "Config JSON missing flag " + i_flagToProcess +
228                 " to execute post fail action for path = " + i_vpdFilePath);
229         }
230 
231         for (const auto& l_tags : (i_parsedConfigJson["frus"][i_vpdFilePath].at(
232                  0))["postFailAction"][i_flagToProcess]
233                                       .items())
234         {
235             auto itrToFunction = funcionMap.find(l_tags.key());
236             if (itrToFunction != funcionMap.end())
237             {
238                 if (!itrToFunction->second(i_parsedConfigJson, i_vpdFilePath,
239                                            "postFailAction", i_flagToProcess))
240                 {
241                     return false;
242                 }
243             }
244         }
245     }
246     catch (const std::exception& l_ex)
247     {
248         logging::logMessage("Execute post fail action failed. Error : " +
249                             std::string(l_ex.what()));
250         return false;
251     }
252 
253     return true;
254 }
255 
256 /**
257  * @brief Process "systemCmd" tag for a given FRU.
258  *
259  * The API will process "systemCmd" tag if it is defined in the config
260  * JSON for the given FRU.
261  *
262  * @param[in] i_parsedConfigJson - config JSON
263  * @param[in] i_vpdFilePath - EEPROM file path
264  * @param[in] i_baseAction - Base action for which this tag has been called.
265  * @param[in] i_flagToProcess - Flag nested under the base action for which this
266  * tag has been called.
267  * @return Execution status.
268  */
269 inline bool processSystemCmdTag(
270     const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
271     const std::string& i_baseAction, const std::string& i_flagToProcess)
272 {
273     try
274     {
275         if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
276             i_baseAction.empty() || i_flagToProcess.empty())
277         {
278             throw std::runtime_error(
279                 std::string(__FUNCTION__) +
280                 " Invalid parameter. Abort processing of processSystemCmd.");
281         }
282 
283         if (!((i_parsedConfigJson["frus"][i_vpdFilePath].at(
284                    0)[i_baseAction][i_flagToProcess]["systemCmd"])
285                   .contains("cmd")))
286         {
287             throw JsonException(
288                 std::string(__FUNCTION__) +
289                 " Config JSON missing required information to execute system command for EEPROM " +
290                 i_vpdFilePath);
291         }
292 
293         const std::string& l_systemCommand =
294             i_parsedConfigJson["frus"][i_vpdFilePath].at(
295                 0)[i_baseAction][i_flagToProcess]["systemCmd"]["cmd"];
296 
297         commonUtility::executeCmd(l_systemCommand);
298         return true;
299     }
300     catch (const std::exception& l_ex)
301     {
302         EventLogger::createSyncPel(
303             EventLogger::getErrorType(l_ex), types::SeverityType::Informational,
304             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
305             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
306         return false;
307     }
308 }
309 
310 /**
311  * @brief Checks for presence of a given FRU using GPIO line.
312  *
313  * This API returns the presence information of the FRU corresponding to the
314  * given VPD file path by setting the presence pin.
315  *
316  * @param[in] i_parsedConfigJson - config JSON
317  * @param[in] i_vpdFilePath - EEPROM file path
318  * @param[in] i_baseAction - Base action for which this tag has been called.
319  * @param[in] i_flagToProcess - Flag nested under the base action for which this
320  * tag has been called.
321  * @return Execution status.
322  */
323 inline bool processGpioPresenceTag(
324     const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
325     const std::string& i_baseAction, const std::string& i_flagToProcess)
326 {
327     std::string l_presencePinName;
328     try
329     {
330         if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
331             i_baseAction.empty() || i_flagToProcess.empty())
332         {
333             throw std::runtime_error(
334                 std::string(__FUNCTION__) +
335                 "Invalid parameter. Abort processing of processGpioPresence tag");
336         }
337 
338         if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at(
339                     0)[i_baseAction][i_flagToProcess]["gpioPresence"])
340                    .contains("pin")) &&
341               ((i_parsedConfigJson["frus"][i_vpdFilePath].at(
342                     0)[i_baseAction][i_flagToProcess]["gpioPresence"])
343                    .contains("value"))))
344         {
345             throw JsonException(
346                 std::string(__FUNCTION__) +
347                 "Config JSON missing required information to detect presence for EEPROM " +
348                 i_vpdFilePath);
349         }
350 
351         // get the pin name
352         l_presencePinName = i_parsedConfigJson["frus"][i_vpdFilePath].at(
353             0)[i_baseAction][i_flagToProcess]["gpioPresence"]["pin"];
354 
355         // get the pin value
356         uint8_t l_presencePinValue =
357             i_parsedConfigJson["frus"][i_vpdFilePath].at(
358                 0)[i_baseAction][i_flagToProcess]["gpioPresence"]["value"];
359 
360         gpiod::line l_presenceLine = gpiod::find_line(l_presencePinName);
361 
362         if (!l_presenceLine)
363         {
364             throw GpioException("Couldn't find the GPIO line.");
365         }
366 
367         l_presenceLine.request({"Read the presence line",
368                                 gpiod::line_request::DIRECTION_INPUT, 0});
369 
370         return (l_presencePinValue == l_presenceLine.get_value());
371     }
372     catch (const std::exception& l_ex)
373     {
374         // No need to continue in case of JSON failure or Firmware error
375         // as these are errors internal to the code and in that case the FRU
376         // should not be processed. Any other error is considered as external
377         // error in this case and a try to read the EEPROM should be done.
378         if (EventLogger::getErrorType(l_ex) == types::ErrorType::JsonFailure ||
379             EventLogger::getErrorType(l_ex) == types::ErrorType::FirmwareError)
380         {
381             EventLogger::createSyncPel(
382                 EventLogger::getErrorType(l_ex),
383                 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
384                 EventLogger::getErrorMsg(l_ex), std::nullopt, std::nullopt,
385                 std::nullopt, std::nullopt);
386             return false;
387         }
388 
389         std::string l_errMsg = "Exception on GPIO line: ";
390         l_errMsg += l_presencePinName;
391         l_errMsg += " Reason: ";
392         l_errMsg += l_ex.what();
393         l_errMsg += " File: " + i_vpdFilePath + " Pel Logged";
394 
395         // ToDo -- Update Internal Rc code.
396         EventLogger::createAsyncPelWithInventoryCallout(
397             EventLogger::getErrorType(l_ex), types::SeverityType::Informational,
398             {{getInventoryObjPathFromJson(i_parsedConfigJson, i_vpdFilePath),
399               types::CalloutPriority::High}},
400             std::source_location::current().file_name(),
401             std::source_location::current().function_name(), 0, l_errMsg,
402             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
403 
404         logging::logMessage(l_errMsg);
405 
406         // Except when GPIO pin value is false, we go and try collecting the
407         // FRU VPD as we couldn't able to read GPIO pin value due to some
408         // error/exception. So returning true in error scenario.
409         return true;
410     }
411 }
412 
413 /**
414  * @brief Process "setGpio" tag for a given FRU.
415  *
416  * This API enables the GPIO line.
417  *
418  * @param[in] i_parsedConfigJson - config JSON
419  * @param[in] i_vpdFilePath - EEPROM file path
420  * @param[in] i_baseAction - Base action for which this tag has been called.
421  * @param[in] i_flagToProcess - Flag nested under the base action for which this
422  * tag has been called.
423  * @return Execution status.
424  */
425 inline bool procesSetGpioTag(
426     const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
427     const std::string& i_baseAction, const std::string& i_flagToProcess)
428 {
429     std::string l_pinName;
430     try
431     {
432         if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
433             i_baseAction.empty() || i_flagToProcess.empty())
434         {
435             throw std::runtime_error(
436                 std::string(__FUNCTION__) +
437                 " Invalid parameter. Abort processing of procesSetGpio.");
438         }
439 
440         if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at(
441                     0)[i_baseAction][i_flagToProcess]["setGpio"])
442                    .contains("pin")) &&
443               ((i_parsedConfigJson["frus"][i_vpdFilePath].at(
444                     0)[i_baseAction][i_flagToProcess]["setGpio"])
445                    .contains("value"))))
446         {
447             throw JsonException(
448                 std::string(__FUNCTION__) +
449                 " Config JSON missing required information to set gpio line for EEPROM " +
450                 i_vpdFilePath);
451         }
452 
453         l_pinName = i_parsedConfigJson["frus"][i_vpdFilePath].at(
454             0)[i_baseAction][i_flagToProcess]["setGpio"]["pin"];
455 
456         // Get the value to set
457         uint8_t l_pinValue = i_parsedConfigJson["frus"][i_vpdFilePath].at(
458             0)[i_baseAction][i_flagToProcess]["setGpio"]["value"];
459 
460         logging::logMessage(
461             "Setting GPIO: " + l_pinName + " to " + std::to_string(l_pinValue));
462 
463         gpiod::line l_outputLine = gpiod::find_line(l_pinName);
464 
465         if (!l_outputLine)
466         {
467             throw GpioException("Couldn't find GPIO line.");
468         }
469 
470         l_outputLine.request(
471             {"FRU Action", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
472             l_pinValue);
473         return true;
474     }
475     catch (const std::exception& l_ex)
476     {
477         if (EventLogger::getErrorType(l_ex) != types::ErrorType::GpioError)
478         {
479             EventLogger::createSyncPel(
480                 EventLogger::getErrorType(l_ex),
481                 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
482                 EventLogger::getErrorMsg(l_ex), std::nullopt, std::nullopt,
483                 std::nullopt, std::nullopt);
484         }
485         else
486         {
487             std::string l_errMsg = "Exception on GPIO line: ";
488             l_errMsg += l_pinName;
489             l_errMsg += " Reason: ";
490             l_errMsg += l_ex.what();
491             l_errMsg += " File: " + i_vpdFilePath + " Pel Logged";
492 
493             // ToDo -- Update Internal RC code
494             EventLogger::createAsyncPelWithInventoryCallout(
495                 EventLogger::getErrorType(l_ex),
496                 types::SeverityType::Informational,
497                 {{getInventoryObjPathFromJson(i_parsedConfigJson,
498                                               i_vpdFilePath),
499                   types::CalloutPriority::High}},
500                 std::source_location::current().file_name(),
501                 std::source_location::current().function_name(), 0, l_errMsg,
502                 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
503 
504             logging::logMessage(l_errMsg);
505         }
506 
507         return false;
508     }
509 }
510 
511 /**
512  * @brief Process any action, if defined in config JSON.
513  *
514  * If any FRU(s) requires any special handling, then this base action can be
515  * defined for that FRU in the config JSON, processing of which will be handled
516  * in this API.
517  * Examples of action - preAction, PostAction etc.
518  *
519  * @param[in] i_parsedConfigJson - config JSON
520  * @param[in] i_action - Base action to be performed.
521  * @param[in] i_vpdFilePath - EEPROM file path
522  * @param[in] i_flagToProcess - To identify which flag(s) needs to be processed
523  * under PreAction tag of config JSON.
524  * @return - success or failure
525  */
526 inline bool executeBaseAction(
527     const nlohmann::json& i_parsedConfigJson, const std::string& i_action,
528     const std::string& i_vpdFilePath, const std::string& i_flagToProcess)
529 {
530     try
531     {
532         if (i_flagToProcess.empty() || i_action.empty() ||
533             i_vpdFilePath.empty() || !i_parsedConfigJson.contains("frus"))
534         {
535             throw std::runtime_error(
536                 std::string(__FUNCTION__) + " Invalid parameter");
537         }
538 
539         if (!i_parsedConfigJson["frus"].contains(i_vpdFilePath))
540         {
541             throw JsonException(std::string(__FUNCTION__) + " File path: " +
542                                 i_vpdFilePath + " not found in JSON");
543         }
544 
545         if (!i_parsedConfigJson["frus"][i_vpdFilePath].at(0).contains(i_action))
546         {
547             throw JsonException(
548                 std::string(__FUNCTION__) + " Action [" + i_action +
549                 "] not defined for file path:" + i_vpdFilePath);
550         }
551 
552         if (!(i_parsedConfigJson["frus"][i_vpdFilePath].at(0))[i_action]
553                  .contains(i_flagToProcess))
554         {
555             throw JsonException(
556                 std::string(__FUNCTION__) + "Config JSON missing flag [" +
557                 i_flagToProcess +
558                 "] to execute action for path = " + i_vpdFilePath);
559         }
560     }
561     catch (const std::exception& l_ex)
562     {
563         EventLogger::createSyncPel(
564             EventLogger::getErrorType(l_ex), types::SeverityType::Informational,
565             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
566             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
567         return false;
568     }
569 
570     const nlohmann::json& l_tagsJson =
571         (i_parsedConfigJson["frus"][i_vpdFilePath].at(
572             0))[i_action][i_flagToProcess];
573 
574     for (const auto& l_tag : l_tagsJson.items())
575     {
576         auto itrToFunction = funcionMap.find(l_tag.key());
577         if (itrToFunction != funcionMap.end())
578         {
579             if (!itrToFunction->second(i_parsedConfigJson, i_vpdFilePath,
580                                        i_action, i_flagToProcess))
581             {
582                 // In case any of the tag fails to execute. Mark action
583                 // as failed for that flag.
584                 return false;
585             }
586         }
587     }
588 
589     return true;
590 }
591 
592 /**
593  * @brief Get redundant FRU path from system config JSON
594  *
595  * Given either D-bus inventory path/FRU path/redundant FRU path, this
596  * API returns the redundant FRU path taken from "redundantEeprom" tag from
597  * system config JSON.
598  *
599  * @param[in] i_sysCfgJsonObj - System config JSON object.
600  * @param[in] i_vpdPath - Path to where VPD is stored.
601  *
602  * @return On success return valid path, on failure return empty string.
603  */
604 inline std::string getRedundantEepromPathFromJson(
605     const nlohmann::json& i_sysCfgJsonObj,
606     const std::string& i_vpdPath) noexcept
607 {
608     try
609     {
610         if (i_vpdPath.empty())
611         {
612             throw std::runtime_error("Path parameter is empty.");
613         }
614 
615         if (!i_sysCfgJsonObj.contains("frus"))
616         {
617             throw std::runtime_error("Missing frus tag in system config JSON.");
618         }
619 
620         // check if given path is FRU path
621         if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
622         {
623             return i_sysCfgJsonObj["frus"][i_vpdPath].at(0).value(
624                 "redundantEeprom", "");
625         }
626 
627         const nlohmann::json& l_fruList =
628             i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
629 
630         for (const auto& l_fru : l_fruList.items())
631         {
632             const std::string& l_fruPath = l_fru.key();
633             const std::string& l_redundantFruPath =
634                 i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
635                     "redundantEeprom", "");
636 
637             // check if given path is inventory path or redundant FRU path
638             if ((i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
639                      "inventoryPath", "") == i_vpdPath) ||
640                 (l_redundantFruPath == i_vpdPath))
641             {
642                 return l_redundantFruPath;
643             }
644         }
645     }
646     catch (const std::exception& l_ex)
647     {
648         logging::logMessage("Failed to get redundant EEPROM path, error: " +
649                             std::string(l_ex.what()));
650     }
651 
652     return std::string();
653 }
654 
655 /**
656  * @brief Get FRU EEPROM path from system config JSON
657  *
658  * Given either D-bus inventory path/FRU EEPROM path/redundant EEPROM path,
659  * this API returns FRU EEPROM path if present in JSON.
660  *
661  * @param[in] i_sysCfgJsonObj - System config JSON object
662  * @param[in] i_vpdPath - Path to where VPD is stored.
663  *
664  * @return On success return valid path, on failure return empty string.
665  */
666 inline std::string getFruPathFromJson(const nlohmann::json& i_sysCfgJsonObj,
667                                       const std::string& i_vpdPath) noexcept
668 {
669     try
670     {
671         if (i_vpdPath.empty())
672         {
673             throw std::runtime_error("Path parameter is empty.");
674         }
675 
676         if (!i_sysCfgJsonObj.contains("frus"))
677         {
678             throw std::runtime_error("Missing frus tag in system config JSON.");
679         }
680 
681         // check if given path is FRU path
682         if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
683         {
684             return i_vpdPath;
685         }
686 
687         const nlohmann::json& l_fruList =
688             i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
689 
690         for (const auto& l_fru : l_fruList.items())
691         {
692             const auto l_fruPath = l_fru.key();
693 
694             // check if given path is redundant FRU path or inventory path
695             if (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
696                                  "redundantEeprom", "") ||
697                 (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
698                                   "inventoryPath", "")))
699             {
700                 return l_fruPath;
701             }
702         }
703     }
704     catch (const std::exception& l_ex)
705     {
706         logging::logMessage("Failed to get FRU path from JSON, error: " +
707                             std::string(l_ex.what()));
708     }
709 
710     return std::string();
711 }
712 
713 /**
714  * @brief An API to check backup and restore VPD is required.
715  *
716  * The API checks if there is provision for backup and restore mentioned in the
717  * system config JSON, by looking "backupRestoreConfigPath" tag.
718  * Checks if the path mentioned is a hardware path, by checking if the file path
719  * exists and size of contents in the path.
720  *
721  * @param[in] i_sysCfgJsonObj - System config JSON object.
722  *
723  * @return true if backup and restore is required, false otherwise.
724  */
725 inline bool isBackupAndRestoreRequired(const nlohmann::json& i_sysCfgJsonObj)
726 {
727     try
728     {
729         const std::string& l_backupAndRestoreCfgFilePath =
730             i_sysCfgJsonObj.value("backupRestoreConfigPath", "");
731         if (!l_backupAndRestoreCfgFilePath.empty() &&
732             std::filesystem::exists(l_backupAndRestoreCfgFilePath) &&
733             !std::filesystem::is_empty(l_backupAndRestoreCfgFilePath))
734         {
735             return true;
736         }
737     }
738     catch (std::exception& ex)
739     {
740         logging::logMessage(ex.what());
741     }
742     return false;
743 }
744 
745 /** @brief API to check if an action is required for given EEPROM path.
746  *
747  * System config JSON can contain pre-action, post-action etc. like actions
748  * defined for an EEPROM path. The API will check if any such action is defined
749  * for the EEPROM.
750  *
751  * @param[in] i_sysCfgJsonObj - System config JSON object.
752  * @param[in] i_vpdFruPath - EEPROM path.
753  * @param[in] i_action - Action to be checked.
754  * @param[in] i_flowFlag - Denotes the flow w.r.t which the action should be
755  * triggered.
756  * @return - True if action is defined for the flow, false otherwise.
757  */
758 inline bool isActionRequired(
759     const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdFruPath,
760     const std::string& i_action, const std::string& i_flowFlag)
761 {
762     if (i_vpdFruPath.empty() || i_action.empty() || i_flowFlag.empty())
763     {
764         logging::logMessage("Invalid parameters recieved.");
765         return false;
766     }
767 
768     if (!i_sysCfgJsonObj.contains("frus"))
769     {
770         logging::logMessage("Invalid JSON object recieved.");
771         return false;
772     }
773 
774     if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
775     {
776         logging::logMessage(
777             "JSON object does not contain EEPROM path " + i_vpdFruPath);
778         return false;
779     }
780 
781     if ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)).contains(i_action))
782     {
783         if ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))[i_action].contains(
784                 i_flowFlag))
785         {
786             return true;
787         }
788 
789         logging::logMessage("Flow flag: [" + i_flowFlag +
790                             "], not found in JSON for path: " + i_vpdFruPath);
791         return false;
792     }
793     return false;
794 }
795 
796 /**
797  * @brief An API to return list of FRUs that needs GPIO polling.
798  *
799  * An API that checks for the FRUs that requires GPIO polling and returns
800  * a list of FRUs that needs polling. Returns an empty list if there are
801  * no FRUs that requires polling.
802  *
803  * @param[in] i_sysCfgJsonObj - System config JSON object.
804  *
805  * @return On success list of FRUs parameters that needs polling. On failure,
806  * empty list.
807  */
808 inline std::vector<std::string> getListOfGpioPollingFrus(
809     const nlohmann::json& i_sysCfgJsonObj) noexcept
810 {
811     std::vector<std::string> l_gpioPollingRequiredFrusList;
812 
813     try
814     {
815         if (i_sysCfgJsonObj.empty())
816         {
817             throw std::runtime_error("Invalid Parameters");
818         }
819 
820         if (!i_sysCfgJsonObj.contains("frus"))
821         {
822             throw std::runtime_error(
823                 "Missing frus section in system config JSON");
824         }
825 
826         for (const auto& l_fru : i_sysCfgJsonObj["frus"].items())
827         {
828             const auto l_fruPath = l_fru.key();
829 
830             if (isActionRequired(i_sysCfgJsonObj, l_fruPath, "pollingRequired",
831                                  "hotPlugging"))
832             {
833                 if (i_sysCfgJsonObj["frus"][l_fruPath]
834                         .at(0)["pollingRequired"]["hotPlugging"]
835                         .contains("gpioPresence"))
836                 {
837                     l_gpioPollingRequiredFrusList.push_back(l_fruPath);
838                 }
839             }
840         }
841     }
842     catch (const std::exception& l_ex)
843     {
844         logging::logMessage("Failed to get list of GPIO polling FRUs, error: " +
845                             std::string(l_ex.what()));
846     }
847 
848     return l_gpioPollingRequiredFrusList;
849 }
850 
851 /**
852  * @brief Get all related path(s) to update keyword value.
853  *
854  * Given FRU EEPROM path/Inventory path needs keyword's value update, this API
855  * returns tuple of FRU EEPROM path, inventory path and redundant EEPROM path if
856  * exists in the system config JSON.
857  *
858  * Note: If the inventory object path or redundant EEPROM path(s) are not found
859  * in the system config JSON, corresponding fields will have empty value in the
860  * returning tuple.
861  *
862  * @param[in] i_sysCfgJsonObj - System config JSON object.
863  * @param[in,out] io_vpdPath - Inventory object path or FRU EEPROM path.
864  *
865  * @return On success returns tuple of EEPROM path, inventory path & redundant
866  * path, on failure returns tuple with given input path alone.
867  */
868 inline std::tuple<std::string, std::string, std::string>
869     getAllPathsToUpdateKeyword(const nlohmann::json& i_sysCfgJsonObj,
870                                std::string io_vpdPath)
871 {
872     types::Path l_inventoryObjPath;
873     types::Path l_redundantFruPath;
874     try
875     {
876         if (!i_sysCfgJsonObj.empty())
877         {
878             // Get hardware path from system config JSON.
879             const types::Path l_fruPath =
880                 jsonUtility::getFruPathFromJson(i_sysCfgJsonObj, io_vpdPath);
881 
882             if (!l_fruPath.empty())
883             {
884                 io_vpdPath = l_fruPath;
885 
886                 // Get inventory object path from system config JSON
887                 l_inventoryObjPath = jsonUtility::getInventoryObjPathFromJson(
888                     i_sysCfgJsonObj, l_fruPath);
889 
890                 // Get redundant hardware path if present in system config JSON
891                 l_redundantFruPath =
892                     jsonUtility::getRedundantEepromPathFromJson(i_sysCfgJsonObj,
893                                                                 l_fruPath);
894             }
895         }
896     }
897     catch (const std::exception& l_exception)
898     {
899         logging::logMessage(
900             "Failed to get all paths to update keyword value, error " +
901             std::string(l_exception.what()));
902     }
903     return std::make_tuple(io_vpdPath, l_inventoryObjPath, l_redundantFruPath);
904 }
905 
906 /**
907  * @brief An API to get DBus service name.
908  *
909  * Given DBus inventory path, this API returns DBus service name if present in
910  * the JSON.
911  *
912  * @param[in] i_sysCfgJsonObj - System config JSON object.
913  * @param[in] l_inventoryPath - DBus inventory path.
914  *
915  * @return On success returns the service name present in the system config
916  * JSON, otherwise empty string.
917  *
918  * Note: Caller has to handle in case of empty string received.
919  */
920 inline std::string getServiceName(const nlohmann::json& i_sysCfgJsonObj,
921                                   const std::string& l_inventoryPath)
922 {
923     try
924     {
925         if (l_inventoryPath.empty())
926         {
927             throw std::runtime_error("Path parameter is empty.");
928         }
929 
930         if (!i_sysCfgJsonObj.contains("frus"))
931         {
932             throw std::runtime_error("Missing frus tag in system config JSON.");
933         }
934 
935         const nlohmann::json& l_listOfFrus =
936             i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
937 
938         for (const auto& l_frus : l_listOfFrus.items())
939         {
940             for (const auto& l_inventoryItem : l_frus.value())
941             {
942                 if (l_inventoryPath.compare(l_inventoryItem["inventoryPath"]) ==
943                     constants::STR_CMP_SUCCESS)
944                 {
945                     return l_inventoryItem["serviceName"];
946                 }
947             }
948         }
949         throw std::runtime_error(
950             "Inventory path not found in the system config JSON");
951     }
952     catch (const std::exception& l_exception)
953     {
954         logging::logMessage(
955             "Error while getting DBus service name for given path " +
956             l_inventoryPath + ", error: " + std::string(l_exception.what()));
957         // TODO:log PEL
958     }
959     return std::string{};
960 }
961 
962 /**
963  * @brief An API to check if a FRU is tagged as "powerOffOnly"
964  *
965  * Given the system config JSON and VPD FRU path, this API checks if the FRU
966  * VPD can be collected at Chassis Power Off state only.
967  *
968  * @param[in] i_sysCfgJsonObj - System config JSON object.
969  * @param[in] i_vpdFruPath - EEPROM path.
970  * @return - True if FRU VPD can be collected at Chassis Power Off state only.
971  *           False otherwise
972  */
973 inline bool isFruPowerOffOnly(const nlohmann::json& i_sysCfgJsonObj,
974                               const std::string& i_vpdFruPath)
975 {
976     if (i_vpdFruPath.empty())
977     {
978         logging::logMessage("FRU path is empty.");
979         return false;
980     }
981 
982     if (!i_sysCfgJsonObj.contains("frus"))
983     {
984         logging::logMessage("Missing frus tag in system config JSON.");
985         return false;
986     }
987 
988     if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
989     {
990         logging::logMessage("JSON object does not contain EEPROM path \'" +
991                             i_vpdFruPath + "\'");
992         return false;
993     }
994 
995     return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
996                 .contains("powerOffOnly") &&
997             (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)["powerOffOnly"]));
998 }
999 
1000 /**
1001  * @brief API which tells if the FRU is replaceable at runtime
1002  *
1003  * @param[in] i_sysCfgJsonObj - System config JSON object.
1004  * @param[in] i_vpdFruPath - EEPROM path.
1005  *
1006  * @return true if FRU is replaceable at runtime. false otherwise.
1007  */
1008 inline bool isFruReplaceableAtRuntime(const nlohmann::json& i_sysCfgJsonObj,
1009                                       const std::string& i_vpdFruPath)
1010 {
1011     try
1012     {
1013         if (i_vpdFruPath.empty())
1014         {
1015             throw std::runtime_error("Given FRU path is empty.");
1016         }
1017 
1018         if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
1019         {
1020             throw std::runtime_error("Invalid system config JSON object.");
1021         }
1022 
1023         return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
1024                     .contains("replaceableAtRuntime") &&
1025                 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(
1026                     0)["replaceableAtRuntime"]));
1027     }
1028     catch (const std::exception& l_error)
1029     {
1030         // TODO: Log PEL
1031         logging::logMessage(l_error.what());
1032     }
1033 
1034     return false;
1035 }
1036 
1037 /**
1038  * @brief API which tells if the FRU is replaceable at standby
1039  *
1040  * @param[in] i_sysCfgJsonObj - System config JSON object.
1041  * @param[in] i_vpdFruPath - EEPROM path.
1042  *
1043  * @return true if FRU is replaceable at standby. false otherwise.
1044  */
1045 inline bool isFruReplaceableAtStandby(const nlohmann::json& i_sysCfgJsonObj,
1046                                       const std::string& i_vpdFruPath)
1047 {
1048     try
1049     {
1050         if (i_vpdFruPath.empty())
1051         {
1052             throw std::runtime_error("Given FRU path is empty.");
1053         }
1054 
1055         if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
1056         {
1057             throw std::runtime_error("Invalid system config JSON object.");
1058         }
1059 
1060         return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
1061                     .contains("replaceableAtStandby") &&
1062                 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(
1063                     0)["replaceableAtStandby"]));
1064     }
1065     catch (const std::exception& l_error)
1066     {
1067         // TODO: Log PEL
1068         logging::logMessage(l_error.what());
1069     }
1070 
1071     return false;
1072 }
1073 
1074 /**
1075  * @brief API to get list of FRUs replaceable at standby from JSON.
1076  *
1077  * The API will return a vector of FRUs inventory path which are replaceable at
1078  * standby.
1079  *
1080  * @param[in] i_sysCfgJsonObj - System config JSON object.
1081  *
1082  * @return - On success, list of FRUs replaceable at standby. On failure, empty
1083  * vector.
1084  */
1085 inline std::vector<std::string> getListOfFrusReplaceableAtStandby(
1086     const nlohmann::json& i_sysCfgJsonObj) noexcept
1087 {
1088     std::vector<std::string> l_frusReplaceableAtStandby;
1089 
1090     try
1091     {
1092         if (!i_sysCfgJsonObj.contains("frus"))
1093         {
1094             throw std::runtime_error("Missing frus tag in system config JSON.");
1095         }
1096 
1097         const nlohmann::json& l_fruList =
1098             i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
1099 
1100         for (const auto& l_fru : l_fruList.items())
1101         {
1102             if (i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
1103                     "replaceableAtStandby", false))
1104             {
1105                 const std::string& l_inventoryObjectPath =
1106                     i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
1107                         "inventoryPath", "");
1108 
1109                 if (!l_inventoryObjectPath.empty())
1110                 {
1111                     l_frusReplaceableAtStandby.emplace_back(
1112                         l_inventoryObjectPath);
1113                 }
1114             }
1115         }
1116     }
1117     catch (const std::exception& l_ex)
1118     {
1119         logging::logMessage(
1120             "Failed to get list of FRUs replaceable at standby, error: " +
1121             std::string(l_ex.what()));
1122     }
1123 
1124     return l_frusReplaceableAtStandby;
1125 }
1126 
1127 /**
1128  * @brief API to select powerVS JSON based on system IM.
1129  *
1130  * The API selects respective JSON based on system IM, parse it and return the
1131  * JSON object. Empty JSON will be returned in case of any error. Caller needs
1132  * to handle empty value.
1133  *
1134  * @param[in] i_imValue - IM value of the system.
1135  * @return Parsed JSON object, empty JSON otherwise.
1136  */
1137 inline nlohmann::json getPowerVsJson(const types::BinaryVector& i_imValue)
1138 {
1139     try
1140     {
1141         if ((i_imValue.at(0) == constants::HEX_VALUE_50) &&
1142             (i_imValue.at(1) == constants::HEX_VALUE_00) &&
1143             (i_imValue.at(2) == constants::HEX_VALUE_30))
1144         {
1145             return jsonUtility::getParsedJson(constants::power_vs_50003_json);
1146         }
1147         else if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
1148                  (i_imValue.at(1) == constants::HEX_VALUE_00) &&
1149                  (i_imValue.at(2) == constants::HEX_VALUE_10))
1150         {
1151             return jsonUtility::getParsedJson(constants::power_vs_50001_json);
1152         }
1153         return nlohmann::json{};
1154     }
1155     catch (const std::exception& l_ex)
1156     {
1157         return nlohmann::json{};
1158     }
1159 }
1160 } // namespace jsonUtility
1161 } // namespace vpd
1162