Read persistent triggers from storage
Read json storage file for persistent triggers.
Add trigger to telemetry service.
Cover code with UTs.
Tested:
- Passed unit tests
- Tested on QEMU
* starting app without configuration
* restart app with configuration stored
* restart app with configuration in incorrect version
* restart app with configuration malformed
Change-Id: I2cb9324abdb8323be8a7f0c932ed7f70c5bc2891
Signed-off-by: Cezary Zwolak <cezary.zwolak@intel.com>
Signed-off-by: Lukasz Kazmierczak <lukasz.kazmierczak@intel.com>
diff --git a/.gitignore b/.gitignore
index caa490f..6ea24d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
xyz.openbmc_project.Telemetry.service
telemetry
__pycache__
+*.bak
# Created by https://www.toptal.com/developers/gitignore/api/vim,intellij,meson,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=vim,intellij,meson,visualstudiocode
@@ -165,4 +166,9 @@
.history
.ionide
+### Eclipse IDE ###
+.cproject
+.project
+.settings/*
+
# End of https://www.toptal.com/developers/gitignore/api/vim,intellij,meson,visualstudiocode
diff --git a/meson.build b/meson.build
index 145805a..2283e20 100644
--- a/meson.build
+++ b/meson.build
@@ -88,6 +88,7 @@
'src/trigger_actions.cpp',
'src/trigger_factory.cpp',
'src/trigger_manager.cpp',
+ 'src/utils/conversion_trigger.cpp',
],
dependencies: [
boost,
diff --git a/src/interfaces/trigger_factory.hpp b/src/interfaces/trigger_factory.hpp
index d493b4a..b84ce73 100644
--- a/src/interfaces/trigger_factory.hpp
+++ b/src/interfaces/trigger_factory.hpp
@@ -20,15 +20,17 @@
virtual ~TriggerFactory() = default;
virtual std::unique_ptr<interfaces::Trigger> make(
- boost::asio::yield_context& yield, const std::string& name,
- bool isDiscrete, bool logToJournal, bool logToRedfish,
- bool updateReport,
- const std::vector<
- std::pair<sdbusplus::message::object_path, std::string>>& sensors,
+ const std::string& name, bool isDiscrete, bool logToJournal,
+ bool logToRedfish, bool updateReport,
const std::vector<std::string>& reportNames,
- const TriggerThresholdParams& thresholdParams,
interfaces::TriggerManager& triggerManager,
- interfaces::JsonStorage& triggerStorage) const = 0;
+ interfaces::JsonStorage& triggerStorage,
+ const LabeledTriggerThresholdParams& labeledThresholdParams,
+ const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const = 0;
+
+ virtual std::vector<LabeledSensorInfo>
+ getLabeledSensorsInfo(boost::asio::yield_context& yield,
+ const SensorsInfo& sensorsInfo) const = 0;
};
} // namespace interfaces
diff --git a/src/interfaces/trigger_types.hpp b/src/interfaces/trigger_types.hpp
index 1833171..568756f 100644
--- a/src/interfaces/trigger_types.hpp
+++ b/src/interfaces/trigger_types.hpp
@@ -111,14 +111,22 @@
utils::tstring::ThresholdValue>;
} // namespace numeric
-using TriggerSensors =
+using SensorsInfo =
std::vector<std::pair<sdbusplus::message::object_path, std::string>>;
-using LabeledTriggerSensor =
- utils::LabeledTuple<std::tuple<std::string, std::string>,
- utils::tstring::SensorPath,
+using LabeledSensorInfo =
+ utils::LabeledTuple<std::tuple<std::string, std::string, std::string>,
+ utils::tstring::Service, utils::tstring::SensorPath,
utils::tstring::SensorMetadata>;
+using TriggerThresholdParamsExt =
+ std::variant<std::monostate, std::vector<numeric::ThresholdParam>,
+ std::vector<discrete::ThresholdParam>>;
+
using TriggerThresholdParams =
std::variant<std::vector<numeric::ThresholdParam>,
std::vector<discrete::ThresholdParam>>;
+
+using LabeledTriggerThresholdParams =
+ std::variant<std::vector<numeric::LabeledThresholdParam>,
+ std::vector<discrete::LabeledThresholdParam>>;
diff --git a/src/trigger.cpp b/src/trigger.cpp
index 2bba3ea..6289a05 100644
--- a/src/trigger.cpp
+++ b/src/trigger.cpp
@@ -1,34 +1,30 @@
#include "trigger.hpp"
+#include "interfaces/trigger_types.hpp"
#include "interfaces/types.hpp"
+#include "utils/conversion_trigger.hpp"
#include "utils/transform.hpp"
#include <phosphor-logging/log.hpp>
-template <class... Ts>
-struct overloaded : Ts...
-{
- using Ts::operator()...;
-};
-template <class... Ts>
-overloaded(Ts...) -> overloaded<Ts...>;
-
Trigger::Trigger(
boost::asio::io_context& ioc,
const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
const std::string& nameIn, const bool isDiscreteIn,
const bool logToJournalIn, const bool logToRedfishIn,
- const bool updateReportIn, const TriggerSensors& sensorsIn,
- const std::vector<std::string>& reportNamesIn,
- const TriggerThresholdParams& thresholdParamsIn,
+ const bool updateReportIn, const std::vector<std::string>& reportNamesIn,
+ const std::vector<LabeledSensorInfo>& LabeledSensorsInfoIn,
+ const LabeledTriggerThresholdParams& labeledThresholdParamsIn,
std::vector<std::shared_ptr<interfaces::Threshold>>&& thresholdsIn,
interfaces::TriggerManager& triggerManager,
interfaces::JsonStorage& triggerStorageIn) :
name(nameIn),
isDiscrete(isDiscreteIn), logToJournal(logToJournalIn),
logToRedfish(logToRedfishIn), updateReport(updateReportIn),
- path(triggerDir + name), sensors(sensorsIn), reportNames(reportNamesIn),
- thresholdParams(thresholdParamsIn), thresholds(std::move(thresholdsIn)),
+ path(triggerDir + name), reportNames(reportNamesIn),
+ labeledSensorsInfo(LabeledSensorsInfoIn),
+ labeledThresholdParams(labeledThresholdParamsIn),
+ thresholds(std::move(thresholdsIn)),
fileName(std::to_string(std::hash<std::string>{}(name))),
triggerStorage(triggerStorageIn)
{
@@ -72,13 +68,23 @@
[this](const auto&) { return persistent; });
dbusIface.register_property_r(
- "Thresholds", thresholdParams,
+ "Thresholds", TriggerThresholdParams{},
sdbusplus::vtable::property_::emits_change,
- [](const auto& x) { return x; });
+ [this](const auto&) {
+ return std::visit(
+ utils::FromLabeledThresholdParamConversion(),
+ labeledThresholdParams);
+ });
+
dbusIface.register_property_r(
- "Sensors", sensors, sdbusplus::vtable::property_::emits_change,
- [](const auto& x) { return x; });
+ "Sensors", SensorsInfo{},
+ sdbusplus::vtable::property_::emits_change,
+ [this](const auto&) {
+ return utils::fromLabeledSensorsInfo(labeledSensorsInfo);
+ });
+
dbusIface.register_property_r(
+
"ReportNames", reportNames,
sdbusplus::vtable::property_::emits_change,
[](const auto& x) { return x; });
@@ -110,44 +116,15 @@
data["Version"] = triggerVersion;
data["Name"] = name;
- data["ThresholdParamsDiscriminator"] = thresholdParams.index();
- data["IsDiscrete"] = isDiscrete;
+ data["ThresholdParamsDiscriminator"] = labeledThresholdParams.index();
+ data["IsDiscrete"] = labeledThresholdParams.index() == 1;
data["LogToJournal"] = logToJournal;
data["LogToRedfish"] = logToRedfish;
data["UpdateReport"] = updateReport;
-
- std::visit(
- overloaded{
- [&](const std::vector<numeric::ThresholdParam>& arg) {
- data["ThresholdParams"] =
- utils::transform(arg, [](const auto& thresholdParam) {
- const auto& [type, dwellTime, direction,
- thresholdValue] = thresholdParam;
- return numeric::LabeledThresholdParam(
- numeric::stringToType(type), dwellTime,
- numeric::stringToDirection(direction),
- thresholdValue);
- });
- },
- [&](const std::vector<discrete::ThresholdParam>& arg) {
- data["ThresholdParams"] =
- utils::transform(arg, [](const auto& thresholdParam) {
- const auto& [userId, severity, dwellTime,
- thresholdValue] = thresholdParam;
- return discrete::LabeledThresholdParam(
- userId, discrete::stringToSeverity(severity),
- dwellTime, thresholdValue);
- });
- },
- },
- thresholdParams);
-
+ data["ThresholdParams"] =
+ utils::labeledThresholdParamsToJson(labeledThresholdParams);
data["ReportNames"] = reportNames;
-
- data["Sensors"] = utils::transform(sensors, [](const auto& sensor) {
- const auto& [sensorPath, sensorMetadata] = sensor;
- return LabeledTriggerSensor(sensorPath, sensorMetadata);
- });
+ data["Sensors"] = labeledSensorsInfo;
triggerStorage.store(fileName, data);
}
@@ -159,4 +136,4 @@
return false;
}
return true;
-}
\ No newline at end of file
+}
diff --git a/src/trigger.hpp b/src/trigger.hpp
index b2bdc1d..a108caf 100644
--- a/src/trigger.hpp
+++ b/src/trigger.hpp
@@ -14,18 +14,17 @@
class Trigger : public interfaces::Trigger
{
public:
- Trigger(
- boost::asio::io_context& ioc,
- const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
- const std::string& name, const bool isDiscrete, const bool logToJournal,
- const bool logToRedfish, const bool updateReport,
- const std::vector<
- std::pair<sdbusplus::message::object_path, std::string>>& sensorsIn,
- const std::vector<std::string>& reportNames,
- const TriggerThresholdParams& thresholdParams,
- std::vector<std::shared_ptr<interfaces::Threshold>>&& thresholds,
- interfaces::TriggerManager& triggerManager,
- interfaces::JsonStorage& triggerStorage);
+ Trigger(boost::asio::io_context& ioc,
+ const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
+ const std::string& name, const bool isDiscrete,
+ const bool logToJournal, const bool logToRedfish,
+ const bool updateReport,
+ const std::vector<std::string>& reportNames,
+ const std::vector<LabeledSensorInfo>& LabeledSensorsInfoIn,
+ const LabeledTriggerThresholdParams& labeledThresholdParamsIn,
+ std::vector<std::shared_ptr<interfaces::Threshold>>&& thresholds,
+ interfaces::TriggerManager& triggerManager,
+ interfaces::JsonStorage& triggerStorage);
Trigger(const Trigger&) = delete;
Trigger(Trigger&&) = delete;
@@ -52,9 +51,9 @@
bool updateReport;
const std::string path;
bool persistent = false;
- TriggerSensors sensors;
std::vector<std::string> reportNames;
- TriggerThresholdParams thresholdParams;
+ std::vector<LabeledSensorInfo> labeledSensorsInfo;
+ LabeledTriggerThresholdParams labeledThresholdParams;
std::unique_ptr<sdbusplus::asio::dbus_interface> deleteIface;
std::unique_ptr<sdbusplus::asio::dbus_interface> triggerIface;
std::vector<std::shared_ptr<interfaces::Threshold>> thresholds;
diff --git a/src/trigger_factory.cpp b/src/trigger_factory.cpp
index e039e1f..b491274 100644
--- a/src/trigger_factory.cpp
+++ b/src/trigger_factory.cpp
@@ -7,6 +7,9 @@
#include "trigger.hpp"
#include "trigger_actions.hpp"
#include "utils/dbus_mapper.hpp"
+#include "utils/transform.hpp"
+
+namespace ts = utils::tstring;
TriggerFactory::TriggerFactory(
std::shared_ptr<sdbusplus::asio::connection> bus,
@@ -17,29 +20,36 @@
reportManager(reportManager)
{}
-std::unique_ptr<interfaces::Trigger>
- TriggerFactory::make(boost::asio::yield_context& yield,
- const std::string& name, bool isDiscrete,
- bool logToJournal, bool logToRedfish,
- bool updateReport, const TriggerSensors& sensorPaths,
- const std::vector<std::string>& reportNames,
- const TriggerThresholdParams& thresholdParams,
- interfaces::TriggerManager& triggerManager,
- interfaces::JsonStorage& triggerStorage) const
+std::unique_ptr<interfaces::Trigger> TriggerFactory::make(
+ const std::string& name, bool isDiscrete, bool logToJournal,
+ bool logToRedfish, bool updateReport,
+ const std::vector<std::string>& reportNames,
+ interfaces::TriggerManager& triggerManager,
+ interfaces::JsonStorage& triggerStorage,
+ const LabeledTriggerThresholdParams& labeledThresholdParams,
+ const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const
{
- auto [sensors, sensorNames] = getSensors(yield, sensorPaths);
+ const auto& [sensors, sensorNames] = getSensors(labeledSensorsInfo);
std::vector<std::shared_ptr<interfaces::Threshold>> thresholds;
if (isDiscrete)
{
- const auto& params =
- std::get<std::vector<discrete::ThresholdParam>>(thresholdParams);
- for (const auto& [thresholdName, severityStr, dwellTime,
- thresholdValue] : params)
+ const auto& labeledDiscreteThresholdParams =
+ std::get<std::vector<discrete::LabeledThresholdParam>>(
+ labeledThresholdParams);
+ for (const auto& labeledThresholdParam : labeledDiscreteThresholdParams)
{
- discrete::Severity severity =
- discrete::stringToSeverity(severityStr);
std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
+
+ std::string thresholdName =
+ labeledThresholdParam.at_label<ts::UserId>();
+ discrete::Severity severity =
+ labeledThresholdParam.at_label<ts::Severity>();
+ std::chrono::milliseconds dwellTime = std::chrono::milliseconds(
+ labeledThresholdParam.at_label<ts::DwellTime>());
+ double thresholdValue =
+ labeledThresholdParam.at_label<ts::ThresholdValue>();
+
if (logToJournal)
{
actions.emplace_back(
@@ -61,7 +71,7 @@
std::chrono::milliseconds(dwellTime), thresholdValue,
thresholdName));
}
- if (params.empty())
+ if (labeledDiscreteThresholdParams.empty())
{
std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
if (logToJournal)
@@ -88,24 +98,34 @@
}
else
{
- const auto& params =
- std::get<std::vector<numeric::ThresholdParam>>(thresholdParams);
- for (const auto& [typeStr, dwellTime, directionStr, value] : params)
+ const auto& labeledNumericThresholdParams =
+ std::get<std::vector<numeric::LabeledThresholdParam>>(
+ labeledThresholdParams);
+
+ for (const auto& labeledThresholdParam : labeledNumericThresholdParams)
{
- numeric::Type type = numeric::stringToType(typeStr);
std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
+ auto type = labeledThresholdParam.at_label<ts::Type>();
+ auto dwellTime = std::chrono::milliseconds(
+ labeledThresholdParam.at_label<ts::DwellTime>());
+ auto direction = labeledThresholdParam.at_label<ts::Direction>();
+ auto thresholdValue =
+ double{labeledThresholdParam.at_label<ts::ThresholdValue>()};
+
if (logToJournal)
{
actions.emplace_back(
- std::make_unique<action::numeric::LogToJournal>(type,
- value));
+ std::make_unique<action::numeric::LogToJournal>(
+ type, thresholdValue));
}
+
if (logToRedfish)
{
actions.emplace_back(
- std::make_unique<action::numeric::LogToRedfish>(type,
- value));
+ std::make_unique<action::numeric::LogToRedfish>(
+ type, thresholdValue));
}
+
if (updateReport)
{
actions.emplace_back(std::make_unique<action::UpdateReport>(
@@ -114,42 +134,34 @@
thresholds.emplace_back(std::make_shared<NumericThreshold>(
bus->get_io_context(), sensors, sensorNames, std::move(actions),
- std::chrono::milliseconds(dwellTime),
- numeric::stringToDirection(directionStr), value));
+ dwellTime, direction, thresholdValue));
}
}
return std::make_unique<Trigger>(
bus->get_io_context(), objServer, name, isDiscrete, logToJournal,
- logToRedfish, updateReport, sensorPaths, reportNames, thresholdParams,
- std::move(thresholds), triggerManager, triggerStorage);
+ logToRedfish, updateReport, reportNames, labeledSensorsInfo,
+ labeledThresholdParams, std::move(thresholds), triggerManager,
+ triggerStorage);
}
std::pair<std::vector<std::shared_ptr<interfaces::Sensor>>,
std::vector<std::string>>
TriggerFactory::getSensors(
- boost::asio::yield_context& yield,
- const std::vector<std::pair<sdbusplus::message::object_path,
- std::string>>& sensorPaths) const
+ const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const
{
- auto tree = utils::getSubTreeSensors(yield, bus);
-
std::vector<std::shared_ptr<interfaces::Sensor>> sensors;
std::vector<std::string> sensorNames;
- for (const auto& [sensorPath, metadata] : sensorPaths)
- {
- auto found = std::find_if(
- tree.begin(), tree.end(),
- [&sensorPath](const auto& x) { return x.first == sensorPath; });
- if (found == tree.end())
- {
- throw std::runtime_error("Not found");
- }
- const auto& service = found->second[0].first;
- const auto& path = found->first;
+ for (const auto& labeledSensorInfo : labeledSensorsInfo)
+ {
+ const auto& service = labeledSensorInfo.at_label<ts::Service>();
+ const auto& sensorPath = labeledSensorInfo.at_label<ts::SensorPath>();
+ const auto& metadata = labeledSensorInfo.at_label<ts::SensorMetadata>();
+
sensors.emplace_back(sensorCache.makeSensor<Sensor>(
- service, path, bus->get_io_context(), bus));
+ service, sensorPath, bus->get_io_context(), bus));
+
if (metadata.empty())
{
sensorNames.emplace_back(sensorPath);
@@ -159,5 +171,27 @@
sensorNames.emplace_back(metadata);
}
}
+
return {sensors, sensorNames};
}
+
+std::vector<LabeledSensorInfo>
+ TriggerFactory::getLabeledSensorsInfo(boost::asio::yield_context& yield,
+ const SensorsInfo& sensorsInfo) const
+{
+ auto tree = utils::getSubTreeSensors(yield, bus);
+
+ return utils::transform(sensorsInfo, [&tree](const auto& item) {
+ const auto& [sensorPath, metadata] = item;
+ auto found = std::find_if(
+ tree.begin(), tree.end(),
+ [&sensorPath](const auto& x) { return x.first == sensorPath; });
+
+ if (tree.end() != found)
+ {
+ const auto& [service, ifaces] = found->second.front();
+ return LabeledSensorInfo(service, sensorPath, metadata);
+ }
+ throw std::runtime_error("Not found");
+ });
+}
diff --git a/src/trigger_factory.hpp b/src/trigger_factory.hpp
index a6fbb68..7c13e55 100644
--- a/src/trigger_factory.hpp
+++ b/src/trigger_factory.hpp
@@ -15,16 +15,19 @@
SensorCache& sensorCache,
interfaces::ReportManager& reportManager);
- std::unique_ptr<interfaces::Trigger> make(
- boost::asio::yield_context& yield, const std::string& name,
- bool isDiscrete, bool logToJournal, bool logToRedfish,
- bool updateReport,
- const std::vector<
- std::pair<sdbusplus::message::object_path, std::string>>& sensors,
- const std::vector<std::string>& reportNames,
- const TriggerThresholdParams& thresholdParams,
- interfaces::TriggerManager& triggerManager,
- interfaces::JsonStorage& triggerStorage) const override;
+ std::unique_ptr<interfaces::Trigger>
+ make(const std::string& name, bool isDiscrete, bool logToJournal,
+ bool logToRedfish, bool updateReport,
+ const std::vector<std::string>& reportNames,
+ interfaces::TriggerManager& triggerManager,
+ interfaces::JsonStorage& triggerStorage,
+ const LabeledTriggerThresholdParams& labeledThresholdParams,
+ const std::vector<LabeledSensorInfo>& labeledSensorsinfo)
+ const override;
+
+ std::vector<LabeledSensorInfo>
+ getLabeledSensorsInfo(boost::asio::yield_context& yield,
+ const SensorsInfo& sensorsInfo) const;
private:
std::shared_ptr<sdbusplus::asio::connection> bus;
@@ -34,8 +37,6 @@
std::pair<std::vector<std::shared_ptr<interfaces::Sensor>>,
std::vector<std::string>>
- getSensors(boost::asio::yield_context& yield,
- const std::vector<
- std::pair<sdbusplus::message::object_path, std::string>>&
- sensorPaths) const;
+ getSensors(
+ const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const;
};
diff --git a/src/trigger_manager.cpp b/src/trigger_manager.cpp
index 3882022..d9d04d2 100644
--- a/src/trigger_manager.cpp
+++ b/src/trigger_manager.cpp
@@ -1,5 +1,12 @@
#include "trigger_manager.hpp"
+#include "interfaces/trigger_types.hpp"
+#include "trigger.hpp"
+#include "utils/conversion_trigger.hpp"
+#include "utils/transform.hpp"
+
+#include <phosphor-logging/log.hpp>
+
TriggerManager::TriggerManager(
std::unique_ptr<interfaces::TriggerFactory> triggerFactoryIn,
std::unique_ptr<interfaces::JsonStorage> triggerStorageIn,
@@ -7,40 +14,31 @@
triggerFactory(std::move(triggerFactoryIn)),
triggerStorage(std::move(triggerStorageIn))
{
+ loadFromPersistent();
+
managerIface = objServer->add_unique_interface(
triggerManagerPath, triggerManagerIfaceName, [this](auto& iface) {
iface.register_method(
"AddTrigger",
- [this](
- boost::asio::yield_context& yield, const std::string& name,
- bool isDiscrete, bool logToJournal, bool logToRedfish,
- bool updateReport,
- const std::vector<std::pair<sdbusplus::message::object_path,
- std::string>>& sensors,
- const std::vector<std::string>& reportNames,
- const TriggerThresholdParams& thresholds) {
- if (triggers.size() >= maxTriggers)
- {
- throw sdbusplus::exception::SdBusError(
- static_cast<int>(std::errc::too_many_files_open),
- "Reached maximal trigger count");
- }
+ [this](boost::asio::yield_context& yield,
+ const std::string& name, bool isDiscrete,
+ bool logToJournal, bool logToRedfish, bool updateReport,
+ const SensorsInfo& sensors,
+ const std::vector<std::string>& reportNames,
+ const TriggerThresholdParamsExt& thresholds) {
+ LabeledTriggerThresholdParams
+ labeledTriggerThresholdParams = std::visit(
+ utils::ToLabeledThresholdParamConversion(),
+ thresholds);
- for (const auto& trigger : triggers)
- {
- if (trigger->getName() == name)
- {
- throw sdbusplus::exception::SdBusError(
- static_cast<int>(std::errc::file_exists),
- "Duplicate trigger");
- }
- }
+ std::vector<LabeledSensorInfo> labeledSensorsInfo =
+ triggerFactory->getLabeledSensorsInfo(yield, sensors);
- triggers.emplace_back(triggerFactory->make(
- yield, name, isDiscrete, logToJournal, logToRedfish,
- updateReport, sensors, reportNames, thresholds, *this,
- *triggerStorage));
- return triggers.back()->getPath();
+ return addTrigger(name, isDiscrete, logToJournal,
+ logToRedfish, updateReport,
+ labeledSensorsInfo, reportNames,
+ labeledTriggerThresholdParams)
+ .getPath();
});
});
}
@@ -52,3 +50,105 @@
[trigger](const auto& x) { return trigger == x.get(); }),
triggers.end());
}
+
+void TriggerManager::verifyAddTrigger(
+ const std::string& triggerName, bool isDiscrete,
+ const LabeledTriggerThresholdParams& thresholdParams)
+{
+ if (triggers.size() >= maxTriggers)
+ {
+ throw sdbusplus::exception::SdBusError(
+ static_cast<int>(std::errc::too_many_files_open),
+ "Reached maximal trigger count");
+ }
+
+ for (const auto& trigger : triggers)
+ {
+ if (trigger->getName() == triggerName)
+ {
+ throw sdbusplus::exception::SdBusError(
+ static_cast<int>(std::errc::file_exists), "Duplicate trigger");
+ }
+ }
+}
+
+interfaces::Trigger& TriggerManager::addTrigger(
+ const std::string& triggerName, bool isDiscrete, bool logToJournal,
+ bool logToRedfish, bool updateReport,
+ const std::vector<LabeledSensorInfo>& labeledSensorsInfo,
+ const std::vector<std::string>& reportNames,
+ const LabeledTriggerThresholdParams& labeledThresholdParams)
+{
+ verifyAddTrigger(triggerName, isDiscrete, labeledThresholdParams);
+
+ triggers.emplace_back(triggerFactory->make(
+ triggerName, isDiscrete, logToJournal, logToRedfish, updateReport,
+ reportNames, *this, *triggerStorage, labeledThresholdParams,
+ labeledSensorsInfo));
+
+ return *triggers.back();
+}
+
+void TriggerManager::loadFromPersistent()
+{
+ std::vector<interfaces::JsonStorage::FilePath> paths =
+ triggerStorage->list();
+
+ for (const auto& path : paths)
+ {
+ std::optional<nlohmann::json> data = triggerStorage->load(path);
+ try
+ {
+ if (!data.has_value())
+ {
+ throw std::runtime_error("Empty storage");
+ }
+ size_t version = data->at("Version").get<size_t>();
+ if (version != Trigger::triggerVersion)
+ {
+ throw std::runtime_error("Invalid version");
+ }
+ const std::string& name = data->at("Name").get_ref<std::string&>();
+ int thresholdParamsDiscriminator =
+ data->at("ThresholdParamsDiscriminator").get<int>();
+ bool isDiscrete = data->at("IsDiscrete").get<bool>();
+ bool logToJournal = data->at("LogToJournal").get<bool>();
+ bool logToRedfish = data->at("LogToRedfish").get<bool>();
+ bool updateReport = data->at("UpdateReport").get<bool>();
+
+ LabeledTriggerThresholdParams labeledThresholdParams;
+ if (0 == thresholdParamsDiscriminator)
+ {
+ labeledThresholdParams =
+ data->at("ThresholdParams")
+ .get<std::vector<numeric::LabeledThresholdParam>>();
+ }
+ else
+ {
+ labeledThresholdParams =
+ data->at("ThresholdParams")
+ .get<std::vector<discrete::LabeledThresholdParam>>();
+ }
+
+ auto reportNames =
+ data->at("ReportNames").get<std::vector<std::string>>();
+
+ auto labeledSensorsInfo =
+ data->at("Sensors").get<std::vector<LabeledSensorInfo>>();
+
+ addTrigger(name, isDiscrete, logToJournal, logToRedfish,
+ updateReport, labeledSensorsInfo, reportNames,
+ labeledThresholdParams);
+ }
+ catch (const std::exception& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Failed to load trigger from storage",
+ phosphor::logging::entry(
+ "FILENAME=%s",
+ static_cast<std::filesystem::path>(path).c_str()),
+ phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
+ triggerStorage->remove(path);
+ }
+ }
+}
diff --git a/src/trigger_manager.hpp b/src/trigger_manager.hpp
index 1db6d73..ec26bdc 100644
--- a/src/trigger_manager.hpp
+++ b/src/trigger_manager.hpp
@@ -30,6 +30,17 @@
std::unique_ptr<sdbusplus::asio::dbus_interface> managerIface;
std::vector<std::unique_ptr<interfaces::Trigger>> triggers;
+ void verifyAddTrigger(const std::string& triggerName, bool isDiscrete,
+ const LabeledTriggerThresholdParams& thresholdParams);
+
+ interfaces::Trigger&
+ addTrigger(const std::string& triggerName, bool isDiscrete,
+ bool logToJournal, bool logToRedfish, bool updateReport,
+ const std::vector<LabeledSensorInfo>& labeledSensors,
+ const std::vector<std::string>& reportNames,
+ const LabeledTriggerThresholdParams& labeledThresholdParams);
+ void loadFromPersistent();
+
public:
static constexpr size_t maxTriggers{TELEMETRY_MAX_TRIGGERS};
static constexpr const char* triggerManagerIfaceName =
diff --git a/src/utils/conversion_trigger.cpp b/src/utils/conversion_trigger.cpp
new file mode 100644
index 0000000..5aae0d5
--- /dev/null
+++ b/src/utils/conversion_trigger.cpp
@@ -0,0 +1,88 @@
+#include "utils/conversion_trigger.hpp"
+
+#include "utils/transform.hpp"
+
+#include <sdbusplus/exception.hpp>
+
+namespace utils
+{
+namespace ts = utils::tstring;
+
+LabeledTriggerThresholdParams ToLabeledThresholdParamConversion::operator()(
+ const std::monostate& arg) const
+{
+ throw sdbusplus::exception::SdBusError(
+ static_cast<int>(std::errc::invalid_argument),
+ "Provided threshold parameter is invalid");
+}
+
+LabeledTriggerThresholdParams ToLabeledThresholdParamConversion::operator()(
+ const std::vector<numeric::ThresholdParam>& arg) const
+{
+ return utils::transform(arg, [](const auto& thresholdParam) {
+ const auto& [type, dwellTime, direction, thresholdValue] =
+ thresholdParam;
+ return numeric::LabeledThresholdParam(
+ numeric::stringToType(type), dwellTime,
+ numeric::stringToDirection(direction), thresholdValue);
+ });
+}
+
+LabeledTriggerThresholdParams ToLabeledThresholdParamConversion::operator()(
+ const std::vector<discrete::ThresholdParam>& arg) const
+{
+ return utils::transform(arg, [](const auto& thresholdParam) {
+ const auto& [userId, severity, dwellTime, thresholdValue] =
+ thresholdParam;
+ return discrete::LabeledThresholdParam(
+ userId, discrete::stringToSeverity(severity), dwellTime,
+ thresholdValue);
+ });
+}
+
+TriggerThresholdParams FromLabeledThresholdParamConversion::operator()(
+ const std::vector<numeric::LabeledThresholdParam>& arg) const
+{
+ return utils::transform(
+ arg, [](const numeric::LabeledThresholdParam& labeledThresholdParam) {
+ return numeric::ThresholdParam(
+ numeric::typeToString(
+ labeledThresholdParam.at_label<ts::Type>()),
+ labeledThresholdParam.at_label<ts::DwellTime>(),
+ numeric::directionToString(
+ labeledThresholdParam.at_label<ts::Direction>()),
+ labeledThresholdParam.at_label<ts::ThresholdValue>());
+ });
+}
+
+TriggerThresholdParams FromLabeledThresholdParamConversion::operator()(
+ const std::vector<discrete::LabeledThresholdParam>& arg) const
+{
+ return utils::transform(
+ arg, [](const discrete::LabeledThresholdParam& labeledThresholdParam) {
+ return discrete::ThresholdParam(
+ labeledThresholdParam.at_label<ts::UserId>(),
+ discrete::severityToString(
+ labeledThresholdParam.at_label<ts::Severity>()),
+ labeledThresholdParam.at_label<ts::DwellTime>(),
+ labeledThresholdParam.at_label<ts::ThresholdValue>());
+ });
+}
+
+SensorsInfo fromLabeledSensorsInfo(const std::vector<LabeledSensorInfo>& infos)
+{
+ return utils::transform(infos, [](const LabeledSensorInfo& val) {
+ return SensorsInfo::value_type(
+ sdbusplus::message::object_path(val.at_label<ts::SensorPath>()),
+ val.at_label<ts::SensorMetadata>());
+ });
+}
+
+nlohmann::json labeledThresholdParamsToJson(
+ const LabeledTriggerThresholdParams& labeledThresholdParams)
+{
+ return std::visit([](const auto& lt) { return nlohmann::json(lt); },
+ labeledThresholdParams);
+}
+
+} // namespace utils
diff --git a/src/utils/conversion_trigger.hpp b/src/utils/conversion_trigger.hpp
new file mode 100644
index 0000000..7ed660f
--- /dev/null
+++ b/src/utils/conversion_trigger.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "interfaces/json_storage.hpp"
+#include "interfaces/trigger_types.hpp"
+
+namespace utils
+{
+
+class ToLabeledThresholdParamConversion
+{
+ public:
+ LabeledTriggerThresholdParams operator()(const std::monostate& arg) const;
+ LabeledTriggerThresholdParams
+ operator()(const std::vector<numeric::ThresholdParam>& arg) const;
+ LabeledTriggerThresholdParams
+ operator()(const std::vector<discrete::ThresholdParam>& arg) const;
+};
+
+class FromLabeledThresholdParamConversion
+{
+ public:
+ TriggerThresholdParams operator()(
+ const std::vector<numeric::LabeledThresholdParam>& arg) const;
+ TriggerThresholdParams operator()(
+ const std::vector<discrete::LabeledThresholdParam>& arg) const;
+};
+
+SensorsInfo fromLabeledSensorsInfo(const std::vector<LabeledSensorInfo>& infos);
+
+nlohmann::json labeledThresholdParamsToJson(
+ const LabeledTriggerThresholdParams& labeledThresholdParams);
+
+} // namespace utils
diff --git a/tests/meson.build b/tests/meson.build
index d2e5aac..e87a616 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -25,6 +25,7 @@
'../src/trigger_actions.cpp',
'../src/trigger_factory.cpp',
'../src/trigger_manager.cpp',
+ '../src/utils/conversion_trigger.cpp',
'src/dbus_environment.cpp',
'src/main.cpp',
'src/stubs/dbus_sensor_object.cpp',
diff --git a/tests/src/mocks/trigger_factory_mock.hpp b/tests/src/mocks/trigger_factory_mock.hpp
index 8249799..3682a46 100644
--- a/tests/src/mocks/trigger_factory_mock.hpp
+++ b/tests/src/mocks/trigger_factory_mock.hpp
@@ -3,6 +3,8 @@
#include "interfaces/trigger_factory.hpp"
#include "mocks/trigger_mock.hpp"
#include "params/trigger_params.hpp"
+#include "trigger.hpp"
+#include "utils/conversion_trigger.hpp"
#include <gmock/gmock.h>
@@ -13,26 +15,28 @@
{
using namespace testing;
- ON_CALL(*this, make(_, _, _, _, _, _, _, _, _, _, _))
- .WillByDefault(WithArgs<1>(Invoke([](const std::string& name) {
+ ON_CALL(*this, make(A<const std::string&>(), _, _, _, _, _, _, _, _, _))
+ .WillByDefault(WithArgs<0>(Invoke([](const std::string& name) {
return std::make_unique<NiceMock<TriggerMock>>(name);
})));
}
- MOCK_METHOD(
- std::unique_ptr<interfaces::Trigger>, make,
- (boost::asio::yield_context&, const std::string& name, bool isDiscrete,
- bool logToJournal, bool logToRedfish, bool updateReport,
- (const std::vector<
- std::pair<sdbusplus::message::object_path, std::string>>& sensors),
- const std::vector<std::string>& reportNames,
- const TriggerThresholdParams& thresholdParams,
- interfaces::TriggerManager& triggerManager,
- interfaces::JsonStorage& triggerStorage),
- (const, override));
+ MOCK_METHOD(std::unique_ptr<interfaces::Trigger>, make,
+ (const std::string& name, bool isDiscrete, bool logToJournal,
+ bool logToRedfish, bool updateReport,
+ const std::vector<std::string>& reportNames,
+ interfaces::TriggerManager& triggerManager,
+ interfaces::JsonStorage& triggerStorage,
+ const LabeledTriggerThresholdParams& labeledThresholdParams,
+ const std::vector<LabeledSensorInfo>& labeledSensorsInfo),
+ (const, override));
+
+ MOCK_METHOD(std::vector<LabeledSensorInfo>, getLabeledSensorsInfo,
+ (boost::asio::yield_context&, const SensorsInfo&),
+ (const, override));
auto& expectMake(
- std::optional<std::reference_wrapper<const TriggerParams>> paramsOpt,
+ std::optional<TriggerParams> paramsOpt,
const testing::Matcher<interfaces::TriggerManager&>& tm,
const testing::Matcher<interfaces::JsonStorage&>& triggerStorage)
{
@@ -41,17 +45,32 @@
if (paramsOpt)
{
const TriggerParams& params = *paramsOpt;
+
+ const auto sensorInfos =
+ utils::fromLabeledSensorsInfo(params.sensors());
+
+ ON_CALL(*this, getLabeledSensorsInfo(_, sensorInfos))
+ .WillByDefault(Return(params.sensors()));
+
return EXPECT_CALL(
- *this, make(_, params.name(), params.isDiscrete(),
- params.logToJournal(), params.logToRedfish(),
- params.updateReport(), params.sensors(),
- params.reportNames(), params.thresholdParams(), tm,
- triggerStorage));
+ *this,
+ make(params.name(), params.isDiscrete(), params.logToJournal(),
+ params.logToRedfish(), params.updateReport(),
+ params.reportNames(), tm, triggerStorage,
+ params.thresholdParams(), params.sensors()));
}
else
{
+ const std::vector<LabeledSensorInfo> dummy = {
+ {"service99",
+ "/xyz/openbmc_project/sensors/temperature/BMC_Temp99",
+ "metadata99"}};
+
+ ON_CALL(*this, getLabeledSensorsInfo(_, _))
+ .WillByDefault(Return(dummy));
+
return EXPECT_CALL(
- *this, make(_, _, _, _, _, _, _, _, _, tm, triggerStorage));
+ *this, make(_, _, _, _, _, _, tm, triggerStorage, _, dummy));
}
}
};
diff --git a/tests/src/params/trigger_params.hpp b/tests/src/params/trigger_params.hpp
index 08bed4b..7e00157 100644
--- a/tests/src/params/trigger_params.hpp
+++ b/tests/src/params/trigger_params.hpp
@@ -53,9 +53,9 @@
return updateReportProperty;
}
- const TriggerSensors& sensors() const
+ const std::vector<LabeledSensorInfo>& sensors() const
{
- return sensorsProperty;
+ return labeledSensorsProperty;
}
const std::vector<std::string>& reportNames() const
@@ -63,15 +63,15 @@
return reportNamesProperty;
}
- TriggerParams& thresholdParams(TriggerThresholdParams val)
+ TriggerParams& thresholdParams(LabeledTriggerThresholdParams val)
{
- thresholdsProperty = std::move(val);
+ labeledThresholdsProperty = std::move(val);
return *this;
}
- const TriggerThresholdParams& thresholdParams() const
+ const LabeledTriggerThresholdParams& thresholdParams() const
{
- return thresholdsProperty;
+ return labeledThresholdsProperty;
}
private:
@@ -80,17 +80,20 @@
bool logToJournalProperty = false;
bool logToRedfishProperty = false;
bool updateReportProperty = true;
- TriggerSensors sensorsProperty = {
- {sdbusplus::message::object_path(
- "/xyz/openbmc_project/sensors/temperature/BMC_Temp"),
- ""}};
+ std::vector<LabeledSensorInfo> labeledSensorsProperty = {
+ {"service1", "/xyz/openbmc_project/sensors/temperature/BMC_Temp",
+ "metadata1"}};
+
std::vector<std::string> reportNamesProperty = {"Report1"};
- TriggerThresholdParams thresholdsProperty =
- std::vector<numeric::ThresholdParam>{
- {numeric::typeToString(numeric::Type::lowerCritical),
- std::chrono::milliseconds(10).count(),
- numeric::directionToString(numeric::Direction::decreasing), 0.0},
- {numeric::typeToString(numeric::Type::upperCritical),
- std::chrono::milliseconds(10).count(),
- numeric::directionToString(numeric::Direction::increasing), 90.0}};
+
+ LabeledTriggerThresholdParams labeledThresholdsProperty =
+ std::vector<numeric::LabeledThresholdParam>{
+ numeric::LabeledThresholdParam{
+ numeric::Type::lowerCritical,
+ std::chrono::milliseconds(10).count(),
+ numeric::Direction::decreasing, 0.5},
+ numeric::LabeledThresholdParam{
+ numeric::Type::upperCritical,
+ std::chrono::milliseconds(10).count(),
+ numeric::Direction::increasing, 90.2}};
};
diff --git a/tests/src/test_trigger.cpp b/tests/src/test_trigger.cpp
index f396589..cf7d96c 100644
--- a/tests/src/test_trigger.cpp
+++ b/tests/src/test_trigger.cpp
@@ -4,7 +4,12 @@
#include "mocks/trigger_manager_mock.hpp"
#include "params/trigger_params.hpp"
#include "trigger.hpp"
+#include "utils/conversion_trigger.hpp"
#include "utils/set_exception.hpp"
+#include "utils/transform.hpp"
+#include "utils/tstring.hpp"
+
+#include <boost/range/combine.hpp>
using namespace testing;
using namespace std::literals::string_literals;
@@ -15,6 +20,18 @@
{
public:
TriggerParams triggerParams;
+ TriggerParams triggerDiscreteParams =
+ TriggerParams()
+ .name("Trigger2")
+ .isDiscrete(true)
+ .thresholdParams(std::vector<discrete::LabeledThresholdParam>{
+ discrete::LabeledThresholdParam{
+ "userId", discrete::Severity::warning,
+ std::chrono::milliseconds(10).count(), 15.2},
+ discrete::LabeledThresholdParam{
+ "userId_2", discrete::Severity::critical,
+ std::chrono::milliseconds(5).count(), 32.7},
+ });
std::unique_ptr<TriggerManagerMock> triggerManagerMockPtr =
std::make_unique<NiceMock<TriggerManagerMock>>();
@@ -26,13 +43,22 @@
sut = makeTrigger(triggerParams);
}
+ static std::vector<LabeledSensorInfo>
+ convertToLabeledSensor(const SensorsInfo& sensorsInfo)
+ {
+ return utils::transform(sensorsInfo, [](const auto& sensorInfo) {
+ const auto& [sensorPath, sensorMetadata] = sensorInfo;
+ return LabeledSensorInfo("service1", sensorPath, sensorMetadata);
+ });
+ }
+
std::unique_ptr<Trigger> makeTrigger(const TriggerParams& params)
{
return std::make_unique<Trigger>(
DbusEnvironment::getIoc(), DbusEnvironment::getObjServer(),
params.name(), params.isDiscrete(), params.logToJournal(),
- params.logToRedfish(), params.updateReport(), params.sensors(),
- params.reportNames(), params.thresholdParams(),
+ params.logToRedfish(), params.updateReport(), params.reportNames(),
+ params.sensors(), params.thresholdParams(),
std::vector<std::shared_ptr<interfaces::Threshold>>{},
*triggerManagerMockPtr, storageMock);
}
@@ -104,16 +130,67 @@
Eq(triggerParams.logToRedfish()));
EXPECT_THAT(getProperty<bool>(sut->getPath(), "UpdateReport"),
Eq(triggerParams.updateReport()));
- EXPECT_THAT((getProperty<std::vector<
- std::pair<sdbusplus::message::object_path, std::string>>>(
- sut->getPath(), "Sensors")),
- Eq(triggerParams.sensors()));
+ EXPECT_THAT((getProperty<SensorsInfo>(sut->getPath(), "Sensors")),
+ Eq(utils::fromLabeledSensorsInfo(triggerParams.sensors())));
EXPECT_THAT(
getProperty<std::vector<std::string>>(sut->getPath(), "ReportNames"),
Eq(triggerParams.reportNames()));
EXPECT_THAT(
getProperty<TriggerThresholdParams>(sut->getPath(), "Thresholds"),
- Eq(triggerParams.thresholdParams()));
+ Eq(std::visit(utils::FromLabeledThresholdParamConversion(),
+ triggerParams.thresholdParams())));
+}
+
+TEST_F(TestTrigger, checkIfNumericCoversionsAreGood)
+{
+ const auto& labeledParamsBase =
+ std::get<std::vector<numeric::LabeledThresholdParam>>(
+ triggerParams.thresholdParams());
+ const auto paramsToCheck =
+ std::visit(utils::FromLabeledThresholdParamConversion(),
+ triggerParams.thresholdParams());
+ const auto labeledParamsToCheck =
+ std::get<std::vector<numeric::LabeledThresholdParam>>(std::visit(
+ utils::ToLabeledThresholdParamConversion(), paramsToCheck));
+
+ for (const auto& [tocheck, base] :
+ boost::combine(labeledParamsToCheck, labeledParamsBase))
+ {
+ EXPECT_THAT(tocheck.at_label<utils::tstring::Type>(),
+ Eq(base.at_label<utils::tstring::Type>()));
+ EXPECT_THAT(tocheck.at_label<utils::tstring::Direction>(),
+ Eq(base.at_label<utils::tstring::Direction>()));
+ EXPECT_THAT(tocheck.at_label<utils::tstring::DwellTime>(),
+ Eq(base.at_label<utils::tstring::DwellTime>()));
+ EXPECT_THAT(tocheck.at_label<utils::tstring::ThresholdValue>(),
+ Eq(base.at_label<utils::tstring::ThresholdValue>()));
+ }
+}
+
+TEST_F(TestTrigger, checkIfDiscreteCoversionsAreGood)
+{
+ const auto& labeledParamsBase =
+ std::get<std::vector<discrete::LabeledThresholdParam>>(
+ triggerDiscreteParams.thresholdParams());
+ const auto paramsToCheck =
+ std::visit(utils::FromLabeledThresholdParamConversion(),
+ triggerDiscreteParams.thresholdParams());
+ const auto labeledParamsToCheck =
+ std::get<std::vector<discrete::LabeledThresholdParam>>(std::visit(
+ utils::ToLabeledThresholdParamConversion(), paramsToCheck));
+
+ for (const auto& [tocheck, base] :
+ boost::combine(labeledParamsToCheck, labeledParamsBase))
+ {
+ EXPECT_THAT(tocheck.at_label<utils::tstring::UserId>(),
+ Eq(base.at_label<utils::tstring::UserId>()));
+ EXPECT_THAT(tocheck.at_label<utils::tstring::Severity>(),
+ Eq(base.at_label<utils::tstring::Severity>()));
+ EXPECT_THAT(tocheck.at_label<utils::tstring::DwellTime>(),
+ Eq(base.at_label<utils::tstring::DwellTime>()));
+ EXPECT_THAT(tocheck.at_label<utils::tstring::ThresholdValue>(),
+ Eq(base.at_label<utils::tstring::ThresholdValue>()));
+ }
}
TEST_F(TestTrigger, deleteTrigger)
@@ -130,12 +207,12 @@
EXPECT_THAT(ec.value(), Eq(EBADR));
}
-TEST_F(TestTrigger, settingPersistencyToFalseRemovesReportFromStorage)
+TEST_F(TestTrigger, settingPersistencyToFalseRemovesTriggerFromStorage)
{
EXPECT_CALL(storageMock, remove(to_file_path(sut->getName())));
bool persistent = false;
- EXPECT_THAT(setProperty(sut->getPath(), "Persistent", persistent).value(),
+ EXPECT_THAT(setProperty(sut->getPath(), "Persistent", persistent),
Eq(boost::system::errc::success));
EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistent"),
Eq(persistent));
@@ -150,7 +227,7 @@
nlohmann::json storedConfiguration;
};
-TEST_F(TestTriggerErrors, throwingExceptionDoesNotStoreTriggerReportNames)
+TEST_F(TestTriggerErrors, exceptionDuringTriggerStoreDisablesPersistency)
{
EXPECT_CALL(storageMock, store(_, _))
.WillOnce(Throw(std::runtime_error("Generic error!")));
@@ -171,15 +248,20 @@
class TestTriggerStore : public TestTrigger
{
public:
+ nlohmann::json storedConfiguration;
+ nlohmann::json storedDiscreteConfiguration;
+ std::unique_ptr<Trigger> sutDiscrete;
+
void SetUp() override
{
ON_CALL(storageMock, store(_, _))
.WillByDefault(SaveArg<1>(&storedConfiguration));
-
sut = makeTrigger(triggerParams);
- }
- nlohmann::json storedConfiguration;
+ ON_CALL(storageMock, store(_, _))
+ .WillByDefault(SaveArg<1>(&storedDiscreteConfiguration));
+ sutDiscrete = makeTrigger(triggerDiscreteParams);
+ }
};
TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerVersion)
@@ -225,29 +307,50 @@
TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerSensors)
{
nlohmann::json expectedItem;
+ expectedItem["service"] = "service1";
expectedItem["sensorPath"] =
"/xyz/openbmc_project/sensors/temperature/BMC_Temp";
- expectedItem["sensorMetadata"] = "";
+ expectedItem["sensorMetadata"] = "metadata1";
ASSERT_THAT(storedConfiguration.at("Sensors"), ElementsAre(expectedItem));
}
TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerThresholdParams)
{
- ASSERT_THAT(storedConfiguration.at("ThresholdParamsDiscriminator"), Eq(0));
-
nlohmann::json expectedItem0;
expectedItem0["type"] = 0;
expectedItem0["dwellTime"] = 10;
expectedItem0["direction"] = 1;
- expectedItem0["thresholdValue"] = 0.0;
+ expectedItem0["thresholdValue"] = 0.5;
nlohmann::json expectedItem1;
expectedItem1["type"] = 3;
expectedItem1["dwellTime"] = 10;
expectedItem1["direction"] = 2;
- expectedItem1["thresholdValue"] = 90.0;
+ expectedItem1["thresholdValue"] = 90.2;
+ ASSERT_THAT(storedConfiguration.at("ThresholdParamsDiscriminator"), Eq(0));
ASSERT_THAT(storedConfiguration.at("ThresholdParams"),
ElementsAre(expectedItem0, expectedItem1));
}
+
+TEST_F(TestTriggerStore,
+ settingPersistencyToTrueStoresDiscreteTriggerThresholdParams)
+{
+ nlohmann::json expectedItem0;
+ expectedItem0["userId"] = "userId";
+ expectedItem0["severity"] = discrete::Severity::warning;
+ expectedItem0["dwellTime"] = 10;
+ expectedItem0["thresholdValue"] = 15.2;
+
+ nlohmann::json expectedItem1;
+ expectedItem1["userId"] = "userId_2";
+ expectedItem1["severity"] = discrete::Severity::critical;
+ expectedItem1["dwellTime"] = 5;
+ expectedItem1["thresholdValue"] = 32.7;
+
+ ASSERT_THAT(storedDiscreteConfiguration.at("ThresholdParamsDiscriminator"),
+ Eq(1));
+ ASSERT_THAT(storedDiscreteConfiguration.at("ThresholdParams"),
+ ElementsAre(expectedItem0, expectedItem1));
+}
diff --git a/tests/src/test_trigger_manager.cpp b/tests/src/test_trigger_manager.cpp
index 1bc4f1b..773b809 100644
--- a/tests/src/test_trigger_manager.cpp
+++ b/tests/src/test_trigger_manager.cpp
@@ -4,7 +4,10 @@
#include "mocks/trigger_factory_mock.hpp"
#include "mocks/trigger_mock.hpp"
#include "params/trigger_params.hpp"
+#include "trigger.hpp"
#include "trigger_manager.hpp"
+#include "utils/conversion_trigger.hpp"
+#include "utils/transform.hpp"
using namespace testing;
@@ -14,6 +17,9 @@
std::pair<boost::system::error_code, std::string>
addTrigger(const TriggerParams& params)
{
+ const auto sensorInfos =
+ utils::fromLabeledSensorsInfo(params.sensors());
+
std::promise<std::pair<boost::system::error_code, std::string>>
addTriggerPromise;
DbusEnvironment::getBus()->async_method_call(
@@ -24,12 +30,25 @@
DbusEnvironment::serviceName(), TriggerManager::triggerManagerPath,
TriggerManager::triggerManagerIfaceName, "AddTrigger",
params.name(), params.isDiscrete(), params.logToJournal(),
- params.logToRedfish(), params.updateReport(), params.sensors(),
- params.reportNames(), params.thresholdParams());
+ params.logToRedfish(), params.updateReport(), sensorInfos,
+ params.reportNames(),
+ std::visit(utils::FromLabeledThresholdParamConversion(),
+ params.thresholdParams()));
return DbusEnvironment::waitForFuture(addTriggerPromise.get_future());
}
- TriggerParams triggerParams;
+ std::unique_ptr<TriggerManager> makeTriggerManager()
+ {
+ return std::make_unique<TriggerManager>(
+ std::move(triggerFactoryMockPtr), std::move(storageMockPtr),
+ DbusEnvironment::getObjServer());
+ }
+
+ void SetUp() override
+ {
+ sut = makeTriggerManager();
+ }
+
std::unique_ptr<StorageMock> storageMockPtr =
std::make_unique<NiceMock<StorageMock>>();
StorageMock& storageMock = *storageMockPtr;
@@ -37,20 +56,18 @@
std::make_unique<NiceMock<TriggerFactoryMock>>();
TriggerFactoryMock& triggerFactoryMock = *triggerFactoryMockPtr;
std::unique_ptr<TriggerMock> triggerMockPtr =
- std::make_unique<NiceMock<TriggerMock>>(triggerParams.name());
+ std::make_unique<NiceMock<TriggerMock>>(TriggerParams().name());
TriggerMock& triggerMock = *triggerMockPtr;
- std::unique_ptr<TriggerManager> sut = std::make_unique<TriggerManager>(
- std::move(triggerFactoryMockPtr), std::move(storageMockPtr),
- std::move(DbusEnvironment::getObjServer()));
+ std::unique_ptr<TriggerManager> sut;
MockFunction<void(std::string)> checkPoint;
};
TEST_F(TestTriggerManager, addTrigger)
{
- triggerFactoryMock.expectMake(triggerParams, Ref(*sut), Ref(storageMock))
+ triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
.WillOnce(Return(ByMove(std::move(triggerMockPtr))));
- auto [ec, path] = addTrigger(triggerParams);
+ auto [ec, path] = addTrigger(TriggerParams());
EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
EXPECT_THAT(path, Eq(triggerMock.getPath()));
}
@@ -58,13 +75,10 @@
TEST_F(TestTriggerManager, addTriggerWithDiscreteThresholds)
{
TriggerParams triggerParamsDiscrete;
- auto thresholds = std::vector<discrete::ThresholdParam>{
- {"discrete_threshold1",
- discrete::severityToString(discrete::Severity::ok), 10, 11.0},
- {"discrete_threshold2",
- discrete::severityToString(discrete::Severity::warning), 10, 12.0},
- {"discrete_threshold3",
- discrete::severityToString(discrete::Severity::critical), 10, 13.0}};
+ auto thresholds = std::vector<discrete::LabeledThresholdParam>{
+ {"discrete_threshold1", discrete::Severity::ok, 10, 11.0},
+ {"discrete_threshold2", discrete::Severity::warning, 10, 12.0},
+ {"discrete_threshold3", discrete::Severity::critical, 10, 13.0}};
triggerParamsDiscrete.thresholdParams(thresholds).isDiscrete(true);
@@ -76,7 +90,7 @@
TEST_F(TestTriggerManager, addDiscreteTriggerWithoutThresholds)
{
TriggerParams triggerParamsDiscrete;
- auto thresholds = std::vector<discrete::ThresholdParam>();
+ auto thresholds = std::vector<discrete::LabeledThresholdParam>();
triggerParamsDiscrete.thresholdParams(thresholds).isDiscrete(true);
@@ -87,30 +101,32 @@
TEST_F(TestTriggerManager, DISABLED_failToAddTriggerTwice)
{
- triggerFactoryMock.expectMake(triggerParams, Ref(*sut), Ref(storageMock))
+ triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
.WillOnce(Return(ByMove(std::move(triggerMockPtr))));
- addTrigger(triggerParams);
+ addTrigger(TriggerParams());
- auto [ec, path] = addTrigger(triggerParams);
+ auto [ec, path] = addTrigger(TriggerParams());
EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists));
EXPECT_THAT(path, Eq(std::string()));
}
TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWhenMaxTriggerIsReached)
{
+ auto triggerParams = TriggerParams();
+
triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
.Times(TriggerManager::maxTriggers);
for (size_t i = 0; i < TriggerManager::maxTriggers; i++)
{
- triggerParams.name(triggerParams.name() + std::to_string(i));
+ triggerParams.name(TriggerParams().name() + std::to_string(i));
auto [ec, path] = addTrigger(triggerParams);
EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
}
- triggerParams.name(triggerParams.name() +
+ triggerParams.name(TriggerParams().name() +
std::to_string(TriggerManager::maxTriggers));
auto [ec, path] = addTrigger(triggerParams);
EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open));
@@ -122,13 +138,13 @@
{
InSequence seq;
triggerFactoryMock
- .expectMake(triggerParams, Ref(*sut), Ref(storageMock))
+ .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
.WillOnce(Return(ByMove(std::move(triggerMockPtr))));
EXPECT_CALL(triggerMock, Die());
EXPECT_CALL(checkPoint, Call("end"));
}
- addTrigger(triggerParams);
+ addTrigger(TriggerParams());
sut->removeTrigger(&triggerMock);
checkPoint.Call("end");
}
@@ -150,14 +166,101 @@
{
InSequence seq;
triggerFactoryMock
- .expectMake(triggerParams, Ref(*sut), Ref(storageMock))
+ .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock))
.WillOnce(Return(ByMove(std::move(triggerMockPtr))));
EXPECT_CALL(triggerMock, Die());
EXPECT_CALL(checkPoint, Call("end"));
}
- addTrigger(triggerParams);
+ addTrigger(TriggerParams());
sut->removeTrigger(&triggerMock);
sut->removeTrigger(&triggerMock);
checkPoint.Call("end");
}
+
+class TestTriggerManagerStorage : public TestTriggerManager
+{
+ public:
+ using FilePath = interfaces::JsonStorage::FilePath;
+ using DirectoryPath = interfaces::JsonStorage::DirectoryPath;
+
+ void SetUp() override
+ {
+ ON_CALL(storageMock, list())
+ .WillByDefault(Return(std::vector<FilePath>{
+ {FilePath("trigger1")}, {FilePath("trigger2")}}));
+
+ ON_CALL(storageMock, load(FilePath("trigger1")))
+ .WillByDefault(InvokeWithoutArgs([this] { return data1; }));
+
+ data2["Name"] = "Trigger2";
+ ON_CALL(storageMock, load(FilePath("trigger2")))
+ .WillByDefault(InvokeWithoutArgs([this] { return data2; }));
+ }
+
+ nlohmann::json data1 = nlohmann::json{
+ {"Version", Trigger::triggerVersion},
+ {"Name", TriggerParams().name()},
+ {"ThresholdParamsDiscriminator",
+ TriggerParams().thresholdParams().index()},
+ {"IsDiscrete", TriggerParams().isDiscrete()},
+ {"LogToJournal", TriggerParams().logToJournal()},
+ {"LogToRedfish", TriggerParams().logToRedfish()},
+ {"UpdateReport", TriggerParams().updateReport()},
+ {"ThresholdParams", utils::labeledThresholdParamsToJson(
+ TriggerParams().thresholdParams())},
+ {"ReportNames", TriggerParams().reportNames()},
+ {"Sensors", TriggerParams().sensors()}};
+
+ nlohmann::json data2 = data1;
+};
+
+TEST_F(TestTriggerManagerStorage, triggerManagerCtorAddTriggerFromStorage)
+{
+ triggerFactoryMock.expectMake(TriggerParams(), _, Ref(storageMock));
+ triggerFactoryMock.expectMake(TriggerParams().name("Trigger2"), _,
+ Ref(storageMock));
+ EXPECT_CALL(storageMock, remove(_)).Times(0);
+
+ sut = makeTriggerManager();
+}
+
+TEST_F(TestTriggerManagerStorage,
+ triggerManagerCtorRemoveDiscreteTriggerFromStorage)
+{
+ LabeledTriggerThresholdParams thresholdParams =
+ std::vector<discrete::LabeledThresholdParam>{
+ {"userId1", discrete::Severity::warning, 15, 10.0},
+ {"userId2", discrete::Severity::critical, 5, 20.0}};
+
+ data1["ThresholdParamsDiscriminator"] = thresholdParams.index();
+
+ data1["ThresholdParams"] =
+ utils::labeledThresholdParamsToJson(thresholdParams);
+
+ EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0);
+
+ sut = makeTriggerManager();
+}
+
+TEST_F(TestTriggerManagerStorage,
+ triggerManagerCtorRemoveDiscreteTriggerFromStorage2)
+{
+ data1["IsDiscrete"] = true;
+
+ EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0);
+
+ sut = makeTriggerManager();
+}
+
+TEST_F(TestTriggerManagerStorage,
+ triggerManagerCtorAddProperRemoveInvalidTriggerFromStorage)
+{
+ data1["Version"] = Trigger::triggerVersion - 1;
+
+ triggerFactoryMock.expectMake(TriggerParams().name("Trigger2"), _,
+ Ref(storageMock));
+ EXPECT_CALL(storageMock, remove(FilePath("trigger1")));
+
+ sut = makeTriggerManager();
+}