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