xref: /openbmc/openpower-vpd-parser/vpd-manager/include/utility/json_utility.hpp (revision ef069f7e2a9e5f41c4324305690b876c43a7e0ff)
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  * @throw std::runtime_error.
559  * @return On success return valid path, on failure return empty string.
560  */
getRedundantEepromPathFromJson(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdPath)561 inline std::string getRedundantEepromPathFromJson(
562     const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdPath)
563 {
564     if (i_vpdPath.empty())
565     {
566         throw std::runtime_error("Path parameter is empty.");
567     }
568 
569     if (!i_sysCfgJsonObj.contains("frus"))
570     {
571         throw std::runtime_error("Missing frus tag in system config JSON.");
572     }
573 
574     // check if given path is FRU path
575     if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
576     {
577         return i_sysCfgJsonObj["frus"][i_vpdPath].at(0).value(
578             "redundantEeprom", "");
579     }
580 
581     const nlohmann::json& l_fruList =
582         i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
583 
584     for (const auto& l_fru : l_fruList.items())
585     {
586         const std::string& l_fruPath = l_fru.key();
587         const std::string& l_redundantFruPath =
588             i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("redundantEeprom",
589                                                            "");
590 
591         // check if given path is inventory path or redundant FRU path
592         if ((i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("inventoryPath",
593                                                             "") == i_vpdPath) ||
594             (l_redundantFruPath == i_vpdPath))
595         {
596             return l_redundantFruPath;
597         }
598     }
599     return std::string();
600 }
601 
602 /**
603  * @brief Get FRU EEPROM path from system config JSON
604  *
605  * Given either D-bus inventory path/FRU EEPROM path/redundant EEPROM path,
606  * this API returns FRU EEPROM path if present in JSON.
607  *
608  * @param[in] i_sysCfgJsonObj - System config JSON object
609  * @param[in] i_vpdPath - Path to where VPD is stored.
610  *
611  * @throw std::runtime_error.
612  *
613  * @return On success return valid path, on failure return empty string.
614  */
getFruPathFromJson(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdPath)615 inline std::string getFruPathFromJson(const nlohmann::json& i_sysCfgJsonObj,
616                                       const std::string& i_vpdPath)
617 {
618     if (i_vpdPath.empty())
619     {
620         throw std::runtime_error("Path parameter is empty.");
621     }
622 
623     if (!i_sysCfgJsonObj.contains("frus"))
624     {
625         throw std::runtime_error("Missing frus tag in system config JSON.");
626     }
627 
628     // check if given path is FRU path
629     if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
630     {
631         return i_vpdPath;
632     }
633 
634     const nlohmann::json& l_fruList =
635         i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
636 
637     for (const auto& l_fru : l_fruList.items())
638     {
639         const auto l_fruPath = l_fru.key();
640 
641         // check if given path is redundant FRU path or inventory path
642         if (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
643                              "redundantEeprom", "") ||
644             (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
645                               "inventoryPath", "")))
646         {
647             return l_fruPath;
648         }
649     }
650     return std::string();
651 }
652 
653 /**
654  * @brief An API to check backup and restore VPD is required.
655  *
656  * The API checks if there is provision for backup and restore mentioned in the
657  * system config JSON, by looking "backupRestoreConfigPath" tag.
658  * Checks if the path mentioned is a hardware path, by checking if the file path
659  * exists and size of contents in the path.
660  *
661  * @param[in] i_sysCfgJsonObj - System config JSON object.
662  *
663  * @return true if backup and restore is required, false otherwise.
664  */
isBackupAndRestoreRequired(const nlohmann::json & i_sysCfgJsonObj)665 inline bool isBackupAndRestoreRequired(const nlohmann::json& i_sysCfgJsonObj)
666 {
667     try
668     {
669         const std::string& l_backupAndRestoreCfgFilePath =
670             i_sysCfgJsonObj.value("backupRestoreConfigPath", "");
671         if (!l_backupAndRestoreCfgFilePath.empty() &&
672             std::filesystem::exists(l_backupAndRestoreCfgFilePath) &&
673             !std::filesystem::is_empty(l_backupAndRestoreCfgFilePath))
674         {
675             return true;
676         }
677     }
678     catch (std::exception& ex)
679     {
680         logging::logMessage(ex.what());
681     }
682     return false;
683 }
684 
685 /** @brief API to check if an action is required for given EEPROM path.
686  *
687  * System config JSON can contain pre-action, post-action etc. like actions
688  * defined for an EEPROM path. The API will check if any such action is defined
689  * for the EEPROM.
690  *
691  * @param[in] i_sysCfgJsonObj - System config JSON object.
692  * @param[in] i_vpdFruPath - EEPROM path.
693  * @param[in] i_action - Action to be checked.
694  * @param[in] i_flowFlag - Denotes the flow w.r.t which the action should be
695  * triggered.
696  * @return - True if action is defined for the flow, false otherwise.
697  */
isActionRequired(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdFruPath,const std::string & i_action,const std::string & i_flowFlag)698 inline bool isActionRequired(
699     const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdFruPath,
700     const std::string& i_action, const std::string& i_flowFlag)
701 {
702     if (i_vpdFruPath.empty() || i_action.empty() || i_flowFlag.empty())
703     {
704         logging::logMessage("Invalid parameters recieved.");
705         return false;
706     }
707 
708     if (!i_sysCfgJsonObj.contains("frus"))
709     {
710         logging::logMessage("Invalid JSON object recieved.");
711         return false;
712     }
713 
714     if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
715     {
716         logging::logMessage(
717             "JSON object does not contain EEPROM path " + i_vpdFruPath);
718         return false;
719     }
720 
721     if ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)).contains(i_action))
722     {
723         if ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))[i_action].contains(
724                 i_flowFlag))
725         {
726             return true;
727         }
728 
729         logging::logMessage("Flow flag: [" + i_flowFlag +
730                             "], not found in JSON for path: " + i_vpdFruPath);
731         return false;
732     }
733     return false;
734 }
735 
736 /**
737  * @brief An API to return list of FRUs that needs GPIO polling.
738  *
739  * An API that checks for the FRUs that requires GPIO polling and returns
740  * a list of FRUs that needs polling. Returns an empty list if there are
741  * no FRUs that requires polling.
742  *
743  * @throw std::runtime_error
744  *
745  * @param[in] i_sysCfgJsonObj - System config JSON object.
746  *
747  * @return list of FRUs parameters that needs polling.
748  */
getListOfGpioPollingFrus(const nlohmann::json & i_sysCfgJsonObj)749 inline std::vector<std::string> getListOfGpioPollingFrus(
750     const nlohmann::json& i_sysCfgJsonObj)
751 {
752     if (i_sysCfgJsonObj.empty())
753     {
754         throw std::runtime_error("Invalid Parameters");
755     }
756 
757     if (!i_sysCfgJsonObj.contains("frus"))
758     {
759         throw std::runtime_error("Missing frus section in system config JSON");
760     }
761 
762     std::vector<std::string> l_gpioPollingRequiredFrusList;
763 
764     for (const auto& l_fru : i_sysCfgJsonObj["frus"].items())
765     {
766         const auto l_fruPath = l_fru.key();
767 
768         try
769         {
770             if (isActionRequired(i_sysCfgJsonObj, l_fruPath, "pollingRequired",
771                                  "hotPlugging"))
772             {
773                 if (i_sysCfgJsonObj["frus"][l_fruPath]
774                         .at(0)["pollingRequired"]["hotPlugging"]
775                         .contains("gpioPresence"))
776                 {
777                     l_gpioPollingRequiredFrusList.push_back(l_fruPath);
778                 }
779             }
780         }
781         catch (const std::exception& l_ex)
782         {
783             logging::logMessage(l_ex.what());
784         }
785     }
786 
787     return l_gpioPollingRequiredFrusList;
788 }
789 
790 /**
791  * @brief Get all related path(s) to update keyword value.
792  *
793  * Given FRU EEPROM path/Inventory path needs keyword's value update, this API
794  * returns tuple of FRU EEPROM path, inventory path and redundant EEPROM path if
795  * exists in the system config JSON.
796  *
797  * Note: If the inventory object path or redundant EEPROM path(s) are not found
798  * in the system config JSON, corresponding fields will have empty value in the
799  * returning tuple.
800  *
801  * @param[in] i_sysCfgJsonObj - System config JSON object.
802  * @param[in,out] io_vpdPath - Inventory object path or FRU EEPROM path.
803  *
804  * @return On success returns tuple of EEPROM path, inventory path & redundant
805  * path, on failure returns tuple with given input path alone.
806  */
807 inline std::tuple<std::string, std::string, std::string>
getAllPathsToUpdateKeyword(const nlohmann::json & i_sysCfgJsonObj,std::string io_vpdPath)808     getAllPathsToUpdateKeyword(const nlohmann::json& i_sysCfgJsonObj,
809                                std::string io_vpdPath)
810 {
811     types::Path l_inventoryObjPath;
812     types::Path l_redundantFruPath;
813     try
814     {
815         if (!i_sysCfgJsonObj.empty())
816         {
817             // Get hardware path from system config JSON.
818             const types::Path l_fruPath =
819                 jsonUtility::getFruPathFromJson(i_sysCfgJsonObj, io_vpdPath);
820 
821             if (!l_fruPath.empty())
822             {
823                 io_vpdPath = l_fruPath;
824 
825                 // Get inventory object path from system config JSON
826                 l_inventoryObjPath = jsonUtility::getInventoryObjPathFromJson(
827                     i_sysCfgJsonObj, l_fruPath);
828 
829                 // Get redundant hardware path if present in system config JSON
830                 l_redundantFruPath =
831                     jsonUtility::getRedundantEepromPathFromJson(i_sysCfgJsonObj,
832                                                                 l_fruPath);
833             }
834         }
835     }
836     catch (const std::exception& l_exception)
837     {
838         logging::logMessage(
839             "Failed to get all paths to update keyword value, error " +
840             std::string(l_exception.what()));
841     }
842     return std::make_tuple(io_vpdPath, l_inventoryObjPath, l_redundantFruPath);
843 }
844 
845 /**
846  * @brief An API to get DBus service name.
847  *
848  * Given DBus inventory path, this API returns DBus service name if present in
849  * the JSON.
850  *
851  * @param[in] i_sysCfgJsonObj - System config JSON object.
852  * @param[in] l_inventoryPath - DBus inventory path.
853  *
854  * @return On success returns the service name present in the system config
855  * JSON, otherwise empty string.
856  *
857  * Note: Caller has to handle in case of empty string received.
858  */
getServiceName(const nlohmann::json & i_sysCfgJsonObj,const std::string & l_inventoryPath)859 inline std::string getServiceName(const nlohmann::json& i_sysCfgJsonObj,
860                                   const std::string& l_inventoryPath)
861 {
862     try
863     {
864         if (l_inventoryPath.empty())
865         {
866             throw std::runtime_error("Path parameter is empty.");
867         }
868 
869         if (!i_sysCfgJsonObj.contains("frus"))
870         {
871             throw std::runtime_error("Missing frus tag in system config JSON.");
872         }
873 
874         const nlohmann::json& l_listOfFrus =
875             i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
876 
877         for (const auto& l_frus : l_listOfFrus.items())
878         {
879             for (const auto& l_inventoryItem : l_frus.value())
880             {
881                 if (l_inventoryPath.compare(l_inventoryItem["inventoryPath"]) ==
882                     constants::STR_CMP_SUCCESS)
883                 {
884                     return l_inventoryItem["serviceName"];
885                 }
886             }
887         }
888         throw std::runtime_error(
889             "Inventory path not found in the system config JSON");
890     }
891     catch (const std::exception& l_exception)
892     {
893         logging::logMessage(
894             "Error while getting DBus service name for given path " +
895             l_inventoryPath + ", error: " + std::string(l_exception.what()));
896         // TODO:log PEL
897     }
898     return std::string{};
899 }
900 
901 /**
902  * @brief An API to check if a FRU is tagged as "powerOffOnly"
903  *
904  * Given the system config JSON and VPD FRU path, this API checks if the FRU
905  * VPD can be collected at Chassis Power Off state only.
906  *
907  * @param[in] i_sysCfgJsonObj - System config JSON object.
908  * @param[in] i_vpdFruPath - EEPROM path.
909  * @return - True if FRU VPD can be collected at Chassis Power Off state only.
910  *           False otherwise
911  */
isFruPowerOffOnly(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdFruPath)912 inline bool isFruPowerOffOnly(const nlohmann::json& i_sysCfgJsonObj,
913                               const std::string& i_vpdFruPath)
914 {
915     if (i_vpdFruPath.empty())
916     {
917         logging::logMessage("FRU path is empty.");
918         return false;
919     }
920 
921     if (!i_sysCfgJsonObj.contains("frus"))
922     {
923         logging::logMessage("Missing frus tag in system config JSON.");
924         return false;
925     }
926 
927     if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
928     {
929         logging::logMessage("JSON object does not contain EEPROM path \'" +
930                             i_vpdFruPath + "\'");
931         return false;
932     }
933 
934     return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
935                 .contains("powerOffOnly") &&
936             (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)["powerOffOnly"]));
937 }
938 
939 /**
940  * @brief API which tells if the FRU is replaceable at runtime
941  *
942  * @param[in] i_sysCfgJsonObj - System config JSON object.
943  * @param[in] i_vpdFruPath - EEPROM path.
944  *
945  * @return true if FRU is replaceable at runtime. false otherwise.
946  */
isFruReplaceableAtRuntime(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdFruPath)947 inline bool isFruReplaceableAtRuntime(const nlohmann::json& i_sysCfgJsonObj,
948                                       const std::string& i_vpdFruPath)
949 {
950     try
951     {
952         if (i_vpdFruPath.empty())
953         {
954             throw std::runtime_error("Given FRU path is empty.");
955         }
956 
957         if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
958         {
959             throw std::runtime_error("Invalid system config JSON object.");
960         }
961 
962         return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
963                     .contains("replaceableAtRuntime") &&
964                 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(
965                     0)["replaceableAtRuntime"]));
966     }
967     catch (const std::exception& l_error)
968     {
969         // TODO: Log PEL
970         logging::logMessage(l_error.what());
971     }
972 
973     return false;
974 }
975 
976 /**
977  * @brief API which tells if the FRU is replaceable at standby
978  *
979  * @param[in] i_sysCfgJsonObj - System config JSON object.
980  * @param[in] i_vpdFruPath - EEPROM path.
981  *
982  * @return true if FRU is replaceable at standby. false otherwise.
983  */
isFruReplaceableAtStandby(const nlohmann::json & i_sysCfgJsonObj,const std::string & i_vpdFruPath)984 inline bool isFruReplaceableAtStandby(const nlohmann::json& i_sysCfgJsonObj,
985                                       const std::string& i_vpdFruPath)
986 {
987     try
988     {
989         if (i_vpdFruPath.empty())
990         {
991             throw std::runtime_error("Given FRU path is empty.");
992         }
993 
994         if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
995         {
996             throw std::runtime_error("Invalid system config JSON object.");
997         }
998 
999         return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
1000                     .contains("replaceableAtStandby") &&
1001                 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(
1002                     0)["replaceableAtStandby"]));
1003     }
1004     catch (const std::exception& l_error)
1005     {
1006         // TODO: Log PEL
1007         logging::logMessage(l_error.what());
1008     }
1009 
1010     return false;
1011 }
1012 
1013 /**
1014  * @brief API to get list of FRUs replaceable at standby from JSON.
1015  *
1016  * The API will return a vector of FRUs inventory path which are replaceable at
1017  * standby.
1018  * The API can throw exception in case of error scenarios. Caller's
1019  * responsibility to handle those exceptions.
1020  *
1021  * @param[in] i_sysCfgJsonObj - System config JSON object.
1022  *
1023  * @return - List of FRUs replaceable at standby.
1024  */
getListOfFrusReplaceableAtStandby(const nlohmann::json & i_sysCfgJsonObj)1025 inline std::vector<std::string> getListOfFrusReplaceableAtStandby(
1026     const nlohmann::json& i_sysCfgJsonObj)
1027 {
1028     std::vector<std::string> l_frusReplaceableAtStandby;
1029 
1030     if (!i_sysCfgJsonObj.contains("frus"))
1031     {
1032         logging::logMessage("Missing frus tag in system config JSON.");
1033         return l_frusReplaceableAtStandby;
1034     }
1035 
1036     const nlohmann::json& l_fruList =
1037         i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
1038 
1039     for (const auto& l_fru : l_fruList.items())
1040     {
1041         if (i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
1042                 "replaceableAtStandby", false))
1043         {
1044             const std::string& l_inventoryObjectPath =
1045                 i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
1046                     "inventoryPath", "");
1047 
1048             if (!l_inventoryObjectPath.empty())
1049             {
1050                 l_frusReplaceableAtStandby.emplace_back(l_inventoryObjectPath);
1051             }
1052         }
1053     }
1054 
1055     return l_frusReplaceableAtStandby;
1056 }
1057 
1058 /**
1059  * @brief API to select powerVS JSON based on system IM.
1060  *
1061  * The API selects respective JSON based on system IM, parse it and return the
1062  * JSON object. Empty JSON will be returned in case of any error. Caller needs
1063  * to handle empty value.
1064  *
1065  * @param[in] i_imValue - IM value of the system.
1066  * @return Parsed JSON object, empty JSON otherwise.
1067  */
getPowerVsJson(const types::BinaryVector & i_imValue)1068 inline nlohmann::json getPowerVsJson(const types::BinaryVector& i_imValue)
1069 {
1070     try
1071     {
1072         if ((i_imValue.at(0) == constants::HEX_VALUE_50) &&
1073             (i_imValue.at(1) == constants::HEX_VALUE_00) &&
1074             (i_imValue.at(2) == constants::HEX_VALUE_30))
1075         {
1076             return jsonUtility::getParsedJson(constants::power_vs_50003_json);
1077         }
1078         else if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
1079                  (i_imValue.at(1) == constants::HEX_VALUE_00) &&
1080                  (i_imValue.at(2) == constants::HEX_VALUE_10))
1081         {
1082             return jsonUtility::getParsedJson(constants::power_vs_50001_json);
1083         }
1084         return nlohmann::json{};
1085     }
1086     catch (const std::exception& l_ex)
1087     {
1088         return nlohmann::json{};
1089     }
1090 }
1091 
1092 /** @brief API to get IM value from persisted data location.
1093  *
1094  * @return IM value on success, empty string otherwise.
1095  */
getImFromPersistedLocation()1096 inline std::string getImFromPersistedLocation() noexcept
1097 {
1098     std::string l_imFilePath{constants::pimPersistVsbpPath};
1099 
1100     try
1101     {
1102         auto l_parsedVsbpJsonObj = getParsedJson(l_imFilePath);
1103         if (!l_parsedVsbpJsonObj.contains("value0") ||
1104             !l_parsedVsbpJsonObj["value0"].contains(constants::kwdIM) ||
1105             !l_parsedVsbpJsonObj["value0"][constants::kwdIM].is_array())
1106         {
1107             throw std::runtime_error(
1108                 "Json is empty or mandatory tag(s) missing from JSON");
1109         }
1110 
1111         types::BinaryVector l_imValue =
1112             l_parsedVsbpJsonObj["value0"][constants::kwdIM]
1113                 .get<types::BinaryVector>();
1114 
1115         std::ostringstream l_imData;
1116         for (auto& l_byte : l_imValue)
1117         {
1118             l_imData << std::setw(2) << std::setfill('0') << std::hex
1119                      << static_cast<int>(l_byte);
1120         }
1121         return l_imData.str();
1122     }
1123     catch (const std::exception& l_ex)
1124     {
1125         logging::logMessage(
1126             "Error while getting IM value from PIM persisted file: " +
1127             l_imFilePath + ", reason: " + std::string(l_ex.what()));
1128     }
1129 
1130     return std::string();
1131 }
1132 } // namespace jsonUtility
1133 } // namespace vpd
1134