Implement Report persistency
Now Report properties are stored in non-volatile memory. It allows
to restore Report after system restart. Persistency of a report is
controlled by Persistency property in Report interface.
Tested:
- Passed unit tests
- Verified that report is stored in /var/lib/telemetry dir
- Verified that report is restored from storage after telemetry
service start
Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
Change-Id: Iccfe21603eecffc4e174a4403f699b03de320db9
diff --git a/src/interfaces/json_storage.hpp b/src/interfaces/json_storage.hpp
index badd938..491d4d9 100644
--- a/src/interfaces/json_storage.hpp
+++ b/src/interfaces/json_storage.hpp
@@ -20,10 +20,10 @@
virtual void store(const FilePath& subPath, const nlohmann::json& data) = 0;
virtual bool remove(const FilePath& subPath) = 0;
+ virtual bool exist(const FilePath& path) const = 0;
virtual std::optional<nlohmann::json>
load(const FilePath& subPath) const = 0;
- virtual std::vector<FilePath>
- list(const DirectoryPath& subDirectory) const = 0;
+ virtual std::vector<FilePath> list() const = 0;
};
} // namespace interfaces
diff --git a/src/interfaces/metric.hpp b/src/interfaces/metric.hpp
index 51fc8fa..50bf5d5 100644
--- a/src/interfaces/metric.hpp
+++ b/src/interfaces/metric.hpp
@@ -2,6 +2,8 @@
#include "metric_value.hpp"
+#include <nlohmann/json.hpp>
+
#include <vector>
namespace interfaces
@@ -13,6 +15,7 @@
virtual ~Metric() = default;
virtual const std::vector<MetricValue>& getReadings() const = 0;
+ virtual nlohmann::json to_json() const = 0;
};
} // namespace interfaces
diff --git a/src/interfaces/report_factory.hpp b/src/interfaces/report_factory.hpp
index 9273d03..056c9e0 100644
--- a/src/interfaces/report_factory.hpp
+++ b/src/interfaces/report_factory.hpp
@@ -1,13 +1,15 @@
#pragma once
+#include "interfaces/json_storage.hpp"
#include "interfaces/report.hpp"
#include "interfaces/report_manager.hpp"
#include "interfaces/types.hpp"
+#include <boost/asio/spawn.hpp>
+
#include <chrono>
#include <memory>
-
-class ReportManager;
+#include <optional>
namespace interfaces
{
@@ -17,12 +19,12 @@
public:
virtual ~ReportFactory() = default;
- virtual std::unique_ptr<interfaces::Report>
- make(const std::string& name, const std::string& reportingType,
- bool emitsReadingsSignal, bool logToMetricReportsCollection,
- std::chrono::milliseconds period,
- const ReadingParameters& metricParams,
- ReportManager& reportManager) const = 0;
+ virtual std::unique_ptr<interfaces::Report> make(
+ std::optional<std::reference_wrapper<boost::asio::yield_context>> yield,
+ const std::string& name, const std::string& reportingType,
+ bool emitsReadingsSignal, bool logToMetricReportsCollection,
+ std::chrono::milliseconds period, const ReadingParameters& metricParams,
+ ReportManager& reportManager, JsonStorage& reportStorage) const = 0;
};
} // namespace interfaces
diff --git a/src/interfaces/types.hpp b/src/interfaces/types.hpp
index 0d5eeb2..1eca51a 100644
--- a/src/interfaces/types.hpp
+++ b/src/interfaces/types.hpp
@@ -1,5 +1,8 @@
#pragma once
+#include "utils/labeled_tuple.hpp"
+#include "utils/tstring.hpp"
+
#include <sdbusplus/message/types.hpp>
#include <string>
@@ -10,6 +13,12 @@
std::vector<std::tuple<std::vector<sdbusplus::message::object_path>,
std::string, std::string, std::string>>;
+using LabeledReadingParameter =
+ utils::LabeledTuple<ReadingParameters::value_type,
+ utils::tstring::SensorPaths,
+ utils::tstring::OperationType, utils::tstring::Id,
+ utils::tstring::MetricMetadata>;
+
using Readings = std::tuple<
uint64_t,
std::vector<std::tuple<std::string, std::string, double, uint64_t>>>;
diff --git a/src/metric.hpp b/src/metric.hpp
index a676e25..76a60fb 100644
--- a/src/metric.hpp
+++ b/src/metric.hpp
@@ -13,7 +13,6 @@
void sensorUpdated(interfaces::Sensor&, uint64_t) override
{}
-
void sensorUpdated(interfaces::Sensor&, uint64_t, double value) override
{}
diff --git a/src/persistent_json_storage.cpp b/src/persistent_json_storage.cpp
index 8fb3fe9..58a2e6c 100644
--- a/src/persistent_json_storage.cpp
+++ b/src/persistent_json_storage.cpp
@@ -92,34 +92,21 @@
}
std::vector<interfaces::JsonStorage::FilePath>
- PersistentJsonStorage::list(const DirectoryPath& subDirectory) const
+ PersistentJsonStorage::list() const
{
- auto result = std::vector<FilePath>();
- const auto path = join(directory, subDirectory);
-
- if (!std::filesystem::exists(path))
+ std::vector<interfaces::JsonStorage::FilePath> result;
+ if (!std::filesystem::exists(directory))
{
return result;
}
- for (const auto& p : std::filesystem::directory_iterator(path))
+ for (const auto& p :
+ std::filesystem::recursive_directory_iterator(directory))
{
- if (std::filesystem::is_directory(p.path()))
+ if (p.is_regular_file())
{
- for (auto& item : list(DirectoryPath(p.path())))
- {
- result.emplace_back(std::move(item));
- }
- }
- else
- {
- const auto item = std::filesystem::relative(
- p.path().parent_path(), std::filesystem::path{directory});
-
- if (std::find(result.begin(), result.end(), item) == result.end())
- {
- result.emplace_back(item);
- }
+ auto item = std::filesystem::relative(p.path(), directory);
+ result.emplace_back(std::move(item));
}
}
@@ -142,3 +129,8 @@
path, std::filesystem::is_directory(path) ? dirPerms : filePerms,
std::filesystem::perm_options::replace);
}
+
+bool PersistentJsonStorage::exist(const FilePath& subPath) const
+{
+ return std::filesystem::exists(join(directory, subPath));
+}
diff --git a/src/persistent_json_storage.hpp b/src/persistent_json_storage.hpp
index 4b18aa1..2b0f365 100644
--- a/src/persistent_json_storage.hpp
+++ b/src/persistent_json_storage.hpp
@@ -9,9 +9,9 @@
void store(const FilePath& subPath, const nlohmann::json& data) override;
bool remove(const FilePath& subPath) override;
+ bool exist(const FilePath& path) const override;
std::optional<nlohmann::json> load(const FilePath& subPath) const override;
- std::vector<FilePath>
- list(const DirectoryPath& subDirectory) const override;
+ std::vector<FilePath> list() const override;
private:
DirectoryPath directory;
diff --git a/src/report.cpp b/src/report.cpp
index 2e68d45..b2ac261 100644
--- a/src/report.cpp
+++ b/src/report.cpp
@@ -1,58 +1,103 @@
#include "report.hpp"
#include "report_manager.hpp"
+#include "utils/transform.hpp"
+
+#include <phosphor-logging/log.hpp>
#include <numeric>
Report::Report(boost::asio::io_context& ioc,
const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
- const std::string& reportName, const std::string& reportingType,
- const bool emitsReadingsSignal,
- const bool logToMetricReportsCollection,
- const std::chrono::milliseconds period,
- const ReadingParameters& metricParams,
+ const std::string& reportName,
+ const std::string& reportingTypeIn,
+ const bool emitsReadingsUpdateIn,
+ const bool logToMetricReportsCollectionIn,
+ const std::chrono::milliseconds intervalIn,
+ const ReadingParameters& readingParametersIn,
interfaces::ReportManager& reportManager,
+ interfaces::JsonStorage& reportStorageIn,
std::vector<std::shared_ptr<interfaces::Metric>> metrics) :
name(reportName),
- path(reportDir + name), interval(period), objServer(objServer),
- metrics(std::move(metrics)), timer(ioc)
+ path(reportDir + name), reportingType(reportingTypeIn),
+ interval(intervalIn), emitsReadingsUpdate(emitsReadingsUpdateIn),
+ logToMetricReportsCollection(logToMetricReportsCollectionIn),
+ readingParameters(readingParametersIn), objServer(objServer),
+ metrics(std::move(metrics)), timer(ioc),
+ fileName(std::to_string(std::hash<std::string>{}(name))),
+ reportStorage(reportStorageIn)
{
+ deleteIface = objServer->add_unique_interface(
+ path, deleteIfaceName, [this, &ioc, &reportManager](auto& dbusIface) {
+ dbusIface.register_method("Delete", [this, &ioc, &reportManager] {
+ if (persistency)
+ {
+ reportStorage.remove(fileName);
+ }
+ boost::asio::post(ioc, [this, &reportManager] {
+ reportManager.removeReport(this);
+ });
+ });
+ });
+
reportIface = objServer->add_unique_interface(
- path, reportIfaceName,
- [this, &reportingType, &emitsReadingsSignal,
- &logToMetricReportsCollection, &metricParams](auto& dbusIface) {
- dbusIface.register_property(
+ path, reportIfaceName, [this](auto& dbusIface) {
+ dbusIface.register_property_rw(
"Interval", static_cast<uint64_t>(interval.count()),
- [this](const uint64_t newVal, uint64_t& actualVal) {
+ sdbusplus::vtable::property_::emits_change,
+ [this](uint64_t newVal, auto&) {
std::chrono::milliseconds newValT(newVal);
if (newValT < ReportManager::minInterval)
{
return false;
}
- actualVal = newVal;
interval = newValT;
return true;
+ },
+ [this](const auto&) {
+ return static_cast<uint64_t>(interval.count());
});
- dbusIface.register_property("Persistency", bool{false});
+ persistency = storeConfiguration();
+ dbusIface.register_property_rw(
+ "Persistency", persistency,
+ sdbusplus::vtable::property_::emits_change,
+ [this](bool newVal, const auto&) {
+ if (newVal == persistency)
+ {
+ return true;
+ }
+ if (newVal)
+ {
+ persistency = storeConfiguration();
+ }
+ else
+ {
+ reportStorage.remove(fileName);
+ persistency = false;
+ }
+ return true;
+ },
+ [this](const auto&) { return persistency; });
dbusIface.register_property_r(
"Readings", readings,
sdbusplus::vtable::property_::emits_change,
[this](const auto&) { return readings; });
- dbusIface.register_property("ReportingType", reportingType);
- dbusIface.register_property("ReadingParameters", metricParams);
- dbusIface.register_property("EmitsReadingsUpdate",
- emitsReadingsSignal);
- dbusIface.register_property("LogToMetricReportsCollection",
- logToMetricReportsCollection);
- });
-
- deleteIface = objServer->add_unique_interface(
- path, deleteIfaceName, [this, &ioc, &reportManager](auto& dbusIface) {
- dbusIface.register_method("Delete", [this, &ioc, &reportManager] {
- boost::asio::post(ioc, [this, &reportManager] {
- reportManager.removeReport(this);
- });
- });
+ dbusIface.register_property_r(
+ "ReportingType", reportingType,
+ sdbusplus::vtable::property_::const_,
+ [this](const auto&) { return reportingType; });
+ dbusIface.register_property_r(
+ "ReadingParameters", readingParameters,
+ sdbusplus::vtable::property_::const_,
+ [this](const auto&) { return readingParameters; });
+ dbusIface.register_property_r(
+ "EmitsReadingsUpdate", emitsReadingsUpdate,
+ sdbusplus::vtable::property_::const_,
+ [this](const auto&) { return emitsReadingsUpdate; });
+ dbusIface.register_property_r(
+ "LogToMetricReportsCollection", logToMetricReportsCollection,
+ sdbusplus::vtable::property_::const_,
+ [this](const auto&) { return logToMetricReportsCollection; });
});
if (reportingType == "Periodic")
@@ -101,5 +146,34 @@
std::get<0>(readings) = std::time(0);
std::get<1>(readings) = readingsCache;
+
reportIface->signal_property("Readings");
}
+
+bool Report::storeConfiguration() const
+{
+ try
+ {
+ nlohmann::json data;
+
+ data["Version"] = reportVersion;
+ data["Name"] = name;
+ data["ReportingType"] = reportingType;
+ data["EmitsReadingsUpdate"] = emitsReadingsUpdate;
+ data["LogToMetricReportsCollection"] = logToMetricReportsCollection;
+ data["Interval"] = interval.count();
+ data["ReadingParameters"] = utils::transform(
+ metrics, [](const auto& metric) { return metric->to_json(); });
+
+ reportStorage.store(fileName, data);
+ }
+ catch (const std::exception& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Failed to store a report in storage",
+ phosphor::logging::entry("msg=", e.what()));
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/report.hpp b/src/report.hpp
index 8cef26a..2020f99 100644
--- a/src/report.hpp
+++ b/src/report.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "interfaces/json_storage.hpp"
#include "interfaces/metric.hpp"
#include "interfaces/report.hpp"
#include "interfaces/report_manager.hpp"
@@ -23,6 +24,7 @@
const std::chrono::milliseconds period,
const ReadingParameters& metricParams,
interfaces::ReportManager& reportManager,
+ interfaces::JsonStorage& reportStorage,
std::vector<std::shared_ptr<interfaces::Metric>> metrics);
~Report() = default;
@@ -41,6 +43,8 @@
return path;
}
+ bool storeConfiguration() const;
+
private:
static void timerProc(boost::system::error_code, Report& self);
void scheduleTimer(std::chrono::milliseconds interval);
@@ -48,7 +52,12 @@
const std::string name;
const std::string path;
+ std::string reportingType;
std::chrono::milliseconds interval;
+ bool emitsReadingsUpdate;
+ bool logToMetricReportsCollection;
+ ReadingParameters readingParameters;
+ bool persistency;
Readings readings = {};
std::tuple_element_t<1, Readings> readingsCache = {};
std::shared_ptr<sdbusplus::asio::object_server> objServer;
@@ -57,6 +66,9 @@
std::vector<std::shared_ptr<interfaces::Metric>> metrics;
boost::asio::steady_timer timer;
+ interfaces::JsonStorage::FilePath fileName;
+ interfaces::JsonStorage& reportStorage;
+
public:
static constexpr const char* reportIfaceName =
"xyz.openbmc_project.Telemetry.Report";
@@ -64,4 +76,5 @@
"/xyz/openbmc_project/Telemetry/Reports/";
static constexpr const char* deleteIfaceName =
"xyz.openbmc_project.Object.Delete";
+ static constexpr size_t reportVersion = 1;
};
diff --git a/src/report_factory.cpp b/src/report_factory.cpp
index 27e4ee7..5ac32f3 100644
--- a/src/report_factory.cpp
+++ b/src/report_factory.cpp
@@ -1,24 +1,28 @@
#include "report_factory.hpp"
#include "report.hpp"
+#include "sensor.hpp"
+#include "utils/transform.hpp"
ReportFactory::ReportFactory(
- boost::asio::io_context& ioc,
+ std::shared_ptr<sdbusplus::asio::connection> bus,
const std::shared_ptr<sdbusplus::asio::object_server>& objServer) :
- ioc(ioc),
+ bus(std::move(bus)),
objServer(objServer)
{}
std::unique_ptr<interfaces::Report> ReportFactory::make(
+ std::optional<std::reference_wrapper<boost::asio::yield_context>> yield,
const std::string& name, const std::string& reportingType,
bool emitsReadingsSignal, bool logToMetricReportsCollection,
std::chrono::milliseconds period, const ReadingParameters& metricParams,
- interfaces::ReportManager& reportManager) const
+ interfaces::ReportManager& reportManager,
+ interfaces::JsonStorage& reportStorage) const
{
std::vector<std::shared_ptr<interfaces::Metric>> metrics;
return std::make_unique<Report>(
- ioc, objServer, name, reportingType, emitsReadingsSignal,
- logToMetricReportsCollection, period, metricParams, reportManager,
- std::move(metrics));
+ bus->get_io_context(), objServer, name, reportingType,
+ emitsReadingsSignal, logToMetricReportsCollection, period, metricParams,
+ reportManager, reportStorage, std::move(metrics));
}
diff --git a/src/report_factory.hpp b/src/report_factory.hpp
index 775ea45..bdf9268 100644
--- a/src/report_factory.hpp
+++ b/src/report_factory.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "interfaces/report_factory.hpp"
+#include "interfaces/sensor.hpp"
#include <boost/asio/io_context.hpp>
#include <sdbusplus/asio/object_server.hpp>
@@ -9,17 +10,18 @@
{
public:
ReportFactory(
- boost::asio::io_context& ioc,
+ std::shared_ptr<sdbusplus::asio::connection> bus,
const std::shared_ptr<sdbusplus::asio::object_server>& objServer);
- std::unique_ptr<interfaces::Report>
- make(const std::string& name, const std::string& reportingType,
- bool emitsReadingsSignal, bool logToMetricReportsCollection,
- std::chrono::milliseconds period,
- const ReadingParameters& metricParams,
- interfaces::ReportManager& reportManager) const override;
+ std::unique_ptr<interfaces::Report> make(
+ std::optional<std::reference_wrapper<boost::asio::yield_context>> yield,
+ const std::string& name, const std::string& reportingType,
+ bool emitsReadingsSignal, bool logToMetricReportsCollection,
+ std::chrono::milliseconds period, const ReadingParameters& metricParams,
+ interfaces::ReportManager& reportManager,
+ interfaces::JsonStorage& reportStorage) const override;
private:
- boost::asio::io_context& ioc;
+ std::shared_ptr<sdbusplus::asio::connection> bus;
std::shared_ptr<sdbusplus::asio::object_server> objServer;
};
diff --git a/src/report_manager.cpp b/src/report_manager.cpp
index 5944fa9..d23b589 100644
--- a/src/report_manager.cpp
+++ b/src/report_manager.cpp
@@ -1,16 +1,22 @@
#include "report_manager.hpp"
+#include "report.hpp"
+
+#include <phosphor-logging/log.hpp>
#include <sdbusplus/exception.hpp>
+#include <stdexcept>
#include <system_error>
ReportManager::ReportManager(
std::unique_ptr<interfaces::ReportFactory> reportFactoryIn,
+ std::unique_ptr<interfaces::JsonStorage> reportStorageIn,
const std::shared_ptr<sdbusplus::asio::object_server>& objServerIn) :
reportFactory(std::move(reportFactoryIn)),
- objServer(objServerIn)
+ reportStorage(std::move(reportStorageIn)), objServer(objServerIn)
{
reports.reserve(maxReports);
+ loadFromPersistent();
reportManagerIface = objServer->add_unique_interface(
reportManagerPath, reportManagerIfaceName, [this](auto& dbusIface) {
@@ -22,42 +28,19 @@
[](const auto&) -> uint64_t { return minInterval.count(); });
dbusIface.register_method(
- "AddReport", [this](const std::string& reportName,
+ "AddReport", [this](boost::asio::yield_context& yield,
+ const std::string& reportName,
const std::string& reportingType,
const bool emitsReadingsUpdate,
const bool logToMetricReportsCollection,
const uint64_t interval,
const ReadingParameters& metricParams) {
- if (reports.size() >= maxReports)
- {
- throw sdbusplus::exception::SdBusError(
- static_cast<int>(std::errc::too_many_files_open),
- "Reached maximal report count");
- }
-
- for (const auto& report : reports)
- {
- if (report->getName() == reportName)
- {
- throw sdbusplus::exception::SdBusError(
- static_cast<int>(std::errc::file_exists),
- "Duplicate report");
- }
- }
-
- std::chrono::milliseconds reportInterval{interval};
- if (reportInterval < minInterval)
- {
- throw sdbusplus::exception::SdBusError(
- static_cast<int>(std::errc::invalid_argument),
- "Invalid interval");
- }
-
- reports.emplace_back(reportFactory->make(
- reportName, reportingType, emitsReadingsUpdate,
- logToMetricReportsCollection, std::move(reportInterval),
- metricParams, *this));
- return reports.back()->getPath();
+ return addReport(yield, reportName, reportingType,
+ emitsReadingsUpdate,
+ logToMetricReportsCollection,
+ std::chrono::milliseconds(interval),
+ metricParams)
+ ->getPath();
});
});
}
@@ -69,3 +52,85 @@
[report](const auto& x) { return report == x.get(); }),
reports.end());
}
+
+std::unique_ptr<interfaces::Report>& ReportManager::addReport(
+ std::optional<std::reference_wrapper<boost::asio::yield_context>> yield,
+ const std::string& reportName, const std::string& reportingType,
+ const bool emitsReadingsUpdate, const bool logToMetricReportsCollection,
+ std::chrono::milliseconds interval, const ReadingParameters& metricParams)
+{
+ if (reports.size() >= maxReports)
+ {
+ throw sdbusplus::exception::SdBusError(
+ static_cast<int>(std::errc::too_many_files_open),
+ "Reached maximal report count");
+ }
+
+ for (const auto& report : reports)
+ {
+ if (report->getName() == reportName)
+ {
+ throw sdbusplus::exception::SdBusError(
+ static_cast<int>(std::errc::file_exists), "Duplicate report");
+ }
+ }
+
+ if (interval < minInterval)
+ {
+ throw sdbusplus::exception::SdBusError(
+ static_cast<int>(std::errc::invalid_argument), "Invalid interval");
+ }
+
+ reports.emplace_back(
+ reportFactory->make(yield, reportName, reportingType,
+ emitsReadingsUpdate, logToMetricReportsCollection,
+ interval, metricParams, *this, *reportStorage));
+ return reports.back();
+}
+
+void ReportManager::loadFromPersistent()
+{
+ std::vector<interfaces::JsonStorage::FilePath> paths =
+ reportStorage->list();
+
+ for (const auto& path : paths)
+ {
+ std::optional<nlohmann::json> data = reportStorage->load(path);
+ try
+ {
+ size_t version = data->at("Version").get<size_t>();
+ if (version != Report::reportVersion)
+ {
+ throw std::logic_error("Invalid version");
+ }
+ std::string& name = data->at("Name").get_ref<std::string&>();
+ std::string& reportingType =
+ data->at("ReportingType").get_ref<std::string&>();
+ bool emitsReadingsSignal =
+ data->at("EmitsReadingsUpdate").get<bool>();
+ bool logToMetricReportsCollection =
+ data->at("LogToMetricReportsCollection").get<bool>();
+ uint64_t interval = data->at("Interval").get<uint64_t>();
+ ReadingParameters readingParameters;
+ for (auto& item : data->at("ReadingParameters"))
+ {
+ readingParameters.emplace_back(
+ LabeledReadingParameter::from_json(item));
+ }
+
+ addReport(std::nullopt, name, reportingType, emitsReadingsSignal,
+ logToMetricReportsCollection,
+ std::chrono::milliseconds(interval), readingParameters);
+ }
+ catch (const std::exception& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Failed to load report from storage",
+ phosphor::logging::entry(
+ "filename=",
+ static_cast<std::filesystem::path>(path).c_str()),
+ phosphor::logging::entry("msg=", e.what()));
+ reportStorage->remove(path);
+ }
+ }
+}
diff --git a/src/report_manager.hpp b/src/report_manager.hpp
index 2427b76..a26b59e 100644
--- a/src/report_manager.hpp
+++ b/src/report_manager.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "interfaces/json_storage.hpp"
#include "interfaces/report.hpp"
#include "interfaces/report_factory.hpp"
#include "interfaces/report_manager.hpp"
@@ -15,6 +16,7 @@
public:
ReportManager(
std::unique_ptr<interfaces::ReportFactory> reportFactory,
+ std::unique_ptr<interfaces::JsonStorage> reportStorage,
const std::shared_ptr<sdbusplus::asio::object_server>& objServer);
~ReportManager() = default;
@@ -28,10 +30,19 @@
private:
std::unique_ptr<interfaces::ReportFactory> reportFactory;
+ std::unique_ptr<interfaces::JsonStorage> reportStorage;
std::shared_ptr<sdbusplus::asio::object_server> objServer;
std::unique_ptr<sdbusplus::asio::dbus_interface> reportManagerIface;
std::vector<std::unique_ptr<interfaces::Report>> reports;
+ std::unique_ptr<interfaces::Report>& addReport(
+ std::optional<std::reference_wrapper<boost::asio::yield_context>> yield,
+ const std::string& reportName, const std::string& reportingType,
+ const bool emitsReadingsUpdate, const bool logToMetricReportsCollection,
+ std::chrono::milliseconds interval,
+ const ReadingParameters& metricParams);
+ void loadFromPersistent();
+
public:
static constexpr uint32_t maxReports{20};
static constexpr std::chrono::milliseconds minInterval{1000};
diff --git a/src/telemetry.hpp b/src/telemetry.hpp
index bfcc99a..4a6a6b0 100644
--- a/src/telemetry.hpp
+++ b/src/telemetry.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "persistent_json_storage.hpp"
#include "report_factory.hpp"
#include "report_manager.hpp"
@@ -13,9 +14,11 @@
public:
Telemetry(std::shared_ptr<sdbusplus::asio::connection> bus) :
objServer(std::make_shared<sdbusplus::asio::object_server>(bus)),
- reportManager(
- std::make_unique<ReportFactory>(bus->get_io_context(), objServer),
- objServer)
+ reportManager(std::make_unique<ReportFactory>(bus, objServer),
+ std::make_unique<PersistentJsonStorage>(
+ interfaces::JsonStorage::DirectoryPath(
+ "/var/lib/telemetry/Reports")),
+ objServer)
{}
private:
diff --git a/src/utils/labeled_tuple.hpp b/src/utils/labeled_tuple.hpp
new file mode 100644
index 0000000..7e75322
--- /dev/null
+++ b/src/utils/labeled_tuple.hpp
@@ -0,0 +1,117 @@
+#pragma once
+
+#include <nlohmann/json.hpp>
+#include <sdbusplus/message/types.hpp>
+
+namespace utils
+{
+
+inline void from_json(const nlohmann::json& j,
+ sdbusplus::message::object_path& o)
+{
+ o = j.get<std::string>();
+}
+
+inline void from_json(const nlohmann::json& j,
+ std::vector<sdbusplus::message::object_path>& o)
+{
+ o.clear();
+ for (const nlohmann::json& item : j)
+ {
+ o.emplace_back(item.get<std::string>());
+ }
+}
+
+namespace detail
+{
+
+template <class T>
+struct has_utils_from_json
+{
+ template <class U>
+ static U& ref();
+
+ template <class U>
+ static std::true_type check(
+ decltype(utils::from_json(ref<const nlohmann::json>(), ref<U>()))*);
+
+ template <class>
+ static std::false_type check(...);
+
+ static constexpr bool value =
+ decltype(check<std::decay_t<T>>(nullptr))::value;
+};
+
+template <class T>
+constexpr bool has_utils_from_json_v = has_utils_from_json<T>::value;
+
+} // namespace detail
+
+template <class, class...>
+struct LabeledTuple;
+
+template <class... Args, class... Labels>
+struct LabeledTuple<std::tuple<Args...>, Labels...>
+{
+ LabeledTuple() = delete;
+
+ static_assert(sizeof...(Args) == sizeof...(Labels));
+
+ static nlohmann::json to_json(const std::tuple<Args...>& tuple)
+ {
+ nlohmann::json j;
+ to_json_all(j, tuple, std::make_index_sequence<sizeof...(Args)>());
+ return j;
+ }
+
+ static std::tuple<Args...> from_json(const nlohmann::json& j)
+ {
+ std::tuple<Args...> value;
+ from_json_all(j, value, std::make_index_sequence<sizeof...(Args)>());
+ return value;
+ }
+
+ private:
+ template <size_t... Idx>
+ static void to_json_all(nlohmann::json& j, const std::tuple<Args...>& tuple,
+ std::index_sequence<Idx...>)
+ {
+ (to_json_item<Idx>(j, tuple), ...);
+ }
+
+ template <size_t Idx>
+ static void to_json_item(nlohmann::json& j,
+ const std::tuple<Args...>& tuple)
+ {
+ using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
+ j[Label::str()] = std::get<Idx>(tuple);
+ }
+
+ template <size_t... Idx>
+ static void from_json_all(const nlohmann::json& j,
+ std::tuple<Args...>& value,
+ std::index_sequence<Idx...>)
+ {
+ (from_json_item<Idx>(j, value), ...);
+ }
+
+ template <size_t Idx>
+ static void from_json_item(const nlohmann::json& j,
+ std::tuple<Args...>& value)
+ {
+ using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
+ using T = std::tuple_element_t<Idx, std::tuple<Args...>>;
+ const nlohmann::json& item = j.at(Label::str());
+ if constexpr (detail::has_utils_from_json_v<T>)
+ {
+ T& v = std::get<Idx>(value);
+ utils::from_json(item, v);
+ }
+ else
+ {
+ std::get<Idx>(value) = item.get<T>();
+ }
+ }
+};
+
+} // namespace utils
diff --git a/src/utils/transform.hpp b/src/utils/transform.hpp
new file mode 100644
index 0000000..b354b0f
--- /dev/null
+++ b/src/utils/transform.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <algorithm>
+#include <iterator>
+
+namespace utils
+{
+namespace detail
+{
+
+template <class T>
+struct has_member_reserve
+{
+ template <class U>
+ static U& ref();
+
+ template <class U>
+ static std::true_type check(decltype(ref<U>().reserve(size_t{}))*);
+
+ template <class>
+ static std::false_type check(...);
+
+ static constexpr bool value =
+ decltype(check<std::decay_t<T>>(nullptr))::value;
+};
+
+template <class T>
+constexpr bool has_member_reserve_v = has_member_reserve<T>::value;
+
+} // namespace detail
+
+template <template <class, class...> class Container, class T, class... Args,
+ class F>
+auto transform(const Container<T, Args...>& container, F&& functor)
+{
+ using R = decltype(functor(*container.begin()));
+
+ Container<R> result;
+ if constexpr (detail::has_member_reserve_v<Container<T, Args...>>)
+ {
+ result.reserve(container.size());
+ }
+ std::transform(container.begin(), container.end(),
+ std::inserter(result, result.end()),
+ std::forward<F>(functor));
+ return result;
+}
+
+} // namespace utils
diff --git a/src/utils/tstring.hpp b/src/utils/tstring.hpp
new file mode 100644
index 0000000..7fcddb3
--- /dev/null
+++ b/src/utils/tstring.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <array>
+#include <string>
+
+namespace utils
+{
+namespace literals
+{
+
+constexpr char id[] = "id";
+constexpr char sensorPaths[] = "sensorPaths";
+constexpr char operationType[] = "operationType";
+constexpr char metricMetadata[] = "metricMetadata";
+
+} // namespace literals
+
+template <const char* const V>
+struct Label
+{
+ static std::string str()
+ {
+ return V;
+ }
+};
+
+namespace tstring
+{
+
+using Id = utils::Label<utils::literals::id>;
+using SensorPaths = utils::Label<utils::literals::sensorPaths>;
+using OperationType = utils::Label<utils::literals::operationType>;
+using MetricMetadata = utils::Label<utils::literals::metricMetadata>;
+
+} // namespace tstring
+} // namespace utils