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