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