#include "backup_restore.hpp" #include "constants.hpp" #include "event_logger.hpp" #include "exceptions.hpp" #include "logger.hpp" #include "parser.hpp" #include "types.hpp" #include #include 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", ""); uint16_t l_errCode = 0; m_backupAndRestoreCfgJsonObj = jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath, l_errCode); if (l_errCode) { throw JsonException( "JSON parsing failed for file [" + l_backupAndRestoreCfgFilePath + "], error : " + commonUtility::getErrCodeMsg(l_errCode), l_backupAndRestoreCfgFilePath); } } std::tuple 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 l_vpdParser = std::make_shared(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 l_vpdParser = std::make_shared(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(&l_srcVpdVariant)) { l_srcVpdMap = *l_srcVpdPtr; } else if (!std::holds_alternative(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(&l_dstVpdVariant)) { l_dstVpdMap = *l_dstVpdPtr; } else if (!std::holds_alternative(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; } uint16_t l_errCode = 0; const std::string l_srcFruPath = jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_srcPath, l_errCode); if (l_errCode) { logging::logMessage( "Failed to get source FRU path for [" + i_srcPath + "], error : " + commonUtility::getErrCodeMsg(l_errCode)); return; } const std::string l_dstFruPath = jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_dstPath, l_errCode); if (l_errCode) { logging::logMessage( "Failed to get destination FRU path for [" + i_dstPath + "], error : " + commonUtility::getErrCodeMsg(l_errCode)); return; } 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, l_errCode); if (l_srcInvPath.empty()) { if (l_errCode) { logging::logMessage( "Couldn't find source inventory path. Error : " + commonUtility::getErrCodeMsg(l_errCode)); return; } logging::logMessage("Couldn't find source inventory path."); return; } const std::string l_dstInvPath = jsonUtility::getInventoryObjPathFromJson( m_sysCfgJsonObj, i_dstPath, l_errCode); if (l_dstInvPath.empty()) { if (l_errCode) { logging::logMessage( "Couldn't find destination inventory path. Error : " + commonUtility::getErrCodeMsg(l_errCode)); return; } logging::logMessage("Couldn't find destination inventory path."); return; } const std::string l_srcServiceName = jsonUtility::getServiceName(m_sysCfgJsonObj, l_srcInvPath, l_errCode); if (l_errCode) { logging::logMessage( "Failed to get service name for source FRU [" + l_srcInvPath + "], error : " + commonUtility::getErrCodeMsg(l_errCode)); return; } const std::string l_dstServiceName = jsonUtility::getServiceName(m_sysCfgJsonObj, l_dstInvPath, l_errCode); if (l_errCode) { logging::logMessage( "Failed to get service name for destination FRU [" + l_dstInvPath + "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 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(); } 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(&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(&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(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(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, const types::WriteVpdParams& i_paramsToWriteData) const noexcept { if (i_fruPath.empty()) { logging::logMessage("Given FRU path is empty."); return constants::FAILURE; } bool l_inputPathIsSourcePath = false; bool l_inputPathIsDestinationPath = false; if (m_backupAndRestoreCfgJsonObj.contains("source") && m_backupAndRestoreCfgJsonObj["source"].value("hardwarePath", "") == i_fruPath && m_backupAndRestoreCfgJsonObj.contains("destination") && !m_backupAndRestoreCfgJsonObj["destination"] .value("hardwarePath", "") .empty()) { l_inputPathIsSourcePath = true; } else if (m_backupAndRestoreCfgJsonObj.contains("destination") && m_backupAndRestoreCfgJsonObj["destination"].value( "hardwarePath", "") == i_fruPath && m_backupAndRestoreCfgJsonObj.contains("source") && !m_backupAndRestoreCfgJsonObj["source"] .value("hardwarePath", "") .empty()) { l_inputPathIsDestinationPath = true; } else { // Input path is neither source or destination path of the // backup&restore JSON or source and destination paths are not hardware // paths in the config JSON. return constants::SUCCESS; } if (m_backupAndRestoreCfgJsonObj["backupMap"].is_array()) { std::string l_inpRecordName; std::string l_inpKeywordName; types::BinaryVector l_inpKeywordValue; if (const types::IpzData* l_ipzData = std::get_if(&i_paramsToWriteData)) { l_inpRecordName = std::get<0>(*l_ipzData); l_inpKeywordName = std::get<1>(*l_ipzData); l_inpKeywordValue = std::get<2>(*l_ipzData); if (l_inpRecordName.empty() || l_inpKeywordName.empty() || l_inpKeywordValue.empty()) { logging::logMessage("Invalid input received"); return constants::FAILURE; } } else { // only IPZ type VPD is supported now. return constants::SUCCESS; } for (const auto& l_aRecordKwInfo : m_backupAndRestoreCfgJsonObj["backupMap"]) { if (l_aRecordKwInfo.value("sourceRecord", "").empty() || l_aRecordKwInfo.value("sourceKeyword", "").empty() || l_aRecordKwInfo.value("destinationRecord", "").empty() || l_aRecordKwInfo.value("destinationKeyword", "").empty()) { // invalid backup map found logging::logMessage( "Invalid backup map found, one or more field(s) found empty or not present in the config JSON: sourceRecord: " + l_aRecordKwInfo.value("sourceRecord", "") + ", sourceKeyword: " + l_aRecordKwInfo.value("sourceKeyword", "") + ", destinationRecord: " + l_aRecordKwInfo.value("destinationRecord", "") + ", destinationKeyword: " + l_aRecordKwInfo.value("destinationKeyword", "")); continue; } if (l_inputPathIsSourcePath && (l_aRecordKwInfo["sourceRecord"] == l_inpRecordName) && (l_aRecordKwInfo["sourceKeyword"] == l_inpKeywordName)) { std::string l_fruPath( m_backupAndRestoreCfgJsonObj["destination"] ["hardwarePath"]); Parser l_parserObj(l_fruPath, m_sysCfgJsonObj); return l_parserObj.updateVpdKeyword(std::make_tuple( l_aRecordKwInfo["destinationRecord"], l_aRecordKwInfo["destinationKeyword"], l_inpKeywordValue)); } else if (l_inputPathIsDestinationPath && (l_aRecordKwInfo["destinationRecord"] == l_inpRecordName) && (l_aRecordKwInfo["destinationKeyword"] == l_inpKeywordName)) { std::string l_fruPath( m_backupAndRestoreCfgJsonObj["source"]["hardwarePath"]); Parser l_parserObj(l_fruPath, m_sysCfgJsonObj); return l_parserObj.updateVpdKeyword(std::make_tuple( l_aRecordKwInfo["sourceRecord"], l_aRecordKwInfo["sourceKeyword"], l_inpKeywordValue)); } } } // Received property is not part of backup & restore JSON. return constants::SUCCESS; } } // namespace vpd