1 #include "backup_restore.hpp" 2 3 #include "constants.hpp" 4 #include "event_logger.hpp" 5 #include "exceptions.hpp" 6 #include "logger.hpp" 7 #include "parser.hpp" 8 #include "types.hpp" 9 10 #include <utility/json_utility.hpp> 11 #include <utility/vpd_specific_utility.hpp> 12 13 namespace vpd 14 { 15 BackupAndRestoreStatus BackupAndRestore::m_backupAndRestoreStatus = 16 BackupAndRestoreStatus::NotStarted; 17 18 BackupAndRestore::BackupAndRestore(const nlohmann::json& i_sysCfgJsonObj) : 19 m_sysCfgJsonObj(i_sysCfgJsonObj) 20 { 21 std::string l_backupAndRestoreCfgFilePath = 22 i_sysCfgJsonObj.value("backupRestoreConfigPath", ""); 23 24 uint16_t l_errCode = 0; 25 m_backupAndRestoreCfgJsonObj = 26 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath, l_errCode); 27 28 if (l_errCode) 29 { 30 throw JsonException( 31 "JSON parsing failed for file [" + l_backupAndRestoreCfgFilePath + 32 "], error : " + commonUtility::getErrCodeMsg(l_errCode), 33 l_backupAndRestoreCfgFilePath); 34 } 35 } 36 37 std::tuple<types::VPDMapVariant, types::VPDMapVariant> 38 BackupAndRestore::backupAndRestore() 39 { 40 auto l_emptyVariantPair = 41 std::make_tuple(std::monostate{}, std::monostate{}); 42 43 if (m_backupAndRestoreStatus >= BackupAndRestoreStatus::Invoked) 44 { 45 logging::logMessage("Backup and restore invoked already."); 46 return l_emptyVariantPair; 47 } 48 49 m_backupAndRestoreStatus = BackupAndRestoreStatus::Invoked; 50 try 51 { 52 if (m_backupAndRestoreCfgJsonObj.empty() || 53 !m_backupAndRestoreCfgJsonObj.contains("source") || 54 !m_backupAndRestoreCfgJsonObj.contains("destination") || 55 !m_backupAndRestoreCfgJsonObj.contains("type") || 56 !m_backupAndRestoreCfgJsonObj.contains("backupMap")) 57 { 58 logging::logMessage( 59 "Backup restore config JSON is missing necessary tag(s), can't initiate backup and restore."); 60 return l_emptyVariantPair; 61 } 62 63 std::string l_srcVpdPath; 64 types::VPDMapVariant l_srcVpdVariant; 65 if (l_srcVpdPath = m_backupAndRestoreCfgJsonObj["source"].value( 66 "hardwarePath", ""); 67 !l_srcVpdPath.empty() && std::filesystem::exists(l_srcVpdPath)) 68 { 69 std::shared_ptr<Parser> l_vpdParser = 70 std::make_shared<Parser>(l_srcVpdPath, m_sysCfgJsonObj); 71 l_srcVpdVariant = l_vpdParser->parse(); 72 } 73 else if (l_srcVpdPath = m_backupAndRestoreCfgJsonObj["source"].value( 74 "inventoryPath", ""); 75 l_srcVpdPath.empty()) 76 { 77 logging::logMessage( 78 "Couldn't extract source path, can't initiate backup and restore."); 79 return l_emptyVariantPair; 80 } 81 82 std::string l_dstVpdPath; 83 types::VPDMapVariant l_dstVpdVariant; 84 if (l_dstVpdPath = m_backupAndRestoreCfgJsonObj["destination"].value( 85 "hardwarePath", ""); 86 !l_dstVpdPath.empty() && std::filesystem::exists(l_dstVpdPath)) 87 { 88 std::shared_ptr<Parser> l_vpdParser = 89 std::make_shared<Parser>(l_dstVpdPath, m_sysCfgJsonObj); 90 l_dstVpdVariant = l_vpdParser->parse(); 91 } 92 else if (l_dstVpdPath = m_backupAndRestoreCfgJsonObj["destination"] 93 .value("inventoryPath", ""); 94 l_dstVpdPath.empty()) 95 { 96 logging::logMessage( 97 "Couldn't extract destination path, can't initiate backup and restore."); 98 return l_emptyVariantPair; 99 } 100 101 // Implement backup and restore for IPZ type VPD 102 auto l_backupAndRestoreType = 103 m_backupAndRestoreCfgJsonObj.value("type", ""); 104 if (l_backupAndRestoreType.compare("IPZ") == constants::STR_CMP_SUCCESS) 105 { 106 types::IPZVpdMap l_srcVpdMap; 107 if (auto l_srcVpdPtr = 108 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant)) 109 { 110 l_srcVpdMap = *l_srcVpdPtr; 111 } 112 else if (!std::holds_alternative<std::monostate>(l_srcVpdVariant)) 113 { 114 logging::logMessage("Source VPD is not of IPZ type."); 115 return l_emptyVariantPair; 116 } 117 118 types::IPZVpdMap l_dstVpdMap; 119 if (auto l_dstVpdPtr = 120 std::get_if<types::IPZVpdMap>(&l_dstVpdVariant)) 121 { 122 l_dstVpdMap = *l_dstVpdPtr; 123 } 124 else if (!std::holds_alternative<std::monostate>(l_dstVpdVariant)) 125 { 126 logging::logMessage("Destination VPD is not of IPZ type."); 127 return l_emptyVariantPair; 128 } 129 130 backupAndRestoreIpzVpd(l_srcVpdMap, l_dstVpdMap, l_srcVpdPath, 131 l_dstVpdPath); 132 m_backupAndRestoreStatus = BackupAndRestoreStatus::Completed; 133 134 return std::make_tuple(l_srcVpdMap, l_dstVpdMap); 135 } 136 // Note: add implementation here to support any other VPD type. 137 } 138 catch (const std::exception& ex) 139 { 140 logging::logMessage("Back up and restore failed with exception: " + 141 std::string(ex.what())); 142 } 143 return l_emptyVariantPair; 144 } 145 146 void BackupAndRestore::backupAndRestoreIpzVpd( 147 types::IPZVpdMap& io_srcVpdMap, types::IPZVpdMap& io_dstVpdMap, 148 const std::string& i_srcPath, const std::string& i_dstPath) 149 { 150 if (!m_backupAndRestoreCfgJsonObj["backupMap"].is_array()) 151 { 152 logging::logMessage( 153 "Invalid value found for tag backupMap, in backup and restore config JSON."); 154 return; 155 } 156 157 uint16_t l_errCode = 0; 158 159 const std::string l_srcFruPath = 160 jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_srcPath, l_errCode); 161 162 if (l_errCode) 163 { 164 logging::logMessage( 165 "Failed to get source FRU path for [" + i_srcPath + 166 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 167 return; 168 } 169 170 const std::string l_dstFruPath = 171 jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_dstPath, l_errCode); 172 173 if (l_errCode) 174 { 175 logging::logMessage( 176 "Failed to get destination FRU path for [" + i_dstPath + 177 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 178 return; 179 } 180 181 if (l_srcFruPath.empty() || l_dstFruPath.empty()) 182 { 183 logging::logMessage( 184 "Couldn't find either source or destination FRU path."); 185 return; 186 } 187 188 const std::string l_srcInvPath = jsonUtility::getInventoryObjPathFromJson( 189 m_sysCfgJsonObj, i_srcPath, l_errCode); 190 191 if (l_srcInvPath.empty()) 192 { 193 if (l_errCode) 194 { 195 logging::logMessage( 196 "Couldn't find source inventory path. Error : " + 197 commonUtility::getErrCodeMsg(l_errCode)); 198 return; 199 } 200 201 logging::logMessage("Couldn't find source inventory path."); 202 return; 203 } 204 205 const std::string l_dstInvPath = jsonUtility::getInventoryObjPathFromJson( 206 m_sysCfgJsonObj, i_dstPath, l_errCode); 207 208 if (l_dstInvPath.empty()) 209 { 210 if (l_errCode) 211 { 212 logging::logMessage( 213 "Couldn't find destination inventory path. Error : " + 214 commonUtility::getErrCodeMsg(l_errCode)); 215 return; 216 } 217 218 logging::logMessage("Couldn't find destination inventory path."); 219 return; 220 } 221 222 const std::string l_srcServiceName = 223 jsonUtility::getServiceName(m_sysCfgJsonObj, l_srcInvPath, l_errCode); 224 225 if (l_errCode) 226 { 227 logging::logMessage( 228 "Failed to get service name for source FRU [" + l_srcInvPath + 229 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 230 return; 231 } 232 233 const std::string l_dstServiceName = 234 jsonUtility::getServiceName(m_sysCfgJsonObj, l_dstInvPath, l_errCode); 235 236 if (l_errCode) 237 { 238 logging::logMessage( 239 "Failed to get service name for destination FRU [" + l_dstInvPath + 240 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 241 return; 242 } 243 244 for (const auto& l_aRecordKwInfo : 245 m_backupAndRestoreCfgJsonObj["backupMap"]) 246 { 247 const std::string& l_srcRecordName = 248 l_aRecordKwInfo.value("sourceRecord", ""); 249 const std::string& l_srcKeywordName = 250 l_aRecordKwInfo.value("sourceKeyword", ""); 251 const std::string& l_dstRecordName = 252 l_aRecordKwInfo.value("destinationRecord", ""); 253 const std::string& l_dstKeywordName = 254 l_aRecordKwInfo.value("destinationKeyword", ""); 255 256 if (l_srcRecordName.empty() || l_dstRecordName.empty() || 257 l_srcKeywordName.empty() || l_dstKeywordName.empty()) 258 { 259 logging::logMessage( 260 "Record or keyword not found in the backup and restore config JSON."); 261 continue; 262 } 263 264 if (!io_srcVpdMap.empty() && 265 io_srcVpdMap.find(l_srcRecordName) == io_srcVpdMap.end()) 266 { 267 logging::logMessage( 268 "Record: " + l_srcRecordName + 269 ", is not found in the source path: " + i_srcPath); 270 continue; 271 } 272 273 if (!io_dstVpdMap.empty() && 274 io_dstVpdMap.find(l_dstRecordName) == io_dstVpdMap.end()) 275 { 276 logging::logMessage( 277 "Record: " + l_dstRecordName + 278 ", is not found in the destination path: " + i_dstPath); 279 continue; 280 } 281 282 types::BinaryVector l_defaultBinaryValue; 283 if (l_aRecordKwInfo.contains("defaultValue") && 284 l_aRecordKwInfo["defaultValue"].is_array()) 285 { 286 l_defaultBinaryValue = 287 l_aRecordKwInfo["defaultValue"].get<types::BinaryVector>(); 288 } 289 else 290 { 291 logging::logMessage( 292 "Couldn't read default value for record name: " + 293 l_srcRecordName + ", keyword name: " + l_srcKeywordName + 294 " from backup and restore config JSON file."); 295 continue; 296 } 297 298 bool l_isPelRequired = l_aRecordKwInfo.value("isPelRequired", false); 299 300 types::BinaryVector l_srcBinaryValue; 301 std::string l_srcStrValue; 302 if (!io_srcVpdMap.empty()) 303 { 304 l_srcStrValue = vpdSpecificUtility::getKwVal( 305 io_srcVpdMap.at(l_srcRecordName), l_srcKeywordName); 306 307 if (l_srcStrValue.empty()) 308 { 309 std::runtime_error( 310 std::string("Failed to get value for keyword [") + 311 l_srcKeywordName + std::string("]")); 312 } 313 314 l_srcBinaryValue = 315 types::BinaryVector(l_srcStrValue.begin(), l_srcStrValue.end()); 316 } 317 else 318 { 319 // Read keyword value from DBus 320 const auto l_value = dbusUtility::readDbusProperty( 321 l_srcServiceName, l_srcInvPath, 322 constants::ipzVpdInf + l_srcRecordName, l_srcKeywordName); 323 if (const auto l_binaryValue = 324 std::get_if<types::BinaryVector>(&l_value)) 325 { 326 l_srcBinaryValue = *l_binaryValue; 327 l_srcStrValue = std::string(l_srcBinaryValue.begin(), 328 l_srcBinaryValue.end()); 329 } 330 } 331 332 types::BinaryVector l_dstBinaryValue; 333 std::string l_dstStrValue; 334 if (!io_dstVpdMap.empty()) 335 { 336 l_dstStrValue = vpdSpecificUtility::getKwVal( 337 io_dstVpdMap.at(l_dstRecordName), l_dstKeywordName); 338 339 if (l_dstStrValue.empty()) 340 { 341 std::runtime_error( 342 std::string("Failed to get value for keyword [") + 343 l_dstKeywordName + std::string("]")); 344 } 345 346 l_dstBinaryValue = 347 types::BinaryVector(l_dstStrValue.begin(), l_dstStrValue.end()); 348 } 349 else 350 { 351 // Read keyword value from DBus 352 const auto l_value = dbusUtility::readDbusProperty( 353 l_dstServiceName, l_dstInvPath, 354 constants::ipzVpdInf + l_dstRecordName, l_dstKeywordName); 355 if (const auto l_binaryValue = 356 std::get_if<types::BinaryVector>(&l_value)) 357 { 358 l_dstBinaryValue = *l_binaryValue; 359 l_dstStrValue = std::string(l_dstBinaryValue.begin(), 360 l_dstBinaryValue.end()); 361 } 362 } 363 364 if (l_srcBinaryValue != l_dstBinaryValue) 365 { 366 // ToDo: Handle if there is no valid default value in the backup and 367 // restore config JSON. 368 if (l_dstBinaryValue == l_defaultBinaryValue) 369 { 370 // Update keyword's value on hardware 371 auto l_vpdParser = 372 std::make_shared<Parser>(l_dstFruPath, m_sysCfgJsonObj); 373 374 auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword( 375 types::IpzData(l_dstRecordName, l_dstKeywordName, 376 l_srcBinaryValue)); 377 378 /* To keep the data in sync between hardware and parsed map 379 updating the io_dstVpdMap. This should only be done if write 380 on hardware returns success.*/ 381 if (!io_dstVpdMap.empty() && l_bytesUpdatedOnHardware > 0) 382 { 383 io_dstVpdMap[l_dstRecordName][l_dstKeywordName] = 384 l_srcStrValue; 385 } 386 continue; 387 } 388 389 if (l_srcBinaryValue == l_defaultBinaryValue) 390 { 391 // Update keyword's value on hardware 392 auto l_vpdParser = 393 std::make_shared<Parser>(l_srcFruPath, m_sysCfgJsonObj); 394 395 auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword( 396 types::IpzData(l_srcRecordName, l_srcKeywordName, 397 l_dstBinaryValue)); 398 399 /* To keep the data in sync between hardware and parsed map 400 updating the io_srcVpdMap. This should only be done if write 401 on hardware returns success.*/ 402 if (!io_srcVpdMap.empty() && l_bytesUpdatedOnHardware > 0) 403 { 404 io_srcVpdMap[l_srcRecordName][l_srcKeywordName] = 405 l_dstStrValue; 406 } 407 } 408 else 409 { 410 /** 411 * Update io_srcVpdMap to publish the same data on DBus, which 412 * is already present on the DBus. Because after calling 413 * backupAndRestore API the map value will get published to DBus 414 * in the worker flow. 415 */ 416 if (!io_srcVpdMap.empty() && io_dstVpdMap.empty()) 417 { 418 io_srcVpdMap[l_srcRecordName][l_srcKeywordName] = 419 l_dstStrValue; 420 } 421 422 std::string l_errorMsg( 423 "Mismatch found between source and destination VPD for record : " + 424 l_srcRecordName + " and keyword : " + l_srcKeywordName + 425 " . Value read from source : " + 426 commonUtility::convertByteVectorToHex(l_srcBinaryValue) + 427 " . Value read from destination : " + 428 commonUtility::convertByteVectorToHex(l_dstBinaryValue)); 429 430 EventLogger::createSyncPel( 431 types::ErrorType::VpdMismatch, types::SeverityType::Warning, 432 __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt, 433 std::nullopt, std::nullopt, std::nullopt); 434 } 435 } 436 else if (l_srcBinaryValue == l_defaultBinaryValue && 437 l_dstBinaryValue == l_defaultBinaryValue && l_isPelRequired) 438 { 439 std::string l_errorMsg( 440 "Default value found on both source and destination VPD, for record: " + 441 l_srcRecordName + " and keyword: " + l_srcKeywordName); 442 443 EventLogger::createSyncPel( 444 types::ErrorType::DefaultValue, types::SeverityType::Error, 445 __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt, 446 std::nullopt, std::nullopt, std::nullopt); 447 } 448 } 449 } 450 451 void BackupAndRestore::setBackupAndRestoreStatus( 452 const BackupAndRestoreStatus& i_status) 453 { 454 m_backupAndRestoreStatus = i_status; 455 } 456 457 int BackupAndRestore::updateKeywordOnPrimaryOrBackupPath( 458 const std::string& i_fruPath, 459 const types::WriteVpdParams& i_paramsToWriteData) const noexcept 460 { 461 if (i_fruPath.empty()) 462 { 463 logging::logMessage("Given FRU path is empty."); 464 return constants::FAILURE; 465 } 466 467 bool l_inputPathIsSourcePath = false; 468 bool l_inputPathIsDestinationPath = false; 469 470 if (m_backupAndRestoreCfgJsonObj.contains("source") && 471 m_backupAndRestoreCfgJsonObj["source"].value("hardwarePath", "") == 472 i_fruPath && 473 m_backupAndRestoreCfgJsonObj.contains("destination") && 474 !m_backupAndRestoreCfgJsonObj["destination"] 475 .value("hardwarePath", "") 476 .empty()) 477 { 478 l_inputPathIsSourcePath = true; 479 } 480 else if (m_backupAndRestoreCfgJsonObj.contains("destination") && 481 m_backupAndRestoreCfgJsonObj["destination"].value( 482 "hardwarePath", "") == i_fruPath && 483 m_backupAndRestoreCfgJsonObj.contains("source") && 484 !m_backupAndRestoreCfgJsonObj["source"] 485 .value("hardwarePath", "") 486 .empty()) 487 { 488 l_inputPathIsDestinationPath = true; 489 } 490 else 491 { 492 // Input path is neither source or destination path of the 493 // backup&restore JSON or source and destination paths are not hardware 494 // paths in the config JSON. 495 return constants::SUCCESS; 496 } 497 498 if (m_backupAndRestoreCfgJsonObj["backupMap"].is_array()) 499 { 500 std::string l_inpRecordName; 501 std::string l_inpKeywordName; 502 types::BinaryVector l_inpKeywordValue; 503 504 if (const types::IpzData* l_ipzData = 505 std::get_if<types::IpzData>(&i_paramsToWriteData)) 506 { 507 l_inpRecordName = std::get<0>(*l_ipzData); 508 l_inpKeywordName = std::get<1>(*l_ipzData); 509 l_inpKeywordValue = std::get<2>(*l_ipzData); 510 511 if (l_inpRecordName.empty() || l_inpKeywordName.empty() || 512 l_inpKeywordValue.empty()) 513 { 514 logging::logMessage("Invalid input received"); 515 return constants::FAILURE; 516 } 517 } 518 else 519 { 520 // only IPZ type VPD is supported now. 521 return constants::SUCCESS; 522 } 523 524 for (const auto& l_aRecordKwInfo : 525 m_backupAndRestoreCfgJsonObj["backupMap"]) 526 { 527 if (l_aRecordKwInfo.value("sourceRecord", "").empty() || 528 l_aRecordKwInfo.value("sourceKeyword", "").empty() || 529 l_aRecordKwInfo.value("destinationRecord", "").empty() || 530 l_aRecordKwInfo.value("destinationKeyword", "").empty()) 531 { 532 // invalid backup map found 533 logging::logMessage( 534 "Invalid backup map found, one or more field(s) found empty or not present in the config JSON: sourceRecord: " + 535 l_aRecordKwInfo.value("sourceRecord", "") + 536 ", sourceKeyword: " + 537 l_aRecordKwInfo.value("sourceKeyword", "") + 538 ", destinationRecord: " + 539 l_aRecordKwInfo.value("destinationRecord", "") + 540 ", destinationKeyword: " + 541 l_aRecordKwInfo.value("destinationKeyword", "")); 542 continue; 543 } 544 545 if (l_inputPathIsSourcePath && 546 (l_aRecordKwInfo["sourceRecord"] == l_inpRecordName) && 547 (l_aRecordKwInfo["sourceKeyword"] == l_inpKeywordName)) 548 { 549 std::string l_fruPath( 550 m_backupAndRestoreCfgJsonObj["destination"] 551 ["hardwarePath"]); 552 Parser l_parserObj(l_fruPath, m_sysCfgJsonObj); 553 554 return l_parserObj.updateVpdKeyword(std::make_tuple( 555 l_aRecordKwInfo["destinationRecord"], 556 l_aRecordKwInfo["destinationKeyword"], l_inpKeywordValue)); 557 } 558 else if (l_inputPathIsDestinationPath && 559 (l_aRecordKwInfo["destinationRecord"] == 560 l_inpRecordName) && 561 (l_aRecordKwInfo["destinationKeyword"] == 562 l_inpKeywordName)) 563 { 564 std::string l_fruPath( 565 m_backupAndRestoreCfgJsonObj["source"]["hardwarePath"]); 566 Parser l_parserObj(l_fruPath, m_sysCfgJsonObj); 567 568 return l_parserObj.updateVpdKeyword(std::make_tuple( 569 l_aRecordKwInfo["sourceRecord"], 570 l_aRecordKwInfo["sourceKeyword"], l_inpKeywordValue)); 571 } 572 } 573 } 574 575 // Received property is not part of backup & restore JSON. 576 return constants::SUCCESS; 577 } 578 579 } // namespace vpd 580