xref: /openbmc/openpower-vpd-parser/vpd-manager/include/utility/json_utility.hpp (revision 410d96ca32a937940f5eb07211728272dedadf53)
1 #pragma once
2 
3 #include "event_logger.hpp"
4 #include "exceptions.hpp"
5 #include "logger.hpp"
6 #include "types.hpp"
7 
8 #include <gpiod.hpp>
9 #include <nlohmann/json.hpp>
10 #include <utility/common_utility.hpp>
11 
12 #include <fstream>
13 #include <type_traits>
14 #include <unordered_map>
15 
16 namespace vpd
17 {
18 namespace jsonUtility
19 {
20 
21 // forward declaration of API for function map.
22 bool processSystemCmdTag(const nlohmann::json& i_parsedConfigJson,
23                          const std::string& i_vpdFilePath,
24                          const std::string& i_baseAction,
25                          const std::string& i_flagToProcess);
26 
27 // forward declaration of API for function map.
28 bool processGpioPresenceTag(
29     const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
30     const std::string& i_baseAction, const std::string& i_flagToProcess);
31 
32 // forward declaration of API for function map.
33 bool procesSetGpioTag(const nlohmann::json& i_parsedConfigJson,
34                       const std::string& i_vpdFilePath,
35                       const std::string& i_baseAction,
36                       const std::string& i_flagToProcess);
37 
38 // Function pointers to process tags from config JSON.
39 typedef bool (*functionPtr)(
40     const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
41     const std::string& i_baseAction, const std::string& i_flagToProcess);
42 
43 inline std::unordered_map<std::string, functionPtr> funcionMap{
44     {"gpioPresence", processGpioPresenceTag},
45     {"setGpio", procesSetGpioTag},
46     {"systemCmd", processSystemCmdTag}};
47 
48 /**
49  * @brief API to read VPD offset from JSON file.
50  *
51  * @param[in] i_sysCfgJsonObj - Parsed system config JSON object.
52  * @param[in] i_vpdFilePath - VPD file path.
53  * @return VPD offset if found in JSON, 0 otherwise.
54  */
getVPDOffset(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdFilePath)55 inline size_t getVPDOffset(const nlohmann::json& i_sysCfgJsonObj,
56                            const std::string& i_vpdFilePath)
57 {
58     if (i_vpdFilePath.empty() || (i_sysCfgJsonObj.empty()) ||
59         (!i_sysCfgJsonObj.contains("frus")))
60     {
61         return 0;
62     }
63 
64     if (i_sysCfgJsonObj["frus"].contains(i_vpdFilePath))
65     {
66         return i_sysCfgJsonObj["frus"][i_vpdFilePath].at(0).value("offset", 0);
67     }
68 
69     const nlohmann::json& l_fruList =
70         i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
71 
72     for (const auto& l_fru : l_fruList.items())
73     {
74         const auto l_fruPath = l_fru.key();
75 
76         // check if given path is redundant FRU path
77         if (i_vpdFilePath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
78                                  "redundantEeprom", ""))
79         {
80             // Return the offset of redundant EEPROM taken from JSON.
81             return i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("offset", 0);
82         }
83     }
84 
85     return 0;
86 }
87 
88 /**
89  * @brief API to parse respective JSON.
90  *
91  * @param[in] pathToJson - Path to JSON.
92  * @return on success parsed JSON. On failure empty JSON object.
93  *
94  * Note: Caller has to handle it in case an empty JSON object is received.
95  */
getParsedJson(const std::string & pathToJson)96 inline nlohmann::json getParsedJson(const std::string& pathToJson) noexcept
97 {
98     try
99     {
100         if (pathToJson.empty())
101         {
102             throw std::runtime_error("Path to JSON is missing");
103         }
104 
105         if (!std::filesystem::exists(pathToJson) ||
106             std::filesystem::is_empty(pathToJson))
107         {
108             throw std::runtime_error(
109                 "File does not exist or empty file: [" + pathToJson + "]");
110         }
111 
112         std::ifstream l_jsonFile(pathToJson);
113         if (!l_jsonFile)
114         {
115             throw std::runtime_error(
116                 "Failed to access Json path = " + pathToJson);
117         }
118 
119         return nlohmann::json::parse(l_jsonFile);
120     }
121     catch (const std::exception& l_ex)
122     {
123         logging::logMessage(
124             "Failed to parse JSON file, error: " + std::string(l_ex.what()));
125     }
126 
127     return nlohmann::json{};
128 }
129 
130 /**
131  * @brief Get inventory object path from system config JSON.
132  *
133  * Given either D-bus inventory path/FRU EEPROM path/redundant EEPROM path,
134  * this API returns D-bus inventory path if present in JSON.
135  *
136  * @param[in] i_sysCfgJsonObj - System config JSON object
137  * @param[in] i_vpdPath - Path to where VPD is stored.
138  *
139  * @return On success a valid path is returned, on failure an empty string is
140  * returned.
141  *
142  * Note: Caller has to handle it in case an empty string is received.
143  */
getInventoryObjPathFromJson(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdPath)144 inline std::string getInventoryObjPathFromJson(
145     const nlohmann::json& i_sysCfgJsonObj,
146     const std::string& i_vpdPath) noexcept
147 {
148     try
149     {
150         if (i_vpdPath.empty())
151         {
152             throw std::runtime_error("Path parameter is empty.");
153         }
154 
155         if (!i_sysCfgJsonObj.contains("frus"))
156         {
157             throw std::runtime_error("Missing frus tag in system config JSON.");
158         }
159 
160         // check if given path is FRU path
161         if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
162         {
163             return i_sysCfgJsonObj["frus"][i_vpdPath].at(0).value(
164                 "inventoryPath", "");
165         }
166 
167         const nlohmann::json& l_fruList =
168             i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
169 
170         for (const auto& l_fru : l_fruList.items())
171         {
172             const auto l_fruPath = l_fru.key();
173             const auto l_invObjPath =
174                 i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("inventoryPath",
175                                                                "");
176 
177             // check if given path is redundant FRU path or inventory path
178             if (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
179                                  "redundantEeprom", "") ||
180                 (i_vpdPath == l_invObjPath))
181             {
182                 return l_invObjPath;
183             }
184         }
185     }
186     catch (const std::exception& l_ex)
187     {
188         logging::logMessage(
189             "Failed to get inventory object path from json, error: " +
190             std::string(l_ex.what()));
191     }
192 
193     return std::string();
194 }
195 
196 /**
197  * @brief Process "PostFailAction" defined in config JSON.
198  *
199  * In case there is some error in the processing of "preAction" execution and a
200  * set of procedure needs to be done as a part of post fail action. This base
201  * action can be defined in the config JSON for that FRU and it will be handled
202  * under this API.
203  *
204  * @param[in] i_parsedConfigJson - config JSON
205  * @param[in] i_vpdFilePath - EEPROM file path
206  * @param[in] i_flagToProcess - To identify which flag(s) needs to be processed
207  * under PostFailAction tag of config JSON.
208  * @return - success or failure
209  */
executePostFailAction(const nlohmann::json & i_parsedConfigJson,const std::string & i_vpdFilePath,const std::string & i_flagToProcess)210 inline bool executePostFailAction(const nlohmann::json& i_parsedConfigJson,
211                                   const std::string& i_vpdFilePath,
212                                   const std::string& i_flagToProcess)
213 {
214     try
215     {
216         if (i_parsedConfigJson.empty() || i_vpdFilePath.empty() ||
217             i_flagToProcess.empty())
218         {
219             throw std::runtime_error(
220                 "Invalid parameters. Abort processing for post fail action");
221         }
222 
223         if (!(i_parsedConfigJson["frus"][i_vpdFilePath].at(0))["postFailAction"]
224                  .contains(i_flagToProcess))
225         {
226             throw std::runtime_error(
227                 "Config JSON missing flag " + i_flagToProcess +
228                 " to execute post fail action for path = " + i_vpdFilePath);
229         }
230 
231         for (const auto& l_tags : (i_parsedConfigJson["frus"][i_vpdFilePath].at(
232                  0))["postFailAction"][i_flagToProcess]
233                                       .items())
234         {
235             auto itrToFunction = funcionMap.find(l_tags.key());
236             if (itrToFunction != funcionMap.end())
237             {
238                 if (!itrToFunction->second(i_parsedConfigJson, i_vpdFilePath,
239                                            "postFailAction", i_flagToProcess))
240                 {
241                     return false;
242                 }
243             }
244         }
245     }
246     catch (const std::exception& l_ex)
247     {
248         logging::logMessage("Execute post fail action failed. Error : " +
249                             std::string(l_ex.what()));
250         return false;
251     }
252 
253     return true;
254 }
255 
256 /**
257  * @brief Process "systemCmd" tag for a given FRU.
258  *
259  * The API will process "systemCmd" tag if it is defined in the config
260  * JSON for the given FRU.
261  *
262  * @param[in] i_parsedConfigJson - config JSON
263  * @param[in] i_vpdFilePath - EEPROM file path
264  * @param[in] i_baseAction - Base action for which this tag has been called.
265  * @param[in] i_flagToProcess - Flag nested under the base action for which this
266  * tag has been called.
267  * @return Execution status.
268  */
processSystemCmdTag(const nlohmann::json & i_parsedConfigJson,const std::string & i_vpdFilePath,const std::string & i_baseAction,const std::string & i_flagToProcess)269 inline bool processSystemCmdTag(
270     const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
271     const std::string& i_baseAction, const std::string& i_flagToProcess)
272 {
273     try
274     {
275         if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
276             i_baseAction.empty() || i_flagToProcess.empty())
277         {
278             throw std::runtime_error(
279                 std::string(__FUNCTION__) +
280                 " Invalid parameter. Abort processing of processSystemCmd.");
281         }
282 
283         if (!((i_parsedConfigJson["frus"][i_vpdFilePath].at(
284                    0)[i_baseAction][i_flagToProcess]["systemCmd"])
285                   .contains("cmd")))
286         {
287             throw JsonException(
288                 std::string(__FUNCTION__) +
289                 " Config JSON missing required information to execute system command for EEPROM " +
290                 i_vpdFilePath);
291         }
292 
293         const std::string& l_systemCommand =
294             i_parsedConfigJson["frus"][i_vpdFilePath].at(
295                 0)[i_baseAction][i_flagToProcess]["systemCmd"]["cmd"];
296 
297         commonUtility::executeCmd(l_systemCommand);
298         return true;
299     }
300     catch (const std::exception& l_ex)
301     {
302         EventLogger::createSyncPel(
303             EventLogger::getErrorType(l_ex), types::SeverityType::Informational,
304             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
305             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
306         return false;
307     }
308 }
309 
310 /**
311  * @brief Checks for presence of a given FRU using GPIO line.
312  *
313  * This API returns the presence information of the FRU corresponding to the
314  * given VPD file path by setting the presence pin.
315  *
316  * @param[in] i_parsedConfigJson - config JSON
317  * @param[in] i_vpdFilePath - EEPROM file path
318  * @param[in] i_baseAction - Base action for which this tag has been called.
319  * @param[in] i_flagToProcess - Flag nested under the base action for which this
320  * tag has been called.
321  * @return Execution status.
322  */
processGpioPresenceTag(const nlohmann::json & i_parsedConfigJson,const std::string & i_vpdFilePath,const std::string & i_baseAction,const std::string & i_flagToProcess)323 inline bool processGpioPresenceTag(
324     const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
325     const std::string& i_baseAction, const std::string& i_flagToProcess)
326 {
327     std::string l_presencePinName;
328     try
329     {
330         if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
331             i_baseAction.empty() || i_flagToProcess.empty())
332         {
333             throw std::runtime_error(
334                 std::string(__FUNCTION__) +
335                 "Invalid parameter. Abort processing of processGpioPresence tag");
336         }
337 
338         if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at(
339                     0)[i_baseAction][i_flagToProcess]["gpioPresence"])
340                    .contains("pin")) &&
341               ((i_parsedConfigJson["frus"][i_vpdFilePath].at(
342                     0)[i_baseAction][i_flagToProcess]["gpioPresence"])
343                    .contains("value"))))
344         {
345             throw JsonException(
346                 std::string(__FUNCTION__) +
347                 "Config JSON missing required information to detect presence for EEPROM " +
348                 i_vpdFilePath);
349         }
350 
351         // get the pin name
352         l_presencePinName = i_parsedConfigJson["frus"][i_vpdFilePath].at(
353             0)[i_baseAction][i_flagToProcess]["gpioPresence"]["pin"];
354 
355         // get the pin value
356         uint8_t l_presencePinValue =
357             i_parsedConfigJson["frus"][i_vpdFilePath].at(
358                 0)[i_baseAction][i_flagToProcess]["gpioPresence"]["value"];
359 
360         gpiod::line l_presenceLine = gpiod::find_line(l_presencePinName);
361 
362         if (!l_presenceLine)
363         {
364             throw GpioException("Couldn't find the GPIO line.");
365         }
366 
367         l_presenceLine.request({"Read the presence line",
368                                 gpiod::line_request::DIRECTION_INPUT, 0});
369 
370         return (l_presencePinValue == l_presenceLine.get_value());
371     }
372     catch (const std::exception& l_ex)
373     {
374         // No need to continue in case of JSON failure or Firmware error
375         // as these are errors internal to the code and in that case the FRU
376         // should not be processed. Any other error is considered as external
377         // error in this case and a try to read the EEPROM should be done.
378         if (EventLogger::getErrorType(l_ex) == types::ErrorType::JsonFailure ||
379             EventLogger::getErrorType(l_ex) == types::ErrorType::FirmwareError)
380         {
381             EventLogger::createSyncPel(
382                 EventLogger::getErrorType(l_ex),
383                 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
384                 EventLogger::getErrorMsg(l_ex), std::nullopt, std::nullopt,
385                 std::nullopt, std::nullopt);
386             return false;
387         }
388 
389         std::string l_errMsg = "Exception on GPIO line: ";
390         l_errMsg += l_presencePinName;
391         l_errMsg += " Reason: ";
392         l_errMsg += l_ex.what();
393         l_errMsg += " File: " + i_vpdFilePath + " Pel Logged";
394 
395         // ToDo -- Update Internal Rc code.
396         EventLogger::createAsyncPelWithInventoryCallout(
397             EventLogger::getErrorType(l_ex), types::SeverityType::Informational,
398             {{getInventoryObjPathFromJson(i_parsedConfigJson, i_vpdFilePath),
399               types::CalloutPriority::High}},
400             std::source_location::current().file_name(),
401             std::source_location::current().function_name(), 0, l_errMsg,
402             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
403 
404         logging::logMessage(l_errMsg);
405 
406         // Except when GPIO pin value is false, we go and try collecting the
407         // FRU VPD as we couldn't able to read GPIO pin value due to some
408         // error/exception. So returning true in error scenario.
409         return true;
410     }
411 }
412 
413 /**
414  * @brief Process "setGpio" tag for a given FRU.
415  *
416  * This API enables the GPIO line.
417  *
418  * @param[in] i_parsedConfigJson - config JSON
419  * @param[in] i_vpdFilePath - EEPROM file path
420  * @param[in] i_baseAction - Base action for which this tag has been called.
421  * @param[in] i_flagToProcess - Flag nested under the base action for which this
422  * tag has been called.
423  * @return Execution status.
424  */
procesSetGpioTag(const nlohmann::json & i_parsedConfigJson,const std::string & i_vpdFilePath,const std::string & i_baseAction,const std::string & i_flagToProcess)425 inline bool procesSetGpioTag(
426     const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
427     const std::string& i_baseAction, const std::string& i_flagToProcess)
428 {
429     std::string l_pinName;
430     try
431     {
432         if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
433             i_baseAction.empty() || i_flagToProcess.empty())
434         {
435             throw std::runtime_error(
436                 std::string(__FUNCTION__) +
437                 " Invalid parameter. Abort processing of procesSetGpio.");
438         }
439 
440         if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at(
441                     0)[i_baseAction][i_flagToProcess]["setGpio"])
442                    .contains("pin")) &&
443               ((i_parsedConfigJson["frus"][i_vpdFilePath].at(
444                     0)[i_baseAction][i_flagToProcess]["setGpio"])
445                    .contains("value"))))
446         {
447             throw JsonException(
448                 std::string(__FUNCTION__) +
449                 " Config JSON missing required information to set gpio line for EEPROM " +
450                 i_vpdFilePath);
451         }
452 
453         l_pinName = i_parsedConfigJson["frus"][i_vpdFilePath].at(
454             0)[i_baseAction][i_flagToProcess]["setGpio"]["pin"];
455 
456         // Get the value to set
457         uint8_t l_pinValue = i_parsedConfigJson["frus"][i_vpdFilePath].at(
458             0)[i_baseAction][i_flagToProcess]["setGpio"]["value"];
459 
460         logging::logMessage(
461             "Setting GPIO: " + l_pinName + " to " + std::to_string(l_pinValue));
462 
463         gpiod::line l_outputLine = gpiod::find_line(l_pinName);
464 
465         if (!l_outputLine)
466         {
467             throw GpioException("Couldn't find GPIO line.");
468         }
469 
470         l_outputLine.request(
471             {"FRU Action", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
472             l_pinValue);
473         return true;
474     }
475     catch (const std::exception& l_ex)
476     {
477         if (EventLogger::getErrorType(l_ex) != types::ErrorType::GpioError)
478         {
479             EventLogger::createSyncPel(
480                 EventLogger::getErrorType(l_ex),
481                 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
482                 EventLogger::getErrorMsg(l_ex), std::nullopt, std::nullopt,
483                 std::nullopt, std::nullopt);
484         }
485         else
486         {
487             std::string l_errMsg = "Exception on GPIO line: ";
488             l_errMsg += l_pinName;
489             l_errMsg += " Reason: ";
490             l_errMsg += l_ex.what();
491             l_errMsg += " File: " + i_vpdFilePath + " Pel Logged";
492 
493             // ToDo -- Update Internal RC code
494             EventLogger::createAsyncPelWithInventoryCallout(
495                 EventLogger::getErrorType(l_ex),
496                 types::SeverityType::Informational,
497                 {{getInventoryObjPathFromJson(i_parsedConfigJson,
498                                               i_vpdFilePath),
499                   types::CalloutPriority::High}},
500                 std::source_location::current().file_name(),
501                 std::source_location::current().function_name(), 0, l_errMsg,
502                 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
503 
504             logging::logMessage(l_errMsg);
505         }
506 
507         return false;
508     }
509 }
510 
511 /**
512  * @brief Process any action, if defined in config JSON.
513  *
514  * If any FRU(s) requires any special handling, then this base action can be
515  * defined for that FRU in the config JSON, processing of which will be handled
516  * in this API.
517  * Examples of action - preAction, PostAction etc.
518  *
519  * @param[in] i_parsedConfigJson - config JSON
520  * @param[in] i_action - Base action to be performed.
521  * @param[in] i_vpdFilePath - EEPROM file path
522  * @param[in] i_flagToProcess - To identify which flag(s) needs to be processed
523  * under PreAction tag of config JSON.
524  * @return - success or failure
525  */
executeBaseAction(const nlohmann::json & i_parsedConfigJson,const std::string & i_action,const std::string & i_vpdFilePath,const std::string & i_flagToProcess)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  */
getRedundantEepromPathFromJson(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdPath)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  */
getFruPathFromJson(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdPath)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  */
isBackupAndRestoreRequired(const nlohmann::json & i_sysCfgJsonObj)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  */
isActionRequired(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdFruPath,const std::string & i_action,const std::string & i_flowFlag)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  */
getListOfGpioPollingFrus(const nlohmann::json & i_sysCfgJsonObj)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>
getAllPathsToUpdateKeyword(const nlohmann::json & i_sysCfgJsonObj,std::string io_vpdPath)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                 // Get inventory object path from system config JSON
888                 l_inventoryObjPath = jsonUtility::getInventoryObjPathFromJson(
889                     i_sysCfgJsonObj, l_fruPath);
890 
891                 // Get redundant hardware path if present in system config JSON
892                 l_redundantFruPath =
893                     jsonUtility::getRedundantEepromPathFromJson(i_sysCfgJsonObj,
894                                                                 l_fruPath);
895             }
896         }
897     }
898     catch (const std::exception& l_exception)
899     {
900         logging::logMessage(
901             "Failed to get all paths to update keyword value, error " +
902             std::string(l_exception.what()));
903     }
904     return std::make_tuple(io_vpdPath, l_inventoryObjPath, l_redundantFruPath);
905 }
906 
907 /**
908  * @brief An API to get DBus service name.
909  *
910  * Given DBus inventory path, this API returns DBus service name if present in
911  * the JSON.
912  *
913  * @param[in] i_sysCfgJsonObj - System config JSON object.
914  * @param[in] l_inventoryPath - DBus inventory path.
915  *
916  * @return On success returns the service name present in the system config
917  * JSON, otherwise empty string.
918  *
919  * Note: Caller has to handle in case of empty string received.
920  */
getServiceName(const nlohmann::json & i_sysCfgJsonObj,const std::string & l_inventoryPath)921 inline std::string getServiceName(const nlohmann::json& i_sysCfgJsonObj,
922                                   const std::string& l_inventoryPath)
923 {
924     try
925     {
926         if (l_inventoryPath.empty())
927         {
928             throw std::runtime_error("Path parameter is empty.");
929         }
930 
931         if (!i_sysCfgJsonObj.contains("frus"))
932         {
933             throw std::runtime_error("Missing frus tag in system config JSON.");
934         }
935 
936         const nlohmann::json& l_listOfFrus =
937             i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
938 
939         for (const auto& l_frus : l_listOfFrus.items())
940         {
941             for (const auto& l_inventoryItem : l_frus.value())
942             {
943                 if (l_inventoryPath.compare(l_inventoryItem["inventoryPath"]) ==
944                     constants::STR_CMP_SUCCESS)
945                 {
946                     return l_inventoryItem["serviceName"];
947                 }
948             }
949         }
950         throw std::runtime_error(
951             "Inventory path not found in the system config JSON");
952     }
953     catch (const std::exception& l_exception)
954     {
955         logging::logMessage(
956             "Error while getting DBus service name for given path " +
957             l_inventoryPath + ", error: " + std::string(l_exception.what()));
958         // TODO:log PEL
959     }
960     return std::string{};
961 }
962 
963 /**
964  * @brief An API to check if a FRU is tagged as "powerOffOnly"
965  *
966  * Given the system config JSON and VPD FRU path, this API checks if the FRU
967  * VPD can be collected at Chassis Power Off state only.
968  *
969  * @param[in] i_sysCfgJsonObj - System config JSON object.
970  * @param[in] i_vpdFruPath - EEPROM path.
971  * @return - True if FRU VPD can be collected at Chassis Power Off state only.
972  *           False otherwise
973  */
isFruPowerOffOnly(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdFruPath)974 inline bool isFruPowerOffOnly(const nlohmann::json& i_sysCfgJsonObj,
975                               const std::string& i_vpdFruPath)
976 {
977     if (i_vpdFruPath.empty())
978     {
979         logging::logMessage("FRU path is empty.");
980         return false;
981     }
982 
983     if (!i_sysCfgJsonObj.contains("frus"))
984     {
985         logging::logMessage("Missing frus tag in system config JSON.");
986         return false;
987     }
988 
989     if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
990     {
991         logging::logMessage("JSON object does not contain EEPROM path \'" +
992                             i_vpdFruPath + "\'");
993         return false;
994     }
995 
996     return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
997                 .contains("powerOffOnly") &&
998             (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)["powerOffOnly"]));
999 }
1000 
1001 /**
1002  * @brief API which tells if the FRU is replaceable at runtime
1003  *
1004  * @param[in] i_sysCfgJsonObj - System config JSON object.
1005  * @param[in] i_vpdFruPath - EEPROM path.
1006  *
1007  * @return true if FRU is replaceable at runtime. false otherwise.
1008  */
isFruReplaceableAtRuntime(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdFruPath)1009 inline bool isFruReplaceableAtRuntime(const nlohmann::json& i_sysCfgJsonObj,
1010                                       const std::string& i_vpdFruPath)
1011 {
1012     try
1013     {
1014         if (i_vpdFruPath.empty())
1015         {
1016             throw std::runtime_error("Given FRU path is empty.");
1017         }
1018 
1019         if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
1020         {
1021             throw std::runtime_error("Invalid system config JSON object.");
1022         }
1023 
1024         return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
1025                     .contains("replaceableAtRuntime") &&
1026                 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(
1027                     0)["replaceableAtRuntime"]));
1028     }
1029     catch (const std::exception& l_error)
1030     {
1031         // TODO: Log PEL
1032         logging::logMessage(l_error.what());
1033     }
1034 
1035     return false;
1036 }
1037 
1038 /**
1039  * @brief API which tells if the FRU is replaceable at standby
1040  *
1041  * @param[in] i_sysCfgJsonObj - System config JSON object.
1042  * @param[in] i_vpdFruPath - EEPROM path.
1043  *
1044  * @return true if FRU is replaceable at standby. false otherwise.
1045  */
isFruReplaceableAtStandby(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdFruPath)1046 inline bool isFruReplaceableAtStandby(const nlohmann::json& i_sysCfgJsonObj,
1047                                       const std::string& i_vpdFruPath)
1048 {
1049     try
1050     {
1051         if (i_vpdFruPath.empty())
1052         {
1053             throw std::runtime_error("Given FRU path is empty.");
1054         }
1055 
1056         if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
1057         {
1058             throw std::runtime_error("Invalid system config JSON object.");
1059         }
1060 
1061         return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
1062                     .contains("replaceableAtStandby") &&
1063                 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(
1064                     0)["replaceableAtStandby"]));
1065     }
1066     catch (const std::exception& l_error)
1067     {
1068         // TODO: Log PEL
1069         logging::logMessage(l_error.what());
1070     }
1071 
1072     return false;
1073 }
1074 
1075 /**
1076  * @brief API to get list of FRUs replaceable at standby from JSON.
1077  *
1078  * The API will return a vector of FRUs inventory path which are replaceable at
1079  * standby.
1080  *
1081  * @param[in] i_sysCfgJsonObj - System config JSON object.
1082  *
1083  * @return - On success, list of FRUs replaceable at standby. On failure, empty
1084  * vector.
1085  */
getListOfFrusReplaceableAtStandby(const nlohmann::json & i_sysCfgJsonObj)1086 inline std::vector<std::string> getListOfFrusReplaceableAtStandby(
1087     const nlohmann::json& i_sysCfgJsonObj) noexcept
1088 {
1089     std::vector<std::string> l_frusReplaceableAtStandby;
1090 
1091     try
1092     {
1093         if (!i_sysCfgJsonObj.contains("frus"))
1094         {
1095             throw std::runtime_error("Missing frus tag in system config JSON.");
1096         }
1097 
1098         const nlohmann::json& l_fruList =
1099             i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
1100 
1101         for (const auto& l_fru : l_fruList.items())
1102         {
1103             if (i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
1104                     "replaceableAtStandby", false))
1105             {
1106                 const std::string& l_inventoryObjectPath =
1107                     i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
1108                         "inventoryPath", "");
1109 
1110                 if (!l_inventoryObjectPath.empty())
1111                 {
1112                     l_frusReplaceableAtStandby.emplace_back(
1113                         l_inventoryObjectPath);
1114                 }
1115             }
1116         }
1117     }
1118     catch (const std::exception& l_ex)
1119     {
1120         logging::logMessage(
1121             "Failed to get list of FRUs replaceable at standby, error: " +
1122             std::string(l_ex.what()));
1123     }
1124 
1125     return l_frusReplaceableAtStandby;
1126 }
1127 
1128 /**
1129  * @brief API to select powerVS JSON based on system IM.
1130  *
1131  * The API selects respective JSON based on system IM, parse it and return the
1132  * JSON object. Empty JSON will be returned in case of any error. Caller needs
1133  * to handle empty value.
1134  *
1135  * @param[in] i_imValue - IM value of the system.
1136  * @return Parsed JSON object, empty JSON otherwise.
1137  */
getPowerVsJson(const types::BinaryVector & i_imValue)1138 inline nlohmann::json getPowerVsJson(const types::BinaryVector& i_imValue)
1139 {
1140     try
1141     {
1142         if ((i_imValue.at(0) == constants::HEX_VALUE_50) &&
1143             (i_imValue.at(1) == constants::HEX_VALUE_00) &&
1144             (i_imValue.at(2) == constants::HEX_VALUE_30))
1145         {
1146             return jsonUtility::getParsedJson(constants::power_vs_50003_json);
1147         }
1148         else if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
1149                  (i_imValue.at(1) == constants::HEX_VALUE_00) &&
1150                  (i_imValue.at(2) == constants::HEX_VALUE_10))
1151         {
1152             return jsonUtility::getParsedJson(constants::power_vs_50001_json);
1153         }
1154         return nlohmann::json{};
1155     }
1156     catch (const std::exception& l_ex)
1157     {
1158         return nlohmann::json{};
1159     }
1160 }
1161 } // namespace jsonUtility
1162 } // namespace vpd
1163