VPD tool : dumpInventory & dumpObject
VPD tool has four options.
a) Dump Complete Inventory - no additional arguments are needed.
b) Dump Specific Object - by providing the object name.
c) Read the keyword - by providing the object name, record name and keyword to be read.
d) Write/Update the keyword - by providing the object name, record name, keyword and the value to be updated.
{value - in ascii or hex & all the other arguments in string}
"--help" option provides details on how to use the above mentioned options.
This commit has implementation of dump inventory and dump specific object.
Output:
---------Dump Inventory---------
Not displaying the complete output.
root@rainier:/tmp# ./vpd-tool -i
[
{
"/system": {
"LocationCode": "U9105.22A.SIMP10R",
"Model": "9105-22A",
"SerialNumber": "SIMP10R",
"type": "xyz.openbmc_project.Inventory.Item.System"
},
"/system/chassis": {
"LocationCode": "U78DA.ND1.1234567",
"type": "xyz.openbmc_project.Inventory.Item.Chassis"
},
.
.
.
.
and so on..
]
---------Dump Object----------
root@rainier:/tmp# ./vpd-tool -o -O /system/chassis/motherboard/ebmc_card_bmc
[
{
"/system/chassis/motherboard/ebmc_card_bmc": {
"CC": "6B58",
"DR": "EBMC ",
"FN": "F191014",
"LocationCode": "U78DA.ND1.1234567-P0-C5",
"PN": "PN12345",
"SN": "YL6B58010000",
"type": "xyz.openbmc_project.Inventory.Item.Bmc"
}
}
]
Flag to enable VPD tool:
There is no seperate flag for VPD tool.
ibm-parser flag will generate the vpd-tool binary.
Steps to build and generate the executable using meson:
meson -Denabled=ibm-parser build
ninja -C build
Test:
Tested on simics.
Signed-off-by: PriyangaRamasamy <priyanga24@in.ibm.com>
Change-Id: I712f7ad4303eefa68f232685b6b0e53646f859f5
diff --git a/Makefile.am b/Makefile.am
index 56a3b57..683ec0f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,7 +9,8 @@
args.hpp \
utils.hpp \
keyword_vpd_parser.hpp \
- ibm_vpd_type_check.hpp
+ ibm_vpd_type_check.hpp \
+ vpd_tool_impl.hpp
if IBM_PARSER
noinst_HEADERS += \
@@ -30,6 +31,14 @@
ibm_read_vpd_LDFLAGS = $(SDBUSPLUS_LIBS) $(PHOSPHOR_LOGGING_LIBS)
ibm_read_vpd_CXXFLAGS = $(SDBUSPLUS_CFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
+bin_PROGRAMS += vpd-tool
+vpd_tool_SOURCES = \
+ vpd_tool.cpp \
+ vpd_tool_impl.cpp
+
+vpd_tool_LDFLAGS = $(SDBUSPLUS_LIBS)
+vpd_tool_CXXFLAGS = $(SDBUSPLUS_CFLAGS)
+
else
# Be sure to build these before compiling
BUILT_SOURCES = \
diff --git a/configure.ac b/configure.ac
index 6657b52..739ec37 100644
--- a/configure.ac
+++ b/configure.ac
@@ -63,7 +63,7 @@
[CLI/CLI.hpp],
[],
[AC_MSG_ERROR([Could not find CLI11 CLI.hpp])]
- )
+)
AC_CHECK_HEADER(
[nlohmann/json.hpp],
[],
@@ -72,6 +72,8 @@
AX_APPEND_COMPILE_FLAGS([-DIPZ_PARSER], [CXXFLAGS])
AC_DEFINE(INVENTORY_JSON, "/usr/share/vpd/vpd_inventory.json", [JSON file that defines inventory blueprint])
])
+ AC_DEFINE(INVENTORY_PATH, "/xyz/openbmc_project/inventory", [Prefix for inventory D-Bus objects])
+ AC_DEFINE(INVENTORY_MANAGER_SERVICE, "xyz.openbmc_project.Inventory.Manager", [Inventory manager service])
# Check/set gtest specific functions.
AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"])
diff --git a/meson.build b/meson.build
index 8d95281..ac6ba29 100644
--- a/meson.build
+++ b/meson.build
@@ -3,7 +3,7 @@
'c',
'cpp',
default_options: [
- 'cpp_std=c++17'
+ 'cpp_std=c++17'
],
version: '1.0'
)
@@ -22,8 +22,10 @@
configure_file(output: 'config.h',
configuration :{
'INVENTORY_JSON': '"'+get_option('INVENTORY_JSON')+'"',
+ 'INVENTORY_PATH': '"'+get_option('INVENTORY_PATH')+'"',
+ 'INVENTORY_MANAGER_SERVICE': '"'+get_option('INVENTORY_MANAGER_SERVICE')+'"'
}
- )
+ )
ibm_read_vpd_SOURCES = ['ibm_vpd_app.cpp',
'ibm_vpd_type_check.cpp',
'parser.cpp',
@@ -44,6 +46,19 @@
install: true,
cpp_args : '-DIPZ_PARSER'
)
+
+ vpd_tool_SOURCES = ['vpd_tool.cpp',
+ 'vpd_tool_impl.cpp'
+ ]
+
+ vpd_tool_exe = executable(
+ 'vpd-tool',
+ vpd_tool_SOURCES,
+ dependencies: [
+ sdbusplus
+ ],
+ install: true,
+ )
else
FRUGEN = '$srcdir/extra-properties.py -e' + get_option('FRU_YAML')
PROPGEN = '$srcdir/extra-properties.py -e' + get_option('PROP_YAML')
diff --git a/meson_options.txt b/meson_options.txt
index f198832..9c094c7 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -4,3 +4,5 @@
option('PROP_YAML',type: 'string', value: 'extra-properties-example.yaml', description: 'YAML PROPERTY')
option('ibm-parser', type: 'feature', description: 'ENABLE IBM PARSER')
option('INVENTORY_JSON',type: 'string', value: '/usr/share/vpd/vpd_inventory.json', description: 'JSON file that defines inventory blueprint')
+option('INVENTORY_PATH',type: 'string', value: '/xyz/openbmc_project/inventory', description: 'Prefix for inventory D-Bus objects')
+option('INVENTORY_MANAGER_SERVICE',type: 'string', value: 'xyz.openbmc_project.Inventory.Manager', description: 'Inventory manager service')
diff --git a/vpd_tool.cpp b/vpd_tool.cpp
new file mode 100644
index 0000000..0498761
--- /dev/null
+++ b/vpd_tool.cpp
@@ -0,0 +1,62 @@
+#include "vpd_tool_impl.hpp"
+
+#include <CLI/CLI.hpp>
+#include <fstream>
+#include <iostream>
+
+using namespace CLI;
+using namespace std;
+
+int main(int argc, char** argv)
+{
+ App app{"VPD Command line tool to dump the inventory and to read and "
+ "update the keywords"};
+
+ string objectPath{};
+
+ auto object =
+ app.add_option("--object, -O", objectPath, "Enter the Object Path");
+
+ auto dumpObjFlag =
+ app.add_flag("--dumpObject, -o",
+ "Dump the given object from the inventory. { "
+ "vpd-tool-exe --dumpObject/-o --object/-O object-name }")
+ ->needs(object);
+
+ auto dumpInvFlag = app.add_flag(
+ "--dumpInventory, -i", "Dump all the inventory objects. { vpd-tool-exe "
+ "--dumpInventory/-i }");
+
+ CLI11_PARSE(app, argc, argv);
+
+ ifstream inventoryJson(INVENTORY_JSON);
+ auto jsObject = json::parse(inventoryJson);
+
+ try
+ {
+ if (*dumpObjFlag)
+ {
+ VpdTool vpdToolObj(move(objectPath));
+ vpdToolObj.dumpObject(jsObject);
+ }
+
+ else if (*dumpInvFlag)
+ {
+ VpdTool vpdToolObj;
+ vpdToolObj.dumpInventory(jsObject);
+ }
+
+ else
+ {
+ throw runtime_error("One of the valid options is required. Refer "
+ "--help for list of options.");
+ }
+ }
+
+ catch (exception& e)
+ {
+ cerr << e.what();
+ }
+
+ return 0;
+}
diff --git a/vpd_tool_impl.cpp b/vpd_tool_impl.cpp
new file mode 100644
index 0000000..ccc2765
--- /dev/null
+++ b/vpd_tool_impl.cpp
@@ -0,0 +1,234 @@
+#include "vpd_tool_impl.hpp"
+
+#include <iostream>
+#include <sdbusplus/bus.hpp>
+#include <sstream>
+#include <variant>
+#include <vector>
+
+using namespace std;
+using sdbusplus::exception::SdBusError;
+using namespace openpower::vpd;
+
+void VpdTool::debugger(json output)
+{
+ cout << output.dump(4) << '\n';
+}
+
+auto VpdTool::makeDBusCall(const string& objectName, const string& interface,
+ const string& kw)
+{
+ auto bus = sdbusplus::bus::new_default();
+ auto properties =
+ bus.new_method_call(INVENTORY_MANAGER_SERVICE, objectName.c_str(),
+ "org.freedesktop.DBus.Properties", "Get");
+ properties.append(interface);
+ properties.append(kw);
+ auto result = bus.call(properties);
+
+ if (result.is_method_error())
+ {
+ throw runtime_error("Get api failed");
+ }
+ return result;
+}
+
+void VpdTool::addFruTypeAndLocation(json exIntf, const string& object,
+ json& kwVal)
+{
+ for (const auto& intf : exIntf.items())
+ {
+ if ((intf.key().find("Item") != string::npos) &&
+ (intf.value().is_null()))
+ {
+ kwVal.emplace("type", intf.key());
+ break;
+ }
+ }
+
+ // Add location code.
+ constexpr auto LOCATION_CODE_IF = "com.ibm.ipzvpd.Location";
+ constexpr auto LOCATION_CODE_PROP = "LocationCode";
+
+ try
+ {
+ variant<string> response;
+ makeDBusCall(object, LOCATION_CODE_IF, LOCATION_CODE_PROP)
+ .read(response);
+
+ if (auto prop = get_if<string>(&response))
+ {
+ kwVal.emplace(LOCATION_CODE_PROP, *prop);
+ }
+ }
+ catch (const SdBusError& e)
+ {
+ kwVal.emplace(LOCATION_CODE_PROP, "");
+ }
+}
+
+json VpdTool::getVINIProperties(string invPath, json exIntf)
+{
+ variant<Binary> response;
+ json output = json::object({});
+ json kwVal = json::object({});
+
+ vector<string> keyword{"CC", "SN", "PN", "FN", "DR"};
+ string interface = "com.ibm.ipzvpd.VINI";
+ string objectName = INVENTORY_PATH + invPath;
+
+ for (string kw : keyword)
+ {
+ try
+ {
+ makeDBusCall(objectName, interface, kw).read(response);
+
+ if (auto vec = get_if<Binary>(&response))
+ {
+ kwVal.emplace(kw, string(vec->begin(), vec->end()));
+ }
+ }
+ catch (const SdBusError& e)
+ {
+ output.emplace(invPath, json::object({}));
+ }
+ }
+
+ addFruTypeAndLocation(exIntf, objectName, kwVal);
+ output.emplace(invPath, kwVal);
+ return output;
+}
+
+void VpdTool::getExtraInterfaceProperties(string invPath, string extraInterface,
+ json prop, json exIntf, json& output)
+{
+ variant<string> response;
+
+ string objectName = INVENTORY_PATH + invPath;
+
+ for (const auto& itProp : prop.items())
+ {
+ string kw = itProp.key();
+ try
+ {
+ makeDBusCall(objectName, extraInterface, kw).read(response);
+
+ if (auto str = get_if<string>(&response))
+ {
+ output.emplace(kw, *str);
+ }
+ }
+ catch (const SdBusError& e)
+ {
+ output.emplace(invPath, json::object({}));
+ }
+ }
+ addFruTypeAndLocation(exIntf, objectName, output);
+}
+
+json VpdTool::interfaceDecider(json& itemEEPROM)
+{
+ if (itemEEPROM.find("inventoryPath") == itemEEPROM.end())
+ {
+ throw runtime_error("Inventory Path not found");
+ }
+
+ if (itemEEPROM.find("extraInterfaces") == itemEEPROM.end())
+ {
+ throw runtime_error("Extra Interfaces not found");
+ }
+
+ bool exIntfCheck = false;
+ json output = json::object({});
+
+ if (itemEEPROM.value("inherit", true))
+ {
+ json j = getVINIProperties(itemEEPROM.at("inventoryPath"),
+ itemEEPROM["extraInterfaces"]);
+ output.insert(j.begin(), j.end());
+ }
+ else
+ {
+ json js;
+ for (const auto& ex : itemEEPROM["extraInterfaces"].items())
+ {
+ if (!(ex.value().is_null()))
+ {
+ exIntfCheck = true;
+ getExtraInterfaceProperties(itemEEPROM.at("inventoryPath"),
+ ex.key(), ex.value(),
+ itemEEPROM["extraInterfaces"], js);
+ }
+ }
+ output.emplace(itemEEPROM.at("inventoryPath"), js);
+ }
+ return output;
+}
+
+json VpdTool::parseInvJson(const json& jsObject, char flag, string fruPath)
+{
+ json output = json::object({});
+ bool validObject = false;
+
+ if (jsObject.find("frus") == jsObject.end())
+ {
+ throw runtime_error("Frus missing in Inventory json");
+ }
+ else
+ {
+ for (const auto& itemFRUS : jsObject["frus"].items())
+ {
+ for (auto itemEEPROM : itemFRUS.value())
+ {
+ try
+ {
+ if (flag == 'O')
+ {
+ if (itemEEPROM.find("inventoryPath") ==
+ itemEEPROM.end())
+ {
+ throw runtime_error("Inventory Path not found");
+ }
+
+ else if (itemEEPROM.at("inventoryPath") == fruPath)
+ {
+ validObject = true;
+ json j = interfaceDecider(itemEEPROM);
+ output.insert(j.begin(), j.end());
+ return output;
+ }
+ }
+ else
+ {
+ json j = interfaceDecider(itemEEPROM);
+ output.insert(j.begin(), j.end());
+ }
+ }
+ catch (exception& e)
+ {
+ cerr << e.what();
+ }
+ }
+ }
+ if ((flag == 'O') && (!validObject))
+ {
+ throw runtime_error(
+ "Invalid object path. Refer --dumpInventory/-i option.");
+ }
+ }
+ return output;
+}
+
+void VpdTool::dumpInventory(const nlohmann::basic_json<>& jsObject)
+{
+ char flag = 'I';
+ json output = parseInvJson(jsObject, flag, "");
+ debugger(output);
+}
+
+void VpdTool::dumpObject(const nlohmann::basic_json<>& jsObject)
+{
+ char flag = 'O';
+ json output = parseInvJson(jsObject, flag, fruPath);
+ debugger(output);
+}
diff --git a/vpd_tool_impl.hpp b/vpd_tool_impl.hpp
new file mode 100644
index 0000000..378ece2
--- /dev/null
+++ b/vpd_tool_impl.hpp
@@ -0,0 +1,136 @@
+#include "config.h"
+
+#include "types.hpp"
+
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+class VpdTool
+{
+ private:
+ const std::string fruPath;
+
+ /**
+ * @brief Debugger
+ * Displays the output in JSON.
+ *
+ * @param[in] output - json output to be displayed
+ */
+ void debugger(json output);
+
+ /**
+ * @brief make Dbus Call
+ *
+ * @param[in] objectName - dbus Object
+ * @param[in] interface - dbus Interface
+ * @param[in] kw - keyword under the interface
+ *
+ * @return dbus call response
+ */
+ auto makeDBusCall(const std::string& objectName,
+ const std::string& interface, const std::string& kw);
+
+ /**
+ * @brief Adds FRU type and Location Code
+ * Appends the type of the FRU and location code to the output
+ *
+ * @param[in] exIntf - extraInterfaces json from INVENTORY_JSON
+ * @param[in] object - The D-Bus object to read the location code from
+ * @param[out] kwVal - JSON object into which the FRU type and location code
+ * are placed
+ */
+ void addFruTypeAndLocation(json exIntf, const std::string& object,
+ json& kwVal);
+
+ /**
+ * @brief Get VINI properties
+ * Making a dbus call for properties [SN, PN, CC, FN, DR]
+ * under VINI interface.
+ *
+ * @param[in] invPath - Value of inventory Path
+ * @param[in] exIntf - extraInterfaces json from INVENTORY_JSON
+ *
+ * @return json output which gives the properties under invPath's VINI
+ * interface
+ */
+ json getVINIProperties(std::string invPath, json exIntf);
+
+ /**
+ * @brief Get ExtraInterface Properties
+ * Making a dbus call for those properties under extraInterfaces.
+ *
+ * @param[in] invPath - Value of inventory path
+ * @param[in] extraInterface - One of the invPath's extraInterfaces whose
+ * value is not null
+ * @param[in] prop - All properties of the extraInterface.
+ *
+ * @return json output which gives the properties under invPath's
+ * extraInterface.
+ */
+ void getExtraInterfaceProperties(std::string invPath,
+ std::string extraInterface, json prop,
+ json exIntf, json& output);
+
+ /**
+ * @brief Interface Decider
+ * Decides whether to make the dbus call for
+ * getting properites from extraInterface or from
+ * VINI interface, depending on the value of
+ * extraInterfaces object in the inventory json.
+ *
+ * @param[in] itemEEPROM - holds the reference of one of the EEPROM objects.
+ *
+ * @return json output for one of the EEPROM objects.
+ */
+ json interfaceDecider(json& itemEEPROM);
+
+ /**
+ * @brief Parse Inventory JSON
+ * Parses the complete inventory json and depending upon
+ * the user option makes the dbuscall for the frus
+ * via interfaceDecider function.
+ *
+ * @param[in] jsObject - Inventory json object
+ * @param[in] flag - flag which tells about the user option(either
+ * dumpInventory or dumpObject)
+ * @param[in] fruPath - fruPath is empty for dumpInventory option and holds
+ * valid fruPath for dumpObject option.
+ *
+ * @return output json
+ */
+ json parseInvJson(const json& jsObject, char flag, std::string fruPath);
+
+ public:
+ /**
+ * @brief Dump the complete inventory in JSON format
+ *
+ * @param[in] jsObject - Inventory JSON specified in configure file.
+ */
+ void dumpInventory(const nlohmann::basic_json<>& jsObject);
+
+ /**
+ * @brief Dump the given inventory object in JSON format
+ *
+ * @param[in] jsObject - Inventory JSON specified in configure file.
+ */
+ void dumpObject(const nlohmann::basic_json<>& jsObject);
+
+ /**
+ * @brief A Constructor
+ * Constructor is called during the
+ * object instantiation for dumpInventory option.
+ */
+ VpdTool()
+ {
+ }
+
+ /**
+ * @brief Constructor
+ * Constructor is called during the
+ * object instantiation for dumpObject option.
+ */
+ VpdTool(const std::string&& fru) : fruPath(std::move(fru))
+ {
+ }
+};