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 <limits> 10 #include <numeric> 11 12 Report::Report(boost::asio::io_context& ioc, 13 const std::shared_ptr<sdbusplus::asio::object_server>& objServer, 14 const std::string& reportName, 15 const ReportingType reportingTypeIn, 16 const bool emitsReadingsUpdateIn, 17 const bool logToMetricReportsCollectionIn, 18 const Milliseconds intervalIn, const uint64_t appendLimitIn, 19 const ReportUpdates reportUpdatesIn, 20 interfaces::ReportManager& reportManager, 21 interfaces::JsonStorage& reportStorageIn, 22 std::vector<std::shared_ptr<interfaces::Metric>> metricsIn, 23 const bool enabledIn) : 24 name(reportName), 25 path(reportDir + name), reportingType(reportingTypeIn), 26 interval(intervalIn), emitsReadingsUpdate(emitsReadingsUpdateIn), 27 logToMetricReportsCollection(logToMetricReportsCollectionIn), 28 sensorCount(getSensorCount(metricsIn)), 29 appendLimit(deduceAppendLimit(appendLimitIn)), 30 reportUpdates(reportUpdatesIn), 31 readingsBuffer(deduceBufferSize(reportUpdates, reportingType)), 32 objServer(objServer), metrics(std::move(metricsIn)), timer(ioc), 33 fileName(std::to_string(std::hash<std::string>{}(name))), 34 reportStorage(reportStorageIn), enabled(enabledIn) 35 { 36 readingParameters = 37 toReadingParameters(utils::transform(metrics, [](const auto& metric) { 38 return metric->dumpConfiguration(); 39 })); 40 41 readingParametersPastVersion = 42 utils::transform(readingParameters, [](const auto& item) { 43 return ReadingParametersPastVersion::value_type( 44 std::get<0>(item).front(), std::get<1>(item), std::get<2>(item), 45 std::get<3>(item)); 46 }); 47 48 deleteIface = objServer->add_unique_interface( 49 path, deleteIfaceName, [this, &ioc, &reportManager](auto& dbusIface) { 50 dbusIface.register_method("Delete", [this, &ioc, &reportManager] { 51 if (persistency) 52 { 53 reportStorage.remove(fileName); 54 } 55 boost::asio::post(ioc, [this, &reportManager] { 56 reportManager.removeReport(this); 57 }); 58 }); 59 }); 60 61 persistency = storeConfiguration(); 62 reportIface = makeReportInterface(); 63 64 if (reportingType == ReportingType::Periodic) 65 { 66 scheduleTimer(interval); 67 } 68 69 if (enabled) 70 { 71 for (auto& metric : this->metrics) 72 { 73 metric->initialize(); 74 } 75 } 76 } 77 78 uint64_t Report::getSensorCount( 79 std::vector<std::shared_ptr<interfaces::Metric>>& metrics) 80 { 81 uint64_t sensorCount = 0; 82 for (auto& metric : metrics) 83 { 84 sensorCount += metric->sensorCount(); 85 } 86 return sensorCount; 87 } 88 89 uint64_t Report::deduceAppendLimit(const uint64_t appendLimitIn) const 90 { 91 if (appendLimitIn == std::numeric_limits<uint64_t>::max()) 92 { 93 return sensorCount; 94 } 95 else 96 { 97 return appendLimitIn; 98 } 99 } 100 101 uint64_t Report::deduceBufferSize(const ReportUpdates reportUpdatesIn, 102 const ReportingType reportingTypeIn) const 103 { 104 if (reportUpdatesIn == ReportUpdates::Overwrite || 105 reportingTypeIn == ReportingType::OnRequest) 106 { 107 return sensorCount; 108 } 109 else 110 { 111 return appendLimit; 112 } 113 } 114 115 void Report::setReportUpdates(const ReportUpdates newReportUpdates) 116 { 117 if (reportUpdates != newReportUpdates) 118 { 119 if (reportingType != ReportingType::OnRequest && 120 (reportUpdates == ReportUpdates::Overwrite || 121 newReportUpdates == ReportUpdates::Overwrite)) 122 { 123 readingsBuffer.clearAndResize( 124 deduceBufferSize(newReportUpdates, reportingType)); 125 } 126 reportUpdates = newReportUpdates; 127 } 128 } 129 130 std::unique_ptr<sdbusplus::asio::dbus_interface> Report::makeReportInterface() 131 { 132 auto dbusIface = objServer->add_unique_interface(path, reportIfaceName); 133 dbusIface->register_property_rw( 134 "Enabled", enabled, sdbusplus::vtable::property_::emits_change, 135 [this](bool newVal, const auto&) { 136 if (newVal != enabled) 137 { 138 if (true == newVal && ReportingType::Periodic == reportingType) 139 { 140 scheduleTimer(interval); 141 } 142 if (newVal) 143 { 144 for (auto& metric : metrics) 145 { 146 metric->initialize(); 147 } 148 } 149 else 150 { 151 for (auto& metric : metrics) 152 { 153 metric->deinitialize(); 154 } 155 } 156 157 enabled = newVal; 158 persistency = storeConfiguration(); 159 } 160 return true; 161 }, 162 [this](const auto&) { return enabled; }); 163 dbusIface->register_property_rw( 164 "Interval", interval.count(), 165 sdbusplus::vtable::property_::emits_change, 166 [this](uint64_t newVal, auto&) { 167 if (Milliseconds newValT{newVal}; 168 newValT >= ReportManager::minInterval) 169 { 170 if (newValT != interval) 171 { 172 interval = newValT; 173 persistency = storeConfiguration(); 174 } 175 return true; 176 } 177 return false; 178 }, 179 [this](const auto&) { return interval.count(); }); 180 dbusIface->register_property_rw( 181 "Persistency", persistency, sdbusplus::vtable::property_::emits_change, 182 [this](bool newVal, const auto&) { 183 if (newVal == persistency) 184 { 185 return true; 186 } 187 if (newVal) 188 { 189 persistency = storeConfiguration(); 190 } 191 else 192 { 193 reportStorage.remove(fileName); 194 persistency = false; 195 } 196 return true; 197 }, 198 [this](const auto&) { return persistency; }); 199 200 auto readingsFlag = sdbusplus::vtable::property_::none; 201 if (emitsReadingsUpdate) 202 { 203 readingsFlag = sdbusplus::vtable::property_::emits_change; 204 } 205 dbusIface->register_property_r("Readings", readings, readingsFlag, 206 [this](const auto&) { return readings; }); 207 dbusIface->register_property_r( 208 "ReportingType", std::string(), sdbusplus::vtable::property_::const_, 209 [this](const auto&) { return reportingTypeToString(reportingType); }); 210 dbusIface->register_property_r( 211 "ReadingParameters", readingParametersPastVersion, 212 sdbusplus::vtable::property_::const_, 213 [this](const auto&) { return readingParametersPastVersion; }); 214 dbusIface->register_property_r( 215 "ReadingParametersFutureVersion", readingParameters, 216 sdbusplus::vtable::property_::const_, 217 [this](const auto&) { return readingParameters; }); 218 dbusIface->register_property_r( 219 "EmitsReadingsUpdate", emitsReadingsUpdate, 220 sdbusplus::vtable::property_::const_, 221 [this](const auto&) { return emitsReadingsUpdate; }); 222 dbusIface->register_property_r( 223 "LogToMetricReportsCollection", logToMetricReportsCollection, 224 sdbusplus::vtable::property_::const_, 225 [this](const auto&) { return logToMetricReportsCollection; }); 226 dbusIface->register_property_r("AppendLimit", appendLimit, 227 sdbusplus::vtable::property_::emits_change, 228 [this](const auto&) { return appendLimit; }); 229 dbusIface->register_property_rw( 230 "ReportUpdates", std::string(), 231 sdbusplus::vtable::property_::emits_change, 232 [this](auto newVal, auto& oldVal) { 233 ReportManager::verifyReportUpdates(newVal); 234 setReportUpdates(stringToReportUpdates(newVal)); 235 oldVal = newVal; 236 return true; 237 }, 238 [this](const auto&) { return reportUpdatesToString(reportUpdates); }); 239 dbusIface->register_method("Update", [this] { 240 if (reportingType == ReportingType::OnRequest) 241 { 242 updateReadings(); 243 } 244 }); 245 constexpr bool skipPropertiesChangedSignal = true; 246 dbusIface->initialize(skipPropertiesChangedSignal); 247 return dbusIface; 248 } 249 250 void Report::timerProc(boost::system::error_code ec, Report& self) 251 { 252 if (ec) 253 { 254 return; 255 } 256 257 self.updateReadings(); 258 self.scheduleTimer(self.interval); 259 } 260 261 void Report::scheduleTimer(Milliseconds timerInterval) 262 { 263 timer.expires_after(timerInterval); 264 timer.async_wait( 265 [this](boost::system::error_code ec) { timerProc(ec, *this); }); 266 } 267 268 void Report::updateReadings() 269 { 270 if (!enabled) 271 { 272 return; 273 } 274 275 if (reportUpdates == ReportUpdates::Overwrite || 276 reportingType == ReportingType::OnRequest) 277 { 278 readingsBuffer.clear(); 279 } 280 281 for (const auto& metric : metrics) 282 { 283 for (const auto& [id, metadata, value, timestamp] : 284 metric->getReadings()) 285 { 286 if (reportUpdates == ReportUpdates::AppendStopsWhenFull && 287 readingsBuffer.isFull()) 288 { 289 enabled = false; 290 for (auto& metric : metrics) 291 { 292 metric->deinitialize(); 293 } 294 break; 295 } 296 readingsBuffer.emplace(id, metadata, value, timestamp); 297 } 298 } 299 300 readings = {std::time(0), std::vector<ReadingData>(readingsBuffer.begin(), 301 readingsBuffer.end())}; 302 303 reportIface->signal_property("Readings"); 304 } 305 306 bool Report::storeConfiguration() const 307 { 308 try 309 { 310 nlohmann::json data; 311 312 data["Enabled"] = enabled; 313 data["Version"] = reportVersion; 314 data["Name"] = name; 315 data["ReportingType"] = reportingTypeToString(reportingType); 316 data["EmitsReadingsUpdate"] = emitsReadingsUpdate; 317 data["LogToMetricReportsCollection"] = logToMetricReportsCollection; 318 data["Interval"] = interval.count(); 319 data["AppendLimit"] = appendLimit; 320 data["ReportUpdates"] = reportUpdatesToString(reportUpdates); 321 data["ReadingParameters"] = 322 utils::transform(metrics, [](const auto& metric) { 323 return metric->dumpConfiguration(); 324 }); 325 326 reportStorage.store(fileName, data); 327 } 328 catch (const std::exception& e) 329 { 330 phosphor::logging::log<phosphor::logging::level::ERR>( 331 "Failed to store a report in storage", 332 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what())); 333 return false; 334 } 335 336 return true; 337 } 338