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