xref: /openbmc/openpower-vpd-parser/vpd-manager/include/utility/json_utility.hpp (revision 480807cf8a3ef27eef4063bac422d69343b123d1)
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  *
1009  * @return true if FRU is replaceable at runtime. false otherwise.
1010  */
1011 inline bool isFruReplaceableAtRuntime(const nlohmann::json& i_sysCfgJsonObj,
1012                                       const std::string& i_vpdFruPath)
1013 {
1014     try
1015     {
1016         if (i_vpdFruPath.empty())
1017         {
1018             throw std::runtime_error("Given FRU path is empty.");
1019         }
1020 
1021         if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
1022         {
1023             throw std::runtime_error("Invalid system config JSON object.");
1024         }
1025 
1026         return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
1027                     .contains("replaceableAtRuntime") &&
1028                 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(
1029                     0)["replaceableAtRuntime"]));
1030     }
1031     catch (const std::exception& l_error)
1032     {
1033         // TODO: Log PEL
1034         logging::logMessage(l_error.what());
1035     }
1036 
1037     return false;
1038 }
1039 
1040 /**
1041  * @brief API which tells if the FRU is replaceable at standby
1042  *
1043  * @param[in] i_sysCfgJsonObj - System config JSON object.
1044  * @param[in] i_vpdFruPath - EEPROM path.
1045  * @param[out] o_errCode - set error code in case of error.
1046  *
1047  * @return true if FRU is replaceable at standby. false otherwise.
1048  */
1049 inline bool isFruReplaceableAtStandby(const nlohmann::json& i_sysCfgJsonObj,
1050                                       const std::string& i_vpdFruPath,
1051                                       uint16_t& o_errCode)
1052 {
1053     if (i_vpdFruPath.empty())
1054     {
1055         o_errCode = error_code::INVALID_INPUT_PARAMETER;
1056         return false;
1057     }
1058 
1059     if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
1060     {
1061         o_errCode = error_code::INVALID_JSON;
1062     }
1063 
1064     return (
1065         (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
1066             .contains("replaceableAtStandby") &&
1067         (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)["replaceableAtStandby"]));
1068 
1069     return false;
1070 }
1071 
1072 /**
1073  * @brief API to get list of FRUs replaceable at standby from JSON.
1074  *
1075  * The API will return a vector of FRUs inventory path which are replaceable at
1076  * standby.
1077  *
1078  * @param[in] i_sysCfgJsonObj - System config JSON object.
1079  *
1080  * @return - On success, list of FRUs replaceable at standby. On failure, empty
1081  * vector.
1082  */
1083 inline std::vector<std::string> getListOfFrusReplaceableAtStandby(
1084     const nlohmann::json& i_sysCfgJsonObj) noexcept
1085 {
1086     std::vector<std::string> l_frusReplaceableAtStandby;
1087 
1088     try
1089     {
1090         if (!i_sysCfgJsonObj.contains("frus"))
1091         {
1092             throw std::runtime_error("Missing frus tag in system config JSON.");
1093         }
1094 
1095         const nlohmann::json& l_fruList =
1096             i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
1097 
1098         for (const auto& l_fru : l_fruList.items())
1099         {
1100             if (i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
1101                     "replaceableAtStandby", false))
1102             {
1103                 const std::string& l_inventoryObjectPath =
1104                     i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
1105                         "inventoryPath", "");
1106 
1107                 if (!l_inventoryObjectPath.empty())
1108                 {
1109                     l_frusReplaceableAtStandby.emplace_back(
1110                         l_inventoryObjectPath);
1111                 }
1112             }
1113         }
1114     }
1115     catch (const std::exception& l_ex)
1116     {
1117         logging::logMessage(
1118             "Failed to get list of FRUs replaceable at standby, error: " +
1119             std::string(l_ex.what()));
1120     }
1121 
1122     return l_frusReplaceableAtStandby;
1123 }
1124 
1125 /**
1126  * @brief API to select powerVS JSON based on system IM.
1127  *
1128  * The API selects respective JSON based on system IM, parse it and return the
1129  * JSON object. Empty JSON will be returned in case of any error. Caller needs
1130  * to handle empty value.
1131  *
1132  * @param[in] i_imValue - IM value of the system.
1133  * @return Parsed JSON object, empty JSON otherwise.
1134  */
1135 inline nlohmann::json getPowerVsJson(const types::BinaryVector& i_imValue)
1136 {
1137     try
1138     {
1139         uint16_t l_errCode = 0;
1140         if ((i_imValue.at(0) == constants::HEX_VALUE_50) &&
1141             (i_imValue.at(1) == constants::HEX_VALUE_00) &&
1142             (i_imValue.at(2) == constants::HEX_VALUE_30))
1143         {
1144             nlohmann::json l_parsedJson = jsonUtility::getParsedJson(
1145                 constants::power_vs_50003_json, l_errCode);
1146 
1147             if (l_errCode)
1148             {
1149                 logging::logMessage(
1150                     "Failed to parse JSON file [ " +
1151                     std::string(constants::power_vs_50003_json) +
1152                     " ], error : " +
1153                     vpdSpecificUtility::getErrCodeMsg(l_errCode));
1154             }
1155 
1156             return l_parsedJson;
1157         }
1158         else if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
1159                  (i_imValue.at(1) == constants::HEX_VALUE_00) &&
1160                  (i_imValue.at(2) == constants::HEX_VALUE_10))
1161         {
1162             nlohmann::json l_parsedJson = jsonUtility::getParsedJson(
1163                 constants::power_vs_50001_json, l_errCode);
1164 
1165             if (l_errCode)
1166             {
1167                 logging::logMessage(
1168                     "Failed to parse JSON file [ " +
1169                     std::string(constants::power_vs_50001_json) +
1170                     " ], error : " +
1171                     vpdSpecificUtility::getErrCodeMsg(l_errCode));
1172             }
1173 
1174             return l_parsedJson;
1175         }
1176         return nlohmann::json{};
1177     }
1178     catch (const std::exception& l_ex)
1179     {
1180         return nlohmann::json{};
1181     }
1182 }
1183 
1184 /**
1185  * @brief API to get list of FRUs for which "monitorPresence" is true.
1186  *
1187  * @param[in] i_sysCfgJsonObj - System config JSON object.
1188  * @param[out] o_errCode - To set error code in case of error.
1189  *
1190  * @return On success, returns list of FRUs for which "monitorPresence" is true,
1191  * empty list on error.
1192  */
1193 inline std::vector<types::Path> getFrusWithPresenceMonitoring(
1194     const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode)
1195 {
1196     std::vector<types::Path> l_frusWithPresenceMonitoring;
1197 
1198     if (!i_sysCfgJsonObj.contains("frus"))
1199     {
1200         o_errCode = error_code::INVALID_JSON;
1201         return l_frusWithPresenceMonitoring;
1202     }
1203 
1204     const nlohmann::json& l_listOfFrus =
1205         i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
1206 
1207     for (const auto& l_aFru : l_listOfFrus)
1208     {
1209         if (l_aFru.at(0).value("monitorPresence", false))
1210         {
1211             l_frusWithPresenceMonitoring.emplace_back(
1212                 l_aFru.at(0).value("inventoryPath", ""));
1213         }
1214     }
1215 
1216     return l_frusWithPresenceMonitoring;
1217 }
1218 
1219 /**
1220  * @brief API which tells if the FRU's presence is handled
1221  *
1222  * For a given FRU, this API checks if it's presence is handled by vpd-manager
1223  * by checking the "handlePresence" tag.
1224  *
1225  * @param[in] i_sysCfgJsonObj - System config JSON object.
1226  * @param[in] i_vpdFruPath - EEPROM path.
1227  * @param[out] o_errCode - To set error code in case of failure.
1228  *
1229  * @return true if FRU presence is handled, false otherwise.
1230  */
1231 inline bool isFruPresenceHandled(const nlohmann::json& i_sysCfgJsonObj,
1232                                  const std::string& i_vpdFruPath,
1233                                  uint16_t& o_errCode)
1234 {
1235     if (i_vpdFruPath.empty())
1236     {
1237         o_errCode = error_code::INVALID_INPUT_PARAMETER;
1238         return false;
1239     }
1240 
1241     if (!i_sysCfgJsonObj.contains("frus"))
1242     {
1243         o_errCode = error_code::INVALID_JSON;
1244         return false;
1245     }
1246 
1247     if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
1248     {
1249         o_errCode = error_code::FRU_PATH_NOT_FOUND;
1250         return false;
1251     }
1252 
1253     return i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0).value(
1254         "handlePresence", true);
1255 }
1256 } // namespace jsonUtility
1257 } // namespace vpd
1258