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