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