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