Enable pre and post actions
This commit enables the VPD parser to take certain
pre and post collection actions.
-- Set a GPIO in order to enable hardware paths (such as I2C)
-- Bind device drivers to the I2C device so as to generate a udev
event.
-- Set a GPIO as a post action on failure to collect VPD.
Pre-actions are taken when collecting system VPD.
Post actions can be taken either after a failure
to bind drivers or after failing to collect/parse VPD.
Change-Id: I26754000a72db53f00a5afc4925de27e3f7c3ba8
Signed-off-by: Alpana Kumari <alpankum@in.ibm.com>
diff --git a/ibm_vpd_app.cpp b/ibm_vpd_app.cpp
index 6241188..b09ed79 100644
--- a/ibm_vpd_app.cpp
+++ b/ibm_vpd_app.cpp
@@ -16,6 +16,7 @@
#include <exception>
#include <filesystem>
#include <fstream>
+#include <gpiod.hpp>
#include <iostream>
#include <iterator>
#include <nlohmann/json.hpp>
@@ -133,6 +134,7 @@
}
return expanded;
}
+
/**
* @brief Populate FRU specific interfaces.
*
@@ -250,7 +252,7 @@
}
}
-Binary getVpdDataInVector(nlohmann::json& js, const string& file)
+static Binary getVpdDataInVector(const nlohmann::json& js, const string& file)
{
uint32_t offset = 0;
// check if offset present?
@@ -275,6 +277,172 @@
return vpdVector;
}
+/* It does nothing. Just an empty function to return null
+ * at the end of variadic template args
+ */
+static string getCommand()
+{
+ return "";
+}
+
+/* This function to arrange all arguments to make command
+ */
+template <typename T, typename... Types>
+static string getCommand(T arg1, Types... args)
+{
+ string cmd = " " + arg1 + getCommand(args...);
+
+ return cmd;
+}
+
+/* This API takes arguments and run that command
+ * returns output of that command
+ */
+template <typename T, typename... Types>
+static vector<string> executeCmd(T&& path, Types... args)
+{
+ vector<string> stdOutput;
+ array<char, 128> buffer;
+
+ string cmd = path + getCommand(args...);
+
+ unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
+ if (!pipe)
+ {
+ throw runtime_error("popen() failed!");
+ }
+ while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
+ {
+ stdOutput.emplace_back(buffer.data());
+ }
+
+ return stdOutput;
+}
+
+/** This API will be called at the end of VPD collection to perform any post
+ * actions.
+ *
+ * @param[in] json - json object
+ * @param[in] file - eeprom file path
+ */
+static void postFailAction(const nlohmann::json& json, const string& file)
+{
+ if ((json["frus"][file].at(0)).find("postActionFail") ==
+ json["frus"][file].at(0).end())
+ {
+ return;
+ }
+
+ uint8_t pinValue = 0;
+ string pinName;
+
+ for (const auto& postAction :
+ (json["frus"][file].at(0))["postActionFail"].items())
+ {
+ if (postAction.key() == "pin")
+ {
+ pinName = postAction.value();
+ }
+ else if (postAction.key() == "value")
+ {
+ // Get the value to set
+ pinValue = postAction.value();
+ }
+ }
+
+ cout << "Setting GPIO: " << pinName << " to " << (int)pinValue << endl;
+
+ try
+ {
+ gpiod::line outputLine = gpiod::find_line(pinName);
+
+ if (!outputLine)
+ {
+ cout << "Couldn't find output line:" << pinName
+ << " on GPIO. Skipping...\n";
+
+ return;
+ }
+ outputLine.request(
+ {"Disable line", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
+ pinValue);
+ }
+ catch (system_error&)
+ {
+ cerr << "Failed to set post-action GPIO" << endl;
+ }
+}
+
+/** Performs any pre-action needed to get the FRU setup for collection.
+ *
+ * @param[in] json - json object
+ * @param[in] file - eeprom file path
+ */
+static void preAction(const nlohmann::json& json, const string& file)
+{
+ if ((json["frus"][file].at(0)).find("preAction") ==
+ json["frus"][file].at(0).end())
+ {
+ return;
+ }
+
+ uint8_t pinValue = 0;
+ string pinName;
+
+ for (const auto& postAction :
+ (json["frus"][file].at(0))["preAction"].items())
+ {
+ if (postAction.key() == "pin")
+ {
+ pinName = postAction.value();
+ }
+ else if (postAction.key() == "value")
+ {
+ // Get the value to set
+ pinValue = postAction.value();
+ }
+ }
+
+ cout << "Setting GPIO: " << pinName << " to " << (int)pinValue << endl;
+ try
+ {
+ gpiod::line outputLine = gpiod::find_line(pinName);
+
+ if (!outputLine)
+ {
+ cout << "Couldn't find output line:" << pinName
+ << " on GPIO. Skipping...\n";
+
+ return;
+ }
+ outputLine.request(
+ {"FRU pre-action", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
+ pinValue);
+ }
+ catch (system_error&)
+ {
+ cerr << "Failed to set pre-action GPIO" << endl;
+ return;
+ }
+
+ // Now bind the device
+ string bind = json["frus"][file].at(0).value("bind", "");
+ cout << "Binding device " << bind << endl;
+ string bindCmd = string("echo \"") + bind +
+ string("\" > /sys/bus/i2c/drivers/at24/bind");
+ cout << bindCmd << endl;
+ executeCmd(bindCmd);
+
+ // Check if device showed up (test for file)
+ if (!fs::exists(file))
+ {
+ cout << "EEPROM " << file << " does not exist. Take failure action"
+ << endl;
+ // If not, then take failure postAction
+ postFailAction(json, file);
+ }
+}
+
/**
* @brief Prime the Inventory
* Prime the inventory by populating only the location code,
@@ -294,6 +462,8 @@
for (auto& itemFRUS : jsObject["frus"].items())
{
+ // Take pre actions
+ preAction(jsObject, itemFRUS.key());
for (auto& itemEEPROM : itemFRUS.value())
{
inventory::InterfaceMap interfaces;
@@ -338,48 +508,6 @@
return objects;
}
-/* It does nothing. Just an empty function to return null
- * at the end of variadic template args
- */
-string getCommand()
-{
- return "";
-}
-
-/* This function to arrange all arguments to make command
- */
-template <typename T, typename... Types>
-string getCommand(T arg1, Types... args)
-{
- string cmd = " " + arg1 + getCommand(args...);
-
- return cmd;
-}
-
-/* This API takes arguments and run that command
- * returns output of that command
- */
-template <typename T, typename... Types>
-static vector<string> executeCmd(T& path, Types... args)
-{
- vector<string> stdOutput;
- array<char, 128> buffer;
-
- string cmd = path + getCommand(args...);
-
- unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
- if (!pipe)
- {
- throw runtime_error("popen() failed!");
- }
- while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
- {
- stdOutput.emplace_back(buffer.data());
- }
-
- return stdOutput;
-}
-
/**
* @brief This API executes command to set environment variable
* And then reboot the system
@@ -619,8 +747,7 @@
string file{};
app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
- ->required()
- ->check(ExistingFile);
+ ->required();
CLI11_PARSE(app, argc, argv);
@@ -657,6 +784,13 @@
return 0;
}
+ if (!fs::exists(file))
+ {
+ cout << "Device path: " << file
+ << " does not exist. Spurious udev event? Exiting." << endl;
+ return 0;
+ }
+
baseFruInventoryPath = js["frus"][file][0]["inventoryPath"];
// Check if we can read the VPD file based on the power state
if (js["frus"][file].at(0).value("powerOffOnly", false))
@@ -675,17 +809,30 @@
variant<KeywordVpdMap, Store> parseResult;
parseResult = parser->parse();
- if (auto pVal = get_if<Store>(&parseResult))
+ try
{
- populateDbus(pVal->getVpdMap(), js, file);
- }
- else if (auto pVal = get_if<KeywordVpdMap>(&parseResult))
- {
- populateDbus(*pVal, js, file);
- }
+ Binary vpdVector = getVpdDataInVector(js, file);
+ ParserInterface* parser = ParserFactory::getParser(move(vpdVector));
- // release the parser object
- ParserFactory::freeParser(parser);
+ variant<KeywordVpdMap, Store> parseResult;
+ parseResult = parser->parse();
+ if (auto pVal = get_if<Store>(&parseResult))
+ {
+ populateDbus(pVal->getVpdMap(), js, file);
+ }
+ else if (auto pVal = get_if<KeywordVpdMap>(&parseResult))
+ {
+ populateDbus(*pVal, js, file);
+ }
+
+ // release the parser object
+ ParserFactory::freeParser(parser);
+ }
+ catch (exception& e)
+ {
+ postFailAction(js, file);
+ throw e;
+ }
}
catch (const VpdJsonException& ex)
{
diff --git a/meson.build b/meson.build
index 1b7e271..32775ad 100644
--- a/meson.build
+++ b/meson.build
@@ -44,6 +44,7 @@
)
if get_option('ibm-parser').enabled()
+ libgpiodcxx = dependency('libgpiodcxx')
ibm_read_vpd_SOURCES = ['ibm_vpd_app.cpp',
'vpd-parser/ipz_parser.cpp',
'impl.cpp',
@@ -61,6 +62,7 @@
dependencies: [
sdbusplus,
phosphor_logging,
+ libgpiodcxx,
],
include_directories : 'vpd-parser/',
install: true,