| #include "prime_inventory.hpp" |
| |
| #include "event_logger.hpp" |
| #include "exceptions.hpp" |
| #include "utility/common_utility.hpp" |
| #include "utility/dbus_utility.hpp" |
| #include "utility/json_utility.hpp" |
| #include "utility/vpd_specific_utility.hpp" |
| |
| #include <string> |
| #include <vector> |
| |
| PrimeInventory::PrimeInventory() |
| { |
| try |
| { |
| uint16_t l_errCode = 0; |
| m_sysCfgJsonObj = |
| vpd::jsonUtility::getParsedJson(INVENTORY_JSON_SYM_LINK, l_errCode); |
| |
| if (l_errCode) |
| { |
| throw std::runtime_error( |
| "JSON parsing failed for file [ " + |
| std::string(INVENTORY_JSON_SYM_LINK) + |
| " ], error : " + vpd::commonUtility::getErrCodeMsg(l_errCode)); |
| } |
| |
| // check for mandatory fields at this point itself. |
| if (!m_sysCfgJsonObj.contains("frus")) |
| { |
| throw std::runtime_error( |
| "Mandatory tag(s) missing from JSON file [" + |
| std::string(INVENTORY_JSON_SYM_LINK) + "]"); |
| } |
| |
| m_logger = vpd::Logger::getLoggerInstance(); |
| } |
| catch (const std::exception& l_ex) |
| { |
| vpd::EventLogger::createSyncPel( |
| vpd::types::ErrorType::JsonFailure, |
| vpd::types::SeverityType::Critical, __FILE__, __FUNCTION__, 0, |
| "Prime inventory failed, reason: " + std::string(l_ex.what()), |
| std::nullopt, std::nullopt, std::nullopt, std::nullopt); |
| |
| throw; |
| } |
| } |
| |
| bool PrimeInventory::isPrimingRequired() const noexcept |
| { |
| 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); |
| } |
| 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; |
| } |
| |
| void PrimeInventory::primeSystemBlueprint() const noexcept |
| { |
| try |
| { |
| if (m_sysCfgJsonObj.empty() || !isPrimingRequired()) |
| { |
| 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) |
| { |
| m_logger->logMessage("Prime system inventory failed, reason: " + |
| std::string(l_ex.what())); |
| } |
| } |
| |
| bool PrimeInventory::primeInventory( |
| vpd::types::ObjectMap& o_objectInterfaceMap, |
| const nlohmann::json& i_fruJsonObj) const noexcept |
| { |
| 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. |
| uint16_t l_errCode = 0; |
| vpd::vpdSpecificUtility::resetDataUnderPIM( |
| std::string(i_fruJsonObj["inventoryPath"]), l_interfaces, |
| l_errCode); |
| |
| if (l_errCode) |
| { |
| m_logger->logMessage( |
| "Failed to reset data under PIM for path [" + |
| std::string(i_fruJsonObj["inventoryPath"]) + |
| "], error : " + vpd::commonUtility::getErrCodeMsg(l_errCode)); |
| } |
| } |
| |
| // 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( |
| const nlohmann::json& i_interfaceJson, |
| vpd::types::InterfaceMap& io_interfaceMap, |
| const vpd::types::VPDMapVariant& i_parsedVpdMap) const noexcept |
| { |
| 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", ""); |
| |
| uint16_t l_errCode = 0; |
| |
| 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_errCode); |
| |
| if (l_errCode) |
| { |
| m_logger->logMessage( |
| "Failed to get encoded keyword value for : " + |
| l_keyword + ", error : " + |
| vpd::commonUtility::getErrCodeMsg(l_errCode)); |
| } |
| |
| 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_errCode); |
| |
| if (l_errCode) |
| { |
| m_logger->logMessage( |
| "Failed to get encoded keyword value for : " + |
| l_keyword + ", error : " + |
| vpd::commonUtility::getErrCodeMsg( |
| l_errCode)); |
| } |
| |
| 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_errCode); |
| |
| if (l_errCode) |
| { |
| m_logger->logMessage( |
| "Failed to get encoded keyword value for : " + |
| l_keyword + ", error : " + |
| vpd::commonUtility::getErrCodeMsg( |
| l_errCode)); |
| } |
| |
| 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( |
| const std::string& i_inventoryObjPath, |
| vpd::types::InterfaceMap& io_interfaces) const noexcept |
| { |
| 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( |
| const std::string& i_inventoryObjPath, |
| vpd::types::InterfaceMap& io_interfaces) const noexcept |
| { |
| 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; |
| } |