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