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