blob: 882acbbb7486f502ea242d3a076bcec0923192eb [file] [log] [blame]
#include "config.h"
#include "item_updater.hpp"
#include "xyz/openbmc_project/Common/error.hpp"
#include <filesystem>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
namespace openpower
{
namespace software
{
namespace updater
{
namespace server = sdbusplus::xyz::openbmc_project::Software::server;
namespace fs = std::filesystem;
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
using namespace phosphor::logging;
void ItemUpdater::createActivation(sdbusplus::message::message& m)
{
using SVersion = server::Version;
using VersionPurpose = SVersion::VersionPurpose;
namespace msg = sdbusplus::message;
namespace variant_ns = msg::variant_ns;
sdbusplus::message::object_path objPath;
std::map<std::string, std::map<std::string, msg::variant<std::string>>>
interfaces;
m.read(objPath, interfaces);
std::string path(std::move(objPath));
std::string filePath;
auto purpose = VersionPurpose::Unknown;
std::string version;
for (const auto& intf : interfaces)
{
if (intf.first == VERSION_IFACE)
{
for (const auto& property : intf.second)
{
if (property.first == "Purpose")
{
// Only process the Host and System images
auto value = SVersion::convertVersionPurposeFromString(
variant_ns::get<std::string>(property.second));
if (value == VersionPurpose::Host ||
value == VersionPurpose::System)
{
purpose = value;
}
}
else if (property.first == "Version")
{
version = variant_ns::get<std::string>(property.second);
}
}
}
else if (intf.first == FILEPATH_IFACE)
{
for (const auto& property : intf.second)
{
if (property.first == "Path")
{
filePath = variant_ns::get<std::string>(property.second);
}
}
}
}
if ((filePath.empty()) || (purpose == VersionPurpose::Unknown))
{
return;
}
// Version id is the last item in the path
auto pos = path.rfind("/");
if (pos == std::string::npos)
{
log<level::ERR>("No version id found in object path",
entry("OBJPATH=%s", path.c_str()));
return;
}
auto versionId = path.substr(pos + 1);
if (activations.find(versionId) == activations.end())
{
// Determine the Activation state by processing the given image dir.
auto activationState = server::Activation::Activations::Invalid;
AssociationList associations = {};
if (validateImage(filePath))
{
activationState = server::Activation::Activations::Ready;
// Create an association to the host inventory item
associations.emplace_back(std::make_tuple(
ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
HOST_INVENTORY_PATH));
}
fs::path manifestPath(filePath);
manifestPath /= MANIFEST_FILE;
std::string extendedVersion =
(Version::getValue(
manifestPath.string(),
std::map<std::string, std::string>{{"extended_version", ""}}))
.begin()
->second;
auto activation = createActivationObject(
path, versionId, extendedVersion, activationState, associations);
activations.emplace(versionId, std::move(activation));
auto versionPtr =
createVersionObject(path, versionId, version, purpose, filePath);
versions.emplace(versionId, std::move(versionPtr));
}
return;
}
void ItemUpdater::createActiveAssociation(const std::string& path)
{
assocs.emplace_back(
std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
associations(assocs);
}
void ItemUpdater::updateFunctionalAssociation(const std::string& versionId)
{
std::string path = std::string{SOFTWARE_OBJPATH} + '/' + versionId;
// remove all functional associations
for (auto iter = assocs.begin(); iter != assocs.end();)
{
if ((std::get<0>(*iter)).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0)
{
iter = assocs.erase(iter);
}
else
{
++iter;
}
}
assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
FUNCTIONAL_REV_ASSOCIATION, path));
associations(assocs);
}
void ItemUpdater::removeAssociation(const std::string& path)
{
for (auto iter = assocs.begin(); iter != assocs.end();)
{
if ((std::get<2>(*iter)).compare(path) == 0)
{
iter = assocs.erase(iter);
associations(assocs);
}
else
{
++iter;
}
}
}
bool ItemUpdater::erase(std::string entryId)
{
if (isVersionFunctional(entryId) && isChassisOn())
{
log<level::ERR>(("Error: Version " + entryId +
" is currently active and running on the host."
" Unable to remove.")
.c_str());
return false;
}
// Removing entry in versions map
auto it = versions.find(entryId);
if (it == versions.end())
{
log<level::ERR>(("Error: Failed to find version " + entryId +
" in item updater versions map."
" Unable to remove.")
.c_str());
}
else
{
versions.erase(entryId);
}
// Removing entry in activations map
auto ita = activations.find(entryId);
if (ita == activations.end())
{
log<level::ERR>(("Error: Failed to find version " + entryId +
" in item updater activations map."
" Unable to remove.")
.c_str());
}
else
{
removeAssociation(ita->second->path);
activations.erase(entryId);
}
return true;
}
bool ItemUpdater::isChassisOn()
{
auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
MAPPER_INTERFACE, "GetObject");
mapperCall.append(CHASSIS_STATE_PATH,
std::vector<std::string>({CHASSIS_STATE_OBJ}));
std::map<std::string, std::vector<std::string>> mapperResponse;
try
{
auto mapperResponseMsg = bus.call(mapperCall);
mapperResponseMsg.read(mapperResponse);
if (mapperResponse.empty())
{
log<level::ERR>("Invalid Response from mapper");
elog<InternalFailure>();
}
}
catch (const sdbusplus::exception::SdBusError& e)
{
log<level::ERR>("Error in Mapper call");
elog<InternalFailure>();
}
auto method = bus.new_method_call((mapperResponse.begin()->first).c_str(),
CHASSIS_STATE_PATH,
SYSTEMD_PROPERTY_INTERFACE, "Get");
method.append(CHASSIS_STATE_OBJ, "CurrentPowerState");
sdbusplus::message::variant<std::string> currentChassisState;
try
{
auto response = bus.call(method);
response.read(currentChassisState);
auto strParam = sdbusplus::message::variant_ns::get<std::string>(
currentChassisState);
return (strParam != CHASSIS_STATE_OFF);
}
catch (const sdbusplus::exception::SdBusError& e)
{
log<level::ERR>("Error in fetching current Chassis State",
entry("MAPPERRESPONSE=%s",
(mapperResponse.begin()->first).c_str()));
elog<InternalFailure>();
}
}
} // namespace updater
} // namespace software
} // namespace openpower