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