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