blob: d6a5deebbec564ff987240c937c7e6f900271216 [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 "callback_manager.hpp"
#include <boost/asio/io_context.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/container/flat_map.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
#include <variant>
constexpr const char* fatalLedPath =
"/xyz/openbmc_project/led/groups/status_critical";
constexpr const char* criticalLedPath =
"/xyz/openbmc_project/led/groups/status_non_critical";
constexpr const char* warningLedPath =
"/xyz/openbmc_project/led/groups/status_degraded";
constexpr const char* okLedPath = "/xyz/openbmc_project/led/groups/status_ok";
constexpr const char* ledIface = "xyz.openbmc_project.Led.Group";
constexpr const char* ledAssertProp = "Asserted";
constexpr const char* ledManagerBusname =
"xyz.openbmc_project.LED.GroupManager";
std::unique_ptr<AssociationManager> associationManager;
enum class StatusSetting
{
none,
ok,
warn,
critical,
fatal
};
constexpr const bool debug = false;
// final led state tracking
StatusSetting currentPriority = StatusSetting::none;
// maps of <object-path, <property, asserted>>
boost::container::flat_map<std::string,
boost::container::flat_map<std::string, bool>>
fatalAssertMap;
boost::container::flat_map<std::string,
boost::container::flat_map<std::string, bool>>
criticalAssertMap;
boost::container::flat_map<std::string,
boost::container::flat_map<std::string, bool>>
warningAssertMap;
std::vector<std::string> assertedInMap(
const boost::container::flat_map<
std::string, boost::container::flat_map<std::string, bool>>& map)
{
std::vector<std::string> ret;
// if any of the properties are true, return true
for (const auto& pair : map)
{
for (const auto& item : pair.second)
{
if (item.second)
{
ret.push_back(pair.first);
}
}
}
return ret;
}
void updateLedStatus(std::shared_ptr<sdbusplus::asio::connection>& conn,
bool forceRefresh = false)
{
std::vector<std::string> fatalVector = assertedInMap(fatalAssertMap);
bool fatal = fatalVector.size();
std::vector<std::string> criticalVector = assertedInMap(criticalAssertMap);
bool critical = criticalVector.size();
std::vector<std::string> warningVector = assertedInMap(warningAssertMap);
bool warn = warningVector.size();
associationManager->setLocalAssociations(fatalVector, criticalVector,
warningVector);
StatusSetting last = currentPriority;
std::vector<std::pair<std::string, std::variant<bool>>> ledsToSet;
if (forceRefresh)
{
ledsToSet.push_back(std::make_pair(fatalLedPath, false));
ledsToSet.push_back(std::make_pair(criticalLedPath, false));
ledsToSet.push_back(std::make_pair(warningLedPath, false));
ledsToSet.push_back(std::make_pair(okLedPath, false));
for (const auto& ledPair : ledsToSet)
{
conn->async_method_call(
[ledPair](const boost::system::error_code ec) {
if (ec)
{
std::cerr << "Cannot set " << ledPair.first << " to "
<< std::boolalpha
<< std::get<bool>(ledPair.second) << "\n";
}
if constexpr (debug)
{
std::cerr << "Set " << ledPair.first << " to "
<< std::boolalpha
<< std::get<bool>(ledPair.second) << "\n";
}
},
ledManagerBusname, ledPair.first,
"org.freedesktop.DBus.Properties", "Set", ledIface,
ledAssertProp, ledPair.second);
}
}
if (fatal)
{
currentPriority = StatusSetting::fatal;
}
else if (critical)
{
currentPriority = StatusSetting::critical;
}
else if (warn)
{
currentPriority = StatusSetting::warn;
}
else
{
currentPriority = StatusSetting::ok;
}
if (last != currentPriority || forceRefresh)
{
switch (currentPriority)
{
case (StatusSetting::fatal):
{
ledsToSet.push_back(std::make_pair(fatalLedPath, true));
ledsToSet.push_back(std::make_pair(criticalLedPath, false));
ledsToSet.push_back(std::make_pair(warningLedPath, false));
ledsToSet.push_back(std::make_pair(okLedPath, false));
break;
}
case (StatusSetting::critical):
{
ledsToSet.push_back(std::make_pair(fatalLedPath, false));
ledsToSet.push_back(std::make_pair(criticalLedPath, true));
ledsToSet.push_back(std::make_pair(warningLedPath, false));
ledsToSet.push_back(std::make_pair(okLedPath, false));
break;
}
case (StatusSetting::warn):
{
ledsToSet.push_back(std::make_pair(fatalLedPath, false));
ledsToSet.push_back(std::make_pair(criticalLedPath, false));
ledsToSet.push_back(std::make_pair(warningLedPath, true));
ledsToSet.push_back(std::make_pair(okLedPath, false));
break;
}
case (StatusSetting::ok):
{
ledsToSet.push_back(std::make_pair(fatalLedPath, false));
ledsToSet.push_back(std::make_pair(criticalLedPath, false));
ledsToSet.push_back(std::make_pair(warningLedPath, false));
ledsToSet.push_back(std::make_pair(okLedPath, true));
break;
}
}
}
for (const auto& ledPair : ledsToSet)
{
conn->async_method_call(
[ledPair](const boost::system::error_code ec) {
if (ec)
{
std::cerr << "Cannot set " << ledPair.first << " to "
<< std::boolalpha
<< std::get<bool>(ledPair.second) << "\n";
}
if constexpr (debug)
{
std::cerr << "Set " << ledPair.first << " to "
<< std::boolalpha
<< std::get<bool>(ledPair.second) << "\n";
}
},
ledManagerBusname, ledPair.first, "org.freedesktop.DBus.Properties",
"Set", ledIface, ledAssertProp, ledPair.second);
}
}
void createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
{
static sdbusplus::bus::match_t match(
static_cast<sdbusplus::bus_t&>(*conn),
"type='signal',member='ThresholdAsserted'",
[&conn](sdbusplus::message_t& message) {
std::string sensorName;
std::string thresholdInterface;
std::string event;
bool assert;
double assertValue;
try
{
message.read(sensorName, thresholdInterface, event, assert,
assertValue);
}
catch (sdbusplus::exception_t&)
{
return;
}
if constexpr (debug)
{
std::cerr << "Threshold callback: SensorName = " << sensorName
<< ", Event = " << event << ", Asserted = " << assert
<< "\n";
}
if (event == "CriticalAlarmLow")
{
criticalAssertMap[message.get_path()]["low"] = assert;
}
else if (event == "CriticalAlarmHigh")
{
criticalAssertMap[message.get_path()]["high"] = assert;
}
else if (event == "WarningAlarmLow")
{
warningAssertMap[message.get_path()]["low"] = assert;
}
else if (event == "WarningAlarmHigh")
{
warningAssertMap[message.get_path()]["high"] = assert;
}
associationManager->setSensorAssociations(
assertedInMap(criticalAssertMap),
assertedInMap(warningAssertMap));
updateLedStatus(conn);
});
}
void createAssociationMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
{
static sdbusplus::bus::match_t match(
static_cast<sdbusplus::bus_t&>(*conn),
"type='signal',interface='org.freedesktop.DBus.Properties',"
"arg0namespace='" +
std::string(associationIface) + "'",
[&conn](sdbusplus::message_t& message) {
if (message.get_path() == rootPath)
{
return; // it's us
}
std::string objectName;
boost::container::flat_map<std::string,
std::variant<std::vector<Association>>>
values;
try
{
message.read(objectName, values);
}
catch (sdbusplus::exception_t&)
{
return;
}
if constexpr (debug)
{
std::cerr << "Association callback " << message.get_path()
<< "\n";
}
auto findAssociations = values.find("Associations");
if (findAssociations == values.end())
{
return;
}
const std::vector<Association>* associations =
std::get_if<std::vector<Association>>(
&findAssociations->second);
if (associations == nullptr)
{
std::cerr << "Illegal Association on " << message.get_path()
<< "\n";
return;
}
bool localWarning = false;
bool localCritical = false;
bool globalWarning = false;
bool globalCritical = false;
for (const auto& [forward, reverse, path] : *associations)
{
if (path == rootPath)
{
globalWarning = globalWarning ? true : reverse == "warning";
globalCritical =
globalCritical ? true : reverse == "critical";
if constexpr (1)
{
std::cerr << "got global ";
}
}
else
{
localWarning = localWarning ? true : reverse == "warning";
localCritical =
localCritical ? true : reverse == "critical";
}
if (globalCritical && localCritical)
{
break;
}
}
bool fatal = globalCritical && localCritical;
bool critical = globalWarning && localCritical;
bool warning = globalWarning && !critical;
fatalAssertMap[message.get_path()]["association"] = fatal;
criticalAssertMap[message.get_path()]["association"] = critical;
warningAssertMap[message.get_path()]["association"] = warning;
updateLedStatus(conn);
});
}
int main(int argc, char** argv)
{
boost::asio::io_context io;
auto conn = std::make_shared<sdbusplus::asio::connection>(io);
conn->request_name("xyz.openbmc_project.CallbackManager");
sdbusplus::asio::object_server objServer(conn);
std::shared_ptr<sdbusplus::asio::dbus_interface> rootIface =
objServer.add_interface(rootPath,
"xyz.openbmc_project.CallbackManager");
rootIface->register_method("RetriggerLEDUpdate",
[&conn]() { updateLedStatus(conn, true); });
rootIface->initialize();
std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
objServer.add_interface(rootPath, globalInventoryIface);
inventoryIface->initialize();
associationManager = std::make_unique<AssociationManager>(objServer, conn);
createThresholdMatch(conn);
createAssociationMatch(conn);
updateLedStatus(conn);
io.run();
return 0;
}