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