blob: fdc0463399758e10dd10e6bacb920a9f0c84572c [file] [log] [blame]
#include "config.h"
#include "manager.hpp"
#include "backup_restore.hpp"
#include "constants.hpp"
#include "exceptions.hpp"
#include "logger.hpp"
#include "parser.hpp"
#include "parser_factory.hpp"
#include "parser_interface.hpp"
#include "types.hpp"
#include "utility/dbus_utility.hpp"
#include "utility/json_utility.hpp"
#include "utility/vpd_specific_utility.hpp"
#include <boost/asio/steady_timer.hpp>
#include <sdbusplus/bus/match.hpp>
#include <sdbusplus/message.hpp>
namespace vpd
{
Manager::Manager(
const std::shared_ptr<boost::asio::io_context>& ioCon,
const std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace,
const std::shared_ptr<sdbusplus::asio::connection>& asioConnection) :
m_ioContext(ioCon), m_interface(iFace), m_asioConnection(asioConnection)
{
try
{
#ifdef IBM_SYSTEM
if (dbusUtility::isChassisPowerOn())
{
// At power on, less number of FRU(s) needs collection. we can scale
// down the threads to reduce CPU utilization.
m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT,
constants::VALUE_1);
}
else
{
// Initialize with default configuration
m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
}
// Set up minimal things that is needed before bus name is claimed.
m_worker->performInitialSetup();
// set callback to detect any asset tag change
registerAssetTagChangeCallback();
// set async timer to detect if system VPD is published on D-Bus.
SetTimerToDetectSVPDOnDbus();
// set async timer to detect if VPD collection is done.
SetTimerToDetectVpdCollectionStatus();
// Instantiate GpioMonitor class
m_gpioMonitor = std::make_shared<GpioMonitor>(
m_worker->getSysCfgJsonObj(), m_worker, m_ioContext);
#endif
// set callback to detect host state change.
registerHostStateChangeCallback();
// For backward compatibility. Should be depricated.
iFace->register_method(
"WriteKeyword",
[this](const sdbusplus::message::object_path i_path,
const std::string i_recordName, const std::string i_keyword,
const types::BinaryVector i_value) -> int {
return this->updateKeyword(
i_path, std::make_tuple(i_recordName, i_keyword, i_value));
});
// Register methods under com.ibm.VPD.Manager interface
iFace->register_method(
"UpdateKeyword",
[this](const types::Path i_vpdPath,
const types::WriteVpdParams i_paramsToWriteData) -> int {
return this->updateKeyword(i_vpdPath, i_paramsToWriteData);
});
iFace->register_method(
"WriteKeywordOnHardware",
[this](const types::Path i_fruPath,
const types::WriteVpdParams i_paramsToWriteData) -> int {
return this->updateKeywordOnHardware(i_fruPath,
i_paramsToWriteData);
});
iFace->register_method(
"ReadKeyword",
[this](const types::Path i_fruPath,
const types::ReadVpdParams i_paramsToReadData)
-> types::DbusVariantType {
return this->readKeyword(i_fruPath, i_paramsToReadData);
});
iFace->register_method(
"CollectFRUVPD",
[this](const sdbusplus::message::object_path& i_dbusObjPath) {
this->collectSingleFruVpd(i_dbusObjPath);
});
iFace->register_method(
"deleteFRUVPD",
[this](const sdbusplus::message::object_path& i_dbusObjPath) {
this->deleteSingleFruVpd(i_dbusObjPath);
});
iFace->register_method(
"GetExpandedLocationCode",
[this](const std::string& i_unexpandedLocationCode,
uint16_t& i_nodeNumber) -> std::string {
return this->getExpandedLocationCode(i_unexpandedLocationCode,
i_nodeNumber);
});
iFace->register_method("GetFRUsByExpandedLocationCode",
[this](const std::string& i_expandedLocationCode)
-> types::ListOfPaths {
return this->getFrusByExpandedLocationCode(
i_expandedLocationCode);
});
iFace->register_method(
"GetFRUsByUnexpandedLocationCode",
[this](const std::string& i_unexpandedLocationCode,
uint16_t& i_nodeNumber) -> types::ListOfPaths {
return this->getFrusByUnexpandedLocationCode(
i_unexpandedLocationCode, i_nodeNumber);
});
iFace->register_method(
"GetHardwarePath",
[this](const sdbusplus::message::object_path& i_dbusObjPath)
-> std::string { return this->getHwPath(i_dbusObjPath); });
iFace->register_method("PerformVPDRecollection", [this]() {
this->performVpdRecollection();
});
// Indicates FRU VPD collection for the system has not started.
iFace->register_property_rw<std::string>(
"CollectionStatus", sdbusplus::vtable::property_::emits_change,
[this](const std::string l_currStatus, const auto&) {
m_vpdCollectionStatus = l_currStatus;
return 0;
},
[this](const auto&) { return m_vpdCollectionStatus; });
}
catch (const std::exception& e)
{
logging::logMessage(
"VPD-Manager service failed. " + std::string(e.what()));
throw;
}
}
#ifdef IBM_SYSTEM
void Manager::registerAssetTagChangeCallback()
{
static std::shared_ptr<sdbusplus::bus::match_t> l_assetMatch =
std::make_shared<sdbusplus::bus::match_t>(
*m_asioConnection,
sdbusplus::bus::match::rules::propertiesChanged(
constants::systemInvPath, constants::assetTagInf),
[this](sdbusplus::message_t& l_msg) {
processAssetTagChangeCallback(l_msg);
});
}
void Manager::processAssetTagChangeCallback(sdbusplus::message_t& i_msg)
{
try
{
if (i_msg.is_method_error())
{
throw std::runtime_error(
"Error reading callback msg for asset tag.");
}
std::string l_objectPath;
types::PropertyMap l_propMap;
i_msg.read(l_objectPath, l_propMap);
const auto& l_itrToAssetTag = l_propMap.find("AssetTag");
if (l_itrToAssetTag != l_propMap.end())
{
if (auto l_assetTag =
std::get_if<std::string>(&(l_itrToAssetTag->second)))
{
// Call Notify to persist the AssetTag
types::ObjectMap l_objectMap = {
{sdbusplus::message::object_path(constants::systemInvPath),
{{constants::assetTagInf, {{"AssetTag", *l_assetTag}}}}}};
// Notify PIM
if (!dbusUtility::callPIM(move(l_objectMap)))
{
throw std::runtime_error(
"Call to PIM failed for asset tag update.");
}
}
}
else
{
throw std::runtime_error(
"Could not find asset tag in callback message.");
}
}
catch (const std::exception& l_ex)
{
// TODO: Log PEL with below description.
logging::logMessage("Asset tag callback update failed with error: " +
std::string(l_ex.what()));
}
}
void Manager::SetTimerToDetectSVPDOnDbus()
{
static boost::asio::steady_timer timer(*m_ioContext);
// timer for 2 seconds
auto asyncCancelled = timer.expires_after(std::chrono::seconds(2));
(asyncCancelled == 0) ? logging::logMessage("Timer started")
: logging::logMessage("Timer re-started");
timer.async_wait([this](const boost::system::error_code& ec) {
if (ec == boost::asio::error::operation_aborted)
{
throw std::runtime_error(
"Timer to detect system VPD collection status was aborted");
}
if (ec)
{
throw std::runtime_error(
"Timer to detect System VPD collection failed");
}
if (m_worker->isSystemVPDOnDBus())
{
// cancel the timer
timer.cancel();
// Triggering FRU VPD collection. Setting status to "In
// Progress".
m_interface->set_property("CollectionStatus",
std::string("InProgress"));
m_worker->collectFrusFromJson();
}
});
}
void Manager::SetTimerToDetectVpdCollectionStatus()
{
// Keeping max retry for 2 minutes. TODO: Make it configurable based on
// system type.
static constexpr auto MAX_RETRY = 12;
static boost::asio::steady_timer l_timer(*m_ioContext);
static uint8_t l_timerRetry = 0;
auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(10));
(l_asyncCancelled == 0)
? logging::logMessage("Collection Timer started")
: logging::logMessage("Collection Timer re-started");
l_timer.async_wait([this](const boost::system::error_code& ec) {
if (ec == boost::asio::error::operation_aborted)
{
throw std::runtime_error(
"Timer to detect thread collection status was aborted");
}
if (ec)
{
throw std::runtime_error(
"Timer to detect thread collection failed");
}
if (m_worker->isAllFruCollectionDone())
{
// cancel the timer
l_timer.cancel();
processFailedEeproms();
// update VPD for powerVS system.
ConfigurePowerVsSystem();
m_interface->set_property("CollectionStatus",
std::string("Completed"));
const nlohmann::json& l_sysCfgJsonObj =
m_worker->getSysCfgJsonObj();
if (jsonUtility::isBackupAndRestoreRequired(l_sysCfgJsonObj))
{
BackupAndRestore l_backupAndRestoreObj(l_sysCfgJsonObj);
l_backupAndRestoreObj.backupAndRestore();
}
}
else
{
auto l_threadCount = m_worker->getActiveThreadCount();
if (l_timerRetry == MAX_RETRY)
{
l_timer.cancel();
logging::logMessage("Taking too long. Active thread = " +
std::to_string(l_threadCount));
}
else
{
l_timerRetry++;
logging::logMessage("Collection is in progress for [" +
std::to_string(l_threadCount) + "] FRUs.");
SetTimerToDetectVpdCollectionStatus();
}
}
});
}
void Manager::checkAndUpdatePowerVsVpd(
const nlohmann::json& i_powerVsJsonObj,
std::vector<std::string>& o_failedPathList)
{
for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items())
{
nlohmann::json l_sysCfgJsonObj{};
if (m_worker.get() != nullptr)
{
l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
}
// The utility method will handle emty JSON case. No explicit
// handling required here.
auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
l_sysCfgJsonObj, l_fruPath);
// Mark it as failed if inventory path not found in JSON.
if (l_inventoryPath.empty())
{
o_failedPathList.push_back(l_fruPath);
continue;
}
// check if the FRU is present
if (!dbusUtility::isInventoryPresent(l_inventoryPath))
{
logging::logMessage(
"Inventory not present, skip updating part number. Path: " +
l_inventoryPath);
continue;
}
// check if the FRU needs CCIN check before updating PN.
if (l_recJson.contains("CCIN"))
{
const auto& l_ccinFromDbus =
vpdSpecificUtility::getCcinFromDbus(l_inventoryPath);
// Not an ideal situation as CCIN can't be empty.
if (l_ccinFromDbus.empty())
{
o_failedPathList.push_back(l_fruPath);
continue;
}
std::vector<std::string> l_ccinListFromJson = l_recJson["CCIN"];
if (find(l_ccinListFromJson.begin(), l_ccinListFromJson.end(),
l_ccinFromDbus) == l_ccinListFromJson.end())
{
// Don't update PN in this case.
continue;
}
}
for (const auto& [l_recordName, l_kwdJson] : l_recJson.items())
{
// Record name can't be CCIN, skip processing as it is there for PN
// update based on CCIN check.
if (l_recordName == constants::kwdCCIN)
{
continue;
}
for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items())
{
// Is value of type array.
if (!l_kwdValue.is_array())
{
o_failedPathList.push_back(l_fruPath);
continue;
}
// Get current Part number.
auto l_retVal = dbusUtility::readDbusProperty(
constants::pimServiceName, l_inventoryPath,
constants::viniInf, constants::kwdPN);
auto l_ptrToPn = std::get_if<types::BinaryVector>(&l_retVal);
if (!l_ptrToPn)
{
o_failedPathList.push_back(l_fruPath);
continue;
}
types::BinaryVector l_binaryKwdValue =
l_kwdValue.get<types::BinaryVector>();
if (l_binaryKwdValue == (*l_ptrToPn))
{
continue;
}
// Update part number only if required.
if (updateKeyword(
l_fruPath,
std::make_tuple(l_recordName, l_kwdName, l_kwdValue)) ==
constants::FAILURE)
{
o_failedPathList.push_back(l_fruPath);
continue;
}
// update the Asset interface PN explicitly
if (!dbusUtility::callPIM(types::ObjectMap{
{l_inventoryPath,
{{constants::viniInf,
{{"PartNumber",
std::string(l_binaryKwdValue.begin(),
l_binaryKwdValue.end())}}}}}}))
{
logging::logMessage(
"Updating PN under Asset interface failed for path [" +
l_inventoryPath + "]");
}
// Just needed for logging.
std::string l_initialPartNum((*l_ptrToPn).begin(),
(*l_ptrToPn).end());
std::string l_finalPartNum(l_binaryKwdValue.begin(),
l_binaryKwdValue.end());
logging::logMessage(
"Part number updated for path [" + l_inventoryPath + "]" +
"From [" + l_initialPartNum + "]" + " to [" +
l_finalPartNum + "]");
}
}
}
}
void Manager::ConfigurePowerVsSystem()
{
std::vector<std::string> l_failedPathList;
try
{
types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
if (l_imValue.empty())
{
throw DbusException("Invalid IM value read from Dbus");
}
if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue))
{
// TODO: Should booting be blocked in case of some
// misconfigurations?
return;
}
const nlohmann::json& l_powerVsJsonObj =
jsonUtility::getPowerVsJson(l_imValue);
if (l_powerVsJsonObj.empty())
{
throw std::runtime_error("PowerVS Json not found");
}
checkAndUpdatePowerVsVpd(l_powerVsJsonObj, l_failedPathList);
if (!l_failedPathList.empty())
{
throw std::runtime_error(
"Part number update failed for following paths: ");
}
}
catch (const std::exception& l_ex)
{
// TODO log appropriate PEL
}
}
void Manager::processFailedEeproms()
{
if (m_worker.get() != nullptr)
{
// TODO:
// - iterate through list of EEPROMs for which thread creation has
// failed
// - For each failed EEPROM, trigger VPD collection
m_worker->getFailedEepromPaths().clear();
}
}
#endif
int Manager::updateKeyword(const types::Path i_vpdPath,
const types::WriteVpdParams i_paramsToWriteData)
{
if (i_vpdPath.empty())
{
logging::logMessage("Given VPD path is empty.");
return -1;
}
types::Path l_fruPath;
nlohmann::json l_sysCfgJsonObj{};
if (m_worker.get() != nullptr)
{
l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
// Get the EEPROM path
if (!l_sysCfgJsonObj.empty())
{
l_fruPath =
jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath);
}
}
if (l_fruPath.empty())
{
l_fruPath = i_vpdPath;
}
try
{
std::shared_ptr<Parser> l_parserObj =
std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
return l_parserObj->updateVpdKeyword(i_paramsToWriteData);
}
catch (const std::exception& l_exception)
{
// TODO:: error log needed
logging::logMessage("Update keyword failed for file[" + i_vpdPath +
"], reason: " + std::string(l_exception.what()));
return -1;
}
}
int Manager::updateKeywordOnHardware(
const types::Path i_fruPath,
const types::WriteVpdParams i_paramsToWriteData) noexcept
{
try
{
if (i_fruPath.empty())
{
throw std::runtime_error("Given FRU path is empty");
}
nlohmann::json l_sysCfgJsonObj{};
if (m_worker.get() != nullptr)
{
l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
}
std::shared_ptr<Parser> l_parserObj =
std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj);
return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData);
}
catch (const std::exception& l_exception)
{
EventLogger::createAsyncPel(
types::ErrorType::InvalidEeprom, types::SeverityType::Informational,
__FILE__, __FUNCTION__, 0,
"Update keyword on hardware failed for file[" + i_fruPath +
"], reason: " + std::string(l_exception.what()),
std::nullopt, std::nullopt, std::nullopt, std::nullopt);
return constants::FAILURE;
}
}
types::DbusVariantType Manager::readKeyword(
const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData)
{
try
{
nlohmann::json l_jsonObj{};
if (m_worker.get() != nullptr)
{
l_jsonObj = m_worker->getSysCfgJsonObj();
}
std::error_code ec;
// Check if given path is filesystem path
if (!std::filesystem::exists(i_fruPath, ec) && (ec))
{
throw std::runtime_error(
"Given file path " + i_fruPath + " not found.");
}
logging::logMessage("Performing VPD read on " + i_fruPath);
std::shared_ptr<vpd::Parser> l_parserObj =
std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj);
std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance =
l_parserObj->getVpdParserInstance();
return (
l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData));
}
catch (const std::exception& e)
{
logging::logMessage(
e.what() + std::string(". VPD manager read operation failed for ") +
i_fruPath);
throw types::DeviceError::ReadFailure();
}
}
void Manager::collectSingleFruVpd(
const sdbusplus::message::object_path& i_dbusObjPath)
{
try
{
if (m_vpdCollectionStatus != "Completed")
{
logging::logMessage(
"Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " +
std::string(i_dbusObjPath));
return;
}
// Get system config JSON object from worker class
nlohmann::json l_sysCfgJsonObj{};
if (m_worker.get() != nullptr)
{
l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
}
// Check if system config JSON is present
if (l_sysCfgJsonObj.empty())
{
logging::logMessage(
"System config JSON object not present. Single FRU VPD collection is not performed for " +
std::string(i_dbusObjPath));
return;
}
// Get FRU path for the given D-bus object path from JSON
const std::string& l_fruPath =
jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_dbusObjPath);
if (l_fruPath.empty())
{
logging::logMessage(
"D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
std::string(i_dbusObjPath));
return;
}
// Check if host is up and running
if (dbusUtility::isHostRunning())
{
if (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
l_fruPath))
{
logging::logMessage(
"Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
std::string(i_dbusObjPath));
return;
}
}
else if (dbusUtility::isBMCReady())
{
if (!jsonUtility::isFruReplaceableAtStandby(l_sysCfgJsonObj,
l_fruPath) &&
(!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
l_fruPath)))
{
logging::logMessage(
"Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
std::string(i_dbusObjPath));
return;
}
}
// Set CollectionStatus as InProgress. Since it's an intermediate state
// D-bus set-property call is good enough to update the status.
const std::string& l_collStatusProp = "CollectionStatus";
if (!dbusUtility::writeDbusProperty(
jsonUtility::getServiceName(l_sysCfgJsonObj,
std::string(i_dbusObjPath)),
std::string(i_dbusObjPath), constants::vpdCollectionInterface,
l_collStatusProp,
types::DbusVariantType{constants::vpdCollectionInProgress}))
{
logging::logMessage(
"Unable to set CollectionStatus as InProgress for " +
std::string(i_dbusObjPath) +
". Continue single FRU VPD collection.");
}
// Parse VPD
types::VPDMapVariant l_parsedVpd = m_worker->parseVpdFile(l_fruPath);
// If l_parsedVpd is pointing to std::monostate
if (l_parsedVpd.index() == 0)
{
throw std::runtime_error(
"VPD parsing failed for " + std::string(i_dbusObjPath));
}
// Get D-bus object map from worker class
types::ObjectMap l_dbusObjectMap;
m_worker->populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
if (l_dbusObjectMap.empty())
{
throw std::runtime_error(
"Failed to create D-bus object map. Single FRU VPD collection failed for " +
std::string(i_dbusObjPath));
}
// Call PIM's Notify method
if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
{
throw std::runtime_error(
"Notify PIM failed. Single FRU VPD collection failed for " +
std::string(i_dbusObjPath));
}
}
catch (const std::exception& l_error)
{
// Notify FRU's VPD CollectionStatus as Failure
if (!dbusUtility::notifyFRUCollectionStatus(
std::string(i_dbusObjPath), constants::vpdCollectionFailure))
{
logging::logMessage(
"Call to PIM Notify method failed to update Collection status as Failure for " +
std::string(i_dbusObjPath));
}
// TODO: Log PEL
logging::logMessage(std::string(l_error.what()));
}
}
void Manager::deleteSingleFruVpd(
const sdbusplus::message::object_path& i_dbusObjPath)
{
try
{
if (std::string(i_dbusObjPath).empty())
{
throw std::runtime_error(
"Given DBus object path is empty. Aborting FRU VPD deletion.");
}
if (m_worker.get() == nullptr)
{
throw std::runtime_error(
"Worker object not found, can't perform FRU VPD deletion for: " +
std::string(i_dbusObjPath));
}
m_worker->deleteFruVpd(std::string(i_dbusObjPath));
}
catch (const std::exception& l_ex)
{
// TODO: Log PEL
logging::logMessage(l_ex.what());
}
}
bool Manager::isValidUnexpandedLocationCode(
const std::string& i_unexpandedLocationCode)
{
if ((i_unexpandedLocationCode.length() <
constants::UNEXP_LOCATION_CODE_MIN_LENGTH) ||
((i_unexpandedLocationCode.compare(0, 4, "Ufcs") !=
constants::STR_CMP_SUCCESS) &&
(i_unexpandedLocationCode.compare(0, 4, "Umts") !=
constants::STR_CMP_SUCCESS)) ||
((i_unexpandedLocationCode.length() >
constants::UNEXP_LOCATION_CODE_MIN_LENGTH) &&
(i_unexpandedLocationCode.find("-") != 4)))
{
return false;
}
return true;
}
std::string Manager::getExpandedLocationCode(
const std::string& i_unexpandedLocationCode,
[[maybe_unused]] const uint16_t i_nodeNumber)
{
if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
{
phosphor::logging::elog<types::DbusInvalidArgument>(
types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
types::InvalidArgument::ARGUMENT_VALUE(
i_unexpandedLocationCode.c_str()));
}
const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
if (!l_sysCfgJsonObj.contains("frus"))
{
logging::logMessage("Missing frus tag in system config JSON");
}
const nlohmann::json& l_listOfFrus =
l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
for (const auto& l_frus : l_listOfFrus.items())
{
for (const auto& l_aFru : l_frus.value())
{
if (l_aFru["extraInterfaces"].contains(
constants::locationCodeInf) &&
l_aFru["extraInterfaces"][constants::locationCodeInf].value(
"LocationCode", "") == i_unexpandedLocationCode)
{
return std::get<std::string>(dbusUtility::readDbusProperty(
l_aFru["serviceName"], l_aFru["inventoryPath"],
constants::locationCodeInf, "LocationCode"));
}
}
}
phosphor::logging::elog<types::DbusInvalidArgument>(
types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
types::InvalidArgument::ARGUMENT_VALUE(
i_unexpandedLocationCode.c_str()));
}
types::ListOfPaths Manager::getFrusByUnexpandedLocationCode(
const std::string& i_unexpandedLocationCode,
[[maybe_unused]] const uint16_t i_nodeNumber)
{
types::ListOfPaths l_inventoryPaths;
if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
{
phosphor::logging::elog<types::DbusInvalidArgument>(
types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
types::InvalidArgument::ARGUMENT_VALUE(
i_unexpandedLocationCode.c_str()));
}
const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
if (!l_sysCfgJsonObj.contains("frus"))
{
logging::logMessage("Missing frus tag in system config JSON");
}
const nlohmann::json& l_listOfFrus =
l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
for (const auto& l_frus : l_listOfFrus.items())
{
for (const auto& l_aFru : l_frus.value())
{
if (l_aFru["extraInterfaces"].contains(
constants::locationCodeInf) &&
l_aFru["extraInterfaces"][constants::locationCodeInf].value(
"LocationCode", "") == i_unexpandedLocationCode)
{
l_inventoryPaths.push_back(
l_aFru.at("inventoryPath")
.get_ref<const nlohmann::json::string_t&>());
}
}
}
if (l_inventoryPaths.empty())
{
phosphor::logging::elog<types::DbusInvalidArgument>(
types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
types::InvalidArgument::ARGUMENT_VALUE(
i_unexpandedLocationCode.c_str()));
}
return l_inventoryPaths;
}
std::string Manager::getHwPath(
const sdbusplus::message::object_path& i_dbusObjPath)
{
// Dummy code to supress unused variable warning. To be removed.
logging::logMessage(std::string(i_dbusObjPath));
return std::string{};
}
std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode(
const std::string& i_expandedLocationCode)
{
/**
* Location code should always start with U and fulfil minimum length
* criteria.
*/
if (i_expandedLocationCode[0] != 'U' ||
i_expandedLocationCode.length() <
constants::EXP_LOCATION_CODE_MIN_LENGTH)
{
phosphor::logging::elog<types::DbusInvalidArgument>(
types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
types::InvalidArgument::ARGUMENT_VALUE(
i_expandedLocationCode.c_str()));
}
std::string l_fcKwd;
auto l_fcKwdValue = dbusUtility::readDbusProperty(
"xyz.openbmc_project.Inventory.Manager",
"/xyz/openbmc_project/inventory/system/chassis/motherboard",
"com.ibm.ipzvpd.VCEN", "FC");
if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue))
{
l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
}
// Get the first part of expanded location code to check for FC or TM.
std::string l_firstKwd = i_expandedLocationCode.substr(1, 4);
std::string l_unexpandedLocationCode{};
uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER;
// Check if this value matches the value of FC keyword.
if (l_fcKwd.substr(0, 4) == l_firstKwd)
{
/**
* Period(.) should be there in expanded location code to seggregate
* FC, node number and SE values.
*/
size_t l_nodeStartPos = i_expandedLocationCode.find('.');
if (l_nodeStartPos == std::string::npos)
{
phosphor::logging::elog<types::DbusInvalidArgument>(
types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
types::InvalidArgument::ARGUMENT_VALUE(
i_expandedLocationCode.c_str()));
}
size_t l_nodeEndPos =
i_expandedLocationCode.find('.', l_nodeStartPos + 1);
if (l_nodeEndPos == std::string::npos)
{
phosphor::logging::elog<types::DbusInvalidArgument>(
types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
types::InvalidArgument::ARGUMENT_VALUE(
i_expandedLocationCode.c_str()));
}
// Skip 3 bytes for '.ND'
l_nodeNummber = std::stoi(i_expandedLocationCode.substr(
l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3)));
/**
* Confirm if there are other details apart FC, node number and SE
* in location code
*/
if (i_expandedLocationCode.length() >
constants::EXP_LOCATION_CODE_MIN_LENGTH)
{
l_unexpandedLocationCode =
i_expandedLocationCode[0] + std::string("fcs") +
i_expandedLocationCode.substr(
l_nodeEndPos + 1 + constants::SE_KWD_LENGTH,
std::string::npos);
}
else
{
l_unexpandedLocationCode = "Ufcs";
}
}
else
{
std::string l_tmKwd;
// Read TM keyword value.
auto l_tmKwdValue = dbusUtility::readDbusProperty(
"xyz.openbmc_project.Inventory.Manager",
"/xyz/openbmc_project/inventory/system/chassis/motherboard",
"com.ibm.ipzvpd.VSYS", "TM");
if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue))
{
l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
}
// Check if the substr matches to TM keyword value.
if (l_tmKwd.substr(0, 4) == l_firstKwd)
{
/**
* System location code will not have node number and any other
* details.
*/
l_unexpandedLocationCode = "Umts";
}
// The given location code is neither "fcs" or "mts".
else
{
phosphor::logging::elog<types::DbusInvalidArgument>(
types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
types::InvalidArgument::ARGUMENT_VALUE(
i_expandedLocationCode.c_str()));
}
}
return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber);
}
types::ListOfPaths Manager::getFrusByExpandedLocationCode(
const std::string& i_expandedLocationCode)
{
std::tuple<std::string, uint16_t> l_locationAndNodePair =
getUnexpandedLocationCode(i_expandedLocationCode);
return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair),
std::get<1>(l_locationAndNodePair));
}
void Manager::registerHostStateChangeCallback()
{
static std::shared_ptr<sdbusplus::bus::match_t> l_hostState =
std::make_shared<sdbusplus::bus::match_t>(
*m_asioConnection,
sdbusplus::bus::match::rules::propertiesChanged(
constants::hostObjectPath, constants::hostInterface),
[this](sdbusplus::message_t& i_msg) {
hostStateChangeCallBack(i_msg);
});
}
void Manager::hostStateChangeCallBack(sdbusplus::message_t& i_msg)
{
try
{
if (i_msg.is_method_error())
{
throw std::runtime_error(
"Error reading callback message for host state");
}
std::string l_objectPath;
types::PropertyMap l_propMap;
i_msg.read(l_objectPath, l_propMap);
const auto l_itr = l_propMap.find("CurrentHostState");
if (l_itr == l_propMap.end())
{
throw std::runtime_error(
"CurrentHostState field is missing in callback message");
}
if (auto l_hostState = std::get_if<std::string>(&(l_itr->second)))
{
// implies system is moving from standby to power on state
if (*l_hostState == "xyz.openbmc_project.State.Host.HostState."
"TransitioningToRunning")
{
// TODO: check for all the essential FRUs in the system.
// Perform recollection.
performVpdRecollection();
return;
}
}
else
{
throw std::runtime_error(
"Invalid type recieved in variant for host state.");
}
}
catch (const std::exception& l_ex)
{
// TODO: Log PEL.
logging::logMessage(l_ex.what());
}
}
void Manager::performVpdRecollection()
{
try
{
if (m_worker.get() != nullptr)
{
nlohmann::json l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
// Check if system config JSON is present
if (l_sysCfgJsonObj.empty())
{
throw std::runtime_error(
"System config json object is empty, can't process recollection.");
}
const auto& l_frusReplaceableAtStandby =
jsonUtility::getListOfFrusReplaceableAtStandby(l_sysCfgJsonObj);
for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
{
// ToDo: Add some logic/trace to know the flow to
// collectSingleFruVpd has been directed via
// performVpdRecollection.
collectSingleFruVpd(l_fruInventoryPath);
}
return;
}
throw std::runtime_error(
"Worker object not found can't process recollection");
}
catch (const std::exception& l_ex)
{
// TODO Log PEL
logging::logMessage(
"VPD recollection failed with error: " + std::string(l_ex.what()));
}
}
} // namespace vpd