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 : " + vpdSpecificUtility::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 const std::string l_srcFruPath = 158 jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_srcPath); 159 const std::string l_dstFruPath = 160 jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_dstPath); 161 if (l_srcFruPath.empty() || l_dstFruPath.empty()) 162 { 163 logging::logMessage( 164 "Couldn't find either source or destination FRU path."); 165 return; 166 } 167 168 uint16_t l_errCode = 0; 169 const std::string l_srcInvPath = jsonUtility::getInventoryObjPathFromJson( 170 m_sysCfgJsonObj, i_srcPath, l_errCode); 171 172 if (l_srcInvPath.empty()) 173 { 174 if (l_errCode) 175 { 176 logging::logMessage( 177 "Couldn't find source inventory path. Error : " + 178 vpdSpecificUtility::getErrCodeMsg(l_errCode)); 179 return; 180 } 181 182 logging::logMessage("Couldn't find source inventory path."); 183 return; 184 } 185 186 const std::string l_dstInvPath = jsonUtility::getInventoryObjPathFromJson( 187 m_sysCfgJsonObj, i_dstPath, l_errCode); 188 189 if (l_dstInvPath.empty()) 190 { 191 if (l_errCode) 192 { 193 logging::logMessage( 194 "Couldn't find destination inventory path. Error : " + 195 vpdSpecificUtility::getErrCodeMsg(l_errCode)); 196 return; 197 } 198 199 logging::logMessage("Couldn't find destination inventory path."); 200 return; 201 } 202 203 const std::string l_srcServiceName = 204 jsonUtility::getServiceName(m_sysCfgJsonObj, l_srcInvPath); 205 const std::string l_dstServiceName = 206 jsonUtility::getServiceName(m_sysCfgJsonObj, l_dstInvPath); 207 if (l_srcServiceName.empty() || l_dstServiceName.empty()) 208 { 209 logging::logMessage( 210 "Couldn't find either source or destination DBus service name."); 211 return; 212 } 213 214 for (const auto& l_aRecordKwInfo : 215 m_backupAndRestoreCfgJsonObj["backupMap"]) 216 { 217 const std::string& l_srcRecordName = 218 l_aRecordKwInfo.value("sourceRecord", ""); 219 const std::string& l_srcKeywordName = 220 l_aRecordKwInfo.value("sourceKeyword", ""); 221 const std::string& l_dstRecordName = 222 l_aRecordKwInfo.value("destinationRecord", ""); 223 const std::string& l_dstKeywordName = 224 l_aRecordKwInfo.value("destinationKeyword", ""); 225 226 if (l_srcRecordName.empty() || l_dstRecordName.empty() || 227 l_srcKeywordName.empty() || l_dstKeywordName.empty()) 228 { 229 logging::logMessage( 230 "Record or keyword not found in the backup and restore config JSON."); 231 continue; 232 } 233 234 if (!io_srcVpdMap.empty() && 235 io_srcVpdMap.find(l_srcRecordName) == io_srcVpdMap.end()) 236 { 237 logging::logMessage( 238 "Record: " + l_srcRecordName + 239 ", is not found in the source path: " + i_srcPath); 240 continue; 241 } 242 243 if (!io_dstVpdMap.empty() && 244 io_dstVpdMap.find(l_dstRecordName) == io_dstVpdMap.end()) 245 { 246 logging::logMessage( 247 "Record: " + l_dstRecordName + 248 ", is not found in the destination path: " + i_dstPath); 249 continue; 250 } 251 252 types::BinaryVector l_defaultBinaryValue; 253 if (l_aRecordKwInfo.contains("defaultValue") && 254 l_aRecordKwInfo["defaultValue"].is_array()) 255 { 256 l_defaultBinaryValue = 257 l_aRecordKwInfo["defaultValue"].get<types::BinaryVector>(); 258 } 259 else 260 { 261 logging::logMessage( 262 "Couldn't read default value for record name: " + 263 l_srcRecordName + ", keyword name: " + l_srcKeywordName + 264 " from backup and restore config JSON file."); 265 continue; 266 } 267 268 bool l_isPelRequired = l_aRecordKwInfo.value("isPelRequired", false); 269 270 types::BinaryVector l_srcBinaryValue; 271 std::string l_srcStrValue; 272 if (!io_srcVpdMap.empty()) 273 { 274 l_srcStrValue = vpdSpecificUtility::getKwVal( 275 io_srcVpdMap.at(l_srcRecordName), l_srcKeywordName); 276 277 if (l_srcStrValue.empty()) 278 { 279 std::runtime_error( 280 std::string("Failed to get value for keyword [") + 281 l_srcKeywordName + std::string("]")); 282 } 283 284 l_srcBinaryValue = 285 types::BinaryVector(l_srcStrValue.begin(), l_srcStrValue.end()); 286 } 287 else 288 { 289 // Read keyword value from DBus 290 const auto l_value = dbusUtility::readDbusProperty( 291 l_srcServiceName, l_srcInvPath, 292 constants::ipzVpdInf + l_srcRecordName, l_srcKeywordName); 293 if (const auto l_binaryValue = 294 std::get_if<types::BinaryVector>(&l_value)) 295 { 296 l_srcBinaryValue = *l_binaryValue; 297 l_srcStrValue = std::string(l_srcBinaryValue.begin(), 298 l_srcBinaryValue.end()); 299 } 300 } 301 302 types::BinaryVector l_dstBinaryValue; 303 std::string l_dstStrValue; 304 if (!io_dstVpdMap.empty()) 305 { 306 l_dstStrValue = vpdSpecificUtility::getKwVal( 307 io_dstVpdMap.at(l_dstRecordName), l_dstKeywordName); 308 309 if (l_dstStrValue.empty()) 310 { 311 std::runtime_error( 312 std::string("Failed to get value for keyword [") + 313 l_dstKeywordName + std::string("]")); 314 } 315 316 l_dstBinaryValue = 317 types::BinaryVector(l_dstStrValue.begin(), l_dstStrValue.end()); 318 } 319 else 320 { 321 // Read keyword value from DBus 322 const auto l_value = dbusUtility::readDbusProperty( 323 l_dstServiceName, l_dstInvPath, 324 constants::ipzVpdInf + l_dstRecordName, l_dstKeywordName); 325 if (const auto l_binaryValue = 326 std::get_if<types::BinaryVector>(&l_value)) 327 { 328 l_dstBinaryValue = *l_binaryValue; 329 l_dstStrValue = std::string(l_dstBinaryValue.begin(), 330 l_dstBinaryValue.end()); 331 } 332 } 333 334 if (l_srcBinaryValue != l_dstBinaryValue) 335 { 336 // ToDo: Handle if there is no valid default value in the backup and 337 // restore config JSON. 338 if (l_dstBinaryValue == l_defaultBinaryValue) 339 { 340 // Update keyword's value on hardware 341 auto l_vpdParser = 342 std::make_shared<Parser>(l_dstFruPath, m_sysCfgJsonObj); 343 344 auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword( 345 types::IpzData(l_dstRecordName, l_dstKeywordName, 346 l_srcBinaryValue)); 347 348 /* To keep the data in sync between hardware and parsed map 349 updating the io_dstVpdMap. This should only be done if write 350 on hardware returns success.*/ 351 if (!io_dstVpdMap.empty() && l_bytesUpdatedOnHardware > 0) 352 { 353 io_dstVpdMap[l_dstRecordName][l_dstKeywordName] = 354 l_srcStrValue; 355 } 356 continue; 357 } 358 359 if (l_srcBinaryValue == l_defaultBinaryValue) 360 { 361 // Update keyword's value on hardware 362 auto l_vpdParser = 363 std::make_shared<Parser>(l_srcFruPath, m_sysCfgJsonObj); 364 365 auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword( 366 types::IpzData(l_srcRecordName, l_srcKeywordName, 367 l_dstBinaryValue)); 368 369 /* To keep the data in sync between hardware and parsed map 370 updating the io_srcVpdMap. This should only be done if write 371 on hardware returns success.*/ 372 if (!io_srcVpdMap.empty() && l_bytesUpdatedOnHardware > 0) 373 { 374 io_srcVpdMap[l_srcRecordName][l_srcKeywordName] = 375 l_dstStrValue; 376 } 377 } 378 else 379 { 380 /** 381 * Update io_srcVpdMap to publish the same data on DBus, which 382 * is already present on the DBus. Because after calling 383 * backupAndRestore API the map value will get published to DBus 384 * in the worker flow. 385 */ 386 if (!io_srcVpdMap.empty() && io_dstVpdMap.empty()) 387 { 388 io_srcVpdMap[l_srcRecordName][l_srcKeywordName] = 389 l_dstStrValue; 390 } 391 392 std::string l_errorMsg( 393 "Mismatch found between source and destination VPD for record : " + 394 l_srcRecordName + " and keyword : " + l_srcKeywordName + 395 " . Value read from source : " + 396 commonUtility::convertByteVectorToHex(l_srcBinaryValue) + 397 " . Value read from destination : " + 398 commonUtility::convertByteVectorToHex(l_dstBinaryValue)); 399 400 EventLogger::createSyncPel( 401 types::ErrorType::VpdMismatch, types::SeverityType::Warning, 402 __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt, 403 std::nullopt, std::nullopt, std::nullopt); 404 } 405 } 406 else if (l_srcBinaryValue == l_defaultBinaryValue && 407 l_dstBinaryValue == l_defaultBinaryValue && l_isPelRequired) 408 { 409 std::string l_errorMsg( 410 "Default value found on both source and destination VPD, for record: " + 411 l_srcRecordName + " and keyword: " + l_srcKeywordName); 412 413 EventLogger::createSyncPel( 414 types::ErrorType::DefaultValue, types::SeverityType::Error, 415 __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt, 416 std::nullopt, std::nullopt, std::nullopt); 417 } 418 } 419 } 420 421 void BackupAndRestore::setBackupAndRestoreStatus( 422 const BackupAndRestoreStatus& i_status) 423 { 424 m_backupAndRestoreStatus = i_status; 425 } 426 427 int BackupAndRestore::updateKeywordOnPrimaryOrBackupPath( 428 const std::string& i_fruPath, 429 const types::WriteVpdParams& i_paramsToWriteData) const noexcept 430 { 431 if (i_fruPath.empty()) 432 { 433 logging::logMessage("Given FRU path is empty."); 434 return constants::FAILURE; 435 } 436 437 bool l_inputPathIsSourcePath = false; 438 bool l_inputPathIsDestinationPath = false; 439 440 if (m_backupAndRestoreCfgJsonObj.contains("source") && 441 m_backupAndRestoreCfgJsonObj["source"].value("hardwarePath", "") == 442 i_fruPath && 443 m_backupAndRestoreCfgJsonObj.contains("destination") && 444 !m_backupAndRestoreCfgJsonObj["destination"] 445 .value("hardwarePath", "") 446 .empty()) 447 { 448 l_inputPathIsSourcePath = true; 449 } 450 else if (m_backupAndRestoreCfgJsonObj.contains("destination") && 451 m_backupAndRestoreCfgJsonObj["destination"].value( 452 "hardwarePath", "") == i_fruPath && 453 m_backupAndRestoreCfgJsonObj.contains("source") && 454 !m_backupAndRestoreCfgJsonObj["source"] 455 .value("hardwarePath", "") 456 .empty()) 457 { 458 l_inputPathIsDestinationPath = true; 459 } 460 else 461 { 462 // Input path is neither source or destination path of the 463 // backup&restore JSON or source and destination paths are not hardware 464 // paths in the config JSON. 465 return constants::SUCCESS; 466 } 467 468 if (m_backupAndRestoreCfgJsonObj["backupMap"].is_array()) 469 { 470 std::string l_inpRecordName; 471 std::string l_inpKeywordName; 472 types::BinaryVector l_inpKeywordValue; 473 474 if (const types::IpzData* l_ipzData = 475 std::get_if<types::IpzData>(&i_paramsToWriteData)) 476 { 477 l_inpRecordName = std::get<0>(*l_ipzData); 478 l_inpKeywordName = std::get<1>(*l_ipzData); 479 l_inpKeywordValue = std::get<2>(*l_ipzData); 480 481 if (l_inpRecordName.empty() || l_inpKeywordName.empty() || 482 l_inpKeywordValue.empty()) 483 { 484 logging::logMessage("Invalid input received"); 485 return constants::FAILURE; 486 } 487 } 488 else 489 { 490 // only IPZ type VPD is supported now. 491 return constants::SUCCESS; 492 } 493 494 for (const auto& l_aRecordKwInfo : 495 m_backupAndRestoreCfgJsonObj["backupMap"]) 496 { 497 if (l_aRecordKwInfo.value("sourceRecord", "").empty() || 498 l_aRecordKwInfo.value("sourceKeyword", "").empty() || 499 l_aRecordKwInfo.value("destinationRecord", "").empty() || 500 l_aRecordKwInfo.value("destinationKeyword", "").empty()) 501 { 502 // invalid backup map found 503 logging::logMessage( 504 "Invalid backup map found, one or more field(s) found empty or not present in the config JSON: sourceRecord: " + 505 l_aRecordKwInfo.value("sourceRecord", "") + 506 ", sourceKeyword: " + 507 l_aRecordKwInfo.value("sourceKeyword", "") + 508 ", destinationRecord: " + 509 l_aRecordKwInfo.value("destinationRecord", "") + 510 ", destinationKeyword: " + 511 l_aRecordKwInfo.value("destinationKeyword", "")); 512 continue; 513 } 514 515 if (l_inputPathIsSourcePath && 516 (l_aRecordKwInfo["sourceRecord"] == l_inpRecordName) && 517 (l_aRecordKwInfo["sourceKeyword"] == l_inpKeywordName)) 518 { 519 std::string l_fruPath( 520 m_backupAndRestoreCfgJsonObj["destination"] 521 ["hardwarePath"]); 522 Parser l_parserObj(l_fruPath, m_sysCfgJsonObj); 523 524 return l_parserObj.updateVpdKeyword(std::make_tuple( 525 l_aRecordKwInfo["destinationRecord"], 526 l_aRecordKwInfo["destinationKeyword"], l_inpKeywordValue)); 527 } 528 else if (l_inputPathIsDestinationPath && 529 (l_aRecordKwInfo["destinationRecord"] == 530 l_inpRecordName) && 531 (l_aRecordKwInfo["destinationKeyword"] == 532 l_inpKeywordName)) 533 { 534 std::string l_fruPath( 535 m_backupAndRestoreCfgJsonObj["source"]["hardwarePath"]); 536 Parser l_parserObj(l_fruPath, m_sysCfgJsonObj); 537 538 return l_parserObj.updateVpdKeyword(std::make_tuple( 539 l_aRecordKwInfo["sourceRecord"], 540 l_aRecordKwInfo["sourceKeyword"], l_inpKeywordValue)); 541 } 542 } 543 } 544 545 // Received property is not part of backup & restore JSON. 546 return constants::SUCCESS; 547 } 548 549 } // namespace vpd 550