blob: 89d7d2a095f36d0c2c06aa1d4488abb1bda63fd3 [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#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
13namespace vpd
14{
15BackupAndRestoreStatus BackupAndRestore::m_backupAndRestoreStatus =
16 BackupAndRestoreStatus::NotStarted;
17
18BackupAndRestore::BackupAndRestore(const nlohmann::json& i_sysCfgJsonObj) :
19 m_sysCfgJsonObj(i_sysCfgJsonObj)
20{
21 std::string l_backupAndRestoreCfgFilePath =
22 i_sysCfgJsonObj.value("backupRestoreConfigPath", "");
RekhaAparna011ef21002025-02-18 23:47:36 -060023
Rekha Aparnaca9a0862025-08-29 04:08:33 -050024 uint16_t l_errCode = 0;
RekhaAparna011ef21002025-02-18 23:47:36 -060025 m_backupAndRestoreCfgJsonObj =
Rekha Aparnaca9a0862025-08-29 04:08:33 -050026 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath, l_errCode);
RekhaAparna011ef21002025-02-18 23:47:36 -060027
Rekha Aparnaca9a0862025-08-29 04:08:33 -050028 if (l_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050029 {
Rekha Aparnaca9a0862025-08-29 04:08:33 -050030 throw JsonException(
31 "JSON parsing failed for file [" + l_backupAndRestoreCfgFilePath +
32 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode),
33 l_backupAndRestoreCfgFilePath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050034 }
35}
36
37std::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
146void 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
Rekha Aparna017567a2025-08-13 02:07:06 -0500168 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())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500173 {
Rekha Aparna017567a2025-08-13 02:07:06 -0500174 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.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500200 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 {
Souvik Roya55fcca2025-02-19 01:33:58 -0600274 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
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500284 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 {
Souvik Roya55fcca2025-02-19 01:33:58 -0600306 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
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500316 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 +
Anupama B R6f142832025-04-11 04:17:49 -0500395 " . Value read from source : " +
396 commonUtility::convertByteVectorToHex(l_srcBinaryValue) +
397 " . Value read from destination : " +
398 commonUtility::convertByteVectorToHex(l_dstBinaryValue));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500399
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
421void BackupAndRestore::setBackupAndRestoreStatus(
422 const BackupAndRestoreStatus& i_status)
423{
424 m_backupAndRestoreStatus = i_status;
425}
Anupama B R8dedd1e2025-03-24 07:43:47 -0500426
427int BackupAndRestore::updateKeywordOnPrimaryOrBackupPath(
428 const std::string& i_fruPath,
Anupama B R7b293cd2025-03-28 05:35:01 -0500429 const types::WriteVpdParams& i_paramsToWriteData) const noexcept
Anupama B R8dedd1e2025-03-24 07:43:47 -0500430{
431 if (i_fruPath.empty())
432 {
433 logging::logMessage("Given FRU path is empty.");
434 return constants::FAILURE;
435 }
436
Anupama B Rd966f272025-07-21 02:40:16 -0500437 bool l_inputPathIsSourcePath = false;
438 bool l_inputPathIsDestinationPath = false;
Anupama B R7b293cd2025-03-28 05:35:01 -0500439
Anupama B R8dedd1e2025-03-24 07:43:47 -0500440 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 {
Anupama B Rd966f272025-07-21 02:40:16 -0500448 l_inputPathIsSourcePath = true;
Anupama B R8dedd1e2025-03-24 07:43:47 -0500449 }
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 {
Anupama B Rd966f272025-07-21 02:40:16 -0500458 l_inputPathIsDestinationPath = true;
Anupama B R7b293cd2025-03-28 05:35:01 -0500459 }
460 else
461 {
462 // Input path is neither source or destination path of the
Anupama B Rd966f272025-07-21 02:40:16 -0500463 // backup&restore JSON or source and destination paths are not hardware
464 // paths in the config JSON.
Anupama B R7b293cd2025-03-28 05:35:01 -0500465 return constants::SUCCESS;
Anupama B R8dedd1e2025-03-24 07:43:47 -0500466 }
467
Anupama B Rd966f272025-07-21 02:40:16 -0500468 if (m_backupAndRestoreCfgJsonObj["backupMap"].is_array())
Anupama B R7b293cd2025-03-28 05:35:01 -0500469 {
470 std::string l_inpRecordName;
471 std::string l_inpKeywordName;
Anupama B Rd966f272025-07-21 02:40:16 -0500472 types::BinaryVector l_inpKeywordValue;
Anupama B R7b293cd2025-03-28 05:35:01 -0500473
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);
Anupama B Rd966f272025-07-21 02:40:16 -0500479 l_inpKeywordValue = std::get<2>(*l_ipzData);
Anupama B R7b293cd2025-03-28 05:35:01 -0500480
481 if (l_inpRecordName.empty() || l_inpKeywordName.empty() ||
Anupama B Rd966f272025-07-21 02:40:16 -0500482 l_inpKeywordValue.empty())
Anupama B R7b293cd2025-03-28 05:35:01 -0500483 {
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
Anupama B Rd966f272025-07-21 02:40:16 -0500515 if (l_inputPathIsSourcePath &&
Anupama B R7b293cd2025-03-28 05:35:01 -0500516 (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"],
Anupama B Rd966f272025-07-21 02:40:16 -0500526 l_aRecordKwInfo["destinationKeyword"], l_inpKeywordValue));
Anupama B R7b293cd2025-03-28 05:35:01 -0500527 }
Anupama B Rd966f272025-07-21 02:40:16 -0500528 else if (l_inputPathIsDestinationPath &&
Anupama B R7b293cd2025-03-28 05:35:01 -0500529 (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"],
Anupama B Rd966f272025-07-21 02:40:16 -0500540 l_aRecordKwInfo["sourceKeyword"], l_inpKeywordValue));
Anupama B R7b293cd2025-03-28 05:35:01 -0500541 }
542 }
543 }
544
545 // Received property is not part of backup & restore JSON.
Anupama B R8dedd1e2025-03-24 07:43:47 -0500546 return constants::SUCCESS;
547}
548
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500549} // namespace vpd