1 #pragma once 2 3 #include "error_codes.hpp" 4 #include "event_logger.hpp" 5 #include "exceptions.hpp" 6 #include "logger.hpp" 7 #include "types.hpp" 8 9 #include <gpiod.hpp> 10 #include <nlohmann/json.hpp> 11 #include <utility/common_utility.hpp> 12 #include <utility/vpd_specific_utility.hpp> 13 14 #include <fstream> 15 #include <type_traits> 16 #include <unordered_map> 17 18 namespace vpd 19 { 20 namespace jsonUtility 21 { 22 23 // forward declaration of API for function map. 24 bool processSystemCmdTag(const nlohmann::json& i_parsedConfigJson, 25 const std::string& i_vpdFilePath, 26 const std::string& i_baseAction, 27 const std::string& i_flagToProcess); 28 29 // forward declaration of API for function map. 30 bool processGpioPresenceTag( 31 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath, 32 const std::string& i_baseAction, const std::string& i_flagToProcess); 33 34 // forward declaration of API for function map. 35 bool procesSetGpioTag(const nlohmann::json& i_parsedConfigJson, 36 const std::string& i_vpdFilePath, 37 const std::string& i_baseAction, 38 const std::string& i_flagToProcess); 39 40 // Function pointers to process tags from config JSON. 41 typedef bool (*functionPtr)( 42 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath, 43 const std::string& i_baseAction, const std::string& i_flagToProcess); 44 45 inline std::unordered_map<std::string, functionPtr> funcionMap{ 46 {"gpioPresence", processGpioPresenceTag}, 47 {"setGpio", procesSetGpioTag}, 48 {"systemCmd", processSystemCmdTag}}; 49 50 /** 51 * @brief API to read VPD offset from JSON file. 52 * 53 * @param[in] i_sysCfgJsonObj - Parsed system config JSON object. 54 * @param[in] i_vpdFilePath - VPD file path. 55 * @param[in] o_errCode - To set error code in case of error. 56 * @return VPD offset if found in JSON, 0 otherwise. 57 */ 58 inline size_t getVPDOffset(const nlohmann::json& i_sysCfgJsonObj, 59 const std::string& i_vpdFilePath, 60 uint16_t& o_errCode) 61 { 62 if (i_vpdFilePath.empty() || (i_sysCfgJsonObj.empty()) || 63 (!i_sysCfgJsonObj.contains("frus"))) 64 { 65 o_errCode = error_code::INVALID_INPUT_PARAMETER; 66 return 0; 67 } 68 69 if (i_sysCfgJsonObj["frus"].contains(i_vpdFilePath)) 70 { 71 return i_sysCfgJsonObj["frus"][i_vpdFilePath].at(0).value("offset", 0); 72 } 73 74 const nlohmann::json& l_fruList = 75 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>(); 76 77 for (const auto& l_fru : l_fruList.items()) 78 { 79 const auto l_fruPath = l_fru.key(); 80 81 // check if given path is redundant FRU path 82 if (i_vpdFilePath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value( 83 "redundantEeprom", "")) 84 { 85 // Return the offset of redundant EEPROM taken from JSON. 86 return i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("offset", 0); 87 } 88 } 89 90 return 0; 91 } 92 93 /** 94 * @brief API to parse respective JSON. 95 * 96 * @param[in] pathToJson - Path to JSON. 97 * @param[out] o_errCode - To set error code in case of error. 98 * @return on success parsed JSON. On failure empty JSON object. 99 * 100 * Note: Caller has to handle it in case an empty JSON object is received. 101 */ 102 inline nlohmann::json getParsedJson(const std::string& pathToJson, 103 uint16_t& o_errCode) noexcept 104 { 105 if (pathToJson.empty()) 106 { 107 o_errCode = error_code::INVALID_INPUT_PARAMETER; 108 return nlohmann::json{}; 109 } 110 111 if (!std::filesystem::exists(pathToJson)) 112 { 113 o_errCode = error_code::FILE_NOT_FOUND; 114 return nlohmann::json{}; 115 } 116 117 if (std::filesystem::is_empty(pathToJson)) 118 { 119 o_errCode = error_code::EMPTY_FILE; 120 return nlohmann::json{}; 121 } 122 123 std::ifstream l_jsonFile(pathToJson); 124 if (!l_jsonFile) 125 { 126 o_errCode = error_code::FILE_ACCESS_ERROR; 127 return nlohmann::json{}; 128 } 129 130 try 131 { 132 return nlohmann::json::parse(l_jsonFile); 133 } 134 catch (const std::exception& l_ex) 135 { 136 o_errCode = error_code::JSON_PARSE_ERROR; 137 return nlohmann::json{}; 138 } 139 } 140 141 /** 142 * @brief Get inventory object path from system config JSON. 143 * 144 * Given either D-bus inventory path/FRU EEPROM path/redundant EEPROM path, 145 * this API returns D-bus inventory path if present in JSON. 146 * 147 * @param[in] i_sysCfgJsonObj - System config JSON object 148 * @param[in] i_vpdPath - Path to where VPD is stored. 149 * @param[in] o_errCode - To set error code in case of error. 150 * 151 * @return On success a valid path is returned, on failure an empty string is 152 * returned. 153 * 154 * Note: Caller has to handle it in case an empty string is received. 155 */ 156 inline std::string getInventoryObjPathFromJson( 157 const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdPath, 158 uint16_t& o_errCode) noexcept 159 { 160 if (i_vpdPath.empty()) 161 { 162 o_errCode = error_code::INVALID_INPUT_PARAMETER; 163 return std::string{}; 164 } 165 166 if (!i_sysCfgJsonObj.contains("frus")) 167 { 168 o_errCode = error_code::INVALID_JSON; 169 return std::string{}; 170 } 171 172 // check if given path is FRU path 173 if (i_sysCfgJsonObj["frus"].contains(i_vpdPath)) 174 { 175 return i_sysCfgJsonObj["frus"][i_vpdPath].at(0).value( 176 "inventoryPath", ""); 177 } 178 179 const nlohmann::json& l_fruList = 180 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>(); 181 182 for (const auto& l_fru : l_fruList.items()) 183 { 184 const auto l_fruPath = l_fru.key(); 185 const auto l_invObjPath = 186 i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("inventoryPath", ""); 187 188 // check if given path is redundant FRU path or inventory path 189 if (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value( 190 "redundantEeprom", "") || 191 (i_vpdPath == l_invObjPath)) 192 { 193 return l_invObjPath; 194 } 195 } 196 197 return std::string{}; 198 } 199 200 /** 201 * @brief Process "PostFailAction" defined in config JSON. 202 * 203 * In case there is some error in the processing of "preAction" execution and a 204 * set of procedure needs to be done as a part of post fail action. This base 205 * action can be defined in the config JSON for that FRU and it will be handled 206 * under this API. 207 * 208 * @param[in] i_parsedConfigJson - config JSON 209 * @param[in] i_vpdFilePath - EEPROM file path 210 * @param[in] i_flagToProcess - To identify which flag(s) needs to be processed 211 * @param[out] o_errCode - To set error code in case of error 212 * under PostFailAction tag of config JSON. 213 * @return - success or failure 214 */ 215 inline bool executePostFailAction( 216 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath, 217 const std::string& i_flagToProcess, uint16_t& o_errCode) 218 { 219 if (i_parsedConfigJson.empty() || i_vpdFilePath.empty() || 220 i_flagToProcess.empty()) 221 { 222 o_errCode = error_code::INVALID_INPUT_PARAMETER; 223 return false; 224 } 225 226 if (!(i_parsedConfigJson["frus"][i_vpdFilePath].at(0))["postFailAction"] 227 .contains(i_flagToProcess)) 228 { 229 o_errCode = error_code::MISSING_FLAG; 230 return false; 231 } 232 233 for (const auto& l_tags : (i_parsedConfigJson["frus"][i_vpdFilePath].at( 234 0))["postFailAction"][i_flagToProcess] 235 .items()) 236 { 237 auto itrToFunction = funcionMap.find(l_tags.key()); 238 if (itrToFunction != funcionMap.end()) 239 { 240 if (!itrToFunction->second(i_parsedConfigJson, i_vpdFilePath, 241 "postFailAction", i_flagToProcess)) 242 { 243 return false; 244 } 245 } 246 } 247 248 return true; 249 } 250 251 /** 252 * @brief Process "systemCmd" tag for a given FRU. 253 * 254 * The API will process "systemCmd" tag if it is defined in the config 255 * JSON for the given FRU. 256 * 257 * @param[in] i_parsedConfigJson - config JSON 258 * @param[in] i_vpdFilePath - EEPROM file path 259 * @param[in] i_baseAction - Base action for which this tag has been called. 260 * @param[in] i_flagToProcess - Flag nested under the base action for which this 261 * tag has been called. 262 * @return Execution status. 263 */ 264 inline bool processSystemCmdTag( 265 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath, 266 const std::string& i_baseAction, const std::string& i_flagToProcess) 267 { 268 try 269 { 270 if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() || 271 i_baseAction.empty() || i_flagToProcess.empty()) 272 { 273 throw std::runtime_error( 274 std::string(__FUNCTION__) + 275 " Invalid parameter. Abort processing of processSystemCmd."); 276 } 277 278 if (!((i_parsedConfigJson["frus"][i_vpdFilePath].at( 279 0)[i_baseAction][i_flagToProcess]["systemCmd"]) 280 .contains("cmd"))) 281 { 282 throw JsonException( 283 std::string(__FUNCTION__) + 284 " Config JSON missing required information to execute system command for EEPROM " + 285 i_vpdFilePath); 286 } 287 288 const std::string& l_systemCommand = 289 i_parsedConfigJson["frus"][i_vpdFilePath].at( 290 0)[i_baseAction][i_flagToProcess]["systemCmd"]["cmd"]; 291 292 commonUtility::executeCmd(l_systemCommand); 293 return true; 294 } 295 catch (const std::exception& l_ex) 296 { 297 EventLogger::createSyncPel( 298 EventLogger::getErrorType(l_ex), types::SeverityType::Informational, 299 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 300 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 301 return false; 302 } 303 } 304 305 /** 306 * @brief Checks for presence of a given FRU using GPIO line. 307 * 308 * This API returns the presence information of the FRU corresponding to the 309 * given VPD file path by setting the presence pin. 310 * 311 * @param[in] i_parsedConfigJson - config JSON 312 * @param[in] i_vpdFilePath - EEPROM file path 313 * @param[in] i_baseAction - Base action for which this tag has been called. 314 * @param[in] i_flagToProcess - Flag nested under the base action for which this 315 * tag has been called. 316 * @return Execution status. 317 */ 318 inline bool processGpioPresenceTag( 319 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath, 320 const std::string& i_baseAction, const std::string& i_flagToProcess) 321 { 322 std::string l_presencePinName; 323 try 324 { 325 if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() || 326 i_baseAction.empty() || i_flagToProcess.empty()) 327 { 328 throw std::runtime_error( 329 std::string(__FUNCTION__) + 330 "Invalid parameter. Abort processing of processGpioPresence tag"); 331 } 332 333 if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at( 334 0)[i_baseAction][i_flagToProcess]["gpioPresence"]) 335 .contains("pin")) && 336 ((i_parsedConfigJson["frus"][i_vpdFilePath].at( 337 0)[i_baseAction][i_flagToProcess]["gpioPresence"]) 338 .contains("value")))) 339 { 340 throw JsonException( 341 std::string(__FUNCTION__) + 342 "Config JSON missing required information to detect presence for EEPROM " + 343 i_vpdFilePath); 344 } 345 346 // get the pin name 347 l_presencePinName = i_parsedConfigJson["frus"][i_vpdFilePath].at( 348 0)[i_baseAction][i_flagToProcess]["gpioPresence"]["pin"]; 349 350 // get the pin value 351 uint8_t l_presencePinValue = 352 i_parsedConfigJson["frus"][i_vpdFilePath].at( 353 0)[i_baseAction][i_flagToProcess]["gpioPresence"]["value"]; 354 355 gpiod::line l_presenceLine = gpiod::find_line(l_presencePinName); 356 357 if (!l_presenceLine) 358 { 359 throw GpioException("Couldn't find the GPIO line."); 360 } 361 362 l_presenceLine.request({"Read the presence line", 363 gpiod::line_request::DIRECTION_INPUT, 0}); 364 365 return (l_presencePinValue == l_presenceLine.get_value()); 366 } 367 catch (const std::exception& l_ex) 368 { 369 // No need to continue in case of JSON failure or Firmware error 370 // as these are errors internal to the code and in that case the FRU 371 // should not be processed. Any other error is considered as external 372 // error in this case and a try to read the EEPROM should be done. 373 if (EventLogger::getErrorType(l_ex) == types::ErrorType::JsonFailure || 374 EventLogger::getErrorType(l_ex) == types::ErrorType::FirmwareError) 375 { 376 EventLogger::createSyncPel( 377 EventLogger::getErrorType(l_ex), 378 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0, 379 EventLogger::getErrorMsg(l_ex), std::nullopt, std::nullopt, 380 std::nullopt, std::nullopt); 381 return false; 382 } 383 384 std::string l_errMsg = "Exception on GPIO line: "; 385 l_errMsg += l_presencePinName; 386 l_errMsg += " Reason: "; 387 l_errMsg += l_ex.what(); 388 l_errMsg += " File: " + i_vpdFilePath + " Pel Logged"; 389 390 uint16_t l_errCode = 0; 391 392 // ToDo -- Update Internal Rc code. 393 EventLogger::createAsyncPelWithInventoryCallout( 394 EventLogger::getErrorType(l_ex), types::SeverityType::Informational, 395 {{getInventoryObjPathFromJson(i_parsedConfigJson, i_vpdFilePath, 396 l_errCode), 397 types::CalloutPriority::High}}, 398 std::source_location::current().file_name(), 399 std::source_location::current().function_name(), 0, l_errMsg, 400 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 401 402 logging::logMessage(l_errMsg); 403 404 // Except when GPIO pin value is false, we go and try collecting the 405 // FRU VPD as we couldn't able to read GPIO pin value due to some 406 // error/exception. So returning true in error scenario. 407 return true; 408 } 409 } 410 411 /** 412 * @brief Process "setGpio" tag for a given FRU. 413 * 414 * This API enables the GPIO line. 415 * 416 * @param[in] i_parsedConfigJson - config JSON 417 * @param[in] i_vpdFilePath - EEPROM file path 418 * @param[in] i_baseAction - Base action for which this tag has been called. 419 * @param[in] i_flagToProcess - Flag nested under the base action for which this 420 * tag has been called. 421 * @return Execution status. 422 */ 423 inline bool procesSetGpioTag( 424 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath, 425 const std::string& i_baseAction, const std::string& i_flagToProcess) 426 { 427 std::string l_pinName; 428 try 429 { 430 if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() || 431 i_baseAction.empty() || i_flagToProcess.empty()) 432 { 433 throw std::runtime_error( 434 std::string(__FUNCTION__) + 435 " Invalid parameter. Abort processing of procesSetGpio."); 436 } 437 438 if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at( 439 0)[i_baseAction][i_flagToProcess]["setGpio"]) 440 .contains("pin")) && 441 ((i_parsedConfigJson["frus"][i_vpdFilePath].at( 442 0)[i_baseAction][i_flagToProcess]["setGpio"]) 443 .contains("value")))) 444 { 445 throw JsonException( 446 std::string(__FUNCTION__) + 447 " Config JSON missing required information to set gpio line for EEPROM " + 448 i_vpdFilePath); 449 } 450 451 l_pinName = i_parsedConfigJson["frus"][i_vpdFilePath].at( 452 0)[i_baseAction][i_flagToProcess]["setGpio"]["pin"]; 453 454 // Get the value to set 455 uint8_t l_pinValue = i_parsedConfigJson["frus"][i_vpdFilePath].at( 456 0)[i_baseAction][i_flagToProcess]["setGpio"]["value"]; 457 458 logging::logMessage( 459 "Setting GPIO: " + l_pinName + " to " + std::to_string(l_pinValue)); 460 461 gpiod::line l_outputLine = gpiod::find_line(l_pinName); 462 463 if (!l_outputLine) 464 { 465 throw GpioException("Couldn't find GPIO line."); 466 } 467 468 l_outputLine.request( 469 {"FRU Action", ::gpiod::line_request::DIRECTION_OUTPUT, 0}, 470 l_pinValue); 471 return true; 472 } 473 catch (const std::exception& l_ex) 474 { 475 if (EventLogger::getErrorType(l_ex) != types::ErrorType::GpioError) 476 { 477 EventLogger::createSyncPel( 478 EventLogger::getErrorType(l_ex), 479 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0, 480 EventLogger::getErrorMsg(l_ex), std::nullopt, std::nullopt, 481 std::nullopt, std::nullopt); 482 } 483 else 484 { 485 std::string l_errMsg = "Exception on GPIO line: "; 486 l_errMsg += l_pinName; 487 l_errMsg += " Reason: "; 488 l_errMsg += l_ex.what(); 489 l_errMsg += " File: " + i_vpdFilePath + " Pel Logged"; 490 491 uint16_t l_errCode = 0; 492 493 // ToDo -- Update Internal RC code 494 EventLogger::createAsyncPelWithInventoryCallout( 495 EventLogger::getErrorType(l_ex), 496 types::SeverityType::Informational, 497 {{getInventoryObjPathFromJson(i_parsedConfigJson, i_vpdFilePath, 498 l_errCode), 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 uint16_t l_errCode = 0; 888 889 // Get inventory object path from system config JSON 890 l_inventoryObjPath = jsonUtility::getInventoryObjPathFromJson( 891 i_sysCfgJsonObj, l_fruPath, l_errCode); 892 893 // Get redundant hardware path if present in system config JSON 894 l_redundantFruPath = 895 jsonUtility::getRedundantEepromPathFromJson(i_sysCfgJsonObj, 896 l_fruPath); 897 } 898 } 899 } 900 catch (const std::exception& l_exception) 901 { 902 logging::logMessage( 903 "Failed to get all paths to update keyword value, error " + 904 std::string(l_exception.what())); 905 } 906 return std::make_tuple(io_vpdPath, l_inventoryObjPath, l_redundantFruPath); 907 } 908 909 /** 910 * @brief An API to get DBus service name. 911 * 912 * Given DBus inventory path, this API returns DBus service name if present in 913 * the JSON. 914 * 915 * @param[in] i_sysCfgJsonObj - System config JSON object. 916 * @param[in] l_inventoryPath - DBus inventory path. 917 * 918 * @return On success returns the service name present in the system config 919 * JSON, otherwise empty string. 920 * 921 * Note: Caller has to handle in case of empty string received. 922 */ 923 inline std::string getServiceName(const nlohmann::json& i_sysCfgJsonObj, 924 const std::string& l_inventoryPath) 925 { 926 try 927 { 928 if (l_inventoryPath.empty()) 929 { 930 throw std::runtime_error("Path parameter is empty."); 931 } 932 933 if (!i_sysCfgJsonObj.contains("frus")) 934 { 935 throw std::runtime_error("Missing frus tag in system config JSON."); 936 } 937 938 const nlohmann::json& l_listOfFrus = 939 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>(); 940 941 for (const auto& l_frus : l_listOfFrus.items()) 942 { 943 for (const auto& l_inventoryItem : l_frus.value()) 944 { 945 if (l_inventoryPath.compare(l_inventoryItem["inventoryPath"]) == 946 constants::STR_CMP_SUCCESS) 947 { 948 return l_inventoryItem["serviceName"]; 949 } 950 } 951 } 952 throw std::runtime_error( 953 "Inventory path not found in the system config JSON"); 954 } 955 catch (const std::exception& l_exception) 956 { 957 logging::logMessage( 958 "Error while getting DBus service name for given path " + 959 l_inventoryPath + ", error: " + std::string(l_exception.what())); 960 // TODO:log PEL 961 } 962 return std::string{}; 963 } 964 965 /** 966 * @brief An API to check if a FRU is tagged as "powerOffOnly" 967 * 968 * Given the system config JSON and VPD FRU path, this API checks if the FRU 969 * VPD can be collected at Chassis Power Off state only. 970 * 971 * @param[in] i_sysCfgJsonObj - System config JSON object. 972 * @param[in] i_vpdFruPath - EEPROM path. 973 * @return - True if FRU VPD can be collected at Chassis Power Off state only. 974 * False otherwise 975 */ 976 inline bool isFruPowerOffOnly(const nlohmann::json& i_sysCfgJsonObj, 977 const std::string& i_vpdFruPath) 978 { 979 if (i_vpdFruPath.empty()) 980 { 981 logging::logMessage("FRU path is empty."); 982 return false; 983 } 984 985 if (!i_sysCfgJsonObj.contains("frus")) 986 { 987 logging::logMessage("Missing frus tag in system config JSON."); 988 return false; 989 } 990 991 if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath)) 992 { 993 logging::logMessage("JSON object does not contain EEPROM path \'" + 994 i_vpdFruPath + "\'"); 995 return false; 996 } 997 998 return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)) 999 .contains("powerOffOnly") && 1000 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)["powerOffOnly"])); 1001 } 1002 1003 /** 1004 * @brief API which tells if the FRU is replaceable at runtime 1005 * 1006 * @param[in] i_sysCfgJsonObj - System config JSON object. 1007 * @param[in] i_vpdFruPath - EEPROM path. 1008 * @param[out] o_errCode - to set error code in case of error. 1009 * 1010 * @return true if FRU is replaceable at runtime. false otherwise. 1011 */ 1012 inline bool isFruReplaceableAtRuntime(const nlohmann::json& i_sysCfgJsonObj, 1013 const std::string& i_vpdFruPath, 1014 uint16_t& o_errCode) 1015 { 1016 if (i_vpdFruPath.empty()) 1017 { 1018 o_errCode = error_code::INVALID_INPUT_PARAMETER; 1019 return false; 1020 } 1021 1022 if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus"))) 1023 { 1024 o_errCode = error_code::INVALID_JSON; 1025 return false; 1026 } 1027 1028 return ( 1029 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)) 1030 .contains("replaceableAtRuntime") && 1031 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)["replaceableAtRuntime"])); 1032 1033 return false; 1034 } 1035 1036 /** 1037 * @brief API which tells if the FRU is replaceable at standby 1038 * 1039 * @param[in] i_sysCfgJsonObj - System config JSON object. 1040 * @param[in] i_vpdFruPath - EEPROM path. 1041 * @param[out] o_errCode - set error code in case of error. 1042 * 1043 * @return true if FRU is replaceable at standby. false otherwise. 1044 */ 1045 inline bool isFruReplaceableAtStandby(const nlohmann::json& i_sysCfgJsonObj, 1046 const std::string& i_vpdFruPath, 1047 uint16_t& o_errCode) 1048 { 1049 if (i_vpdFruPath.empty()) 1050 { 1051 o_errCode = error_code::INVALID_INPUT_PARAMETER; 1052 return false; 1053 } 1054 1055 if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus"))) 1056 { 1057 o_errCode = error_code::INVALID_JSON; 1058 } 1059 1060 return ( 1061 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)) 1062 .contains("replaceableAtStandby") && 1063 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)["replaceableAtStandby"])); 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 uint16_t l_errCode = 0; 1136 if ((i_imValue.at(0) == constants::HEX_VALUE_50) && 1137 (i_imValue.at(1) == constants::HEX_VALUE_00) && 1138 (i_imValue.at(2) == constants::HEX_VALUE_30)) 1139 { 1140 nlohmann::json l_parsedJson = jsonUtility::getParsedJson( 1141 constants::power_vs_50003_json, l_errCode); 1142 1143 if (l_errCode) 1144 { 1145 logging::logMessage( 1146 "Failed to parse JSON file [ " + 1147 std::string(constants::power_vs_50003_json) + 1148 " ], error : " + 1149 vpdSpecificUtility::getErrCodeMsg(l_errCode)); 1150 } 1151 1152 return l_parsedJson; 1153 } 1154 else if (i_imValue.at(0) == constants::HEX_VALUE_50 && 1155 (i_imValue.at(1) == constants::HEX_VALUE_00) && 1156 (i_imValue.at(2) == constants::HEX_VALUE_10)) 1157 { 1158 nlohmann::json l_parsedJson = jsonUtility::getParsedJson( 1159 constants::power_vs_50001_json, l_errCode); 1160 1161 if (l_errCode) 1162 { 1163 logging::logMessage( 1164 "Failed to parse JSON file [ " + 1165 std::string(constants::power_vs_50001_json) + 1166 " ], error : " + 1167 vpdSpecificUtility::getErrCodeMsg(l_errCode)); 1168 } 1169 1170 return l_parsedJson; 1171 } 1172 return nlohmann::json{}; 1173 } 1174 catch (const std::exception& l_ex) 1175 { 1176 return nlohmann::json{}; 1177 } 1178 } 1179 1180 /** 1181 * @brief API to get list of FRUs for which "monitorPresence" is true. 1182 * 1183 * @param[in] i_sysCfgJsonObj - System config JSON object. 1184 * @param[out] o_errCode - To set error code in case of error. 1185 * 1186 * @return On success, returns list of FRUs for which "monitorPresence" is true, 1187 * empty list on error. 1188 */ 1189 inline std::vector<types::Path> getFrusWithPresenceMonitoring( 1190 const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode) 1191 { 1192 std::vector<types::Path> l_frusWithPresenceMonitoring; 1193 1194 if (!i_sysCfgJsonObj.contains("frus")) 1195 { 1196 o_errCode = error_code::INVALID_JSON; 1197 return l_frusWithPresenceMonitoring; 1198 } 1199 1200 const nlohmann::json& l_listOfFrus = 1201 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>(); 1202 1203 for (const auto& l_aFru : l_listOfFrus) 1204 { 1205 if (l_aFru.at(0).value("monitorPresence", false)) 1206 { 1207 l_frusWithPresenceMonitoring.emplace_back( 1208 l_aFru.at(0).value("inventoryPath", "")); 1209 } 1210 } 1211 1212 return l_frusWithPresenceMonitoring; 1213 } 1214 1215 /** 1216 * @brief API which tells if the FRU's presence is handled 1217 * 1218 * For a given FRU, this API checks if it's presence is handled by vpd-manager 1219 * by checking the "handlePresence" tag. 1220 * 1221 * @param[in] i_sysCfgJsonObj - System config JSON object. 1222 * @param[in] i_vpdFruPath - EEPROM path. 1223 * @param[out] o_errCode - To set error code in case of failure. 1224 * 1225 * @return true if FRU presence is handled, false otherwise. 1226 */ 1227 inline bool isFruPresenceHandled(const nlohmann::json& i_sysCfgJsonObj, 1228 const std::string& i_vpdFruPath, 1229 uint16_t& o_errCode) 1230 { 1231 if (i_vpdFruPath.empty()) 1232 { 1233 o_errCode = error_code::INVALID_INPUT_PARAMETER; 1234 return false; 1235 } 1236 1237 if (!i_sysCfgJsonObj.contains("frus")) 1238 { 1239 o_errCode = error_code::INVALID_JSON; 1240 return false; 1241 } 1242 1243 if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath)) 1244 { 1245 o_errCode = error_code::FRU_PATH_NOT_FOUND; 1246 return false; 1247 } 1248 1249 return i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0).value( 1250 "handlePresence", true); 1251 } 1252 } // namespace jsonUtility 1253 } // namespace vpd 1254