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