functions: Parse json file to set bios attr table
The elements.json file have entries as follow:
"lids": [
{
"element_name": "HBD.RAINIER_2U_XML",
"short_lid_name": "81e00630",
},
{
"element_name": "HBD.RAINIER_2U_XML.iplTime",
"short_lid_name": "81e0068d",
},
{
"element_name": "NVRAM.P10",
"short_lid_name": "81e0066b",
},
{
"element_name": "HDAT.RAINIER_4U_XML",
"short_lid_name": "81e00675",
},
Need to parse the element name and lid name depending on the system. The
.P10 files are common to all P10 systems, the .<system> applies only to
the system name provided by Entity Manager. The .iplTIme takes
precedence and should be the lid number that it's picked up, so use []
instead of insert to overwrite an entry if it had already being set.
In the example above, the string for a rainier 2U should look like:
HBD=81e0068d,NVRAM=81e0066b.
Change-Id: I55b317ffef6a7fbe3fd5ee92e7f0188d831b7972
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/functions.cpp b/functions.cpp
index 2b6fbad..40bfc83 100644
--- a/functions.cpp
+++ b/functions.cpp
@@ -6,6 +6,7 @@
#include "functions.hpp"
+#include <nlohmann/json.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/bus/match.hpp>
@@ -14,6 +15,7 @@
#include <sdeventplus/event.hpp>
#include <filesystem>
+#include <fstream>
#include <functional>
#include <iostream>
#include <map>
@@ -196,13 +198,113 @@
}
/**
- * @brief Set the bios attribute table with details of the host firmware data
- * for this system.
+ * @brief Parse the elements json file and construct a string with the data to
+ * be used to update the bios attribute table.
+ *
+ * @param[in] elementsJsonFilePath - The path to the host firmware json file.
+ * @param[in] extensions - The extensions of the firmware blob files.
*/
-void setBiosAttr()
+std::string getBiosAttrStr(const std::filesystem::path& elementsJsonFilePath,
+ const std::vector<std::string>& extensions)
{
std::string biosAttrStr{};
+ std::ifstream jsonFile(elementsJsonFilePath.c_str());
+ if (!jsonFile)
+ {
+ return {};
+ }
+
+ std::map<std::string, std::string> attr;
+ auto data = nlohmann::json::parse(jsonFile, nullptr, false);
+ if (data.is_discarded())
+ {
+ log<level::ERR>("Error parsing JSON file",
+ entry("FILE=%s", elementsJsonFilePath.c_str()));
+ return {};
+ }
+
+ // .get requires a non-const iterator
+ for (auto& iter : data["lids"])
+ {
+ std::string name{};
+ std::string lid{};
+
+ try
+ {
+ name = iter["element_name"].get<std::string>();
+ lid = iter["short_lid_name"].get<std::string>();
+ }
+ catch (std::exception& e)
+ {
+ // Possibly the element or lid name field was not found
+ log<level::ERR>("Error reading JSON field",
+ entry("FILE=%s", elementsJsonFilePath.c_str()),
+ entry("ERROR=%s", e.what()));
+ continue;
+ }
+
+ // The elements with the ipl extension have higher priority. Therefore
+ // Use operator[] to overwrite value if an entry for it already exists.
+ // Ex: if the JSON contains an entry A.P10 followed by A.P10.iplTime,
+ // the lid value for the latter one will be overwrite the value of the
+ // first one.
+ constexpr auto iplExtension = ".iplTime";
+ std::filesystem::path path(name);
+ if (path.extension() == iplExtension)
+ {
+ // Some elements have an additional extension, ex: .P10.iplTime
+ // Strip off the ipl extension with stem(), then check if there is
+ // an additional extension with extension().
+ if (!path.stem().extension().empty())
+ {
+ // Check if the extension matches the extensions for this system
+ if (std::find(extensions.begin(), extensions.end(),
+ path.stem().extension()) == extensions.end())
+ {
+ continue;
+ }
+ }
+ // Get the element name without extensions by calling stem() twice
+ // since stem() returns the base name if no periods are found.
+ // Therefore both "element.P10" and "element.P10.iplTime" would
+ // become "element".
+ attr[path.stem().stem()] = lid;
+ continue;
+ }
+
+ // Process all other extensions. The extension should match the list of
+ // supported extensions for this system. Use .insert() to only add
+ // entries that do not exist, so to not overwrite the values that may
+ // had been added that had the ipl extension.
+ if (std::find(extensions.begin(), extensions.end(), path.extension()) !=
+ extensions.end())
+ {
+ attr.insert({path.stem(), lid});
+ }
+ }
+ for (const auto& a : attr)
+ {
+ // Build the bios attribute string with format:
+ // "element1=lid1,element2=lid2,elementN=lidN,"
+ biosAttrStr += a.first + "=" + a.second + ",";
+ }
+
+ return biosAttrStr;
+}
+
+/**
+ * @brief Set the bios attribute table with details of the host firmware data
+ * for this system.
+ *
+ * @param[in] elementsJsonFilePath - The path to the host firmware json file.
+ * @param[in] extentions - The extensions of the firmware blob files.
+ */
+void setBiosAttr(const std::filesystem::path& elementsJsonFilePath,
+ const std::vector<std::string>& extensions)
+{
+ auto biosAttrStr = getBiosAttrStr(elementsJsonFilePath, extensions);
+
constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager";
constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager";
constexpr auto dbusAttrName = "hb_lid_ids";
@@ -370,18 +472,20 @@
* @param[in] extensionMap a map of
* xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
* file extensions.
+ * @param[in] elementsJsonFilePath The file path to the json file
* @param[in] ibmCompatibleSystem The names property of an instance of
* xyz.openbmc_project.Configuration.IBMCompatibleSystem
*/
void maybeSetBiosAttr(
const std::map<std::string, std::vector<std::string>>& extensionMap,
+ const std::filesystem::path& elementsJsonFilePath,
const std::vector<std::string>& ibmCompatibleSystem)
{
std::vector<std::string> extensions;
if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
extensions))
{
- setBiosAttr();
+ setBiosAttr(elementsJsonFilePath, extensions);
}
}
@@ -524,16 +628,20 @@
* @param[in] bus - D-Bus client connection.
* @param[in] extensionMap - Map of IBMCompatibleSystem names and host firmware
* file extensions.
+ * @param[in] elementsJsonFilePath - The Path to the json file
* @param[in] loop - Program event loop.
* @return nullptr
*/
std::shared_ptr<void> updateBiosAttrTable(
sdbusplus::bus::bus& bus,
std::map<std::string, std::vector<std::string>> extensionMap,
- sdeventplus::Event& loop)
+ std::filesystem::path elementsJsonFilePath, sdeventplus::Event& loop)
{
auto pExtensionMap =
std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
+ auto pElementsJsonFilePath =
+ std::make_shared<decltype(elementsJsonFilePath)>(
+ std::move(elementsJsonFilePath));
auto getManagedObjects = bus.new_method_call(
"xyz.openbmc_project.EntityManager", "/",
@@ -551,8 +659,9 @@
catch (const sdbusplus::exception::SdBusError& e)
{}
- auto maybeSetAttrWithArgsBound = std::bind(
- maybeSetBiosAttr, std::cref(*pExtensionMap), std::placeholders::_1);
+ auto maybeSetAttrWithArgsBound =
+ std::bind(maybeSetBiosAttr, std::cref(*pExtensionMap),
+ std::cref(*pElementsJsonFilePath), std::placeholders::_1);
for (const auto& pair : objects)
{
diff --git a/functions.hpp b/functions.hpp
index b364393..bd4007d 100644
--- a/functions.hpp
+++ b/functions.hpp
@@ -48,6 +48,6 @@
std::shared_ptr<void>
updateBiosAttrTable(sdbusplus::bus::bus&,
std::map<std::string, std::vector<std::string>>,
- sdeventplus::Event&);
+ std::filesystem::path, sdeventplus::Event&);
} // namespace process_hostfirmware
} // namespace functions
diff --git a/item_updater_main.cpp b/item_updater_main.cpp
index 21164e9..c6a8175 100644
--- a/item_updater_main.cpp
+++ b/item_updater_main.cpp
@@ -91,9 +91,11 @@
"Update the bios attribute table with the host "
"firmware data details.")
->callback([&bus, &loop, &subcommandContext, extensionMap]() {
+ auto elementsJsonFilePath = "/usr/share/hostfw/elements.json"s;
subcommandContext.push_back(
functions::process_hostfirmware::updateBiosAttrTable(
- bus, extensionMap, loop));
+ bus, extensionMap, std::move(elementsJsonFilePath),
+ loop));
}));
CLI11_PARSE(app, argc, argv);