Implementation of priming in wait-vpd-parsers
As part of PST priming system inventory and FRUs VPD collection will be
part of systemd target, instead of being triggered internally by
vpd-manager.
This commit implements priming system blueprint in wait-vpd-parsers
application and removes the priming from vpd-manager initialization
flow.
```
Testing performed:
1. Removed PIM cache path in the system.
2. Restarted PIM service.
systemctl restart xyz.openbmc_project.Inventory.Manager
3. Observed inventory paths are removed under PIM using below command
busctl tree xyz.openbmc_project.Inventory.Manager
3. Restarted wait-vpd-parsers service
systemctl restart wait-vpd-parsers
4. Observed that system is primed again with all inventory object paths, using below command
busctl tree xyz.openbmc_project.Inventory.Manager
```
Change-Id: Ic93f54e909a247587208082830c5ebbe48f69f50
Signed-off-by: Anupama B R <anupama.b.r1@ibm.com>
diff --git a/wait-vpd-parser/src/prime_inventory.cpp b/wait-vpd-parser/src/prime_inventory.cpp
index 79125fe..b09b635 100644
--- a/wait-vpd-parser/src/prime_inventory.cpp
+++ b/wait-vpd-parser/src/prime_inventory.cpp
@@ -8,6 +8,7 @@
#include "utility/vpd_specific_utility.hpp"
#include <string>
+#include <vector>
PrimeInventory::PrimeInventory()
{
@@ -32,6 +33,8 @@
"Mandatory tag(s) missing from JSON file [" +
std::string(INVENTORY_JSON_SYM_LINK) + "]");
}
+
+ m_logger = vpd::Logger::getLoggerInstance();
}
catch (const std::exception& l_ex)
{
@@ -47,7 +50,42 @@
bool PrimeInventory::isPrimingRequired() const noexcept
{
- // ToDo: Check if priming is required.
+ try
+ {
+ // get all object paths under PIM
+ const auto l_objectPaths = vpd::dbusUtility::GetSubTreePaths(
+ vpd::constants::systemInvPath, 0,
+ std::vector<std::string>{vpd::constants::vpdCollectionInterface});
+
+ const nlohmann::json& l_listOfFrus =
+ m_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ size_t l_invPathCount = 0;
+
+ for (const auto& l_itemFRUS : l_listOfFrus.items())
+ {
+ for (const auto& l_Fru : l_itemFRUS.value())
+ {
+ if (l_Fru.contains("ccin") || (l_Fru.contains("noprime") &&
+ l_Fru.value("noprime", false)))
+ {
+ continue;
+ }
+
+ l_invPathCount += 1;
+ }
+ }
+ return ((l_objectPaths.size() >= l_invPathCount) ? false : true);
+ }
+ catch (const std::exception& l_ex)
+ {
+ m_logger->logMessage(
+ "Error while checking is priming required or not, error: " +
+ std::string(l_ex.what()));
+ }
+
+ // In case of any error, perform priming, as it's unclear whether priming is
+ // required.
return true;
}
@@ -55,45 +93,353 @@
{
try
{
- /*ToDo:
- * Check if priming is required.
- * Traverse the system config JSON &
- prime all the FRU paths which qualifies for priming.
- */
+ if (m_sysCfgJsonObj.empty())
+ {
+ return;
+ }
+
+ const nlohmann::json& l_listOfFrus =
+ m_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ vpd::types::ObjectMap l_objectInterfaceMap;
+ for (const auto& l_itemFRUS : l_listOfFrus.items())
+ {
+ const std::string& l_vpdFilePath = l_itemFRUS.key();
+
+ if (l_vpdFilePath == SYSTEM_VPD_FILE_PATH)
+ {
+ continue;
+ }
+
+ // Prime the inventry for FRUs
+ for (const auto& l_Fru : m_sysCfgJsonObj["frus"][l_vpdFilePath])
+ {
+ if (!primeInventory(l_objectInterfaceMap, l_Fru))
+ {
+ m_logger->logMessage(
+ "Priming of inventory failed for FRU " +
+ std::string(l_Fru["inventoryPath"]));
+ }
+ }
+ }
+
+ // Notify PIM
+ if (!l_objectInterfaceMap.empty())
+ {
+ if (!vpd::dbusUtility::callPIM(move(l_objectInterfaceMap)))
+ {
+ m_logger->logMessage(
+ "Call to PIM failed while priming inventory");
+ }
+ }
+ else
+ {
+ m_logger->logMessage("Priming inventory failed");
+ }
}
catch (const std::exception& l_ex)
{
- // ToDo: log an error
+ m_logger->logMessage("Prime system inventory failed, reason: " +
+ std::string(l_ex.what()));
}
}
bool PrimeInventory::primeInventory(
- [[maybe_unused]] const std::string& i_vpdFilePath) const noexcept
+ vpd::types::ObjectMap& o_objectInterfaceMap,
+ const nlohmann::json& i_fruJsonObj) const noexcept
{
- // ToDo: Travers system config JSON & prime inventory objects found under
- // the EEPROM.
+ if (i_fruJsonObj.empty())
+ {
+ m_logger->logMessage("Empty FRU JSON given");
+ return false;
+ }
+
+ vpd::types::InterfaceMap l_interfaces;
+ sdbusplus::message::object_path l_fruObjectPath(
+ i_fruJsonObj["inventoryPath"]);
+
+ if (i_fruJsonObj.contains("ccin"))
+ {
+ return true;
+ }
+
+ if (i_fruJsonObj.contains("noprime") &&
+ i_fruJsonObj.value("noprime", false))
+ {
+ return true;
+ }
+
+ // Reset data under PIM for this FRU only if the FRU is not synthesized
+ // and we handle it's Present property.
+ if (isPresentPropertyHandlingRequired(i_fruJsonObj))
+ {
+ // Clear data under PIM if already exists.
+ vpd::vpdSpecificUtility::resetDataUnderPIM(
+ std::string(i_fruJsonObj["inventoryPath"]), l_interfaces);
+ }
+
+ // Add extra interfaces mentioned in the Json config file
+ if (i_fruJsonObj.contains("extraInterfaces"))
+ {
+ populateInterfaces(i_fruJsonObj["extraInterfaces"], l_interfaces,
+ std::monostate{});
+ }
+
+ vpd::types::PropertyMap l_propertyValueMap;
+
+ // Update Present property for this FRU only if we handle Present
+ // property for the FRU.
+ if (isPresentPropertyHandlingRequired(i_fruJsonObj))
+ {
+ l_propertyValueMap.emplace("Present", false);
+
+ // TODO: Present based on file will be taken care in future.
+ // By default present is set to false for FRU at the time of
+ // priming. Once collection goes through, it will be set to true in
+ // that flow.
+ /*if (std::filesystem::exists(i_vpdFilePath))
+ {
+ l_propertyValueMap["Present"] = true;
+ }*/
+ }
+
+ vpd::vpdSpecificUtility::insertOrMerge(
+ l_interfaces, "xyz.openbmc_project.Inventory.Item",
+ move(l_propertyValueMap));
+
+ if (i_fruJsonObj.value("inherit", true) &&
+ m_sysCfgJsonObj.contains("commonInterfaces"))
+ {
+ populateInterfaces(m_sysCfgJsonObj["commonInterfaces"], l_interfaces,
+ std::monostate{});
+ }
+
+ processFunctionalProperty(i_fruJsonObj["inventoryPath"], l_interfaces);
+ processEnabledProperty(i_fruJsonObj["inventoryPath"], l_interfaces);
+
+ // Emplace the default state of FRU VPD collection
+ vpd::types::PropertyMap l_fruCollectionProperty = {
+ {"Status", vpd::constants::vpdCollectionNotStarted}};
+
+ vpd::vpdSpecificUtility::insertOrMerge(
+ l_interfaces, vpd::constants::vpdCollectionInterface,
+ std::move(l_fruCollectionProperty));
+
+ o_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
+ std::move(l_interfaces));
+
return true;
}
void PrimeInventory::populateInterfaces(
- [[maybe_unused]] const nlohmann::json& i_interfaceJson,
- [[maybe_unused]] vpd::types::InterfaceMap& io_interfaceMap,
- [[maybe_unused]] const vpd::types::VPDMapVariant& i_parsedVpdMap)
- const noexcept
+ const nlohmann::json& i_interfaceJson,
+ vpd::types::InterfaceMap& io_interfaceMap,
+ const vpd::types::VPDMapVariant& i_parsedVpdMap) const noexcept
{
- // ToDo: Populate interfaces needs to be published on Dbus.
+ for (const auto& l_interfacesPropPair : i_interfaceJson.items())
+ {
+ const std::string& l_interface = l_interfacesPropPair.key();
+ vpd::types::PropertyMap l_propertyMap;
+
+ for (const auto& l_propValuePair : l_interfacesPropPair.value().items())
+ {
+ const std::string l_property = l_propValuePair.key();
+
+ if (l_propValuePair.value().is_boolean())
+ {
+ l_propertyMap.emplace(l_property,
+ l_propValuePair.value().get<bool>());
+ }
+ else if (l_propValuePair.value().is_string())
+ {
+ if (l_property.compare("LocationCode") == 0 &&
+ l_interface.compare("com.ibm.ipzvpd.Location") == 0)
+ {
+ std::string l_value =
+ vpd::vpdSpecificUtility::getExpandedLocationCode(
+ l_propValuePair.value().get<std::string>(),
+ i_parsedVpdMap);
+ l_propertyMap.emplace(l_property, l_value);
+
+ auto l_locCodeProperty = l_propertyMap;
+ vpd::vpdSpecificUtility::insertOrMerge(
+ io_interfaceMap,
+ std::string(vpd::constants::xyzLocationCodeInf),
+ move(l_locCodeProperty));
+ }
+ else
+ {
+ l_propertyMap.emplace(
+ l_property, l_propValuePair.value().get<std::string>());
+ }
+ }
+ else if (l_propValuePair.value().is_array())
+ {
+ try
+ {
+ l_propertyMap.emplace(l_property,
+ l_propValuePair.value()
+ .get<vpd::types::BinaryVector>());
+ }
+ catch (const nlohmann::detail::type_error& e)
+ {
+ std::cerr << "Type exception: " << e.what() << "\n";
+ }
+ }
+ else if (l_propValuePair.value().is_number())
+ {
+ // For now assume the value is a size_t. In the future it would
+ // be nice to come up with a way to get the type from the JSON.
+ l_propertyMap.emplace(l_property,
+ l_propValuePair.value().get<size_t>());
+ }
+ else if (l_propValuePair.value().is_object())
+ {
+ const std::string& l_record =
+ l_propValuePair.value().value("recordName", "");
+ const std::string& l_keyword =
+ l_propValuePair.value().value("keywordName", "");
+ const std::string& l_encoding =
+ l_propValuePair.value().value("encoding", "");
+
+ if (auto l_ipzVpdMap =
+ std::get_if<vpd::types::IPZVpdMap>(&i_parsedVpdMap))
+ {
+ if (!l_record.empty() && !l_keyword.empty() &&
+ (*l_ipzVpdMap).count(l_record) &&
+ (*l_ipzVpdMap).at(l_record).count(l_keyword))
+ {
+ auto l_encoded = vpd::vpdSpecificUtility::encodeKeyword(
+ ((*l_ipzVpdMap).at(l_record).at(l_keyword)),
+ l_encoding);
+ l_propertyMap.emplace(l_property, l_encoded);
+ }
+ }
+ else if (auto l_kwdVpdMap =
+ std::get_if<vpd::types::KeywordVpdMap>(
+ &i_parsedVpdMap))
+ {
+ if (!l_keyword.empty() && (*l_kwdVpdMap).count(l_keyword))
+ {
+ if (auto l_kwValue =
+ std::get_if<vpd::types::BinaryVector>(
+ &(*l_kwdVpdMap).at(l_keyword)))
+ {
+ auto l_encodedValue =
+ vpd::vpdSpecificUtility::encodeKeyword(
+ std::string((*l_kwValue).begin(),
+ (*l_kwValue).end()),
+ l_encoding);
+
+ l_propertyMap.emplace(l_property, l_encodedValue);
+ }
+ else if (auto l_kwValue = std::get_if<std::string>(
+ &(*l_kwdVpdMap).at(l_keyword)))
+ {
+ auto l_encodedValue =
+ vpd::vpdSpecificUtility::encodeKeyword(
+ std::string((*l_kwValue).begin(),
+ (*l_kwValue).end()),
+ l_encoding);
+
+ l_propertyMap.emplace(l_property, l_encodedValue);
+ }
+ else if (auto l_uintValue = std::get_if<size_t>(
+ &(*l_kwdVpdMap).at(l_keyword)))
+ {
+ l_propertyMap.emplace(l_property, *l_uintValue);
+ }
+ else
+ {
+ m_logger->logMessage(
+ "Unknown keyword found, Keywrod = " +
+ l_keyword);
+ }
+ }
+ }
+ }
+ }
+ vpd::vpdSpecificUtility::insertOrMerge(io_interfaceMap, l_interface,
+ move(l_propertyMap));
+ }
}
void PrimeInventory::processFunctionalProperty(
- [[maybe_unused]] const std::string& i_inventoryObjPath,
- [[maybe_unused]] vpd::types::InterfaceMap& io_interfaces) const noexcept
+ const std::string& i_inventoryObjPath,
+ vpd::types::InterfaceMap& io_interfaces) const noexcept
{
- // ToDo: Populate interface to publish Functional property on Dbus.
+ if (!vpd::dbusUtility::isChassisPowerOn())
+ {
+ std::vector<std::string> l_operationalStatusInf{
+ vpd::constants::operationalStatusInf};
+
+ auto l_mapperObjectMap = vpd::dbusUtility::getObjectMap(
+ i_inventoryObjPath, l_operationalStatusInf);
+
+ // If the object has been found. Check if it is under PIM.
+ if (l_mapperObjectMap.size() != 0)
+ {
+ for (const auto& [l_serviceName, l_interfaceLsit] :
+ l_mapperObjectMap)
+ {
+ if (l_serviceName == vpd::constants::pimServiceName)
+ {
+ // The object is already under PIM. No need to process
+ // again. Retain the old value.
+ return;
+ }
+ }
+ }
+
+ // Implies value is not there in D-Bus. Populate it with default
+ // value "true".
+ vpd::types::PropertyMap l_functionalProp;
+ l_functionalProp.emplace("Functional", true);
+ vpd::vpdSpecificUtility::insertOrMerge(
+ io_interfaces, vpd::constants::operationalStatusInf,
+ move(l_functionalProp));
+ }
+
+ // if chassis is power on. Functional property should be there on D-Bus.
+ // Don't process.
+ return;
}
void PrimeInventory::processEnabledProperty(
- [[maybe_unused]] const std::string& i_inventoryObjPath,
- [[maybe_unused]] vpd::types::InterfaceMap& io_interfaces) const noexcept
+ const std::string& i_inventoryObjPath,
+ vpd::types::InterfaceMap& io_interfaces) const noexcept
{
- // ToDo: Populate interface to publish Enabled property on Dbus.
+ if (!vpd::dbusUtility::isChassisPowerOn())
+ {
+ std::vector<std::string> l_enableInf{vpd::constants::enableInf};
+
+ auto l_mapperObjectMap =
+ vpd::dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
+
+ // If the object has been found. Check if it is under PIM.
+ if (l_mapperObjectMap.size() != 0)
+ {
+ for (const auto& [l_serviceName, l_interfaceLsit] :
+ l_mapperObjectMap)
+ {
+ if (l_serviceName == vpd::constants::pimServiceName)
+ {
+ // The object is already under PIM. No need to process
+ // again. Retain the old value.
+ return;
+ }
+ }
+ }
+
+ // Implies value is not there in D-Bus. Populate it with default
+ // value "true".
+ vpd::types::PropertyMap l_enabledProp;
+ l_enabledProp.emplace("Enabled", true);
+ vpd::vpdSpecificUtility::insertOrMerge(
+ io_interfaces, vpd::constants::enableInf, move(l_enabledProp));
+ }
+
+ // if chassis is power on. Enabled property should be there on D-Bus.
+ // Don't process.
+ return;
}