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