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