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.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;
+}