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 m_backupAndRestoreCfgJsonObj = 25 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath); 26 27 if (m_backupAndRestoreCfgJsonObj.empty()) 28 { 29 throw JsonException("JSON parsing failed", 30 l_backupAndRestoreCfgFilePath); 31 } 32 } 33 34 std::tuple<types::VPDMapVariant, types::VPDMapVariant> 35 BackupAndRestore::backupAndRestore() 36 { 37 auto l_emptyVariantPair = 38 std::make_tuple(std::monostate{}, std::monostate{}); 39 40 if (m_backupAndRestoreStatus >= BackupAndRestoreStatus::Invoked) 41 { 42 logging::logMessage("Backup and restore invoked already."); 43 return l_emptyVariantPair; 44 } 45 46 m_backupAndRestoreStatus = BackupAndRestoreStatus::Invoked; 47 try 48 { 49 if (m_backupAndRestoreCfgJsonObj.empty() || 50 !m_backupAndRestoreCfgJsonObj.contains("source") || 51 !m_backupAndRestoreCfgJsonObj.contains("destination") || 52 !m_backupAndRestoreCfgJsonObj.contains("type") || 53 !m_backupAndRestoreCfgJsonObj.contains("backupMap")) 54 { 55 logging::logMessage( 56 "Backup restore config JSON is missing necessary tag(s), can't initiate backup and restore."); 57 return l_emptyVariantPair; 58 } 59 60 std::string l_srcVpdPath; 61 types::VPDMapVariant l_srcVpdVariant; 62 if (l_srcVpdPath = m_backupAndRestoreCfgJsonObj["source"].value( 63 "hardwarePath", ""); 64 !l_srcVpdPath.empty() && std::filesystem::exists(l_srcVpdPath)) 65 { 66 std::shared_ptr<Parser> l_vpdParser = 67 std::make_shared<Parser>(l_srcVpdPath, m_sysCfgJsonObj); 68 l_srcVpdVariant = l_vpdParser->parse(); 69 } 70 else if (l_srcVpdPath = m_backupAndRestoreCfgJsonObj["source"].value( 71 "inventoryPath", ""); 72 l_srcVpdPath.empty()) 73 { 74 logging::logMessage( 75 "Couldn't extract source path, can't initiate backup and restore."); 76 return l_emptyVariantPair; 77 } 78 79 std::string l_dstVpdPath; 80 types::VPDMapVariant l_dstVpdVariant; 81 if (l_dstVpdPath = m_backupAndRestoreCfgJsonObj["destination"].value( 82 "hardwarePath", ""); 83 !l_dstVpdPath.empty() && std::filesystem::exists(l_dstVpdPath)) 84 { 85 std::shared_ptr<Parser> l_vpdParser = 86 std::make_shared<Parser>(l_dstVpdPath, m_sysCfgJsonObj); 87 l_dstVpdVariant = l_vpdParser->parse(); 88 } 89 else if (l_dstVpdPath = m_backupAndRestoreCfgJsonObj["destination"] 90 .value("inventoryPath", ""); 91 l_dstVpdPath.empty()) 92 { 93 logging::logMessage( 94 "Couldn't extract destination path, can't initiate backup and restore."); 95 return l_emptyVariantPair; 96 } 97 98 // Implement backup and restore for IPZ type VPD 99 auto l_backupAndRestoreType = 100 m_backupAndRestoreCfgJsonObj.value("type", ""); 101 if (l_backupAndRestoreType.compare("IPZ") == constants::STR_CMP_SUCCESS) 102 { 103 types::IPZVpdMap l_srcVpdMap; 104 if (auto l_srcVpdPtr = 105 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant)) 106 { 107 l_srcVpdMap = *l_srcVpdPtr; 108 } 109 else if (!std::holds_alternative<std::monostate>(l_srcVpdVariant)) 110 { 111 logging::logMessage("Source VPD is not of IPZ type."); 112 return l_emptyVariantPair; 113 } 114 115 types::IPZVpdMap l_dstVpdMap; 116 if (auto l_dstVpdPtr = 117 std::get_if<types::IPZVpdMap>(&l_dstVpdVariant)) 118 { 119 l_dstVpdMap = *l_dstVpdPtr; 120 } 121 else if (!std::holds_alternative<std::monostate>(l_dstVpdVariant)) 122 { 123 logging::logMessage("Destination VPD is not of IPZ type."); 124 return l_emptyVariantPair; 125 } 126 127 backupAndRestoreIpzVpd(l_srcVpdMap, l_dstVpdMap, l_srcVpdPath, 128 l_dstVpdPath); 129 m_backupAndRestoreStatus = BackupAndRestoreStatus::Completed; 130 131 return std::make_tuple(l_srcVpdMap, l_dstVpdMap); 132 } 133 // Note: add implementation here to support any other VPD type. 134 } 135 catch (const std::exception& ex) 136 { 137 logging::logMessage("Back up and restore failed with exception: " + 138 std::string(ex.what())); 139 } 140 return l_emptyVariantPair; 141 } 142 143 void BackupAndRestore::backupAndRestoreIpzVpd( 144 types::IPZVpdMap& io_srcVpdMap, types::IPZVpdMap& io_dstVpdMap, 145 const std::string& i_srcPath, const std::string& i_dstPath) 146 { 147 if (!m_backupAndRestoreCfgJsonObj["backupMap"].is_array()) 148 { 149 logging::logMessage( 150 "Invalid value found for tag backupMap, in backup and restore config JSON."); 151 return; 152 } 153 154 const std::string l_srcFruPath = 155 jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_srcPath); 156 const std::string l_dstFruPath = 157 jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_dstPath); 158 if (l_srcFruPath.empty() || l_dstFruPath.empty()) 159 { 160 logging::logMessage( 161 "Couldn't find either source or destination FRU path."); 162 return; 163 } 164 165 const std::string l_srcInvPath = 166 jsonUtility::getInventoryObjPathFromJson(m_sysCfgJsonObj, i_srcPath); 167 const std::string l_dstInvPath = 168 jsonUtility::getInventoryObjPathFromJson(m_sysCfgJsonObj, i_dstPath); 169 if (l_srcInvPath.empty() || l_dstInvPath.empty()) 170 { 171 logging::logMessage( 172 "Couldn't find either source or destination inventory path."); 173 return; 174 } 175 176 const std::string l_srcServiceName = 177 jsonUtility::getServiceName(m_sysCfgJsonObj, l_srcInvPath); 178 const std::string l_dstServiceName = 179 jsonUtility::getServiceName(m_sysCfgJsonObj, l_dstInvPath); 180 if (l_srcServiceName.empty() || l_dstServiceName.empty()) 181 { 182 logging::logMessage( 183 "Couldn't find either source or destination DBus service name."); 184 return; 185 } 186 187 for (const auto& l_aRecordKwInfo : 188 m_backupAndRestoreCfgJsonObj["backupMap"]) 189 { 190 const std::string& l_srcRecordName = 191 l_aRecordKwInfo.value("sourceRecord", ""); 192 const std::string& l_srcKeywordName = 193 l_aRecordKwInfo.value("sourceKeyword", ""); 194 const std::string& l_dstRecordName = 195 l_aRecordKwInfo.value("destinationRecord", ""); 196 const std::string& l_dstKeywordName = 197 l_aRecordKwInfo.value("destinationKeyword", ""); 198 199 if (l_srcRecordName.empty() || l_dstRecordName.empty() || 200 l_srcKeywordName.empty() || l_dstKeywordName.empty()) 201 { 202 logging::logMessage( 203 "Record or keyword not found in the backup and restore config JSON."); 204 continue; 205 } 206 207 if (!io_srcVpdMap.empty() && 208 io_srcVpdMap.find(l_srcRecordName) == io_srcVpdMap.end()) 209 { 210 logging::logMessage( 211 "Record: " + l_srcRecordName + 212 ", is not found in the source path: " + i_srcPath); 213 continue; 214 } 215 216 if (!io_dstVpdMap.empty() && 217 io_dstVpdMap.find(l_dstRecordName) == io_dstVpdMap.end()) 218 { 219 logging::logMessage( 220 "Record: " + l_dstRecordName + 221 ", is not found in the destination path: " + i_dstPath); 222 continue; 223 } 224 225 types::BinaryVector l_defaultBinaryValue; 226 if (l_aRecordKwInfo.contains("defaultValue") && 227 l_aRecordKwInfo["defaultValue"].is_array()) 228 { 229 l_defaultBinaryValue = 230 l_aRecordKwInfo["defaultValue"].get<types::BinaryVector>(); 231 } 232 else 233 { 234 logging::logMessage( 235 "Couldn't read default value for record name: " + 236 l_srcRecordName + ", keyword name: " + l_srcKeywordName + 237 " from backup and restore config JSON file."); 238 continue; 239 } 240 241 bool l_isPelRequired = l_aRecordKwInfo.value("isPelRequired", false); 242 243 types::BinaryVector l_srcBinaryValue; 244 std::string l_srcStrValue; 245 if (!io_srcVpdMap.empty()) 246 { 247 vpdSpecificUtility::getKwVal(io_srcVpdMap.at(l_srcRecordName), 248 l_srcKeywordName, l_srcStrValue); 249 l_srcBinaryValue = 250 types::BinaryVector(l_srcStrValue.begin(), l_srcStrValue.end()); 251 } 252 else 253 { 254 // Read keyword value from DBus 255 const auto l_value = dbusUtility::readDbusProperty( 256 l_srcServiceName, l_srcInvPath, 257 constants::ipzVpdInf + l_srcRecordName, l_srcKeywordName); 258 if (const auto l_binaryValue = 259 std::get_if<types::BinaryVector>(&l_value)) 260 { 261 l_srcBinaryValue = *l_binaryValue; 262 l_srcStrValue = std::string(l_srcBinaryValue.begin(), 263 l_srcBinaryValue.end()); 264 } 265 } 266 267 types::BinaryVector l_dstBinaryValue; 268 std::string l_dstStrValue; 269 if (!io_dstVpdMap.empty()) 270 { 271 vpdSpecificUtility::getKwVal(io_dstVpdMap.at(l_dstRecordName), 272 l_dstKeywordName, l_dstStrValue); 273 l_dstBinaryValue = 274 types::BinaryVector(l_dstStrValue.begin(), l_dstStrValue.end()); 275 } 276 else 277 { 278 // Read keyword value from DBus 279 const auto l_value = dbusUtility::readDbusProperty( 280 l_dstServiceName, l_dstInvPath, 281 constants::ipzVpdInf + l_dstRecordName, l_dstKeywordName); 282 if (const auto l_binaryValue = 283 std::get_if<types::BinaryVector>(&l_value)) 284 { 285 l_dstBinaryValue = *l_binaryValue; 286 l_dstStrValue = std::string(l_dstBinaryValue.begin(), 287 l_dstBinaryValue.end()); 288 } 289 } 290 291 if (l_srcBinaryValue != l_dstBinaryValue) 292 { 293 // ToDo: Handle if there is no valid default value in the backup and 294 // restore config JSON. 295 if (l_dstBinaryValue == l_defaultBinaryValue) 296 { 297 // Update keyword's value on hardware 298 auto l_vpdParser = 299 std::make_shared<Parser>(l_dstFruPath, m_sysCfgJsonObj); 300 301 auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword( 302 types::IpzData(l_dstRecordName, l_dstKeywordName, 303 l_srcBinaryValue)); 304 305 /* To keep the data in sync between hardware and parsed map 306 updating the io_dstVpdMap. This should only be done if write 307 on hardware returns success.*/ 308 if (!io_dstVpdMap.empty() && l_bytesUpdatedOnHardware > 0) 309 { 310 io_dstVpdMap[l_dstRecordName][l_dstKeywordName] = 311 l_srcStrValue; 312 } 313 continue; 314 } 315 316 if (l_srcBinaryValue == l_defaultBinaryValue) 317 { 318 // Update keyword's value on hardware 319 auto l_vpdParser = 320 std::make_shared<Parser>(l_srcFruPath, m_sysCfgJsonObj); 321 322 auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword( 323 types::IpzData(l_srcRecordName, l_srcKeywordName, 324 l_dstBinaryValue)); 325 326 /* To keep the data in sync between hardware and parsed map 327 updating the io_srcVpdMap. This should only be done if write 328 on hardware returns success.*/ 329 if (!io_srcVpdMap.empty() && l_bytesUpdatedOnHardware > 0) 330 { 331 io_srcVpdMap[l_srcRecordName][l_srcKeywordName] = 332 l_dstStrValue; 333 } 334 } 335 else 336 { 337 /** 338 * Update io_srcVpdMap to publish the same data on DBus, which 339 * is already present on the DBus. Because after calling 340 * backupAndRestore API the map value will get published to DBus 341 * in the worker flow. 342 */ 343 if (!io_srcVpdMap.empty() && io_dstVpdMap.empty()) 344 { 345 io_srcVpdMap[l_srcRecordName][l_srcKeywordName] = 346 l_dstStrValue; 347 } 348 349 std::string l_errorMsg( 350 "Mismatch found between source and destination VPD for record : " + 351 l_srcRecordName + " and keyword : " + l_srcKeywordName + 352 " . Value read from source : " + l_srcStrValue + 353 " . Value read from destination : " + l_dstStrValue); 354 355 EventLogger::createSyncPel( 356 types::ErrorType::VpdMismatch, types::SeverityType::Warning, 357 __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt, 358 std::nullopt, std::nullopt, std::nullopt); 359 } 360 } 361 else if (l_srcBinaryValue == l_defaultBinaryValue && 362 l_dstBinaryValue == l_defaultBinaryValue && l_isPelRequired) 363 { 364 std::string l_errorMsg( 365 "Default value found on both source and destination VPD, for record: " + 366 l_srcRecordName + " and keyword: " + l_srcKeywordName); 367 368 EventLogger::createSyncPel( 369 types::ErrorType::DefaultValue, types::SeverityType::Error, 370 __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt, 371 std::nullopt, std::nullopt, std::nullopt); 372 } 373 } 374 } 375 376 void BackupAndRestore::setBackupAndRestoreStatus( 377 const BackupAndRestoreStatus& i_status) 378 { 379 m_backupAndRestoreStatus = i_status; 380 } 381 } // namespace vpd 382