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