xref: /openbmc/openpower-vpd-parser/vpd-manager/src/backup_restore.cpp (revision 022112bc235c7f91ee998bb131f06e212dee1f6a)
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