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