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