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