| #include "backup_restore.hpp" |
| |
| #include "constants.hpp" |
| #include "event_logger.hpp" |
| #include "exceptions.hpp" |
| #include "logger.hpp" |
| #include "parser.hpp" |
| #include "types.hpp" |
| |
| #include <utility/json_utility.hpp> |
| #include <utility/vpd_specific_utility.hpp> |
| |
| namespace vpd |
| { |
| BackupAndRestoreStatus BackupAndRestore::m_backupAndRestoreStatus = |
| BackupAndRestoreStatus::NotStarted; |
| |
| BackupAndRestore::BackupAndRestore(const nlohmann::json& i_sysCfgJsonObj) : |
| m_sysCfgJsonObj(i_sysCfgJsonObj) |
| { |
| std::string l_backupAndRestoreCfgFilePath = |
| i_sysCfgJsonObj.value("backupRestoreConfigPath", ""); |
| |
| m_backupAndRestoreCfgJsonObj = |
| jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath); |
| |
| if (m_backupAndRestoreCfgJsonObj.empty()) |
| { |
| throw JsonException("JSON parsing failed", |
| l_backupAndRestoreCfgFilePath); |
| } |
| } |
| |
| std::tuple<types::VPDMapVariant, types::VPDMapVariant> |
| BackupAndRestore::backupAndRestore() |
| { |
| auto l_emptyVariantPair = |
| std::make_tuple(std::monostate{}, std::monostate{}); |
| |
| if (m_backupAndRestoreStatus >= BackupAndRestoreStatus::Invoked) |
| { |
| logging::logMessage("Backup and restore invoked already."); |
| return l_emptyVariantPair; |
| } |
| |
| m_backupAndRestoreStatus = BackupAndRestoreStatus::Invoked; |
| try |
| { |
| if (m_backupAndRestoreCfgJsonObj.empty() || |
| !m_backupAndRestoreCfgJsonObj.contains("source") || |
| !m_backupAndRestoreCfgJsonObj.contains("destination") || |
| !m_backupAndRestoreCfgJsonObj.contains("type") || |
| !m_backupAndRestoreCfgJsonObj.contains("backupMap")) |
| { |
| logging::logMessage( |
| "Backup restore config JSON is missing necessary tag(s), can't initiate backup and restore."); |
| return l_emptyVariantPair; |
| } |
| |
| std::string l_srcVpdPath; |
| types::VPDMapVariant l_srcVpdVariant; |
| if (l_srcVpdPath = m_backupAndRestoreCfgJsonObj["source"].value( |
| "hardwarePath", ""); |
| !l_srcVpdPath.empty() && std::filesystem::exists(l_srcVpdPath)) |
| { |
| std::shared_ptr<Parser> l_vpdParser = |
| std::make_shared<Parser>(l_srcVpdPath, m_sysCfgJsonObj); |
| l_srcVpdVariant = l_vpdParser->parse(); |
| } |
| else if (l_srcVpdPath = m_backupAndRestoreCfgJsonObj["source"].value( |
| "inventoryPath", ""); |
| l_srcVpdPath.empty()) |
| { |
| logging::logMessage( |
| "Couldn't extract source path, can't initiate backup and restore."); |
| return l_emptyVariantPair; |
| } |
| |
| std::string l_dstVpdPath; |
| types::VPDMapVariant l_dstVpdVariant; |
| if (l_dstVpdPath = m_backupAndRestoreCfgJsonObj["destination"].value( |
| "hardwarePath", ""); |
| !l_dstVpdPath.empty() && std::filesystem::exists(l_dstVpdPath)) |
| { |
| std::shared_ptr<Parser> l_vpdParser = |
| std::make_shared<Parser>(l_dstVpdPath, m_sysCfgJsonObj); |
| l_dstVpdVariant = l_vpdParser->parse(); |
| } |
| else if (l_dstVpdPath = m_backupAndRestoreCfgJsonObj["destination"] |
| .value("inventoryPath", ""); |
| l_dstVpdPath.empty()) |
| { |
| logging::logMessage( |
| "Couldn't extract destination path, can't initiate backup and restore."); |
| return l_emptyVariantPair; |
| } |
| |
| // Implement backup and restore for IPZ type VPD |
| auto l_backupAndRestoreType = |
| m_backupAndRestoreCfgJsonObj.value("type", ""); |
| if (l_backupAndRestoreType.compare("IPZ") == constants::STR_CMP_SUCCESS) |
| { |
| types::IPZVpdMap l_srcVpdMap; |
| if (auto l_srcVpdPtr = |
| std::get_if<types::IPZVpdMap>(&l_srcVpdVariant)) |
| { |
| l_srcVpdMap = *l_srcVpdPtr; |
| } |
| else if (!std::holds_alternative<std::monostate>(l_srcVpdVariant)) |
| { |
| logging::logMessage("Source VPD is not of IPZ type."); |
| return l_emptyVariantPair; |
| } |
| |
| types::IPZVpdMap l_dstVpdMap; |
| if (auto l_dstVpdPtr = |
| std::get_if<types::IPZVpdMap>(&l_dstVpdVariant)) |
| { |
| l_dstVpdMap = *l_dstVpdPtr; |
| } |
| else if (!std::holds_alternative<std::monostate>(l_dstVpdVariant)) |
| { |
| logging::logMessage("Destination VPD is not of IPZ type."); |
| return l_emptyVariantPair; |
| } |
| |
| backupAndRestoreIpzVpd(l_srcVpdMap, l_dstVpdMap, l_srcVpdPath, |
| l_dstVpdPath); |
| m_backupAndRestoreStatus = BackupAndRestoreStatus::Completed; |
| |
| return std::make_tuple(l_srcVpdMap, l_dstVpdMap); |
| } |
| // Note: add implementation here to support any other VPD type. |
| } |
| catch (const std::exception& ex) |
| { |
| logging::logMessage("Back up and restore failed with exception: " + |
| std::string(ex.what())); |
| } |
| return l_emptyVariantPair; |
| } |
| |
| void BackupAndRestore::backupAndRestoreIpzVpd( |
| types::IPZVpdMap& io_srcVpdMap, types::IPZVpdMap& io_dstVpdMap, |
| const std::string& i_srcPath, const std::string& i_dstPath) |
| { |
| if (!m_backupAndRestoreCfgJsonObj["backupMap"].is_array()) |
| { |
| logging::logMessage( |
| "Invalid value found for tag backupMap, in backup and restore config JSON."); |
| return; |
| } |
| |
| const std::string l_srcFruPath = |
| jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_srcPath); |
| const std::string l_dstFruPath = |
| jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_dstPath); |
| if (l_srcFruPath.empty() || l_dstFruPath.empty()) |
| { |
| logging::logMessage( |
| "Couldn't find either source or destination FRU path."); |
| return; |
| } |
| |
| const std::string l_srcInvPath = |
| jsonUtility::getInventoryObjPathFromJson(m_sysCfgJsonObj, i_srcPath); |
| const std::string l_dstInvPath = |
| jsonUtility::getInventoryObjPathFromJson(m_sysCfgJsonObj, i_dstPath); |
| if (l_srcInvPath.empty() || l_dstInvPath.empty()) |
| { |
| logging::logMessage( |
| "Couldn't find either source or destination inventory path."); |
| return; |
| } |
| |
| const std::string l_srcServiceName = |
| jsonUtility::getServiceName(m_sysCfgJsonObj, l_srcInvPath); |
| const std::string l_dstServiceName = |
| jsonUtility::getServiceName(m_sysCfgJsonObj, l_dstInvPath); |
| if (l_srcServiceName.empty() || l_dstServiceName.empty()) |
| { |
| logging::logMessage( |
| "Couldn't find either source or destination DBus service name."); |
| return; |
| } |
| |
| for (const auto& l_aRecordKwInfo : |
| m_backupAndRestoreCfgJsonObj["backupMap"]) |
| { |
| const std::string& l_srcRecordName = |
| l_aRecordKwInfo.value("sourceRecord", ""); |
| const std::string& l_srcKeywordName = |
| l_aRecordKwInfo.value("sourceKeyword", ""); |
| const std::string& l_dstRecordName = |
| l_aRecordKwInfo.value("destinationRecord", ""); |
| const std::string& l_dstKeywordName = |
| l_aRecordKwInfo.value("destinationKeyword", ""); |
| |
| if (l_srcRecordName.empty() || l_dstRecordName.empty() || |
| l_srcKeywordName.empty() || l_dstKeywordName.empty()) |
| { |
| logging::logMessage( |
| "Record or keyword not found in the backup and restore config JSON."); |
| continue; |
| } |
| |
| if (!io_srcVpdMap.empty() && |
| io_srcVpdMap.find(l_srcRecordName) == io_srcVpdMap.end()) |
| { |
| logging::logMessage( |
| "Record: " + l_srcRecordName + |
| ", is not found in the source path: " + i_srcPath); |
| continue; |
| } |
| |
| if (!io_dstVpdMap.empty() && |
| io_dstVpdMap.find(l_dstRecordName) == io_dstVpdMap.end()) |
| { |
| logging::logMessage( |
| "Record: " + l_dstRecordName + |
| ", is not found in the destination path: " + i_dstPath); |
| continue; |
| } |
| |
| types::BinaryVector l_defaultBinaryValue; |
| if (l_aRecordKwInfo.contains("defaultValue") && |
| l_aRecordKwInfo["defaultValue"].is_array()) |
| { |
| l_defaultBinaryValue = |
| l_aRecordKwInfo["defaultValue"].get<types::BinaryVector>(); |
| } |
| else |
| { |
| logging::logMessage( |
| "Couldn't read default value for record name: " + |
| l_srcRecordName + ", keyword name: " + l_srcKeywordName + |
| " from backup and restore config JSON file."); |
| continue; |
| } |
| |
| bool l_isPelRequired = l_aRecordKwInfo.value("isPelRequired", false); |
| |
| types::BinaryVector l_srcBinaryValue; |
| std::string l_srcStrValue; |
| if (!io_srcVpdMap.empty()) |
| { |
| l_srcStrValue = vpdSpecificUtility::getKwVal( |
| io_srcVpdMap.at(l_srcRecordName), l_srcKeywordName); |
| |
| if (l_srcStrValue.empty()) |
| { |
| std::runtime_error( |
| std::string("Failed to get value for keyword [") + |
| l_srcKeywordName + std::string("]")); |
| } |
| |
| l_srcBinaryValue = |
| types::BinaryVector(l_srcStrValue.begin(), l_srcStrValue.end()); |
| } |
| else |
| { |
| // Read keyword value from DBus |
| const auto l_value = dbusUtility::readDbusProperty( |
| l_srcServiceName, l_srcInvPath, |
| constants::ipzVpdInf + l_srcRecordName, l_srcKeywordName); |
| if (const auto l_binaryValue = |
| std::get_if<types::BinaryVector>(&l_value)) |
| { |
| l_srcBinaryValue = *l_binaryValue; |
| l_srcStrValue = std::string(l_srcBinaryValue.begin(), |
| l_srcBinaryValue.end()); |
| } |
| } |
| |
| types::BinaryVector l_dstBinaryValue; |
| std::string l_dstStrValue; |
| if (!io_dstVpdMap.empty()) |
| { |
| l_dstStrValue = vpdSpecificUtility::getKwVal( |
| io_dstVpdMap.at(l_dstRecordName), l_dstKeywordName); |
| |
| if (l_dstStrValue.empty()) |
| { |
| std::runtime_error( |
| std::string("Failed to get value for keyword [") + |
| l_dstKeywordName + std::string("]")); |
| } |
| |
| l_dstBinaryValue = |
| types::BinaryVector(l_dstStrValue.begin(), l_dstStrValue.end()); |
| } |
| else |
| { |
| // Read keyword value from DBus |
| const auto l_value = dbusUtility::readDbusProperty( |
| l_dstServiceName, l_dstInvPath, |
| constants::ipzVpdInf + l_dstRecordName, l_dstKeywordName); |
| if (const auto l_binaryValue = |
| std::get_if<types::BinaryVector>(&l_value)) |
| { |
| l_dstBinaryValue = *l_binaryValue; |
| l_dstStrValue = std::string(l_dstBinaryValue.begin(), |
| l_dstBinaryValue.end()); |
| } |
| } |
| |
| if (l_srcBinaryValue != l_dstBinaryValue) |
| { |
| // ToDo: Handle if there is no valid default value in the backup and |
| // restore config JSON. |
| if (l_dstBinaryValue == l_defaultBinaryValue) |
| { |
| // Update keyword's value on hardware |
| auto l_vpdParser = |
| std::make_shared<Parser>(l_dstFruPath, m_sysCfgJsonObj); |
| |
| auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword( |
| types::IpzData(l_dstRecordName, l_dstKeywordName, |
| l_srcBinaryValue)); |
| |
| /* To keep the data in sync between hardware and parsed map |
| updating the io_dstVpdMap. This should only be done if write |
| on hardware returns success.*/ |
| if (!io_dstVpdMap.empty() && l_bytesUpdatedOnHardware > 0) |
| { |
| io_dstVpdMap[l_dstRecordName][l_dstKeywordName] = |
| l_srcStrValue; |
| } |
| continue; |
| } |
| |
| if (l_srcBinaryValue == l_defaultBinaryValue) |
| { |
| // Update keyword's value on hardware |
| auto l_vpdParser = |
| std::make_shared<Parser>(l_srcFruPath, m_sysCfgJsonObj); |
| |
| auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword( |
| types::IpzData(l_srcRecordName, l_srcKeywordName, |
| l_dstBinaryValue)); |
| |
| /* To keep the data in sync between hardware and parsed map |
| updating the io_srcVpdMap. This should only be done if write |
| on hardware returns success.*/ |
| if (!io_srcVpdMap.empty() && l_bytesUpdatedOnHardware > 0) |
| { |
| io_srcVpdMap[l_srcRecordName][l_srcKeywordName] = |
| l_dstStrValue; |
| } |
| } |
| else |
| { |
| /** |
| * Update io_srcVpdMap to publish the same data on DBus, which |
| * is already present on the DBus. Because after calling |
| * backupAndRestore API the map value will get published to DBus |
| * in the worker flow. |
| */ |
| if (!io_srcVpdMap.empty() && io_dstVpdMap.empty()) |
| { |
| io_srcVpdMap[l_srcRecordName][l_srcKeywordName] = |
| l_dstStrValue; |
| } |
| |
| std::string l_errorMsg( |
| "Mismatch found between source and destination VPD for record : " + |
| l_srcRecordName + " and keyword : " + l_srcKeywordName + |
| " . Value read from source : " + |
| commonUtility::convertByteVectorToHex(l_srcBinaryValue) + |
| " . Value read from destination : " + |
| commonUtility::convertByteVectorToHex(l_dstBinaryValue)); |
| |
| EventLogger::createSyncPel( |
| types::ErrorType::VpdMismatch, types::SeverityType::Warning, |
| __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt, |
| std::nullopt, std::nullopt, std::nullopt); |
| } |
| } |
| else if (l_srcBinaryValue == l_defaultBinaryValue && |
| l_dstBinaryValue == l_defaultBinaryValue && l_isPelRequired) |
| { |
| std::string l_errorMsg( |
| "Default value found on both source and destination VPD, for record: " + |
| l_srcRecordName + " and keyword: " + l_srcKeywordName); |
| |
| EventLogger::createSyncPel( |
| types::ErrorType::DefaultValue, types::SeverityType::Error, |
| __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt, |
| std::nullopt, std::nullopt, std::nullopt); |
| } |
| } |
| } |
| |
| void BackupAndRestore::setBackupAndRestoreStatus( |
| const BackupAndRestoreStatus& i_status) |
| { |
| m_backupAndRestoreStatus = i_status; |
| } |
| |
| int BackupAndRestore::updateKeywordOnPrimaryOrBackupPath( |
| const std::string& i_fruPath, |
| [[maybe_unused]] const types::WriteVpdParams& i_paramsToWriteData) |
| const noexcept |
| { |
| if (i_fruPath.empty()) |
| { |
| logging::logMessage("Given FRU path is empty."); |
| return constants::FAILURE; |
| } |
| |
| if (m_backupAndRestoreCfgJsonObj.contains("source") && |
| m_backupAndRestoreCfgJsonObj["source"].value("hardwarePath", "") == |
| i_fruPath && |
| m_backupAndRestoreCfgJsonObj.contains("destination") && |
| !m_backupAndRestoreCfgJsonObj["destination"] |
| .value("hardwarePath", "") |
| .empty()) |
| { |
| // ToDo implementation needs to be added |
| } |
| else if (m_backupAndRestoreCfgJsonObj.contains("destination") && |
| m_backupAndRestoreCfgJsonObj["destination"].value( |
| "hardwarePath", "") == i_fruPath && |
| m_backupAndRestoreCfgJsonObj.contains("source") && |
| !m_backupAndRestoreCfgJsonObj["source"] |
| .value("hardwarePath", "") |
| .empty()) |
| { |
| // ToDo implementation needs to be added |
| } |
| |
| return constants::SUCCESS; |
| } |
| |
| } // namespace vpd |