blob: 08ed491291a485c4cbd026949ce24468b97ebd43 [file] [log] [blame]
/*
// Copyright (c) 2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#include "types.hpp"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/asio/post.hpp>
#include <boost/container/flat_set.hpp>
#include <cold_redundancy.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
#include <sdbusplus/asio/sd_event.hpp>
#include <sdbusplus/bus/match.hpp>
#include <array>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <regex>
namespace
{
constexpr const std::array<const char*, 1> psuInterfaceTypes = {
"xyz.openbmc_project.Configuration.pmbus"};
std::string inventoryPath = std::string(INVENTORY_OBJ_PATH) + "/system";
const constexpr char* eventPath = "/xyz/openbmc_project/State/Decorator";
std::vector<std::unique_ptr<PowerSupply>> powerSupplies;
} // namespace
ColdRedundancy::ColdRedundancy(
boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
std::shared_ptr<sdbusplus::asio::connection>& systemBus) :
filterTimer(io),
systemBus(systemBus)
{
post(io,
[this, &io, &objectServer, &systemBus]() { createPSU(systemBus); });
std::function<void(sdbusplus::message_t&)> eventHandler =
[this, &io, &objectServer, &systemBus](sdbusplus::message_t& message) {
if (message.is_method_error())
{
std::cerr << "callback method error\n";
return;
}
filterTimer.expires_after(std::chrono::seconds(1));
filterTimer.async_wait([this, &io, &objectServer, &systemBus](
const boost::system::error_code& ec) {
if (ec == boost::asio::error::operation_aborted)
{
return;
}
else if (ec)
{
std::cerr << "timer error\n";
}
createPSU(systemBus);
});
};
std::function<void(sdbusplus::message_t&)> eventCollect =
[&](sdbusplus::message_t& message) {
std::string objectName;
boost::container::flat_map<std::string, std::variant<bool>> values;
std::string path = message.get_path();
std::size_t slantingPos = path.find_last_of("/\\");
if ((slantingPos == std::string::npos) ||
((slantingPos + 1) >= path.size()))
{
std::cerr << "Unable to get PSU state name from path\n";
return;
}
std::string statePSUName = path.substr(slantingPos + 1);
std::size_t hypenPos = statePSUName.find("_");
if (hypenPos == std::string::npos)
{
std::cerr << "Unable to get PSU name from PSU path\n";
return;
}
std::string psuName = statePSUName.substr(0, hypenPos);
try
{
message.read(objectName, values);
}
catch (const sdbusplus::exception_t& e)
{
std::cerr << "Failed to read message from PSU Event\n";
return;
}
for (auto& psu : powerSupplies)
{
if (psu->name != psuName)
{
continue;
}
std::string psuEventName = "functional";
auto findEvent = values.find(psuEventName);
if (findEvent != values.end())
{
bool* functional = std::get_if<bool>(&(findEvent->second));
if (functional == nullptr)
{
std::cerr << "Unable to get valid functional status\n";
continue;
}
if (*functional)
{
psu->state = CR::PSUState::normal;
}
else
{
psu->state = CR::PSUState::acLost;
}
}
}
};
using namespace sdbusplus::bus::match::rules;
for (const char* type : psuInterfaceTypes)
{
auto match = std::make_unique<sdbusplus::bus::match_t>(
static_cast<sdbusplus::bus_t&>(*systemBus),
type::signal() + member("PropertiesChanged") +
path_namespace(inventoryPath) + arg0namespace(type),
eventHandler);
matches.emplace_back(std::move(match));
}
for (const char* eventType : psuEventInterface)
{
auto eventMatch = std::make_unique<sdbusplus::bus::match_t>(
static_cast<sdbusplus::bus_t&>(*systemBus),
type::signal() + member("PropertiesChanged") +
path_namespace(eventPath) + arg0namespace(eventType),
eventCollect);
matches.emplace_back(std::move(eventMatch));
}
}
static const constexpr int psuDepth = 3;
// Check PSU information from entity-manager D-Bus interface and use the bus
// address to create PSU Class for cold redundancy.
void ColdRedundancy::createPSU(
std::shared_ptr<sdbusplus::asio::connection>& conn)
{
numberOfPSU = 0;
powerSupplies.clear();
// call mapper to get matched obj paths
conn->async_method_call(
[this, &conn](const boost::system::error_code ec,
CR::GetSubTreeType subtree) {
if (ec)
{
std::cerr << "Exception happened when communicating to "
"ObjectMapper\n";
return;
}
for (const auto& object : subtree)
{
std::string pathName = object.first;
for (const auto& serviceIface : object.second)
{
std::string serviceName = serviceIface.first;
for (const auto& interface : serviceIface.second)
{
// only get property of matched interface
bool isIfaceMatched = false;
for (const auto& type : psuInterfaceTypes)
{
if (type == interface)
{
isIfaceMatched = true;
break;
}
}
if (!isIfaceMatched)
continue;
conn->async_method_call(
[this, &conn,
interface](const boost::system::error_code ec,
CR::PropertyMapType propMap) {
if (ec)
{
std::cerr
<< "Exception happened when get all "
"properties\n";
return;
}
auto configName =
std::get_if<std::string>(&propMap["Name"]);
if (configName == nullptr)
{
std::cerr << "error finding necessary "
"entry in configuration\n";
return;
}
auto configBus =
std::get_if<uint64_t>(&propMap["Bus"]);
auto configAddress =
std::get_if<uint64_t>(&propMap["Address"]);
if (configBus == nullptr ||
configAddress == nullptr)
{
std::cerr << "error finding necessary "
"entry in configuration\n";
return;
}
for (auto& psu : powerSupplies)
{
if ((static_cast<uint8_t>(*configBus) ==
psu->bus) &&
(static_cast<uint8_t>(*configAddress) ==
psu->address))
{
return;
}
}
uint8_t order = 0;
powerSupplies.emplace_back(
std::make_unique<PowerSupply>(
*configName,
static_cast<uint8_t>(*configBus),
static_cast<uint8_t>(*configAddress),
order, conn));
numberOfPSU++;
std::vector<uint8_t> orders = {};
for (auto& psu : powerSupplies)
{
orders.push_back(psu->order);
}
},
serviceName.c_str(), pathName.c_str(),
"org.freedesktop.DBus.Properties", "GetAll",
interface);
}
}
}
},
"xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTree",
"/xyz/openbmc_project/inventory/system", psuDepth, psuInterfaceTypes);
}
PowerSupply::PowerSupply(
std::string& name, uint8_t bus, uint8_t address, uint8_t order,
const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection) :
name(name),
bus(bus), address(address), order(order)
{
CR::getPSUEvent(dbusConnection, name, state);
}