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 l_srcStrValue = vpdSpecificUtility::getKwVal( 248 io_srcVpdMap.at(l_srcRecordName), l_srcKeywordName); 249 250 if (l_srcStrValue.empty()) 251 { 252 std::runtime_error( 253 std::string("Failed to get value for keyword [") + 254 l_srcKeywordName + std::string("]")); 255 } 256 257 l_srcBinaryValue = 258 types::BinaryVector(l_srcStrValue.begin(), l_srcStrValue.end()); 259 } 260 else 261 { 262 // Read keyword value from DBus 263 const auto l_value = dbusUtility::readDbusProperty( 264 l_srcServiceName, l_srcInvPath, 265 constants::ipzVpdInf + l_srcRecordName, l_srcKeywordName); 266 if (const auto l_binaryValue = 267 std::get_if<types::BinaryVector>(&l_value)) 268 { 269 l_srcBinaryValue = *l_binaryValue; 270 l_srcStrValue = std::string(l_srcBinaryValue.begin(), 271 l_srcBinaryValue.end()); 272 } 273 } 274 275 types::BinaryVector l_dstBinaryValue; 276 std::string l_dstStrValue; 277 if (!io_dstVpdMap.empty()) 278 { 279 l_dstStrValue = vpdSpecificUtility::getKwVal( 280 io_dstVpdMap.at(l_dstRecordName), l_dstKeywordName); 281 282 if (l_dstStrValue.empty()) 283 { 284 std::runtime_error( 285 std::string("Failed to get value for keyword [") + 286 l_dstKeywordName + std::string("]")); 287 } 288 289 l_dstBinaryValue = 290 types::BinaryVector(l_dstStrValue.begin(), l_dstStrValue.end()); 291 } 292 else 293 { 294 // Read keyword value from DBus 295 const auto l_value = dbusUtility::readDbusProperty( 296 l_dstServiceName, l_dstInvPath, 297 constants::ipzVpdInf + l_dstRecordName, l_dstKeywordName); 298 if (const auto l_binaryValue = 299 std::get_if<types::BinaryVector>(&l_value)) 300 { 301 l_dstBinaryValue = *l_binaryValue; 302 l_dstStrValue = std::string(l_dstBinaryValue.begin(), 303 l_dstBinaryValue.end()); 304 } 305 } 306 307 if (l_srcBinaryValue != l_dstBinaryValue) 308 { 309 // ToDo: Handle if there is no valid default value in the backup and 310 // restore config JSON. 311 if (l_dstBinaryValue == l_defaultBinaryValue) 312 { 313 // Update keyword's value on hardware 314 auto l_vpdParser = 315 std::make_shared<Parser>(l_dstFruPath, m_sysCfgJsonObj); 316 317 auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword( 318 types::IpzData(l_dstRecordName, l_dstKeywordName, 319 l_srcBinaryValue)); 320 321 /* To keep the data in sync between hardware and parsed map 322 updating the io_dstVpdMap. This should only be done if write 323 on hardware returns success.*/ 324 if (!io_dstVpdMap.empty() && l_bytesUpdatedOnHardware > 0) 325 { 326 io_dstVpdMap[l_dstRecordName][l_dstKeywordName] = 327 l_srcStrValue; 328 } 329 continue; 330 } 331 332 if (l_srcBinaryValue == l_defaultBinaryValue) 333 { 334 // Update keyword's value on hardware 335 auto l_vpdParser = 336 std::make_shared<Parser>(l_srcFruPath, m_sysCfgJsonObj); 337 338 auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword( 339 types::IpzData(l_srcRecordName, l_srcKeywordName, 340 l_dstBinaryValue)); 341 342 /* To keep the data in sync between hardware and parsed map 343 updating the io_srcVpdMap. This should only be done if write 344 on hardware returns success.*/ 345 if (!io_srcVpdMap.empty() && l_bytesUpdatedOnHardware > 0) 346 { 347 io_srcVpdMap[l_srcRecordName][l_srcKeywordName] = 348 l_dstStrValue; 349 } 350 } 351 else 352 { 353 /** 354 * Update io_srcVpdMap to publish the same data on DBus, which 355 * is already present on the DBus. Because after calling 356 * backupAndRestore API the map value will get published to DBus 357 * in the worker flow. 358 */ 359 if (!io_srcVpdMap.empty() && io_dstVpdMap.empty()) 360 { 361 io_srcVpdMap[l_srcRecordName][l_srcKeywordName] = 362 l_dstStrValue; 363 } 364 365 std::string l_errorMsg( 366 "Mismatch found between source and destination VPD for record : " + 367 l_srcRecordName + " and keyword : " + l_srcKeywordName + 368 " . Value read from source : " + l_srcStrValue + 369 " . Value read from destination : " + l_dstStrValue); 370 371 EventLogger::createSyncPel( 372 types::ErrorType::VpdMismatch, types::SeverityType::Warning, 373 __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt, 374 std::nullopt, std::nullopt, std::nullopt); 375 } 376 } 377 else if (l_srcBinaryValue == l_defaultBinaryValue && 378 l_dstBinaryValue == l_defaultBinaryValue && l_isPelRequired) 379 { 380 std::string l_errorMsg( 381 "Default value found on both source and destination VPD, for record: " + 382 l_srcRecordName + " and keyword: " + l_srcKeywordName); 383 384 EventLogger::createSyncPel( 385 types::ErrorType::DefaultValue, types::SeverityType::Error, 386 __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt, 387 std::nullopt, std::nullopt, std::nullopt); 388 } 389 } 390 } 391 392 void BackupAndRestore::setBackupAndRestoreStatus( 393 const BackupAndRestoreStatus& i_status) 394 { 395 m_backupAndRestoreStatus = i_status; 396 } 397 } // namespace vpd 398