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