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