Save persistent triggers to storage
Create json storage file for persistent triggers.
Handle persistent dbus property.
Save/remove persistent triggers on add/delete.
Cover code with UTs.
Tested:
- Passed unit tests
- Tested on QEMU
* adding new valid and invalid trigger from cli
* verifying if valid trigger is properly stored
* deleting existed trigger from storage
Change-Id: I243326e84833a8cb22075fbf565573b62b205b4a
Signed-off-by: Cezary Zwolak <cezary.zwolak@intel.com>
Signed-off-by: Lukasz Kazmierczak <lukasz.kazmierczak@intel.com>
diff --git a/src/interfaces/trigger_factory.hpp b/src/interfaces/trigger_factory.hpp
index 7d448d0..d493b4a 100644
--- a/src/interfaces/trigger_factory.hpp
+++ b/src/interfaces/trigger_factory.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "interfaces/json_storage.hpp"
#include "interfaces/trigger.hpp"
#include "interfaces/trigger_manager.hpp"
#include "interfaces/trigger_types.hpp"
@@ -26,7 +27,8 @@
std::pair<sdbusplus::message::object_path, std::string>>& sensors,
const std::vector<std::string>& reportNames,
const TriggerThresholdParams& thresholdParams,
- interfaces::TriggerManager& triggerManager) const = 0;
+ interfaces::TriggerManager& triggerManager,
+ interfaces::JsonStorage& triggerStorage) const = 0;
};
} // namespace interfaces
diff --git a/src/interfaces/trigger_types.hpp b/src/interfaces/trigger_types.hpp
index 90c673d..1833171 100644
--- a/src/interfaces/trigger_types.hpp
+++ b/src/interfaces/trigger_types.hpp
@@ -1,6 +1,8 @@
#pragma once
#include "utils/conversion.hpp"
+#include "utils/labeled_tuple.hpp"
+#include "utils/tstring.hpp"
#include <string>
#include <tuple>
@@ -38,6 +40,12 @@
}
using ThresholdParam = std::tuple<std::string, std::string, uint64_t, double>;
+
+using LabeledThresholdParam =
+ utils::LabeledTuple<std::tuple<std::string, Severity, uint64_t, double>,
+ utils::tstring::UserId, utils::tstring::Severity,
+ utils::tstring::DwellTime,
+ utils::tstring::ThresholdValue>;
} // namespace discrete
namespace numeric
@@ -95,8 +103,22 @@
}
using ThresholdParam = std::tuple<std::string, uint64_t, std::string, double>;
+
+using LabeledThresholdParam =
+ utils::LabeledTuple<std::tuple<Type, uint64_t, Direction, double>,
+ utils::tstring::Type, utils::tstring::DwellTime,
+ utils::tstring::Direction,
+ utils::tstring::ThresholdValue>;
} // namespace numeric
+using TriggerSensors =
+ std::vector<std::pair<sdbusplus::message::object_path, std::string>>;
+
+using LabeledTriggerSensor =
+ utils::LabeledTuple<std::tuple<std::string, std::string>,
+ utils::tstring::SensorPath,
+ utils::tstring::SensorMetadata>;
+
using TriggerThresholdParams =
std::variant<std::vector<numeric::ThresholdParam>,
std::vector<discrete::ThresholdParam>>;
diff --git a/src/interfaces/types.hpp b/src/interfaces/types.hpp
index a5ed0db..a3a1b1c 100644
--- a/src/interfaces/types.hpp
+++ b/src/interfaces/types.hpp
@@ -28,4 +28,4 @@
using Readings = std::tuple<
uint64_t,
- std::vector<std::tuple<std::string, std::string, double, uint64_t>>>;
+ std::vector<std::tuple<std::string, std::string, double, uint64_t>>>;
\ No newline at end of file
diff --git a/src/telemetry.hpp b/src/telemetry.hpp
index fb7f2a3..be2d81c 100644
--- a/src/telemetry.hpp
+++ b/src/telemetry.hpp
@@ -25,6 +25,9 @@
objServer),
triggerManager(std::make_unique<TriggerFactory>(
bus, objServer, sensorCache, reportManager),
+ std::make_unique<PersistentJsonStorage>(
+ interfaces::JsonStorage::DirectoryPath(
+ "/var/lib/telemetry/Triggers")),
objServer)
{}
diff --git a/src/trigger.cpp b/src/trigger.cpp
index 471ad10..2bba3ea 100644
--- a/src/trigger.cpp
+++ b/src/trigger.cpp
@@ -1,26 +1,44 @@
#include "trigger.hpp"
#include "interfaces/types.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 isDiscrete, const bool logToJournal,
- const bool logToRedfish, const bool updateReport,
- const std::vector<std::pair<sdbusplus::message::object_path, std::string>>&
- sensorsIn,
+ 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,
std::vector<std::shared_ptr<interfaces::Threshold>>&& thresholdsIn,
- interfaces::TriggerManager& triggerManager) :
+ interfaces::TriggerManager& triggerManager,
+ interfaces::JsonStorage& triggerStorageIn) :
name(nameIn),
- path(triggerDir + name), persistent(false), sensors(sensorsIn),
- reportNames(reportNamesIn), thresholdParams(thresholdParamsIn),
- thresholds(std::move(thresholdsIn))
+ isDiscrete(isDiscreteIn), logToJournal(logToJournalIn),
+ logToRedfish(logToRedfishIn), updateReport(updateReportIn),
+ path(triggerDir + name), sensors(sensorsIn), reportNames(reportNamesIn),
+ thresholdParams(thresholdParamsIn), thresholds(std::move(thresholdsIn)),
+ fileName(std::to_string(std::hash<std::string>{}(name))),
+ triggerStorage(triggerStorageIn)
{
deleteIface = objServer->add_unique_interface(
path, deleteIfaceName, [this, &ioc, &triggerManager](auto& dbusIface) {
dbusIface.register_method("Delete", [this, &ioc, &triggerManager] {
+ if (persistent)
+ {
+ triggerStorage.remove(fileName);
+ }
boost::asio::post(ioc, [this, &triggerManager] {
triggerManager.removeTrigger(this);
});
@@ -29,12 +47,30 @@
triggerIface = objServer->add_unique_interface(
path, triggerIfaceName,
- [this, isDiscrete, logToJournal, logToRedfish,
- updateReport](auto& dbusIface) {
- dbusIface.register_property_r(
+ [this, isDiscreteIn, logToJournalIn, logToRedfishIn,
+ updateReportIn](auto& dbusIface) {
+ persistent = storeConfiguration();
+ dbusIface.register_property_rw(
"Persistent", persistent,
sdbusplus::vtable::property_::emits_change,
- [](const auto& x) { return x; });
+ [this](bool newVal, const auto&) {
+ if (newVal == persistent)
+ {
+ return true;
+ }
+ if (newVal)
+ {
+ persistent = storeConfiguration();
+ }
+ else
+ {
+ triggerStorage.remove(fileName);
+ persistent = false;
+ }
+ return true;
+ },
+ [this](const auto&) { return persistent; });
+
dbusIface.register_property_r(
"Thresholds", thresholdParams,
sdbusplus::vtable::property_::emits_change,
@@ -65,3 +101,62 @@
threshold->initialize();
}
}
+
+bool Trigger::storeConfiguration() const
+{
+ try
+ {
+ nlohmann::json data;
+
+ data["Version"] = triggerVersion;
+ data["Name"] = name;
+ data["ThresholdParamsDiscriminator"] = thresholdParams.index();
+ data["IsDiscrete"] = isDiscrete;
+ 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["ReportNames"] = reportNames;
+
+ data["Sensors"] = utils::transform(sensors, [](const auto& sensor) {
+ const auto& [sensorPath, sensorMetadata] = sensor;
+ return LabeledTriggerSensor(sensorPath, sensorMetadata);
+ });
+
+ triggerStorage.store(fileName, data);
+ }
+ catch (const std::exception& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Failed to store a trigger in storage",
+ phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
+ return false;
+ }
+ return true;
+}
\ No newline at end of file
diff --git a/src/trigger.hpp b/src/trigger.hpp
index 3405c5b..b2bdc1d 100644
--- a/src/trigger.hpp
+++ b/src/trigger.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "interfaces/json_storage.hpp"
#include "interfaces/threshold.hpp"
#include "interfaces/trigger.hpp"
#include "interfaces/trigger_manager.hpp"
@@ -23,7 +24,8 @@
const std::vector<std::string>& reportNames,
const TriggerThresholdParams& thresholdParams,
std::vector<std::shared_ptr<interfaces::Threshold>>&& thresholds,
- interfaces::TriggerManager& triggerManager);
+ interfaces::TriggerManager& triggerManager,
+ interfaces::JsonStorage& triggerStorage);
Trigger(const Trigger&) = delete;
Trigger(Trigger&&) = delete;
@@ -40,18 +42,26 @@
return path;
}
+ bool storeConfiguration() const;
+
private:
const std::string name;
+ bool isDiscrete;
+ bool logToJournal;
+ bool logToRedfish;
+ bool updateReport;
const std::string path;
- bool persistent;
- std::vector<std::pair<sdbusplus::message::object_path, std::string>>
- sensors;
+ bool persistent = false;
+ TriggerSensors sensors;
std::vector<std::string> reportNames;
TriggerThresholdParams thresholdParams;
std::unique_ptr<sdbusplus::asio::dbus_interface> deleteIface;
std::unique_ptr<sdbusplus::asio::dbus_interface> triggerIface;
std::vector<std::shared_ptr<interfaces::Threshold>> thresholds;
+ interfaces::JsonStorage::FilePath fileName;
+ interfaces::JsonStorage& triggerStorage;
+
public:
static constexpr const char* triggerIfaceName =
"xyz.openbmc_project.Telemetry.Trigger";
@@ -59,4 +69,5 @@
"/xyz/openbmc_project/Telemetry/Triggers/";
static constexpr const char* deleteIfaceName =
"xyz.openbmc_project.Object.Delete";
+ static constexpr size_t triggerVersion = 0;
};
diff --git a/src/trigger_factory.cpp b/src/trigger_factory.cpp
index 3114e9a..e039e1f 100644
--- a/src/trigger_factory.cpp
+++ b/src/trigger_factory.cpp
@@ -17,14 +17,15 @@
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 std::vector<std::pair<sdbusplus::message::object_path, std::string>>&
- sensorPaths,
- const std::vector<std::string>& reportNames,
- const TriggerThresholdParams& thresholdParams,
- interfaces::TriggerManager& triggerManager) const
+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
{
auto [sensors, sensorNames] = getSensors(yield, sensorPaths);
std::vector<std::shared_ptr<interfaces::Threshold>> thresholds;
@@ -121,7 +122,7 @@
return std::make_unique<Trigger>(
bus->get_io_context(), objServer, name, isDiscrete, logToJournal,
logToRedfish, updateReport, sensorPaths, reportNames, thresholdParams,
- std::move(thresholds), triggerManager);
+ std::move(thresholds), triggerManager, triggerStorage);
}
std::pair<std::vector<std::shared_ptr<interfaces::Sensor>>,
diff --git a/src/trigger_factory.hpp b/src/trigger_factory.hpp
index c1f2473..a6fbb68 100644
--- a/src/trigger_factory.hpp
+++ b/src/trigger_factory.hpp
@@ -23,7 +23,8 @@
std::pair<sdbusplus::message::object_path, std::string>>& sensors,
const std::vector<std::string>& reportNames,
const TriggerThresholdParams& thresholdParams,
- interfaces::TriggerManager& triggerManager) const override;
+ interfaces::TriggerManager& triggerManager,
+ interfaces::JsonStorage& triggerStorage) const override;
private:
std::shared_ptr<sdbusplus::asio::connection> bus;
diff --git a/src/trigger_manager.cpp b/src/trigger_manager.cpp
index bf372c2..3882022 100644
--- a/src/trigger_manager.cpp
+++ b/src/trigger_manager.cpp
@@ -2,8 +2,10 @@
TriggerManager::TriggerManager(
std::unique_ptr<interfaces::TriggerFactory> triggerFactoryIn,
+ std::unique_ptr<interfaces::JsonStorage> triggerStorageIn,
const std::shared_ptr<sdbusplus::asio::object_server>& objServer) :
- triggerFactory(std::move(triggerFactoryIn))
+ triggerFactory(std::move(triggerFactoryIn)),
+ triggerStorage(std::move(triggerStorageIn))
{
managerIface = objServer->add_unique_interface(
triggerManagerPath, triggerManagerIfaceName, [this](auto& iface) {
@@ -36,7 +38,8 @@
triggers.emplace_back(triggerFactory->make(
yield, name, isDiscrete, logToJournal, logToRedfish,
- updateReport, sensors, reportNames, thresholds, *this));
+ updateReport, sensors, reportNames, thresholds, *this,
+ *triggerStorage));
return triggers.back()->getPath();
});
});
diff --git a/src/trigger_manager.hpp b/src/trigger_manager.hpp
index 41257eb..1db6d73 100644
--- a/src/trigger_manager.hpp
+++ b/src/trigger_manager.hpp
@@ -14,6 +14,7 @@
public:
TriggerManager(
std::unique_ptr<interfaces::TriggerFactory> triggerFactory,
+ std::unique_ptr<interfaces::JsonStorage> triggerStorage,
const std::shared_ptr<sdbusplus::asio::object_server>& objServer);
TriggerManager(TriggerManager&) = delete;
@@ -25,6 +26,7 @@
private:
std::unique_ptr<interfaces::TriggerFactory> triggerFactory;
+ std::unique_ptr<interfaces::JsonStorage> triggerStorage;
std::unique_ptr<sdbusplus::asio::dbus_interface> managerIface;
std::vector<std::unique_ptr<interfaces::Trigger>> triggers;
diff --git a/src/utils/tstring.hpp b/src/utils/tstring.hpp
index a8d3e90..5b17d98 100644
--- a/src/utils/tstring.hpp
+++ b/src/utils/tstring.hpp
@@ -24,6 +24,14 @@
}
};
+struct SensorMetadata
+{
+ static std::string str()
+ {
+ return "sensorMetadata";
+ }
+};
+
struct OperationType
{
static std::string str()
@@ -56,5 +64,53 @@
}
};
+struct Type
+{
+ static std::string str()
+ {
+ return "type";
+ }
+};
+
+struct DwellTime
+{
+ static std::string str()
+ {
+ return "dwellTime";
+ }
+};
+
+struct Direction
+{
+ static std::string str()
+ {
+ return "direction";
+ }
+};
+
+struct ThresholdValue
+{
+ static std::string str()
+ {
+ return "thresholdValue";
+ }
+};
+
+struct UserId
+{
+ static std::string str()
+ {
+ return "userId";
+ }
+};
+
+struct Severity
+{
+ static std::string str()
+ {
+ return "severity";
+ }
+};
+
} // namespace tstring
} // namespace utils