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