blob: 8463ffbf81ee863887f4561bdda17006db72f627 [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#pragma once
2
Rekha Aparna017567a2025-08-13 02:07:06 -05003#include "error_codes.hpp"
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05004#include "event_logger.hpp"
5#include "exceptions.hpp"
6#include "logger.hpp"
7#include "types.hpp"
8
9#include <gpiod.hpp>
10#include <nlohmann/json.hpp>
11#include <utility/common_utility.hpp>
Rekha Aparnaca9a0862025-08-29 04:08:33 -050012#include <utility/vpd_specific_utility.hpp>
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050013
14#include <fstream>
15#include <type_traits>
16#include <unordered_map>
17
18namespace vpd
19{
20namespace jsonUtility
21{
22
23// forward declaration of API for function map.
Sunny Srivastava84c3d232025-09-03 00:47:10 -050024bool processSystemCmdTag(
25 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
26 const std::string& i_baseAction, const std::string& i_flagToProcess,
27 uint16_t& o_errCode);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050028
29// forward declaration of API for function map.
30bool processGpioPresenceTag(
31 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
Sunny Srivastava84c3d232025-09-03 00:47:10 -050032 const std::string& i_baseAction, const std::string& i_flagToProcess,
33 uint16_t& o_errCode);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050034
35// forward declaration of API for function map.
36bool procesSetGpioTag(const nlohmann::json& i_parsedConfigJson,
37 const std::string& i_vpdFilePath,
38 const std::string& i_baseAction,
Sunny Srivastava84c3d232025-09-03 00:47:10 -050039 const std::string& i_flagToProcess, uint16_t& o_errCode);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050040
41// Function pointers to process tags from config JSON.
42typedef bool (*functionPtr)(
43 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
Sunny Srivastava84c3d232025-09-03 00:47:10 -050044 const std::string& i_baseAction, const std::string& i_flagToProcess,
45 uint16_t& o_errCode);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050046
47inline std::unordered_map<std::string, functionPtr> funcionMap{
48 {"gpioPresence", processGpioPresenceTag},
49 {"setGpio", procesSetGpioTag},
50 {"systemCmd", processSystemCmdTag}};
51
52/**
53 * @brief API to read VPD offset from JSON file.
54 *
55 * @param[in] i_sysCfgJsonObj - Parsed system config JSON object.
56 * @param[in] i_vpdFilePath - VPD file path.
Rekha Aparna017567a2025-08-13 02:07:06 -050057 * @param[in] o_errCode - To set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050058 * @return VPD offset if found in JSON, 0 otherwise.
59 */
60inline size_t getVPDOffset(const nlohmann::json& i_sysCfgJsonObj,
Rekha Aparna017567a2025-08-13 02:07:06 -050061 const std::string& i_vpdFilePath,
62 uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050063{
64 if (i_vpdFilePath.empty() || (i_sysCfgJsonObj.empty()) ||
65 (!i_sysCfgJsonObj.contains("frus")))
66 {
Rekha Aparna017567a2025-08-13 02:07:06 -050067 o_errCode = error_code::INVALID_INPUT_PARAMETER;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050068 return 0;
69 }
70
71 if (i_sysCfgJsonObj["frus"].contains(i_vpdFilePath))
72 {
73 return i_sysCfgJsonObj["frus"][i_vpdFilePath].at(0).value("offset", 0);
74 }
75
76 const nlohmann::json& l_fruList =
77 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
78
79 for (const auto& l_fru : l_fruList.items())
80 {
81 const auto l_fruPath = l_fru.key();
82
83 // check if given path is redundant FRU path
84 if (i_vpdFilePath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
85 "redundantEeprom", ""))
86 {
87 // Return the offset of redundant EEPROM taken from JSON.
88 return i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("offset", 0);
89 }
90 }
91
92 return 0;
93}
94
95/**
96 * @brief API to parse respective JSON.
97 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050098 * @param[in] pathToJson - Path to JSON.
Rekha Aparnaca9a0862025-08-29 04:08:33 -050099 * @param[out] o_errCode - To set error code in case of error.
RekhaAparna011ef21002025-02-18 23:47:36 -0600100 * @return on success parsed JSON. On failure empty JSON object.
101 *
102 * Note: Caller has to handle it in case an empty JSON object is received.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500103 */
Rekha Aparnaca9a0862025-08-29 04:08:33 -0500104inline nlohmann::json getParsedJson(const std::string& pathToJson,
105 uint16_t& o_errCode) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500106{
Rekha Aparnaca9a0862025-08-29 04:08:33 -0500107 if (pathToJson.empty())
108 {
109 o_errCode = error_code::INVALID_INPUT_PARAMETER;
110 return nlohmann::json{};
111 }
112
113 if (!std::filesystem::exists(pathToJson))
114 {
115 o_errCode = error_code::FILE_NOT_FOUND;
116 return nlohmann::json{};
117 }
118
119 if (std::filesystem::is_empty(pathToJson))
120 {
121 o_errCode = error_code::EMPTY_FILE;
122 return nlohmann::json{};
123 }
124
125 std::ifstream l_jsonFile(pathToJson);
126 if (!l_jsonFile)
127 {
128 o_errCode = error_code::FILE_ACCESS_ERROR;
129 return nlohmann::json{};
130 }
131
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500132 try
133 {
RekhaAparna011ef21002025-02-18 23:47:36 -0600134 return nlohmann::json::parse(l_jsonFile);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500135 }
RekhaAparna011ef21002025-02-18 23:47:36 -0600136 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500137 {
Rekha Aparnaca9a0862025-08-29 04:08:33 -0500138 o_errCode = error_code::JSON_PARSE_ERROR;
139 return nlohmann::json{};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500140 }
141}
142
143/**
144 * @brief Get inventory object path from system config JSON.
145 *
146 * Given either D-bus inventory path/FRU EEPROM path/redundant EEPROM path,
147 * this API returns D-bus inventory path if present in JSON.
148 *
149 * @param[in] i_sysCfgJsonObj - System config JSON object
150 * @param[in] i_vpdPath - Path to where VPD is stored.
Rekha Aparna017567a2025-08-13 02:07:06 -0500151 * @param[in] o_errCode - To set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500152 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500153 * @return On success a valid path is returned, on failure an empty string is
RekhaAparna011ef21002025-02-18 23:47:36 -0600154 * returned.
155 *
156 * Note: Caller has to handle it in case an empty string is received.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500157 */
158inline std::string getInventoryObjPathFromJson(
Rekha Aparna017567a2025-08-13 02:07:06 -0500159 const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdPath,
160 uint16_t& o_errCode) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500161{
Rekha Aparna017567a2025-08-13 02:07:06 -0500162 if (i_vpdPath.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500163 {
Rekha Aparna017567a2025-08-13 02:07:06 -0500164 o_errCode = error_code::INVALID_INPUT_PARAMETER;
165 return std::string{};
RekhaAparna011ef21002025-02-18 23:47:36 -0600166 }
167
Rekha Aparna017567a2025-08-13 02:07:06 -0500168 if (!i_sysCfgJsonObj.contains("frus"))
169 {
170 o_errCode = error_code::INVALID_JSON;
171 return std::string{};
172 }
173
174 // check if given path is FRU path
175 if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
176 {
177 return i_sysCfgJsonObj["frus"][i_vpdPath].at(0).value(
178 "inventoryPath", "");
179 }
180
181 const nlohmann::json& l_fruList =
182 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
183
184 for (const auto& l_fru : l_fruList.items())
185 {
186 const auto l_fruPath = l_fru.key();
187 const auto l_invObjPath =
188 i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("inventoryPath", "");
189
190 // check if given path is redundant FRU path or inventory path
191 if (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
192 "redundantEeprom", "") ||
193 (i_vpdPath == l_invObjPath))
194 {
195 return l_invObjPath;
196 }
197 }
198
199 return std::string{};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500200}
201
202/**
203 * @brief Process "PostFailAction" defined in config JSON.
204 *
205 * In case there is some error in the processing of "preAction" execution and a
206 * set of procedure needs to be done as a part of post fail action. This base
207 * action can be defined in the config JSON for that FRU and it will be handled
208 * under this API.
209 *
210 * @param[in] i_parsedConfigJson - config JSON
211 * @param[in] i_vpdFilePath - EEPROM file path
212 * @param[in] i_flagToProcess - To identify which flag(s) needs to be processed
Rekha Aparnaff7d7992025-09-01 11:08:53 -0500213 * @param[out] o_errCode - To set error code in case of error
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500214 * under PostFailAction tag of config JSON.
215 * @return - success or failure
216 */
Rekha Aparnaff7d7992025-09-01 11:08:53 -0500217inline bool executePostFailAction(
218 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
219 const std::string& i_flagToProcess, uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500220{
Rekha Aparnaff7d7992025-09-01 11:08:53 -0500221 if (i_parsedConfigJson.empty() || i_vpdFilePath.empty() ||
222 i_flagToProcess.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500223 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -0500224 o_errCode = error_code::INVALID_INPUT_PARAMETER;
225 return false;
226 }
RekhaAparna01c11e8b62025-02-20 00:34:35 -0600227
Rekha Aparna41f47e72025-09-18 01:44:09 -0500228 if (!i_parsedConfigJson.contains("frus"))
229 {
230 o_errCode = error_code::INVALID_JSON;
231 return false;
232 }
233
234 if (!i_parsedConfigJson["frus"].contains(i_vpdFilePath))
235 {
236 o_errCode = error_code::FRU_PATH_NOT_FOUND;
237 return false;
238 }
239
240 if (!i_parsedConfigJson["frus"][i_vpdFilePath].at(0).contains(
241 "postFailAction"))
242 {
243 o_errCode = error_code::MISSING_ACTION_TAG;
244 return false;
245 }
246
Rekha Aparnaff7d7992025-09-01 11:08:53 -0500247 if (!(i_parsedConfigJson["frus"][i_vpdFilePath].at(0))["postFailAction"]
248 .contains(i_flagToProcess))
249 {
250 o_errCode = error_code::MISSING_FLAG;
251 return false;
252 }
RekhaAparna01c11e8b62025-02-20 00:34:35 -0600253
Rekha Aparnaff7d7992025-09-01 11:08:53 -0500254 for (const auto& l_tags : (i_parsedConfigJson["frus"][i_vpdFilePath].at(
255 0))["postFailAction"][i_flagToProcess]
256 .items())
257 {
258 auto itrToFunction = funcionMap.find(l_tags.key());
259 if (itrToFunction != funcionMap.end())
RekhaAparna01c11e8b62025-02-20 00:34:35 -0600260 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -0500261 if (!itrToFunction->second(i_parsedConfigJson, i_vpdFilePath,
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500262 "postFailAction", i_flagToProcess,
263 o_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500264 {
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500265 if (o_errCode)
266 {
267 logging::logMessage(
268 l_tags.key() + " failed for [" + i_vpdFilePath +
Rekha Aparnac6159a22025-10-09 12:20:20 +0530269 "]. Reason " + commonUtility::getErrCodeMsg(o_errCode));
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500270 }
Rekha Aparnaff7d7992025-09-01 11:08:53 -0500271 return false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500272 }
273 }
274 }
275
276 return true;
277}
278
279/**
280 * @brief Process "systemCmd" tag for a given FRU.
281 *
282 * The API will process "systemCmd" tag if it is defined in the config
283 * JSON for the given FRU.
284 *
285 * @param[in] i_parsedConfigJson - config JSON
286 * @param[in] i_vpdFilePath - EEPROM file path
287 * @param[in] i_baseAction - Base action for which this tag has been called.
288 * @param[in] i_flagToProcess - Flag nested under the base action for which this
289 * tag has been called.
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500290 * @param[out] o_errCode - To set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500291 * @return Execution status.
292 */
293inline bool processSystemCmdTag(
294 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500295 const std::string& i_baseAction, const std::string& i_flagToProcess,
296 uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500297{
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500298 if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
299 i_baseAction.empty() || i_flagToProcess.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500300 {
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500301 o_errCode = error_code::INVALID_INPUT_PARAMETER;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500302 return false;
303 }
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500304
Rekha Aparna41f47e72025-09-18 01:44:09 -0500305 try
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500306 {
Rekha Aparna41f47e72025-09-18 01:44:09 -0500307 if (!((i_parsedConfigJson["frus"][i_vpdFilePath].at(
308 0)[i_baseAction][i_flagToProcess]["systemCmd"])
309 .contains("cmd")))
310 {
311 o_errCode = error_code::MISSING_FLAG;
312 return false;
313 }
314
315 const std::string& l_systemCommand =
316 i_parsedConfigJson["frus"][i_vpdFilePath].at(
317 0)[i_baseAction][i_flagToProcess]["systemCmd"]["cmd"];
318
319 commonUtility::executeCmd(l_systemCommand);
320 }
321 catch (const std::exception& l_ex)
322 {
323 o_errCode = error_code::ERROR_PROCESSING_SYSTEM_CMD;
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500324 return false;
325 }
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500326 return true;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500327}
328
329/**
330 * @brief Checks for presence of a given FRU using GPIO line.
331 *
332 * This API returns the presence information of the FRU corresponding to the
333 * given VPD file path by setting the presence pin.
334 *
335 * @param[in] i_parsedConfigJson - config JSON
336 * @param[in] i_vpdFilePath - EEPROM file path
337 * @param[in] i_baseAction - Base action for which this tag has been called.
338 * @param[in] i_flagToProcess - Flag nested under the base action for which this
339 * tag has been called.
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500340 * @param[out] o_errCode - To set error code in case of error
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500341 * @return Execution status.
342 */
343inline bool processGpioPresenceTag(
344 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500345 const std::string& i_baseAction, const std::string& i_flagToProcess,
346 uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500347{
Sunny Srivastava0a5fce12025-04-09 11:09:51 +0530348 std::string l_presencePinName;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500349 try
350 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +0530351 if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
352 i_baseAction.empty() || i_flagToProcess.empty())
353 {
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500354 o_errCode = error_code::INVALID_INPUT_PARAMETER;
355 return false;
Sunny Srivastava0a5fce12025-04-09 11:09:51 +0530356 }
357
358 if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at(
359 0)[i_baseAction][i_flagToProcess]["gpioPresence"])
360 .contains("pin")) &&
361 ((i_parsedConfigJson["frus"][i_vpdFilePath].at(
362 0)[i_baseAction][i_flagToProcess]["gpioPresence"])
363 .contains("value"))))
364 {
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500365 o_errCode = error_code::JSON_MISSING_GPIO_INFO;
366 return false;
Sunny Srivastava0a5fce12025-04-09 11:09:51 +0530367 }
368
369 // get the pin name
370 l_presencePinName = i_parsedConfigJson["frus"][i_vpdFilePath].at(
371 0)[i_baseAction][i_flagToProcess]["gpioPresence"]["pin"];
372
373 // get the pin value
374 uint8_t l_presencePinValue =
375 i_parsedConfigJson["frus"][i_vpdFilePath].at(
376 0)[i_baseAction][i_flagToProcess]["gpioPresence"]["value"];
377
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500378 gpiod::line l_presenceLine = gpiod::find_line(l_presencePinName);
379
380 if (!l_presenceLine)
381 {
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500382 o_errCode = error_code::DEVICE_PRESENCE_UNKNOWN;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500383 throw GpioException("Couldn't find the GPIO line.");
384 }
385
386 l_presenceLine.request({"Read the presence line",
387 gpiod::line_request::DIRECTION_INPUT, 0});
388
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500389 if (l_presencePinValue != l_presenceLine.get_value())
Sunny Srivastava0a5fce12025-04-09 11:09:51 +0530390 {
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500391 // As false is being returned in this case, caller needs to know
392 // that it is not due to some exception. It is because the pin was
393 // read correctly but was not having expected value.
394 o_errCode = error_code::DEVICE_NOT_PRESENT;
Sunny Srivastava0a5fce12025-04-09 11:09:51 +0530395 return false;
396 }
397
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500398 return true;
399 }
400 catch (const std::exception& l_ex)
401 {
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500402 std::string l_errMsg = "Exception on GPIO line: ";
403 l_errMsg += l_presencePinName;
404 l_errMsg += " Reason: ";
Sunny Srivastava0a5fce12025-04-09 11:09:51 +0530405 l_errMsg += l_ex.what();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500406 l_errMsg += " File: " + i_vpdFilePath + " Pel Logged";
407
Rekha Aparna017567a2025-08-13 02:07:06 -0500408 uint16_t l_errCode = 0;
409
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500410 // ToDo -- Update Internal Rc code.
411 EventLogger::createAsyncPelWithInventoryCallout(
Sunny Srivastava0a5fce12025-04-09 11:09:51 +0530412 EventLogger::getErrorType(l_ex), types::SeverityType::Informational,
Rekha Aparna017567a2025-08-13 02:07:06 -0500413 {{getInventoryObjPathFromJson(i_parsedConfigJson, i_vpdFilePath,
414 l_errCode),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500415 types::CalloutPriority::High}},
416 std::source_location::current().file_name(),
417 std::source_location::current().function_name(), 0, l_errMsg,
418 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
419
420 logging::logMessage(l_errMsg);
421
422 // Except when GPIO pin value is false, we go and try collecting the
423 // FRU VPD as we couldn't able to read GPIO pin value due to some
424 // error/exception. So returning true in error scenario.
425 return true;
426 }
427}
428
429/**
430 * @brief Process "setGpio" tag for a given FRU.
431 *
432 * This API enables the GPIO line.
433 *
434 * @param[in] i_parsedConfigJson - config JSON
435 * @param[in] i_vpdFilePath - EEPROM file path
436 * @param[in] i_baseAction - Base action for which this tag has been called.
437 * @param[in] i_flagToProcess - Flag nested under the base action for which this
438 * tag has been called.
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500439 * @param[out] o_errCode - To set error code in case of error
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500440 * @return Execution status.
441 */
442inline bool procesSetGpioTag(
443 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500444 const std::string& i_baseAction, const std::string& i_flagToProcess,
445 uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500446{
Sunny Srivastava0a5fce12025-04-09 11:09:51 +0530447 std::string l_pinName;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500448 try
449 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +0530450 if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
451 i_baseAction.empty() || i_flagToProcess.empty())
452 {
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500453 o_errCode = error_code::INVALID_INPUT_PARAMETER;
454 return false;
Sunny Srivastava0a5fce12025-04-09 11:09:51 +0530455 }
456
457 if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at(
458 0)[i_baseAction][i_flagToProcess]["setGpio"])
459 .contains("pin")) &&
460 ((i_parsedConfigJson["frus"][i_vpdFilePath].at(
461 0)[i_baseAction][i_flagToProcess]["setGpio"])
462 .contains("value"))))
463 {
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500464 o_errCode = error_code::JSON_MISSING_GPIO_INFO;
465 return false;
Sunny Srivastava0a5fce12025-04-09 11:09:51 +0530466 }
467
468 l_pinName = i_parsedConfigJson["frus"][i_vpdFilePath].at(
469 0)[i_baseAction][i_flagToProcess]["setGpio"]["pin"];
470
471 // Get the value to set
472 uint8_t l_pinValue = i_parsedConfigJson["frus"][i_vpdFilePath].at(
473 0)[i_baseAction][i_flagToProcess]["setGpio"]["value"];
474
475 logging::logMessage(
476 "Setting GPIO: " + l_pinName + " to " + std::to_string(l_pinValue));
477
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500478 gpiod::line l_outputLine = gpiod::find_line(l_pinName);
479
480 if (!l_outputLine)
481 {
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500482 o_errCode = error_code::GPIO_LINE_EXCEPTION;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500483 throw GpioException("Couldn't find GPIO line.");
484 }
485
486 l_outputLine.request(
487 {"FRU Action", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
488 l_pinValue);
489 return true;
490 }
Sunny Srivastava0a5fce12025-04-09 11:09:51 +0530491 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500492 {
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500493 std::string l_errMsg = "Exception on GPIO line: ";
494 l_errMsg += l_pinName;
495 l_errMsg += " Reason: ";
496 l_errMsg += l_ex.what();
497 l_errMsg += " File: " + i_vpdFilePath + " Pel Logged";
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500498
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500499 uint16_t l_errCode = 0;
Rekha Aparna017567a2025-08-13 02:07:06 -0500500
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500501 // ToDo -- Update Internal RC code
502 EventLogger::createAsyncPelWithInventoryCallout(
503 EventLogger::getErrorType(l_ex), types::SeverityType::Informational,
504 {{getInventoryObjPathFromJson(i_parsedConfigJson, i_vpdFilePath,
505 l_errCode),
506 types::CalloutPriority::High}},
507 std::source_location::current().file_name(),
508 std::source_location::current().function_name(), 0, l_errMsg,
509 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500510
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500511 logging::logMessage(l_errMsg);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500512
513 return false;
514 }
515}
516
517/**
518 * @brief Process any action, if defined in config JSON.
519 *
520 * If any FRU(s) requires any special handling, then this base action can be
521 * defined for that FRU in the config JSON, processing of which will be handled
522 * in this API.
523 * Examples of action - preAction, PostAction etc.
524 *
525 * @param[in] i_parsedConfigJson - config JSON
526 * @param[in] i_action - Base action to be performed.
527 * @param[in] i_vpdFilePath - EEPROM file path
528 * @param[in] i_flagToProcess - To identify which flag(s) needs to be processed
529 * under PreAction tag of config JSON.
Sunny Srivastava4f053df2025-09-03 02:27:37 -0500530 * @param[out] o_errCode - To set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500531 * @return - success or failure
532 */
533inline bool executeBaseAction(
534 const nlohmann::json& i_parsedConfigJson, const std::string& i_action,
Sunny Srivastava4f053df2025-09-03 02:27:37 -0500535 const std::string& i_vpdFilePath, const std::string& i_flagToProcess,
536 uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500537{
Sunny Srivastava4f053df2025-09-03 02:27:37 -0500538 if (i_flagToProcess.empty() || i_action.empty() || i_vpdFilePath.empty() ||
539 !i_parsedConfigJson.contains("frus"))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500540 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -0500541 o_errCode = error_code::INVALID_INPUT_PARAMETER;
542 return false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500543 }
Sunny Srivastava4f053df2025-09-03 02:27:37 -0500544 if (!i_parsedConfigJson["frus"].contains(i_vpdFilePath))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500545 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -0500546 o_errCode = error_code::FILE_NOT_FOUND;
547 return false;
548 }
549 if (!i_parsedConfigJson["frus"][i_vpdFilePath].at(0).contains(i_action))
550 {
551 o_errCode = error_code::MISSING_ACTION_TAG;
552 return false;
553 }
554
555 if (!(i_parsedConfigJson["frus"][i_vpdFilePath].at(0))[i_action].contains(
556 i_flagToProcess))
557 {
558 o_errCode = error_code::MISSING_FLAG;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500559 return false;
560 }
561
562 const nlohmann::json& l_tagsJson =
563 (i_parsedConfigJson["frus"][i_vpdFilePath].at(
564 0))[i_action][i_flagToProcess];
565
566 for (const auto& l_tag : l_tagsJson.items())
567 {
568 auto itrToFunction = funcionMap.find(l_tag.key());
569 if (itrToFunction != funcionMap.end())
570 {
571 if (!itrToFunction->second(i_parsedConfigJson, i_vpdFilePath,
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500572 i_action, i_flagToProcess, o_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500573 {
574 // In case any of the tag fails to execute. Mark action
575 // as failed for that flag.
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500576 if (o_errCode)
577 {
578 logging::logMessage(
579 l_tag.key() + " failed for [" + i_vpdFilePath +
Rekha Aparnac6159a22025-10-09 12:20:20 +0530580 "]. Reason " + commonUtility::getErrCodeMsg(o_errCode));
Sunny Srivastava84c3d232025-09-03 00:47:10 -0500581 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500582 return false;
583 }
584 }
585 }
586
587 return true;
588}
589
590/**
591 * @brief Get redundant FRU path from system config JSON
592 *
593 * Given either D-bus inventory path/FRU path/redundant FRU path, this
594 * API returns the redundant FRU path taken from "redundantEeprom" tag from
595 * system config JSON.
596 *
597 * @param[in] i_sysCfgJsonObj - System config JSON object.
598 * @param[in] i_vpdPath - Path to where VPD is stored.
Rekha Aparnabb52af42025-09-02 04:05:18 -0500599 * @param[out] o_errCode - To set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500600 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500601 * @return On success return valid path, on failure return empty string.
602 */
603inline std::string getRedundantEepromPathFromJson(
Rekha Aparnabb52af42025-09-02 04:05:18 -0500604 const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdPath,
605 uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500606{
Rekha Aparnabb52af42025-09-02 04:05:18 -0500607 if (i_vpdPath.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500608 {
Rekha Aparnabb52af42025-09-02 04:05:18 -0500609 o_errCode = error_code::INVALID_INPUT_PARAMETER;
610 return std::string{};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500611 }
Rekha Aparnabb52af42025-09-02 04:05:18 -0500612
613 if (!i_sysCfgJsonObj.contains("frus"))
RekhaAparna017fea9f52025-02-17 04:14:02 -0600614 {
Rekha Aparnabb52af42025-09-02 04:05:18 -0500615 o_errCode = error_code::INVALID_JSON;
616 return std::string{};
617 }
618
619 // check if given path is FRU path
620 if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
621 {
622 return i_sysCfgJsonObj["frus"][i_vpdPath].at(0).value(
623 "redundantEeprom", "");
624 }
625
626 const nlohmann::json& l_fruList =
627 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
628
629 for (const auto& l_fru : l_fruList.items())
630 {
631 const std::string& l_fruPath = l_fru.key();
632 const std::string& l_redundantFruPath =
633 i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("redundantEeprom",
634 "");
635
636 // check if given path is inventory path or redundant FRU path
637 if ((i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("inventoryPath",
638 "") == i_vpdPath) ||
639 (l_redundantFruPath == i_vpdPath))
640 {
641 return l_redundantFruPath;
642 }
RekhaAparna017fea9f52025-02-17 04:14:02 -0600643 }
644
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500645 return std::string();
646}
647
648/**
649 * @brief Get FRU EEPROM path from system config JSON
650 *
651 * Given either D-bus inventory path/FRU EEPROM path/redundant EEPROM path,
652 * this API returns FRU EEPROM path if present in JSON.
653 *
654 * @param[in] i_sysCfgJsonObj - System config JSON object
655 * @param[in] i_vpdPath - Path to where VPD is stored.
Rekha Aparna0578dd22025-09-02 08:20:21 -0500656 * @param[out] o_errCode - To set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500657 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500658 * @return On success return valid path, on failure return empty string.
659 */
660inline std::string getFruPathFromJson(const nlohmann::json& i_sysCfgJsonObj,
Rekha Aparna0578dd22025-09-02 08:20:21 -0500661 const std::string& i_vpdPath,
662 uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500663{
Rekha Aparna0578dd22025-09-02 08:20:21 -0500664 if (i_vpdPath.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500665 {
Rekha Aparna0578dd22025-09-02 08:20:21 -0500666 o_errCode = error_code::INVALID_INPUT_PARAMETER;
667 return std::string{};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500668 }
Rekha Aparna0578dd22025-09-02 08:20:21 -0500669
670 if (!i_sysCfgJsonObj.contains("frus"))
RekhaAparna017fea9f52025-02-17 04:14:02 -0600671 {
Rekha Aparna0578dd22025-09-02 08:20:21 -0500672 o_errCode = error_code::INVALID_JSON;
673 return std::string{};
674 }
675
676 // check if given path is FRU path
677 if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
678 {
679 return i_vpdPath;
680 }
681
682 const nlohmann::json& l_fruList =
683 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
684
685 for (const auto& l_fru : l_fruList.items())
686 {
687 const auto l_fruPath = l_fru.key();
688
689 // check if given path is redundant FRU path or inventory path
690 if (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
691 "redundantEeprom", "") ||
692 (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
693 "inventoryPath", "")))
694 {
695 return l_fruPath;
696 }
RekhaAparna017fea9f52025-02-17 04:14:02 -0600697 }
698
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500699 return std::string();
700}
701
702/**
703 * @brief An API to check backup and restore VPD is required.
704 *
705 * The API checks if there is provision for backup and restore mentioned in the
706 * system config JSON, by looking "backupRestoreConfigPath" tag.
707 * Checks if the path mentioned is a hardware path, by checking if the file path
708 * exists and size of contents in the path.
709 *
710 * @param[in] i_sysCfgJsonObj - System config JSON object.
Rekha Aparna196e3082025-09-08 20:40:35 -0500711 * @param[out] o_errCode - To set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500712 *
713 * @return true if backup and restore is required, false otherwise.
714 */
Rekha Aparna196e3082025-09-08 20:40:35 -0500715inline bool isBackupAndRestoreRequired(const nlohmann::json& i_sysCfgJsonObj,
716 uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500717{
Rekha Aparna196e3082025-09-08 20:40:35 -0500718 if (i_sysCfgJsonObj.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500719 {
Rekha Aparna196e3082025-09-08 20:40:35 -0500720 o_errCode = error_code::INVALID_INPUT_PARAMETER;
721 return false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500722 }
Rekha Aparna196e3082025-09-08 20:40:35 -0500723
724 const std::string& l_backupAndRestoreCfgFilePath =
725 i_sysCfgJsonObj.value("backupRestoreConfigPath", "");
726
727 if (!l_backupAndRestoreCfgFilePath.empty() &&
728 std::filesystem::exists(l_backupAndRestoreCfgFilePath) &&
729 !std::filesystem::is_empty(l_backupAndRestoreCfgFilePath))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500730 {
Rekha Aparna196e3082025-09-08 20:40:35 -0500731 return true;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500732 }
Rekha Aparna196e3082025-09-08 20:40:35 -0500733
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500734 return false;
735}
736
737/** @brief API to check if an action is required for given EEPROM path.
738 *
739 * System config JSON can contain pre-action, post-action etc. like actions
740 * defined for an EEPROM path. The API will check if any such action is defined
741 * for the EEPROM.
742 *
743 * @param[in] i_sysCfgJsonObj - System config JSON object.
744 * @param[in] i_vpdFruPath - EEPROM path.
745 * @param[in] i_action - Action to be checked.
746 * @param[in] i_flowFlag - Denotes the flow w.r.t which the action should be
747 * triggered.
Rekha Aparnab50bf0e2025-09-02 21:13:26 -0500748 * @param[out] o_errCode - To set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500749 * @return - True if action is defined for the flow, false otherwise.
750 */
Rekha Aparnab50bf0e2025-09-02 21:13:26 -0500751inline bool isActionRequired(const nlohmann::json& i_sysCfgJsonObj,
752 const std::string& i_vpdFruPath,
753 const std::string& i_action,
754 const std::string& i_flowFlag, uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500755{
756 if (i_vpdFruPath.empty() || i_action.empty() || i_flowFlag.empty())
757 {
Rekha Aparnab50bf0e2025-09-02 21:13:26 -0500758 o_errCode = error_code::INVALID_INPUT_PARAMETER;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500759 return false;
760 }
761
762 if (!i_sysCfgJsonObj.contains("frus"))
763 {
Rekha Aparnab50bf0e2025-09-02 21:13:26 -0500764 o_errCode = error_code::INVALID_JSON;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500765 return false;
766 }
767
768 if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
769 {
Rekha Aparnab50bf0e2025-09-02 21:13:26 -0500770 o_errCode = error_code::FRU_PATH_NOT_FOUND;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500771 return false;
772 }
773
774 if ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)).contains(i_action))
775 {
776 if ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))[i_action].contains(
777 i_flowFlag))
778 {
779 return true;
780 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500781 }
782 return false;
783}
784
785/**
786 * @brief An API to return list of FRUs that needs GPIO polling.
787 *
788 * An API that checks for the FRUs that requires GPIO polling and returns
789 * a list of FRUs that needs polling. Returns an empty list if there are
790 * no FRUs that requires polling.
791 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500792 * @param[in] i_sysCfgJsonObj - System config JSON object.
Rekha Aparnadc47adb2025-09-08 11:29:58 -0500793 * @param[out] o_errCode - To set error codes in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500794 *
RekhaAparna017fea9f52025-02-17 04:14:02 -0600795 * @return On success list of FRUs parameters that needs polling. On failure,
796 * empty list.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500797 */
Patrick Williams43fedab2025-02-03 14:28:05 -0500798inline std::vector<std::string> getListOfGpioPollingFrus(
Rekha Aparnadc47adb2025-09-08 11:29:58 -0500799 const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500800{
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500801 std::vector<std::string> l_gpioPollingRequiredFrusList;
802
Rekha Aparnadc47adb2025-09-08 11:29:58 -0500803 if (i_sysCfgJsonObj.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500804 {
Rekha Aparnadc47adb2025-09-08 11:29:58 -0500805 o_errCode = error_code::INVALID_INPUT_PARAMETER;
806 return l_gpioPollingRequiredFrusList;
RekhaAparna017fea9f52025-02-17 04:14:02 -0600807 }
Rekha Aparnadc47adb2025-09-08 11:29:58 -0500808
809 if (!i_sysCfgJsonObj.contains("frus"))
RekhaAparna017fea9f52025-02-17 04:14:02 -0600810 {
Rekha Aparnadc47adb2025-09-08 11:29:58 -0500811 o_errCode = error_code::INVALID_JSON;
812 return l_gpioPollingRequiredFrusList;
813 }
814
815 for (const auto& l_fru : i_sysCfgJsonObj["frus"].items())
816 {
817 const auto l_fruPath = l_fru.key();
818
819 bool l_isHotPluggableFru =
820 isActionRequired(i_sysCfgJsonObj, l_fruPath, "pollingRequired",
821 "hotPlugging", o_errCode);
822
823 if (o_errCode)
824 {
825 logging::logMessage(
826 "Error while checking if action required for FRU [" +
827 std::string(l_fruPath) +
Rekha Aparnac6159a22025-10-09 12:20:20 +0530828 "], error : " + commonUtility::getErrCodeMsg(o_errCode));
Rekha Aparnadc47adb2025-09-08 11:29:58 -0500829
830 return l_gpioPollingRequiredFrusList;
831 }
832
833 if (l_isHotPluggableFru)
834 {
835 if (i_sysCfgJsonObj["frus"][l_fruPath]
836 .at(0)["pollingRequired"]["hotPlugging"]
837 .contains("gpioPresence"))
838 {
839 l_gpioPollingRequiredFrusList.push_back(l_fruPath);
840 }
841 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500842 }
843
844 return l_gpioPollingRequiredFrusList;
845}
846
847/**
848 * @brief Get all related path(s) to update keyword value.
849 *
850 * Given FRU EEPROM path/Inventory path needs keyword's value update, this API
851 * returns tuple of FRU EEPROM path, inventory path and redundant EEPROM path if
852 * exists in the system config JSON.
853 *
854 * Note: If the inventory object path or redundant EEPROM path(s) are not found
855 * in the system config JSON, corresponding fields will have empty value in the
856 * returning tuple.
857 *
858 * @param[in] i_sysCfgJsonObj - System config JSON object.
859 * @param[in,out] io_vpdPath - Inventory object path or FRU EEPROM path.
Rekha Aparna2d6f0712025-09-02 03:52:57 -0500860 * @param[out] o_errCode - To set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500861 *
862 * @return On success returns tuple of EEPROM path, inventory path & redundant
863 * path, on failure returns tuple with given input path alone.
864 */
865inline std::tuple<std::string, std::string, std::string>
866 getAllPathsToUpdateKeyword(const nlohmann::json& i_sysCfgJsonObj,
Rekha Aparna2d6f0712025-09-02 03:52:57 -0500867 std::string io_vpdPath, uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500868{
869 types::Path l_inventoryObjPath;
870 types::Path l_redundantFruPath;
Rekha Aparna2d6f0712025-09-02 03:52:57 -0500871 o_errCode = 0;
872
873 if (i_sysCfgJsonObj.empty() || io_vpdPath.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500874 {
Rekha Aparna2d6f0712025-09-02 03:52:57 -0500875 o_errCode = error_code::INVALID_INPUT_PARAMETER;
876 return std::make_tuple(io_vpdPath, l_inventoryObjPath,
877 l_redundantFruPath);
878 }
Rekha Aparna0578dd22025-09-02 08:20:21 -0500879
Rekha Aparna2d6f0712025-09-02 03:52:57 -0500880 // Get hardware path from system config JSON.
881 const types::Path l_fruPath =
882 jsonUtility::getFruPathFromJson(i_sysCfgJsonObj, io_vpdPath, o_errCode);
883
884 if (!l_fruPath.empty())
885 {
886 io_vpdPath = l_fruPath;
887
888 // Get inventory object path from system config JSON
889 l_inventoryObjPath = jsonUtility::getInventoryObjPathFromJson(
890 i_sysCfgJsonObj, l_fruPath, o_errCode);
891
892 if (l_inventoryObjPath.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500893 {
Rekha Aparna2d6f0712025-09-02 03:52:57 -0500894 if (o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500895 {
Rekha Aparna2d6f0712025-09-02 03:52:57 -0500896 logging::logMessage(
897 "Failed to get inventory path from JSON for [" +
Rekha Aparnac6159a22025-10-09 12:20:20 +0530898 io_vpdPath +
899 "], error : " + commonUtility::getErrCodeMsg(o_errCode));
Rekha Aparna2d6f0712025-09-02 03:52:57 -0500900 }
901 else
902 {
903 o_errCode = error_code::FRU_PATH_NOT_FOUND;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500904 }
Rekha Aparna0578dd22025-09-02 08:20:21 -0500905
Rekha Aparna2d6f0712025-09-02 03:52:57 -0500906 return std::make_tuple(io_vpdPath, l_inventoryObjPath,
907 l_redundantFruPath);
908 }
909
910 // Get redundant hardware path if present in system config JSON
911 l_redundantFruPath = jsonUtility::getRedundantEepromPathFromJson(
912 i_sysCfgJsonObj, l_fruPath, o_errCode);
913
914 if (l_redundantFruPath.empty())
915 {
916 if (o_errCode)
917 {
918 logging::logMessage(
919 "Failed to get redundant EEPROM path for FRU [" +
Rekha Aparnac6159a22025-10-09 12:20:20 +0530920 l_fruPath +
921 "], error : " + commonUtility::getErrCodeMsg(o_errCode));
Rekha Aparna2d6f0712025-09-02 03:52:57 -0500922
923 o_errCode = error_code::ERROR_GETTING_REDUNDANT_PATH;
924 }
925 else
926 {
927 o_errCode = error_code::REDUNDANT_PATH_NOT_FOUND;
928 }
Rekha Aparna0578dd22025-09-02 08:20:21 -0500929
930 return std::make_tuple(io_vpdPath, l_inventoryObjPath,
931 l_redundantFruPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500932 }
933 }
Rekha Aparna2d6f0712025-09-02 03:52:57 -0500934 else if (o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500935 {
936 logging::logMessage(
Rekha Aparna2d6f0712025-09-02 03:52:57 -0500937 "Failed to get FRU path from JSON for [" + io_vpdPath +
Rekha Aparnac6159a22025-10-09 12:20:20 +0530938 "], error : " + commonUtility::getErrCodeMsg(o_errCode));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500939 }
Rekha Aparna2d6f0712025-09-02 03:52:57 -0500940 else
941 {
942 o_errCode = error_code::NO_EEPROM_PATH;
943 }
944
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500945 return std::make_tuple(io_vpdPath, l_inventoryObjPath, l_redundantFruPath);
946}
947
948/**
949 * @brief An API to get DBus service name.
950 *
951 * Given DBus inventory path, this API returns DBus service name if present in
952 * the JSON.
953 *
954 * @param[in] i_sysCfgJsonObj - System config JSON object.
955 * @param[in] l_inventoryPath - DBus inventory path.
Rekha Aparnae0d24c02025-09-02 00:15:06 -0500956 * @param[out] o_errCode - To set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500957 *
958 * @return On success returns the service name present in the system config
959 * JSON, otherwise empty string.
960 *
961 * Note: Caller has to handle in case of empty string received.
962 */
963inline std::string getServiceName(const nlohmann::json& i_sysCfgJsonObj,
Rekha Aparnae0d24c02025-09-02 00:15:06 -0500964 const std::string& l_inventoryPath,
965 uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500966{
Rekha Aparnae0d24c02025-09-02 00:15:06 -0500967 if (l_inventoryPath.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500968 {
Rekha Aparnae0d24c02025-09-02 00:15:06 -0500969 o_errCode = error_code::INVALID_INPUT_PARAMETER;
970 return std::string{};
971 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500972
Rekha Aparnae0d24c02025-09-02 00:15:06 -0500973 if (!i_sysCfgJsonObj.contains("frus"))
974 {
975 o_errCode = error_code::INVALID_JSON;
976 return std::string{};
977 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500978
Rekha Aparnae0d24c02025-09-02 00:15:06 -0500979 const nlohmann::json& l_listOfFrus =
980 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500981
Rekha Aparnae0d24c02025-09-02 00:15:06 -0500982 for (const auto& l_frus : l_listOfFrus.items())
983 {
984 for (const auto& l_inventoryItem : l_frus.value())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500985 {
Rekha Aparnae0d24c02025-09-02 00:15:06 -0500986 if (l_inventoryPath.compare(l_inventoryItem["inventoryPath"]) ==
987 constants::STR_CMP_SUCCESS)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500988 {
Rekha Aparnae0d24c02025-09-02 00:15:06 -0500989 if (l_inventoryItem.contains("serviceName"))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500990 {
Rekha Aparnae0d24c02025-09-02 00:15:06 -0500991 return l_inventoryItem.value("serviceName", "");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500992 }
Rekha Aparnae0d24c02025-09-02 00:15:06 -0500993
994 o_errCode = error_code::JSON_MISSING_SERVICE_NAME;
995 return std::string{};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500996 }
997 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500998 }
Rekha Aparnae0d24c02025-09-02 00:15:06 -0500999
1000 o_errCode = error_code::FRU_PATH_NOT_FOUND;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001001 return std::string{};
1002}
1003
1004/**
1005 * @brief An API to check if a FRU is tagged as "powerOffOnly"
1006 *
1007 * Given the system config JSON and VPD FRU path, this API checks if the FRU
1008 * VPD can be collected at Chassis Power Off state only.
1009 *
1010 * @param[in] i_sysCfgJsonObj - System config JSON object.
1011 * @param[in] i_vpdFruPath - EEPROM path.
Rekha Aparna52041882025-09-01 20:48:07 -05001012 * @param[out] o_errCode - To set error code for the error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001013 * @return - True if FRU VPD can be collected at Chassis Power Off state only.
1014 * False otherwise
1015 */
1016inline bool isFruPowerOffOnly(const nlohmann::json& i_sysCfgJsonObj,
Rekha Aparna52041882025-09-01 20:48:07 -05001017 const std::string& i_vpdFruPath,
1018 uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001019{
1020 if (i_vpdFruPath.empty())
1021 {
Rekha Aparna52041882025-09-01 20:48:07 -05001022 o_errCode = error_code::INVALID_INPUT_PARAMETER;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001023 return false;
1024 }
1025
1026 if (!i_sysCfgJsonObj.contains("frus"))
1027 {
Rekha Aparna52041882025-09-01 20:48:07 -05001028 o_errCode = error_code::INVALID_JSON;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001029 return false;
1030 }
1031
1032 if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
1033 {
Rekha Aparna52041882025-09-01 20:48:07 -05001034 o_errCode = error_code::FRU_PATH_NOT_FOUND;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001035 return false;
1036 }
1037
1038 return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
1039 .contains("powerOffOnly") &&
1040 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)["powerOffOnly"]));
1041}
1042
1043/**
1044 * @brief API which tells if the FRU is replaceable at runtime
1045 *
1046 * @param[in] i_sysCfgJsonObj - System config JSON object.
1047 * @param[in] i_vpdFruPath - EEPROM path.
Rekha Aparnaad0db9e2025-09-01 20:29:18 -05001048 * @param[out] o_errCode - to set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001049 *
1050 * @return true if FRU is replaceable at runtime. false otherwise.
1051 */
1052inline bool isFruReplaceableAtRuntime(const nlohmann::json& i_sysCfgJsonObj,
Rekha Aparnaad0db9e2025-09-01 20:29:18 -05001053 const std::string& i_vpdFruPath,
1054 uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001055{
Rekha Aparnaad0db9e2025-09-01 20:29:18 -05001056 if (i_vpdFruPath.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001057 {
Rekha Aparnaad0db9e2025-09-01 20:29:18 -05001058 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1059 return false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001060 }
Rekha Aparnaad0db9e2025-09-01 20:29:18 -05001061
1062 if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001063 {
Rekha Aparnaad0db9e2025-09-01 20:29:18 -05001064 o_errCode = error_code::INVALID_JSON;
1065 return false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001066 }
1067
Rekha Aparna41f47e72025-09-18 01:44:09 -05001068 if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
1069 {
1070 o_errCode = error_code::FRU_PATH_NOT_FOUND;
1071 return false;
1072 }
1073
Rekha Aparnaad0db9e2025-09-01 20:29:18 -05001074 return (
1075 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
1076 .contains("replaceableAtRuntime") &&
1077 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)["replaceableAtRuntime"]));
1078
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001079 return false;
1080}
1081
1082/**
1083 * @brief API which tells if the FRU is replaceable at standby
1084 *
1085 * @param[in] i_sysCfgJsonObj - System config JSON object.
1086 * @param[in] i_vpdFruPath - EEPROM path.
Rekha Aparna40845612025-09-01 19:58:56 -05001087 * @param[out] o_errCode - set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001088 *
1089 * @return true if FRU is replaceable at standby. false otherwise.
1090 */
1091inline bool isFruReplaceableAtStandby(const nlohmann::json& i_sysCfgJsonObj,
Rekha Aparna40845612025-09-01 19:58:56 -05001092 const std::string& i_vpdFruPath,
1093 uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001094{
Rekha Aparna40845612025-09-01 19:58:56 -05001095 if (i_vpdFruPath.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001096 {
Rekha Aparna40845612025-09-01 19:58:56 -05001097 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1098 return false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001099 }
Rekha Aparna40845612025-09-01 19:58:56 -05001100
1101 if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001102 {
Rekha Aparna40845612025-09-01 19:58:56 -05001103 o_errCode = error_code::INVALID_JSON;
Rekha Aparna41f47e72025-09-18 01:44:09 -05001104 return false;
1105 }
1106
1107 if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
1108 {
1109 o_errCode = error_code::FRU_PATH_NOT_FOUND;
1110 return false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001111 }
1112
Rekha Aparna40845612025-09-01 19:58:56 -05001113 return (
1114 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
1115 .contains("replaceableAtStandby") &&
1116 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)["replaceableAtStandby"]));
1117
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001118 return false;
1119}
1120
1121/**
1122 * @brief API to get list of FRUs replaceable at standby from JSON.
1123 *
1124 * The API will return a vector of FRUs inventory path which are replaceable at
1125 * standby.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001126 *
1127 * @param[in] i_sysCfgJsonObj - System config JSON object.
Rekha Aparna88d53302025-09-01 18:16:55 -05001128 * @param[out] o_errCode - To set error code in case of error.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001129 *
RekhaAparna017fea9f52025-02-17 04:14:02 -06001130 * @return - On success, list of FRUs replaceable at standby. On failure, empty
1131 * vector.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001132 */
Patrick Williams43fedab2025-02-03 14:28:05 -05001133inline std::vector<std::string> getListOfFrusReplaceableAtStandby(
Rekha Aparna88d53302025-09-01 18:16:55 -05001134 const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001135{
1136 std::vector<std::string> l_frusReplaceableAtStandby;
1137
Rekha Aparna88d53302025-09-01 18:16:55 -05001138 if (!i_sysCfgJsonObj.contains("frus"))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001139 {
Rekha Aparna88d53302025-09-01 18:16:55 -05001140 o_errCode = error_code::INVALID_JSON;
1141 return l_frusReplaceableAtStandby;
1142 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001143
Rekha Aparna88d53302025-09-01 18:16:55 -05001144 const nlohmann::json& l_fruList =
1145 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
RekhaAparna017fea9f52025-02-17 04:14:02 -06001146
Rekha Aparna88d53302025-09-01 18:16:55 -05001147 for (const auto& l_fru : l_fruList.items())
1148 {
1149 if (i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
1150 "replaceableAtStandby", false))
RekhaAparna017fea9f52025-02-17 04:14:02 -06001151 {
Rekha Aparna88d53302025-09-01 18:16:55 -05001152 const std::string& l_inventoryObjectPath =
1153 i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
1154 "inventoryPath", "");
1155
1156 if (!l_inventoryObjectPath.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001157 {
Rekha Aparna88d53302025-09-01 18:16:55 -05001158 l_frusReplaceableAtStandby.emplace_back(l_inventoryObjectPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001159 }
1160 }
1161 }
1162
1163 return l_frusReplaceableAtStandby;
1164}
1165
Sunny Srivastava022112b2025-02-19 19:53:29 +05301166/**
1167 * @brief API to select powerVS JSON based on system IM.
1168 *
1169 * The API selects respective JSON based on system IM, parse it and return the
1170 * JSON object. Empty JSON will be returned in case of any error. Caller needs
1171 * to handle empty value.
1172 *
1173 * @param[in] i_imValue - IM value of the system.
Rekha Aparnaadf85262025-09-09 00:41:00 -05001174 * @param[out] o_errCode - to set error code in case of error.
Sunny Srivastava022112b2025-02-19 19:53:29 +05301175 * @return Parsed JSON object, empty JSON otherwise.
1176 */
Rekha Aparnaadf85262025-09-09 00:41:00 -05001177inline nlohmann::json getPowerVsJson(const types::BinaryVector& i_imValue,
1178 uint16_t& o_errCode)
Sunny Srivastava022112b2025-02-19 19:53:29 +05301179{
Rekha Aparnaadf85262025-09-09 00:41:00 -05001180 if (i_imValue.empty() || i_imValue.size() < 4)
Sunny Srivastava022112b2025-02-19 19:53:29 +05301181 {
Rekha Aparnaadf85262025-09-09 00:41:00 -05001182 o_errCode = error_code::INVALID_INPUT_PARAMETER;
Sunny Srivastava022112b2025-02-19 19:53:29 +05301183 return nlohmann::json{};
1184 }
Rekha Aparnaadf85262025-09-09 00:41:00 -05001185
1186 o_errCode = 0;
1187 if ((i_imValue.at(0) == constants::HEX_VALUE_50) &&
1188 (i_imValue.at(1) == constants::HEX_VALUE_00) &&
1189 (i_imValue.at(2) == constants::HEX_VALUE_30))
Sunny Srivastava022112b2025-02-19 19:53:29 +05301190 {
Rekha Aparnaadf85262025-09-09 00:41:00 -05001191 nlohmann::json l_parsedJson = jsonUtility::getParsedJson(
1192 constants::power_vs_50003_json, o_errCode);
1193
1194 if (o_errCode)
1195 {
1196 logging::logMessage(
1197 "Failed to parse JSON file [ " +
1198 std::string(constants::power_vs_50003_json) +
Rekha Aparnac6159a22025-10-09 12:20:20 +05301199 " ], error : " + commonUtility::getErrCodeMsg(o_errCode));
Rekha Aparnaadf85262025-09-09 00:41:00 -05001200 }
1201
1202 return l_parsedJson;
Sunny Srivastava022112b2025-02-19 19:53:29 +05301203 }
Rekha Aparnaadf85262025-09-09 00:41:00 -05001204 else if (i_imValue.at(0) == constants::HEX_VALUE_50 &&
1205 (i_imValue.at(1) == constants::HEX_VALUE_00) &&
1206 (i_imValue.at(2) == constants::HEX_VALUE_10))
1207 {
1208 nlohmann::json l_parsedJson = jsonUtility::getParsedJson(
1209 constants::power_vs_50001_json, o_errCode);
1210
1211 if (o_errCode)
1212 {
1213 logging::logMessage(
1214 "Failed to parse JSON file [ " +
1215 std::string(constants::power_vs_50001_json) +
Rekha Aparnac6159a22025-10-09 12:20:20 +05301216 " ], error : " + commonUtility::getErrCodeMsg(o_errCode));
Rekha Aparnaadf85262025-09-09 00:41:00 -05001217 }
1218
1219 return l_parsedJson;
1220 }
1221 return nlohmann::json{};
Sunny Srivastava022112b2025-02-19 19:53:29 +05301222}
Souvik Roy495eedd2025-07-02 02:13:43 -05001223
1224/**
1225 * @brief API to get list of FRUs for which "monitorPresence" is true.
1226 *
1227 * @param[in] i_sysCfgJsonObj - System config JSON object.
Rekha Aparna2362bed2025-09-01 13:18:40 -05001228 * @param[out] o_errCode - To set error code in case of error.
Souvik Roy495eedd2025-07-02 02:13:43 -05001229 *
1230 * @return On success, returns list of FRUs for which "monitorPresence" is true,
1231 * empty list on error.
1232 */
1233inline std::vector<types::Path> getFrusWithPresenceMonitoring(
Rekha Aparna2362bed2025-09-01 13:18:40 -05001234 const nlohmann::json& i_sysCfgJsonObj, uint16_t& o_errCode)
Souvik Roy495eedd2025-07-02 02:13:43 -05001235{
1236 std::vector<types::Path> l_frusWithPresenceMonitoring;
Rekha Aparna2362bed2025-09-01 13:18:40 -05001237
1238 if (!i_sysCfgJsonObj.contains("frus"))
Souvik Roy495eedd2025-07-02 02:13:43 -05001239 {
Rekha Aparna2362bed2025-09-01 13:18:40 -05001240 o_errCode = error_code::INVALID_JSON;
1241 return l_frusWithPresenceMonitoring;
1242 }
Souvik Roy495eedd2025-07-02 02:13:43 -05001243
Rekha Aparna2362bed2025-09-01 13:18:40 -05001244 const nlohmann::json& l_listOfFrus =
1245 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
Souvik Roy495eedd2025-07-02 02:13:43 -05001246
Rekha Aparna2362bed2025-09-01 13:18:40 -05001247 for (const auto& l_aFru : l_listOfFrus)
1248 {
1249 if (l_aFru.at(0).value("monitorPresence", false))
Souvik Roy495eedd2025-07-02 02:13:43 -05001250 {
Rekha Aparna2362bed2025-09-01 13:18:40 -05001251 l_frusWithPresenceMonitoring.emplace_back(
1252 l_aFru.at(0).value("inventoryPath", ""));
Souvik Roy495eedd2025-07-02 02:13:43 -05001253 }
1254 }
Rekha Aparna2362bed2025-09-01 13:18:40 -05001255
Souvik Roy495eedd2025-07-02 02:13:43 -05001256 return l_frusWithPresenceMonitoring;
1257}
Souvik Roye9120152025-07-02 08:24:38 -05001258
1259/**
1260 * @brief API which tells if the FRU's presence is handled
1261 *
1262 * For a given FRU, this API checks if it's presence is handled by vpd-manager
1263 * by checking the "handlePresence" tag.
1264 *
1265 * @param[in] i_sysCfgJsonObj - System config JSON object.
1266 * @param[in] i_vpdFruPath - EEPROM path.
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001267 * @param[out] o_errCode - To set error code in case of failure.
Souvik Roye9120152025-07-02 08:24:38 -05001268 *
1269 * @return true if FRU presence is handled, false otherwise.
1270 */
1271inline bool isFruPresenceHandled(const nlohmann::json& i_sysCfgJsonObj,
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001272 const std::string& i_vpdFruPath,
1273 uint16_t& o_errCode)
Souvik Roye9120152025-07-02 08:24:38 -05001274{
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001275 if (i_vpdFruPath.empty())
Souvik Roye9120152025-07-02 08:24:38 -05001276 {
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001277 o_errCode = error_code::INVALID_INPUT_PARAMETER;
1278 return false;
Souvik Roye9120152025-07-02 08:24:38 -05001279 }
1280
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001281 if (!i_sysCfgJsonObj.contains("frus"))
1282 {
1283 o_errCode = error_code::INVALID_JSON;
1284 return false;
1285 }
1286
1287 if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
1288 {
1289 o_errCode = error_code::FRU_PATH_NOT_FOUND;
1290 return false;
1291 }
1292
1293 return i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0).value(
1294 "handlePresence", true);
Souvik Roye9120152025-07-02 08:24:38 -05001295}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001296} // namespace jsonUtility
1297} // namespace vpd