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/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);
+        }
+    }
+}