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