1 #include "report_manager.hpp" 2 3 #include "report.hpp" 4 #include "types/types.hpp" 5 #include "utils/conversion.hpp" 6 #include "utils/transform.hpp" 7 8 #include <phosphor-logging/log.hpp> 9 #include <sdbusplus/exception.hpp> 10 11 #include <stdexcept> 12 #include <system_error> 13 14 ReadingParameters 15 convertToReadingParameters(ReadingParametersPastVersion params) 16 { 17 return utils::transform(params, [](const auto& param) { 18 using namespace std::chrono_literals; 19 20 return ReadingParameters::value_type( 21 std::get<0>(param), std::get<1>(param), std::get<2>(param), 22 std::get<3>(param), utils::enumToString(CollectionTimeScope::point), 23 0u); 24 }); 25 } 26 27 ReportManager::ReportManager( 28 std::unique_ptr<interfaces::ReportFactory> reportFactoryIn, 29 std::unique_ptr<interfaces::JsonStorage> reportStorageIn, 30 const std::shared_ptr<sdbusplus::asio::object_server>& objServerIn) : 31 reportFactory(std::move(reportFactoryIn)), 32 reportStorage(std::move(reportStorageIn)), objServer(objServerIn) 33 { 34 reports.reserve(maxReports); 35 36 loadFromPersistent(); 37 38 reportManagerIface = objServer->add_unique_interface( 39 reportManagerPath, reportManagerIfaceName, [this](auto& dbusIface) { 40 dbusIface.register_property_r( 41 "MaxReports", size_t{}, sdbusplus::vtable::property_::const_, 42 [](const auto&) { return maxReports; }); 43 dbusIface.register_property_r( 44 "MinInterval", uint64_t{}, sdbusplus::vtable::property_::const_, 45 [](const auto&) -> uint64_t { return minInterval.count(); }); 46 47 dbusIface.register_method( 48 "AddReport", [this](boost::asio::yield_context& yield, 49 const std::string& reportName, 50 const std::string& reportingType, 51 const bool emitsReadingsUpdate, 52 const bool logToMetricReportsCollection, 53 const uint64_t interval, 54 ReadingParametersPastVersion metricParams) { 55 return addReport(yield, reportName, reportingType, 56 emitsReadingsUpdate, 57 logToMetricReportsCollection, 58 std::chrono::milliseconds(interval), 59 convertToReadingParameters( 60 std::move(metricParams))) 61 .getPath(); 62 }); 63 64 dbusIface.register_method( 65 "AddReportFutureVersion", 66 [this](boost::asio::yield_context& yield, 67 const std::string& reportName, 68 const std::string& reportingType, 69 const bool emitsReadingsUpdate, 70 const bool logToMetricReportsCollection, 71 const uint64_t interval, 72 ReadingParameters metricParams) { 73 return addReport(yield, reportName, reportingType, 74 emitsReadingsUpdate, 75 logToMetricReportsCollection, 76 std::chrono::milliseconds(interval), 77 std::move(metricParams)) 78 .getPath(); 79 }); 80 }); 81 } 82 83 void ReportManager::removeReport(const interfaces::Report* report) 84 { 85 reports.erase( 86 std::remove_if(reports.begin(), reports.end(), 87 [report](const auto& x) { return report == x.get(); }), 88 reports.end()); 89 } 90 91 void ReportManager::verifyAddReport(const std::string& reportName, 92 const std::string& reportingType, 93 std::chrono::milliseconds interval, 94 const ReadingParameters& readingParams) 95 { 96 if (reports.size() >= maxReports) 97 { 98 throw sdbusplus::exception::SdBusError( 99 static_cast<int>(std::errc::too_many_files_open), 100 "Reached maximal report count"); 101 } 102 103 for (const auto& report : reports) 104 { 105 if (report->getName() == reportName) 106 { 107 throw sdbusplus::exception::SdBusError( 108 static_cast<int>(std::errc::file_exists), "Duplicate report"); 109 } 110 } 111 112 auto found = std::find(supportedReportingType.begin(), 113 supportedReportingType.end(), reportingType); 114 if (found == supportedReportingType.end()) 115 { 116 throw sdbusplus::exception::SdBusError( 117 static_cast<int>(std::errc::invalid_argument), 118 "Invalid reportingType"); 119 } 120 121 if (reportingType == "Periodic" && interval < minInterval) 122 { 123 throw sdbusplus::exception::SdBusError( 124 static_cast<int>(std::errc::invalid_argument), "Invalid interval"); 125 } 126 127 if (readingParams.size() > maxReadingParams) 128 129 { 130 throw sdbusplus::exception::SdBusError( 131 static_cast<int>(std::errc::argument_list_too_long), 132 "Too many reading parameters"); 133 } 134 135 try 136 { 137 for (const auto& item : readingParams) 138 { 139 utils::stringToOperationType(std::get<1>(item)); 140 } 141 } 142 catch (const std::exception& e) 143 { 144 throw sdbusplus::exception::SdBusError( 145 static_cast<int>(std::errc::invalid_argument), e.what()); 146 } 147 } 148 149 interfaces::Report& ReportManager::addReport( 150 boost::asio::yield_context& yield, const std::string& reportName, 151 const std::string& reportingType, const bool emitsReadingsUpdate, 152 const bool logToMetricReportsCollection, std::chrono::milliseconds interval, 153 ReadingParameters metricParams) 154 { 155 verifyAddReport(reportName, reportingType, interval, metricParams); 156 157 reports.emplace_back(reportFactory->make( 158 yield, reportName, reportingType, emitsReadingsUpdate, 159 logToMetricReportsCollection, interval, std::move(metricParams), *this, 160 *reportStorage)); 161 return *reports.back(); 162 } 163 164 interfaces::Report& ReportManager::addReport( 165 const std::string& reportName, const std::string& reportingType, 166 const bool emitsReadingsUpdate, const bool logToMetricReportsCollection, 167 std::chrono::milliseconds interval, 168 std::vector<LabeledMetricParameters> labeledMetricParams) 169 { 170 auto metricParams = utils::transform( 171 labeledMetricParams, [](const LabeledMetricParameters& param) { 172 using namespace utils::tstring; 173 174 return ReadingParameters::value_type( 175 sdbusplus::message::object_path( 176 param.at_index<0>().at_label<Path>()), 177 utils::enumToString(param.at_index<1>()), param.at_index<2>(), 178 param.at_index<3>(), utils::enumToString(param.at_index<4>()), 179 param.at_index<5>().t.count()); 180 }); 181 182 verifyAddReport(reportName, reportingType, interval, metricParams); 183 184 reports.emplace_back(reportFactory->make( 185 reportName, reportingType, emitsReadingsUpdate, 186 logToMetricReportsCollection, interval, std::move(metricParams), *this, 187 *reportStorage, labeledMetricParams)); 188 return *reports.back(); 189 } 190 191 void ReportManager::loadFromPersistent() 192 { 193 std::vector<interfaces::JsonStorage::FilePath> paths = 194 reportStorage->list(); 195 196 for (const auto& path : paths) 197 { 198 std::optional<nlohmann::json> data = reportStorage->load(path); 199 try 200 { 201 size_t version = data->at("Version").get<size_t>(); 202 if (version != Report::reportVersion) 203 { 204 throw std::logic_error("Invalid version"); 205 } 206 std::string& name = data->at("Name").get_ref<std::string&>(); 207 std::string& reportingType = 208 data->at("ReportingType").get_ref<std::string&>(); 209 bool emitsReadingsSignal = 210 data->at("EmitsReadingsUpdate").get<bool>(); 211 bool logToMetricReportsCollection = 212 data->at("LogToMetricReportsCollection").get<bool>(); 213 uint64_t interval = data->at("Interval").get<uint64_t>(); 214 auto readingParameters = 215 data->at("ReadingParameters") 216 .get<std::vector<LabeledMetricParameters>>(); 217 218 addReport(name, reportingType, emitsReadingsSignal, 219 logToMetricReportsCollection, 220 std::chrono::milliseconds(interval), 221 std::move(readingParameters)); 222 } 223 catch (const std::exception& e) 224 { 225 phosphor::logging::log<phosphor::logging::level::ERR>( 226 "Failed to load report from storage", 227 phosphor::logging::entry( 228 "FILENAME=%s", 229 static_cast<std::filesystem::path>(path).c_str()), 230 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what())); 231 reportStorage->remove(path); 232 } 233 } 234 } 235 236 void ReportManager::updateReport(const std::string& name) 237 { 238 for (auto& report : reports) 239 { 240 if (report->getName() == name) 241 { 242 report->updateReadings(); 243 return; 244 } 245 } 246 } 247