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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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> 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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