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