blob: 05e3fda27f80f4a91326719fa7bc7024cbfa7104 [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#pragma once
2
3#include "event_logger.hpp"
4#include "exceptions.hpp"
5#include "logger.hpp"
6#include "types.hpp"
7
8#include <gpiod.hpp>
9#include <nlohmann/json.hpp>
10#include <utility/common_utility.hpp>
11
12#include <fstream>
13#include <type_traits>
14#include <unordered_map>
15
16namespace vpd
17{
18namespace jsonUtility
19{
20
21// forward declaration of API for function map.
22bool processSystemCmdTag(const nlohmann::json& i_parsedConfigJson,
23 const std::string& i_vpdFilePath,
24 const std::string& i_baseAction,
25 const std::string& i_flagToProcess);
26
27// forward declaration of API for function map.
28bool processGpioPresenceTag(
29 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
30 const std::string& i_baseAction, const std::string& i_flagToProcess);
31
32// forward declaration of API for function map.
33bool procesSetGpioTag(const nlohmann::json& i_parsedConfigJson,
34 const std::string& i_vpdFilePath,
35 const std::string& i_baseAction,
36 const std::string& i_flagToProcess);
37
38// Function pointers to process tags from config JSON.
39typedef bool (*functionPtr)(
40 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
41 const std::string& i_baseAction, const std::string& i_flagToProcess);
42
43inline std::unordered_map<std::string, functionPtr> funcionMap{
44 {"gpioPresence", processGpioPresenceTag},
45 {"setGpio", procesSetGpioTag},
46 {"systemCmd", processSystemCmdTag}};
47
48/**
49 * @brief API to read VPD offset from JSON file.
50 *
51 * @param[in] i_sysCfgJsonObj - Parsed system config JSON object.
52 * @param[in] i_vpdFilePath - VPD file path.
53 * @return VPD offset if found in JSON, 0 otherwise.
54 */
55inline size_t getVPDOffset(const nlohmann::json& i_sysCfgJsonObj,
56 const std::string& i_vpdFilePath)
57{
58 if (i_vpdFilePath.empty() || (i_sysCfgJsonObj.empty()) ||
59 (!i_sysCfgJsonObj.contains("frus")))
60 {
61 return 0;
62 }
63
64 if (i_sysCfgJsonObj["frus"].contains(i_vpdFilePath))
65 {
66 return i_sysCfgJsonObj["frus"][i_vpdFilePath].at(0).value("offset", 0);
67 }
68
69 const nlohmann::json& l_fruList =
70 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
71
72 for (const auto& l_fru : l_fruList.items())
73 {
74 const auto l_fruPath = l_fru.key();
75
76 // check if given path is redundant FRU path
77 if (i_vpdFilePath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
78 "redundantEeprom", ""))
79 {
80 // Return the offset of redundant EEPROM taken from JSON.
81 return i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("offset", 0);
82 }
83 }
84
85 return 0;
86}
87
88/**
89 * @brief API to parse respective JSON.
90 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050091 * @param[in] pathToJson - Path to JSON.
RekhaAparna011ef21002025-02-18 23:47:36 -060092 * @return on success parsed JSON. On failure empty JSON object.
93 *
94 * Note: Caller has to handle it in case an empty JSON object is received.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050095 */
RekhaAparna011ef21002025-02-18 23:47:36 -060096inline nlohmann::json getParsedJson(const std::string& pathToJson) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050097{
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050098 try
99 {
RekhaAparna011ef21002025-02-18 23:47:36 -0600100 if (pathToJson.empty())
101 {
102 throw std::runtime_error("Path to JSON is missing");
103 }
104
105 if (!std::filesystem::exists(pathToJson) ||
106 std::filesystem::is_empty(pathToJson))
107 {
108 throw std::runtime_error("Incorrect file Path or empty file");
109 }
110
111 std::ifstream l_jsonFile(pathToJson);
112 if (!l_jsonFile)
113 {
114 throw std::runtime_error(
115 "Failed to access Json path = " + pathToJson);
116 }
117
118 return nlohmann::json::parse(l_jsonFile);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500119 }
RekhaAparna011ef21002025-02-18 23:47:36 -0600120 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500121 {
RekhaAparna011ef21002025-02-18 23:47:36 -0600122 logging::logMessage(
123 "Failed to parse JSON file, error: " + std::string(l_ex.what()));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500124 }
RekhaAparna011ef21002025-02-18 23:47:36 -0600125
126 return nlohmann::json{};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500127}
128
129/**
130 * @brief Get inventory object path from system config JSON.
131 *
132 * Given either D-bus inventory path/FRU EEPROM path/redundant EEPROM path,
133 * this API returns D-bus inventory path if present in JSON.
134 *
135 * @param[in] i_sysCfgJsonObj - System config JSON object
136 * @param[in] i_vpdPath - Path to where VPD is stored.
137 *
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500138 * @return On success a valid path is returned, on failure an empty string is
RekhaAparna011ef21002025-02-18 23:47:36 -0600139 * returned.
140 *
141 * Note: Caller has to handle it in case an empty string is received.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500142 */
143inline std::string getInventoryObjPathFromJson(
RekhaAparna011ef21002025-02-18 23:47:36 -0600144 const nlohmann::json& i_sysCfgJsonObj,
145 const std::string& i_vpdPath) noexcept
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500146{
RekhaAparna011ef21002025-02-18 23:47:36 -0600147 try
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500148 {
RekhaAparna011ef21002025-02-18 23:47:36 -0600149 if (i_vpdPath.empty())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500150 {
RekhaAparna011ef21002025-02-18 23:47:36 -0600151 throw std::runtime_error("Path parameter is empty.");
152 }
153
154 if (!i_sysCfgJsonObj.contains("frus"))
155 {
156 throw std::runtime_error("Missing frus tag in system config JSON.");
157 }
158
159 // check if given path is FRU path
160 if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
161 {
162 return i_sysCfgJsonObj["frus"][i_vpdPath].at(0).value(
163 "inventoryPath", "");
164 }
165
166 const nlohmann::json& l_fruList =
167 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
168
169 for (const auto& l_fru : l_fruList.items())
170 {
171 const auto l_fruPath = l_fru.key();
172 const auto l_invObjPath =
173 i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("inventoryPath",
174 "");
175
176 // check if given path is redundant FRU path or inventory path
177 if (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
178 "redundantEeprom", "") ||
179 (i_vpdPath == l_invObjPath))
180 {
181 return l_invObjPath;
182 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500183 }
184 }
RekhaAparna011ef21002025-02-18 23:47:36 -0600185 catch (const std::exception& l_ex)
186 {
187 logging::logMessage(
188 "Failed to get inventory object path from json, error: " +
189 std::string(l_ex.what()));
190 }
191
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500192 return std::string();
193}
194
195/**
196 * @brief Process "PostFailAction" defined in config JSON.
197 *
198 * In case there is some error in the processing of "preAction" execution and a
199 * set of procedure needs to be done as a part of post fail action. This base
200 * action can be defined in the config JSON for that FRU and it will be handled
201 * under this API.
202 *
203 * @param[in] i_parsedConfigJson - config JSON
204 * @param[in] i_vpdFilePath - EEPROM file path
205 * @param[in] i_flagToProcess - To identify which flag(s) needs to be processed
206 * under PostFailAction tag of config JSON.
207 * @return - success or failure
208 */
209inline bool executePostFailAction(const nlohmann::json& i_parsedConfigJson,
210 const std::string& i_vpdFilePath,
211 const std::string& i_flagToProcess)
212{
213 if (i_parsedConfigJson.empty() || i_vpdFilePath.empty() ||
214 i_flagToProcess.empty())
215 {
216 logging::logMessage(
217 "Invalid parameters. Abort processing for post fail action");
218
219 return false;
220 }
221
Sunny Srivastava4c164382025-01-28 03:17:33 -0600222 if (!(i_parsedConfigJson["frus"][i_vpdFilePath].at(0))["postFailAction"]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500223 .contains(i_flagToProcess))
224 {
225 logging::logMessage(
226 "Config JSON missing flag " + i_flagToProcess +
227 " to execute post fail action for path = " + i_vpdFilePath);
228
229 return false;
230 }
231
232 for (const auto& l_tags : (i_parsedConfigJson["frus"][i_vpdFilePath].at(
Sunny Srivastava4c164382025-01-28 03:17:33 -0600233 0))["postFailAction"][i_flagToProcess]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500234 .items())
235 {
236 auto itrToFunction = funcionMap.find(l_tags.key());
237 if (itrToFunction != funcionMap.end())
238 {
239 if (!itrToFunction->second(i_parsedConfigJson, i_vpdFilePath,
Sunny Srivastava4c164382025-01-28 03:17:33 -0600240 "postFailAction", i_flagToProcess))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500241 {
242 return false;
243 }
244 }
245 }
246
247 return true;
248}
249
250/**
251 * @brief Process "systemCmd" tag for a given FRU.
252 *
253 * The API will process "systemCmd" tag if it is defined in the config
254 * JSON for the given FRU.
255 *
256 * @param[in] i_parsedConfigJson - config JSON
257 * @param[in] i_vpdFilePath - EEPROM file path
258 * @param[in] i_baseAction - Base action for which this tag has been called.
259 * @param[in] i_flagToProcess - Flag nested under the base action for which this
260 * tag has been called.
261 * @return Execution status.
262 */
263inline bool processSystemCmdTag(
264 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
265 const std::string& i_baseAction, const std::string& i_flagToProcess)
266{
267 if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
268 i_baseAction.empty() || i_flagToProcess.empty())
269 {
270 logging::logMessage(
271 "Invalid parameter. Abort processing of processSystemCmd.");
272 return false;
273 }
274
275 if (!((i_parsedConfigJson["frus"][i_vpdFilePath].at(
276 0)[i_baseAction][i_flagToProcess]["systemCmd"])
277 .contains("cmd")))
278 {
279 logging::logMessage(
280 "Config JSON missing required information to execute system command for EEPROM " +
281 i_vpdFilePath);
282
283 return false;
284 }
285
286 try
287 {
288 const std::string& l_systemCommand =
289 i_parsedConfigJson["frus"][i_vpdFilePath].at(
290 0)[i_baseAction][i_flagToProcess]["systemCmd"]["cmd"];
291
292 commonUtility::executeCmd(l_systemCommand);
293 return true;
294 }
295 catch (const std::exception& e)
296 {
297 std::string l_errMsg = "Process system tag failed for exception: ";
298 l_errMsg += e.what();
299
300 logging::logMessage(l_errMsg);
301 return false;
302 }
303}
304
305/**
306 * @brief Checks for presence of a given FRU using GPIO line.
307 *
308 * This API returns the presence information of the FRU corresponding to the
309 * given VPD file path by setting the presence pin.
310 *
311 * @param[in] i_parsedConfigJson - config JSON
312 * @param[in] i_vpdFilePath - EEPROM file path
313 * @param[in] i_baseAction - Base action for which this tag has been called.
314 * @param[in] i_flagToProcess - Flag nested under the base action for which this
315 * tag has been called.
316 * @return Execution status.
317 */
318inline bool processGpioPresenceTag(
319 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
320 const std::string& i_baseAction, const std::string& i_flagToProcess)
321{
322 if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
323 i_baseAction.empty() || i_flagToProcess.empty())
324 {
325 logging::logMessage(
326 "Invalid parameter. Abort processing of processGpioPresence tag");
327 return false;
328 }
329
330 if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at(
331 0)[i_baseAction][i_flagToProcess]["gpioPresence"])
332 .contains("pin")) &&
333 ((i_parsedConfigJson["frus"][i_vpdFilePath].at(
334 0)[i_baseAction][i_flagToProcess]["gpioPresence"])
335 .contains("value"))))
336 {
337 logging::logMessage(
338 "Config JSON missing required information to detect presence for EEPROM " +
339 i_vpdFilePath);
340
341 return false;
342 }
343
344 // get the pin name
345 const std::string& l_presencePinName =
346 i_parsedConfigJson["frus"][i_vpdFilePath].at(
347 0)[i_baseAction][i_flagToProcess]["gpioPresence"]["pin"];
348
349 // get the pin value
350 uint8_t l_presencePinValue = i_parsedConfigJson["frus"][i_vpdFilePath].at(
351 0)[i_baseAction][i_flagToProcess]["gpioPresence"]["value"];
352
353 try
354 {
355 gpiod::line l_presenceLine = gpiod::find_line(l_presencePinName);
356
357 if (!l_presenceLine)
358 {
359 throw GpioException("Couldn't find the GPIO line.");
360 }
361
362 l_presenceLine.request({"Read the presence line",
363 gpiod::line_request::DIRECTION_INPUT, 0});
364
365 return (l_presencePinValue == l_presenceLine.get_value());
366 }
367 catch (const std::exception& ex)
368 {
369 std::string l_errMsg = "Exception on GPIO line: ";
370 l_errMsg += l_presencePinName;
371 l_errMsg += " Reason: ";
372 l_errMsg += ex.what();
373 l_errMsg += " File: " + i_vpdFilePath + " Pel Logged";
374
375 // ToDo -- Update Internal Rc code.
376 EventLogger::createAsyncPelWithInventoryCallout(
377 types::ErrorType::GpioError, types::SeverityType::Informational,
378 {{getInventoryObjPathFromJson(i_parsedConfigJson, i_vpdFilePath),
379 types::CalloutPriority::High}},
380 std::source_location::current().file_name(),
381 std::source_location::current().function_name(), 0, l_errMsg,
382 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
383
384 logging::logMessage(l_errMsg);
385
386 // Except when GPIO pin value is false, we go and try collecting the
387 // FRU VPD as we couldn't able to read GPIO pin value due to some
388 // error/exception. So returning true in error scenario.
389 return true;
390 }
391}
392
393/**
394 * @brief Process "setGpio" tag for a given FRU.
395 *
396 * This API enables the GPIO line.
397 *
398 * @param[in] i_parsedConfigJson - config JSON
399 * @param[in] i_vpdFilePath - EEPROM file path
400 * @param[in] i_baseAction - Base action for which this tag has been called.
401 * @param[in] i_flagToProcess - Flag nested under the base action for which this
402 * tag has been called.
403 * @return Execution status.
404 */
405inline bool procesSetGpioTag(
406 const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
407 const std::string& i_baseAction, const std::string& i_flagToProcess)
408{
409 if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
410 i_baseAction.empty() || i_flagToProcess.empty())
411 {
412 logging::logMessage(
413 "Invalid parameter. Abort processing of procesSetGpio.");
414 return false;
415 }
416
417 if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at(
418 0)[i_baseAction][i_flagToProcess]["setGpio"])
419 .contains("pin")) &&
420 ((i_parsedConfigJson["frus"][i_vpdFilePath].at(
421 0)[i_baseAction][i_flagToProcess]["setGpio"])
422 .contains("value"))))
423 {
424 logging::logMessage(
425 "Config JSON missing required information to set gpio line for EEPROM " +
426 i_vpdFilePath);
427
428 return false;
429 }
430
431 const std::string& l_pinName = i_parsedConfigJson["frus"][i_vpdFilePath].at(
432 0)[i_baseAction][i_flagToProcess]["setGpio"]["pin"];
433
434 // Get the value to set
435 uint8_t l_pinValue = i_parsedConfigJson["frus"][i_vpdFilePath].at(
436 0)[i_baseAction][i_flagToProcess]["setGpio"]["value"];
437
438 logging::logMessage(
439 "Setting GPIO: " + l_pinName + " to " + std::to_string(l_pinValue));
440 try
441 {
442 gpiod::line l_outputLine = gpiod::find_line(l_pinName);
443
444 if (!l_outputLine)
445 {
446 throw GpioException("Couldn't find GPIO line.");
447 }
448
449 l_outputLine.request(
450 {"FRU Action", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
451 l_pinValue);
452 return true;
453 }
454 catch (const std::exception& ex)
455 {
456 std::string l_errMsg = "Exception on GPIO line: ";
457 l_errMsg += l_pinName;
458 l_errMsg += " Reason: ";
459 l_errMsg += ex.what();
460 l_errMsg += " File: " + i_vpdFilePath + " Pel Logged";
461
462 // ToDo -- Update Internal RC code
463 EventLogger::createAsyncPelWithInventoryCallout(
464 types::ErrorType::GpioError, types::SeverityType::Informational,
465 {{getInventoryObjPathFromJson(i_parsedConfigJson, i_vpdFilePath),
466 types::CalloutPriority::High}},
467 std::source_location::current().file_name(),
468 std::source_location::current().function_name(), 0, l_errMsg,
469 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
470
471 logging::logMessage(l_errMsg);
472
473 return false;
474 }
475}
476
477/**
478 * @brief Process any action, if defined in config JSON.
479 *
480 * If any FRU(s) requires any special handling, then this base action can be
481 * defined for that FRU in the config JSON, processing of which will be handled
482 * in this API.
483 * Examples of action - preAction, PostAction etc.
484 *
485 * @param[in] i_parsedConfigJson - config JSON
486 * @param[in] i_action - Base action to be performed.
487 * @param[in] i_vpdFilePath - EEPROM file path
488 * @param[in] i_flagToProcess - To identify which flag(s) needs to be processed
489 * under PreAction tag of config JSON.
490 * @return - success or failure
491 */
492inline bool executeBaseAction(
493 const nlohmann::json& i_parsedConfigJson, const std::string& i_action,
494 const std::string& i_vpdFilePath, const std::string& i_flagToProcess)
495{
496 if (i_flagToProcess.empty() || i_action.empty() || i_vpdFilePath.empty() ||
497 !i_parsedConfigJson.contains("frus"))
498 {
499 logging::logMessage("Invalid parameter");
500 return false;
501 }
502
503 if (!i_parsedConfigJson["frus"].contains(i_vpdFilePath))
504 {
505 logging::logMessage(
506 "File path: " + i_vpdFilePath + " not found in JSON");
507 return false;
508 }
509
510 if (!i_parsedConfigJson["frus"][i_vpdFilePath].at(0).contains(i_action))
511 {
512 logging::logMessage("Action [" + i_action +
513 "] not defined for file path:" + i_vpdFilePath);
514 return false;
515 }
516
517 if (!(i_parsedConfigJson["frus"][i_vpdFilePath].at(0))[i_action].contains(
518 i_flagToProcess))
519 {
520 logging::logMessage("Config JSON missing flag [" + i_flagToProcess +
521 "] to execute action for path = " + i_vpdFilePath);
522
523 return false;
524 }
525
526 const nlohmann::json& l_tagsJson =
527 (i_parsedConfigJson["frus"][i_vpdFilePath].at(
528 0))[i_action][i_flagToProcess];
529
530 for (const auto& l_tag : l_tagsJson.items())
531 {
532 auto itrToFunction = funcionMap.find(l_tag.key());
533 if (itrToFunction != funcionMap.end())
534 {
535 if (!itrToFunction->second(i_parsedConfigJson, i_vpdFilePath,
536 i_action, i_flagToProcess))
537 {
538 // In case any of the tag fails to execute. Mark action
539 // as failed for that flag.
540 return false;
541 }
542 }
543 }
544
545 return true;
546}
547
548/**
549 * @brief Get redundant FRU path from system config JSON
550 *
551 * Given either D-bus inventory path/FRU path/redundant FRU path, this
552 * API returns the redundant FRU path taken from "redundantEeprom" tag from
553 * system config JSON.
554 *
555 * @param[in] i_sysCfgJsonObj - System config JSON object.
556 * @param[in] i_vpdPath - Path to where VPD is stored.
557 *
558 * @throw std::runtime_error.
559 * @return On success return valid path, on failure return empty string.
560 */
561inline std::string getRedundantEepromPathFromJson(
562 const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdPath)
563{
564 if (i_vpdPath.empty())
565 {
566 throw std::runtime_error("Path parameter is empty.");
567 }
568
569 if (!i_sysCfgJsonObj.contains("frus"))
570 {
571 throw std::runtime_error("Missing frus tag in system config JSON.");
572 }
573
574 // check if given path is FRU path
575 if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
576 {
577 return i_sysCfgJsonObj["frus"][i_vpdPath].at(0).value(
578 "redundantEeprom", "");
579 }
580
581 const nlohmann::json& l_fruList =
582 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
583
584 for (const auto& l_fru : l_fruList.items())
585 {
586 const std::string& l_fruPath = l_fru.key();
587 const std::string& l_redundantFruPath =
588 i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("redundantEeprom",
589 "");
590
591 // check if given path is inventory path or redundant FRU path
592 if ((i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("inventoryPath",
593 "") == i_vpdPath) ||
594 (l_redundantFruPath == i_vpdPath))
595 {
596 return l_redundantFruPath;
597 }
598 }
599 return std::string();
600}
601
602/**
603 * @brief Get FRU EEPROM path from system config JSON
604 *
605 * Given either D-bus inventory path/FRU EEPROM path/redundant EEPROM path,
606 * this API returns FRU EEPROM path if present in JSON.
607 *
608 * @param[in] i_sysCfgJsonObj - System config JSON object
609 * @param[in] i_vpdPath - Path to where VPD is stored.
610 *
611 * @throw std::runtime_error.
612 *
613 * @return On success return valid path, on failure return empty string.
614 */
615inline std::string getFruPathFromJson(const nlohmann::json& i_sysCfgJsonObj,
616 const std::string& i_vpdPath)
617{
618 if (i_vpdPath.empty())
619 {
620 throw std::runtime_error("Path parameter is empty.");
621 }
622
623 if (!i_sysCfgJsonObj.contains("frus"))
624 {
625 throw std::runtime_error("Missing frus tag in system config JSON.");
626 }
627
628 // check if given path is FRU path
629 if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
630 {
631 return i_vpdPath;
632 }
633
634 const nlohmann::json& l_fruList =
635 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
636
637 for (const auto& l_fru : l_fruList.items())
638 {
639 const auto l_fruPath = l_fru.key();
640
641 // check if given path is redundant FRU path or inventory path
642 if (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
643 "redundantEeprom", "") ||
644 (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
645 "inventoryPath", "")))
646 {
647 return l_fruPath;
648 }
649 }
650 return std::string();
651}
652
653/**
654 * @brief An API to check backup and restore VPD is required.
655 *
656 * The API checks if there is provision for backup and restore mentioned in the
657 * system config JSON, by looking "backupRestoreConfigPath" tag.
658 * Checks if the path mentioned is a hardware path, by checking if the file path
659 * exists and size of contents in the path.
660 *
661 * @param[in] i_sysCfgJsonObj - System config JSON object.
662 *
663 * @return true if backup and restore is required, false otherwise.
664 */
665inline bool isBackupAndRestoreRequired(const nlohmann::json& i_sysCfgJsonObj)
666{
667 try
668 {
669 const std::string& l_backupAndRestoreCfgFilePath =
670 i_sysCfgJsonObj.value("backupRestoreConfigPath", "");
671 if (!l_backupAndRestoreCfgFilePath.empty() &&
672 std::filesystem::exists(l_backupAndRestoreCfgFilePath) &&
673 !std::filesystem::is_empty(l_backupAndRestoreCfgFilePath))
674 {
675 return true;
676 }
677 }
678 catch (std::exception& ex)
679 {
680 logging::logMessage(ex.what());
681 }
682 return false;
683}
684
685/** @brief API to check if an action is required for given EEPROM path.
686 *
687 * System config JSON can contain pre-action, post-action etc. like actions
688 * defined for an EEPROM path. The API will check if any such action is defined
689 * for the EEPROM.
690 *
691 * @param[in] i_sysCfgJsonObj - System config JSON object.
692 * @param[in] i_vpdFruPath - EEPROM path.
693 * @param[in] i_action - Action to be checked.
694 * @param[in] i_flowFlag - Denotes the flow w.r.t which the action should be
695 * triggered.
696 * @return - True if action is defined for the flow, false otherwise.
697 */
698inline bool isActionRequired(
699 const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdFruPath,
700 const std::string& i_action, const std::string& i_flowFlag)
701{
702 if (i_vpdFruPath.empty() || i_action.empty() || i_flowFlag.empty())
703 {
704 logging::logMessage("Invalid parameters recieved.");
705 return false;
706 }
707
708 if (!i_sysCfgJsonObj.contains("frus"))
709 {
710 logging::logMessage("Invalid JSON object recieved.");
711 return false;
712 }
713
714 if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
715 {
716 logging::logMessage(
717 "JSON object does not contain EEPROM path " + i_vpdFruPath);
718 return false;
719 }
720
721 if ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)).contains(i_action))
722 {
723 if ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))[i_action].contains(
724 i_flowFlag))
725 {
726 return true;
727 }
728
729 logging::logMessage("Flow flag: [" + i_flowFlag +
730 "], not found in JSON for path: " + i_vpdFruPath);
731 return false;
732 }
733 return false;
734}
735
736/**
737 * @brief An API to return list of FRUs that needs GPIO polling.
738 *
739 * An API that checks for the FRUs that requires GPIO polling and returns
740 * a list of FRUs that needs polling. Returns an empty list if there are
741 * no FRUs that requires polling.
742 *
743 * @throw std::runtime_error
744 *
745 * @param[in] i_sysCfgJsonObj - System config JSON object.
746 *
747 * @return list of FRUs parameters that needs polling.
748 */
Patrick Williams43fedab2025-02-03 14:28:05 -0500749inline std::vector<std::string> getListOfGpioPollingFrus(
750 const nlohmann::json& i_sysCfgJsonObj)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500751{
752 if (i_sysCfgJsonObj.empty())
753 {
754 throw std::runtime_error("Invalid Parameters");
755 }
756
757 if (!i_sysCfgJsonObj.contains("frus"))
758 {
759 throw std::runtime_error("Missing frus section in system config JSON");
760 }
761
762 std::vector<std::string> l_gpioPollingRequiredFrusList;
763
764 for (const auto& l_fru : i_sysCfgJsonObj["frus"].items())
765 {
766 const auto l_fruPath = l_fru.key();
767
768 try
769 {
770 if (isActionRequired(i_sysCfgJsonObj, l_fruPath, "pollingRequired",
771 "hotPlugging"))
772 {
773 if (i_sysCfgJsonObj["frus"][l_fruPath]
774 .at(0)["pollingRequired"]["hotPlugging"]
775 .contains("gpioPresence"))
776 {
777 l_gpioPollingRequiredFrusList.push_back(l_fruPath);
778 }
779 }
780 }
781 catch (const std::exception& l_ex)
782 {
783 logging::logMessage(l_ex.what());
784 }
785 }
786
787 return l_gpioPollingRequiredFrusList;
788}
789
790/**
791 * @brief Get all related path(s) to update keyword value.
792 *
793 * Given FRU EEPROM path/Inventory path needs keyword's value update, this API
794 * returns tuple of FRU EEPROM path, inventory path and redundant EEPROM path if
795 * exists in the system config JSON.
796 *
797 * Note: If the inventory object path or redundant EEPROM path(s) are not found
798 * in the system config JSON, corresponding fields will have empty value in the
799 * returning tuple.
800 *
801 * @param[in] i_sysCfgJsonObj - System config JSON object.
802 * @param[in,out] io_vpdPath - Inventory object path or FRU EEPROM path.
803 *
804 * @return On success returns tuple of EEPROM path, inventory path & redundant
805 * path, on failure returns tuple with given input path alone.
806 */
807inline std::tuple<std::string, std::string, std::string>
808 getAllPathsToUpdateKeyword(const nlohmann::json& i_sysCfgJsonObj,
809 std::string io_vpdPath)
810{
811 types::Path l_inventoryObjPath;
812 types::Path l_redundantFruPath;
813 try
814 {
815 if (!i_sysCfgJsonObj.empty())
816 {
817 // Get hardware path from system config JSON.
818 const types::Path l_fruPath =
819 jsonUtility::getFruPathFromJson(i_sysCfgJsonObj, io_vpdPath);
820
821 if (!l_fruPath.empty())
822 {
823 io_vpdPath = l_fruPath;
824
825 // Get inventory object path from system config JSON
826 l_inventoryObjPath = jsonUtility::getInventoryObjPathFromJson(
827 i_sysCfgJsonObj, l_fruPath);
828
829 // Get redundant hardware path if present in system config JSON
830 l_redundantFruPath =
831 jsonUtility::getRedundantEepromPathFromJson(i_sysCfgJsonObj,
832 l_fruPath);
833 }
834 }
835 }
836 catch (const std::exception& l_exception)
837 {
838 logging::logMessage(
839 "Failed to get all paths to update keyword value, error " +
840 std::string(l_exception.what()));
841 }
842 return std::make_tuple(io_vpdPath, l_inventoryObjPath, l_redundantFruPath);
843}
844
845/**
846 * @brief An API to get DBus service name.
847 *
848 * Given DBus inventory path, this API returns DBus service name if present in
849 * the JSON.
850 *
851 * @param[in] i_sysCfgJsonObj - System config JSON object.
852 * @param[in] l_inventoryPath - DBus inventory path.
853 *
854 * @return On success returns the service name present in the system config
855 * JSON, otherwise empty string.
856 *
857 * Note: Caller has to handle in case of empty string received.
858 */
859inline std::string getServiceName(const nlohmann::json& i_sysCfgJsonObj,
860 const std::string& l_inventoryPath)
861{
862 try
863 {
864 if (l_inventoryPath.empty())
865 {
866 throw std::runtime_error("Path parameter is empty.");
867 }
868
869 if (!i_sysCfgJsonObj.contains("frus"))
870 {
871 throw std::runtime_error("Missing frus tag in system config JSON.");
872 }
873
874 const nlohmann::json& l_listOfFrus =
875 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
876
877 for (const auto& l_frus : l_listOfFrus.items())
878 {
879 for (const auto& l_inventoryItem : l_frus.value())
880 {
881 if (l_inventoryPath.compare(l_inventoryItem["inventoryPath"]) ==
882 constants::STR_CMP_SUCCESS)
883 {
884 return l_inventoryItem["serviceName"];
885 }
886 }
887 }
888 throw std::runtime_error(
889 "Inventory path not found in the system config JSON");
890 }
891 catch (const std::exception& l_exception)
892 {
893 logging::logMessage(
894 "Error while getting DBus service name for given path " +
895 l_inventoryPath + ", error: " + std::string(l_exception.what()));
896 // TODO:log PEL
897 }
898 return std::string{};
899}
900
901/**
902 * @brief An API to check if a FRU is tagged as "powerOffOnly"
903 *
904 * Given the system config JSON and VPD FRU path, this API checks if the FRU
905 * VPD can be collected at Chassis Power Off state only.
906 *
907 * @param[in] i_sysCfgJsonObj - System config JSON object.
908 * @param[in] i_vpdFruPath - EEPROM path.
909 * @return - True if FRU VPD can be collected at Chassis Power Off state only.
910 * False otherwise
911 */
912inline bool isFruPowerOffOnly(const nlohmann::json& i_sysCfgJsonObj,
913 const std::string& i_vpdFruPath)
914{
915 if (i_vpdFruPath.empty())
916 {
917 logging::logMessage("FRU path is empty.");
918 return false;
919 }
920
921 if (!i_sysCfgJsonObj.contains("frus"))
922 {
923 logging::logMessage("Missing frus tag in system config JSON.");
924 return false;
925 }
926
927 if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
928 {
929 logging::logMessage("JSON object does not contain EEPROM path \'" +
930 i_vpdFruPath + "\'");
931 return false;
932 }
933
934 return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
935 .contains("powerOffOnly") &&
936 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)["powerOffOnly"]));
937}
938
939/**
940 * @brief API which tells if the FRU is replaceable at runtime
941 *
942 * @param[in] i_sysCfgJsonObj - System config JSON object.
943 * @param[in] i_vpdFruPath - EEPROM path.
944 *
945 * @return true if FRU is replaceable at runtime. false otherwise.
946 */
947inline bool isFruReplaceableAtRuntime(const nlohmann::json& i_sysCfgJsonObj,
948 const std::string& i_vpdFruPath)
949{
950 try
951 {
952 if (i_vpdFruPath.empty())
953 {
954 throw std::runtime_error("Given FRU path is empty.");
955 }
956
957 if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
958 {
959 throw std::runtime_error("Invalid system config JSON object.");
960 }
961
962 return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
963 .contains("replaceableAtRuntime") &&
964 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(
965 0)["replaceableAtRuntime"]));
966 }
967 catch (const std::exception& l_error)
968 {
969 // TODO: Log PEL
970 logging::logMessage(l_error.what());
971 }
972
973 return false;
974}
975
976/**
977 * @brief API which tells if the FRU is replaceable at standby
978 *
979 * @param[in] i_sysCfgJsonObj - System config JSON object.
980 * @param[in] i_vpdFruPath - EEPROM path.
981 *
982 * @return true if FRU is replaceable at standby. false otherwise.
983 */
984inline bool isFruReplaceableAtStandby(const nlohmann::json& i_sysCfgJsonObj,
985 const std::string& i_vpdFruPath)
986{
987 try
988 {
989 if (i_vpdFruPath.empty())
990 {
991 throw std::runtime_error("Given FRU path is empty.");
992 }
993
994 if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
995 {
996 throw std::runtime_error("Invalid system config JSON object.");
997 }
998
999 return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
1000 .contains("replaceableAtStandby") &&
1001 (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(
1002 0)["replaceableAtStandby"]));
1003 }
1004 catch (const std::exception& l_error)
1005 {
1006 // TODO: Log PEL
1007 logging::logMessage(l_error.what());
1008 }
1009
1010 return false;
1011}
1012
1013/**
1014 * @brief API to get list of FRUs replaceable at standby from JSON.
1015 *
1016 * The API will return a vector of FRUs inventory path which are replaceable at
1017 * standby.
1018 * The API can throw exception in case of error scenarios. Caller's
1019 * responsibility to handle those exceptions.
1020 *
1021 * @param[in] i_sysCfgJsonObj - System config JSON object.
1022 *
1023 * @return - List of FRUs replaceable at standby.
1024 */
Patrick Williams43fedab2025-02-03 14:28:05 -05001025inline std::vector<std::string> getListOfFrusReplaceableAtStandby(
1026 const nlohmann::json& i_sysCfgJsonObj)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001027{
1028 std::vector<std::string> l_frusReplaceableAtStandby;
1029
1030 if (!i_sysCfgJsonObj.contains("frus"))
1031 {
1032 logging::logMessage("Missing frus tag in system config JSON.");
1033 return l_frusReplaceableAtStandby;
1034 }
1035
1036 const nlohmann::json& l_fruList =
1037 i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
1038
1039 for (const auto& l_fru : l_fruList.items())
1040 {
1041 if (i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
1042 "replaceableAtStandby", false))
1043 {
1044 const std::string& l_inventoryObjectPath =
1045 i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
1046 "inventoryPath", "");
1047
1048 if (!l_inventoryObjectPath.empty())
1049 {
1050 l_frusReplaceableAtStandby.emplace_back(l_inventoryObjectPath);
1051 }
1052 }
1053 }
1054
1055 return l_frusReplaceableAtStandby;
1056}
1057
1058} // namespace jsonUtility
1059} // namespace vpd