1 #include "report.hpp" 2 3 #include "report_manager.hpp" 4 #include "utils/transform.hpp" 5 6 #include <phosphor-logging/log.hpp> 7 #include <sdbusplus/vtable.hpp> 8 9 #include <numeric> 10 11 Report::Report(boost::asio::io_context& ioc, 12 const std::shared_ptr<sdbusplus::asio::object_server>& objServer, 13 const std::string& reportName, 14 const std::string& reportingTypeIn, 15 const bool emitsReadingsUpdateIn, 16 const bool logToMetricReportsCollectionIn, 17 const std::chrono::milliseconds intervalIn, 18 const ReadingParameters& readingParametersIn, 19 interfaces::ReportManager& reportManager, 20 interfaces::JsonStorage& reportStorageIn, 21 std::vector<std::shared_ptr<interfaces::Metric>> metrics) : 22 name(reportName), 23 path(reportDir + name), reportingType(reportingTypeIn), 24 interval(intervalIn), emitsReadingsUpdate(emitsReadingsUpdateIn), 25 logToMetricReportsCollection(logToMetricReportsCollectionIn), 26 readingParameters(readingParametersIn), objServer(objServer), 27 metrics(std::move(metrics)), timer(ioc), 28 fileName(std::to_string(std::hash<std::string>{}(name))), 29 reportStorage(reportStorageIn) 30 { 31 deleteIface = objServer->add_unique_interface( 32 path, deleteIfaceName, [this, &ioc, &reportManager](auto& dbusIface) { 33 dbusIface.register_method("Delete", [this, &ioc, &reportManager] { 34 if (persistency) 35 { 36 reportStorage.remove(fileName); 37 } 38 boost::asio::post(ioc, [this, &reportManager] { 39 reportManager.removeReport(this); 40 }); 41 }); 42 }); 43 44 persistency = storeConfiguration(); 45 reportIface = makeReportInterface(); 46 47 if (reportingType == "Periodic") 48 { 49 scheduleTimer(interval); 50 } 51 52 for (auto& metric : this->metrics) 53 { 54 metric->initialize(); 55 } 56 } 57 58 std::unique_ptr<sdbusplus::asio::dbus_interface> Report::makeReportInterface() 59 { 60 auto dbusIface = objServer->add_unique_interface(path, reportIfaceName); 61 dbusIface->register_property_rw( 62 "Interval", static_cast<uint64_t>(interval.count()), 63 sdbusplus::vtable::property_::emits_change, 64 [this](uint64_t newVal, auto&) { 65 std::chrono::milliseconds newValT(newVal); 66 if (newValT < ReportManager::minInterval) 67 { 68 return false; 69 } 70 interval = newValT; 71 return true; 72 }, 73 [this](const auto&) { 74 return static_cast<uint64_t>(interval.count()); 75 }); 76 dbusIface->register_property_rw( 77 "Persistency", persistency, sdbusplus::vtable::property_::emits_change, 78 [this](bool newVal, const auto&) { 79 if (newVal == persistency) 80 { 81 return true; 82 } 83 if (newVal) 84 { 85 persistency = storeConfiguration(); 86 } 87 else 88 { 89 reportStorage.remove(fileName); 90 persistency = false; 91 } 92 return true; 93 }, 94 [this](const auto&) { return persistency; }); 95 96 auto readingsFlag = sdbusplus::vtable::property_::none; 97 if (emitsReadingsUpdate) 98 { 99 readingsFlag = sdbusplus::vtable::property_::emits_change; 100 } 101 dbusIface->register_property_r("Readings", readings, readingsFlag, 102 [this](const auto&) { return readings; }); 103 dbusIface->register_property_r( 104 "ReportingType", reportingType, sdbusplus::vtable::property_::const_, 105 [this](const auto&) { return reportingType; }); 106 dbusIface->register_property_r( 107 "ReadingParameters", readingParameters, 108 sdbusplus::vtable::property_::const_, 109 [this](const auto&) { return readingParameters; }); 110 dbusIface->register_property_r( 111 "EmitsReadingsUpdate", emitsReadingsUpdate, 112 sdbusplus::vtable::property_::const_, 113 [this](const auto&) { return emitsReadingsUpdate; }); 114 dbusIface->register_property_r( 115 "LogToMetricReportsCollection", logToMetricReportsCollection, 116 sdbusplus::vtable::property_::const_, 117 [this](const auto&) { return logToMetricReportsCollection; }); 118 dbusIface->register_method("Update", [this] { 119 if (reportingType == "OnRequest") 120 { 121 updateReadings(); 122 } 123 }); 124 constexpr bool skipPropertiesChangedSignal = true; 125 dbusIface->initialize(skipPropertiesChangedSignal); 126 return dbusIface; 127 } 128 129 void Report::timerProc(boost::system::error_code ec, Report& self) 130 { 131 if (ec) 132 { 133 return; 134 } 135 136 self.updateReadings(); 137 self.scheduleTimer(self.interval); 138 } 139 140 void Report::scheduleTimer(std::chrono::milliseconds timerInterval) 141 { 142 timer.expires_after(timerInterval); 143 timer.async_wait( 144 [this](boost::system::error_code ec) { timerProc(ec, *this); }); 145 } 146 147 void Report::updateReadings() 148 { 149 std::tuple_element_t<1, Readings> readingsCache(metrics.size()); 150 151 std::transform(std::begin(metrics), std::end(metrics), 152 std::begin(readingsCache), [](const auto& metric) { 153 const auto& reading = metric->getReading(); 154 return std::make_tuple(reading.id, reading.metadata, 155 reading.value, reading.timestamp); 156 }); 157 158 std::get<0>(readings) = std::time(0); 159 std::get<1>(readings) = std::move(readingsCache); 160 161 reportIface->signal_property("Readings"); 162 } 163 164 bool Report::storeConfiguration() const 165 { 166 try 167 { 168 nlohmann::json data; 169 170 data["Version"] = reportVersion; 171 data["Name"] = name; 172 data["ReportingType"] = reportingType; 173 data["EmitsReadingsUpdate"] = emitsReadingsUpdate; 174 data["LogToMetricReportsCollection"] = logToMetricReportsCollection; 175 data["Interval"] = interval.count(); 176 data["ReadingParameters"] = 177 utils::transform(metrics, [](const auto& metric) { 178 return metric->dumpConfiguration(); 179 }); 180 181 reportStorage.store(fileName, data); 182 } 183 catch (const std::exception& e) 184 { 185 phosphor::logging::log<phosphor::logging::level::ERR>( 186 "Failed to store a report in storage", 187 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what())); 188 return false; 189 } 190 191 return true; 192 } 193