xref: /openbmc/openpower-vpd-parser/vpd-manager/src/backup_restore.cpp (revision a55fcca1879010cad2279c429b24117ab220ef4f)
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     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>
backupAndRestore()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 
backupAndRestoreIpzVpd(types::IPZVpdMap & io_srcVpdMap,types::IPZVpdMap & io_dstVpdMap,const std::string & i_srcPath,const std::string & i_dstPath)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 
setBackupAndRestoreStatus(const BackupAndRestoreStatus & i_status)392 void BackupAndRestore::setBackupAndRestoreStatus(
393     const BackupAndRestoreStatus& i_status)
394 {
395     m_backupAndRestoreStatus = i_status;
396 }
397 } // namespace vpd
398