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, l_errCode); 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("], error : ") + 312 commonUtility::getErrCodeMsg(l_errCode)); 313 } 314 315 l_srcBinaryValue = 316 types::BinaryVector(l_srcStrValue.begin(), l_srcStrValue.end()); 317 } 318 else 319 { 320 // Read keyword value from DBus 321 const auto l_value = dbusUtility::readDbusProperty( 322 l_srcServiceName, l_srcInvPath, 323 constants::ipzVpdInf + l_srcRecordName, l_srcKeywordName); 324 if (const auto l_binaryValue = 325 std::get_if<types::BinaryVector>(&l_value)) 326 { 327 l_srcBinaryValue = *l_binaryValue; 328 l_srcStrValue = std::string(l_srcBinaryValue.begin(), 329 l_srcBinaryValue.end()); 330 } 331 } 332 333 types::BinaryVector l_dstBinaryValue; 334 std::string l_dstStrValue; 335 if (!io_dstVpdMap.empty()) 336 { 337 l_dstStrValue = vpdSpecificUtility::getKwVal( 338 io_dstVpdMap.at(l_dstRecordName), l_dstKeywordName, l_errCode); 339 340 if (l_dstStrValue.empty()) 341 { 342 std::runtime_error( 343 std::string("Failed to get value for keyword [") + 344 l_dstKeywordName + std::string("], error : ") + 345 commonUtility::getErrCodeMsg(l_errCode)); 346 } 347 348 l_dstBinaryValue = 349 types::BinaryVector(l_dstStrValue.begin(), l_dstStrValue.end()); 350 } 351 else 352 { 353 // Read keyword value from DBus 354 const auto l_value = dbusUtility::readDbusProperty( 355 l_dstServiceName, l_dstInvPath, 356 constants::ipzVpdInf + l_dstRecordName, l_dstKeywordName); 357 if (const auto l_binaryValue = 358 std::get_if<types::BinaryVector>(&l_value)) 359 { 360 l_dstBinaryValue = *l_binaryValue; 361 l_dstStrValue = std::string(l_dstBinaryValue.begin(), 362 l_dstBinaryValue.end()); 363 } 364 } 365 366 if (l_srcBinaryValue != l_dstBinaryValue) 367 { 368 // ToDo: Handle if there is no valid default value in the backup and 369 // restore config JSON. 370 if (l_dstBinaryValue == l_defaultBinaryValue) 371 { 372 // Update keyword's value on hardware 373 auto l_vpdParser = 374 std::make_shared<Parser>(l_dstFruPath, m_sysCfgJsonObj); 375 376 auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword( 377 types::IpzData(l_dstRecordName, l_dstKeywordName, 378 l_srcBinaryValue)); 379 380 /* To keep the data in sync between hardware and parsed map 381 updating the io_dstVpdMap. This should only be done if write 382 on hardware returns success.*/ 383 if (!io_dstVpdMap.empty() && l_bytesUpdatedOnHardware > 0) 384 { 385 io_dstVpdMap[l_dstRecordName][l_dstKeywordName] = 386 l_srcStrValue; 387 } 388 continue; 389 } 390 391 if (l_srcBinaryValue == l_defaultBinaryValue) 392 { 393 // Update keyword's value on hardware 394 auto l_vpdParser = 395 std::make_shared<Parser>(l_srcFruPath, m_sysCfgJsonObj); 396 397 auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword( 398 types::IpzData(l_srcRecordName, l_srcKeywordName, 399 l_dstBinaryValue)); 400 401 /* To keep the data in sync between hardware and parsed map 402 updating the io_srcVpdMap. This should only be done if write 403 on hardware returns success.*/ 404 if (!io_srcVpdMap.empty() && l_bytesUpdatedOnHardware > 0) 405 { 406 io_srcVpdMap[l_srcRecordName][l_srcKeywordName] = 407 l_dstStrValue; 408 } 409 } 410 else 411 { 412 /** 413 * Update io_srcVpdMap to publish the same data on DBus, which 414 * is already present on the DBus. Because after calling 415 * backupAndRestore API the map value will get published to DBus 416 * in the worker flow. 417 */ 418 if (!io_srcVpdMap.empty() && io_dstVpdMap.empty()) 419 { 420 io_srcVpdMap[l_srcRecordName][l_srcKeywordName] = 421 l_dstStrValue; 422 } 423 424 std::string l_errorMsg( 425 "Mismatch found between source and destination VPD for record : " + 426 l_srcRecordName + " and keyword : " + l_srcKeywordName + 427 " . Value read from source : " + 428 commonUtility::convertByteVectorToHex(l_srcBinaryValue) + 429 " . Value read from destination : " + 430 commonUtility::convertByteVectorToHex(l_dstBinaryValue)); 431 432 EventLogger::createSyncPel( 433 types::ErrorType::VpdMismatch, types::SeverityType::Warning, 434 __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt, 435 std::nullopt, std::nullopt, std::nullopt); 436 } 437 } 438 else if (l_srcBinaryValue == l_defaultBinaryValue && 439 l_dstBinaryValue == l_defaultBinaryValue && l_isPelRequired) 440 { 441 std::string l_errorMsg( 442 "Default value found on both source and destination VPD, for record: " + 443 l_srcRecordName + " and keyword: " + l_srcKeywordName); 444 445 EventLogger::createSyncPel( 446 types::ErrorType::DefaultValue, types::SeverityType::Error, 447 __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt, 448 std::nullopt, std::nullopt, std::nullopt); 449 } 450 } 451 } 452 453 void BackupAndRestore::setBackupAndRestoreStatus( 454 const BackupAndRestoreStatus& i_status) 455 { 456 m_backupAndRestoreStatus = i_status; 457 } 458 459 int BackupAndRestore::updateKeywordOnPrimaryOrBackupPath( 460 const std::string& i_fruPath, 461 const types::WriteVpdParams& i_paramsToWriteData) const noexcept 462 { 463 if (i_fruPath.empty()) 464 { 465 logging::logMessage("Given FRU path is empty."); 466 return constants::FAILURE; 467 } 468 469 bool l_inputPathIsSourcePath = false; 470 bool l_inputPathIsDestinationPath = false; 471 472 if (m_backupAndRestoreCfgJsonObj.contains("source") && 473 m_backupAndRestoreCfgJsonObj["source"].value("hardwarePath", "") == 474 i_fruPath && 475 m_backupAndRestoreCfgJsonObj.contains("destination") && 476 !m_backupAndRestoreCfgJsonObj["destination"] 477 .value("hardwarePath", "") 478 .empty()) 479 { 480 l_inputPathIsSourcePath = true; 481 } 482 else if (m_backupAndRestoreCfgJsonObj.contains("destination") && 483 m_backupAndRestoreCfgJsonObj["destination"].value( 484 "hardwarePath", "") == i_fruPath && 485 m_backupAndRestoreCfgJsonObj.contains("source") && 486 !m_backupAndRestoreCfgJsonObj["source"] 487 .value("hardwarePath", "") 488 .empty()) 489 { 490 l_inputPathIsDestinationPath = true; 491 } 492 else 493 { 494 // Input path is neither source or destination path of the 495 // backup&restore JSON or source and destination paths are not hardware 496 // paths in the config JSON. 497 return constants::SUCCESS; 498 } 499 500 if (m_backupAndRestoreCfgJsonObj["backupMap"].is_array()) 501 { 502 std::string l_inpRecordName; 503 std::string l_inpKeywordName; 504 types::BinaryVector l_inpKeywordValue; 505 506 if (const types::IpzData* l_ipzData = 507 std::get_if<types::IpzData>(&i_paramsToWriteData)) 508 { 509 l_inpRecordName = std::get<0>(*l_ipzData); 510 l_inpKeywordName = std::get<1>(*l_ipzData); 511 l_inpKeywordValue = std::get<2>(*l_ipzData); 512 513 if (l_inpRecordName.empty() || l_inpKeywordName.empty() || 514 l_inpKeywordValue.empty()) 515 { 516 logging::logMessage("Invalid input received"); 517 return constants::FAILURE; 518 } 519 } 520 else 521 { 522 // only IPZ type VPD is supported now. 523 return constants::SUCCESS; 524 } 525 526 for (const auto& l_aRecordKwInfo : 527 m_backupAndRestoreCfgJsonObj["backupMap"]) 528 { 529 if (l_aRecordKwInfo.value("sourceRecord", "").empty() || 530 l_aRecordKwInfo.value("sourceKeyword", "").empty() || 531 l_aRecordKwInfo.value("destinationRecord", "").empty() || 532 l_aRecordKwInfo.value("destinationKeyword", "").empty()) 533 { 534 // invalid backup map found 535 logging::logMessage( 536 "Invalid backup map found, one or more field(s) found empty or not present in the config JSON: sourceRecord: " + 537 l_aRecordKwInfo.value("sourceRecord", "") + 538 ", sourceKeyword: " + 539 l_aRecordKwInfo.value("sourceKeyword", "") + 540 ", destinationRecord: " + 541 l_aRecordKwInfo.value("destinationRecord", "") + 542 ", destinationKeyword: " + 543 l_aRecordKwInfo.value("destinationKeyword", "")); 544 continue; 545 } 546 547 if (l_inputPathIsSourcePath && 548 (l_aRecordKwInfo["sourceRecord"] == l_inpRecordName) && 549 (l_aRecordKwInfo["sourceKeyword"] == l_inpKeywordName)) 550 { 551 std::string l_fruPath( 552 m_backupAndRestoreCfgJsonObj["destination"] 553 ["hardwarePath"]); 554 Parser l_parserObj(l_fruPath, m_sysCfgJsonObj); 555 556 return l_parserObj.updateVpdKeyword(std::make_tuple( 557 l_aRecordKwInfo["destinationRecord"], 558 l_aRecordKwInfo["destinationKeyword"], l_inpKeywordValue)); 559 } 560 else if (l_inputPathIsDestinationPath && 561 (l_aRecordKwInfo["destinationRecord"] == 562 l_inpRecordName) && 563 (l_aRecordKwInfo["destinationKeyword"] == 564 l_inpKeywordName)) 565 { 566 std::string l_fruPath( 567 m_backupAndRestoreCfgJsonObj["source"]["hardwarePath"]); 568 Parser l_parserObj(l_fruPath, m_sysCfgJsonObj); 569 570 return l_parserObj.updateVpdKeyword(std::make_tuple( 571 l_aRecordKwInfo["sourceRecord"], 572 l_aRecordKwInfo["sourceKeyword"], l_inpKeywordValue)); 573 } 574 } 575 } 576 577 // Received property is not part of backup & restore JSON. 578 return constants::SUCCESS; 579 } 580 581 } // namespace vpd 582