1 #include "report_manager.hpp" 2 3 #include "report.hpp" 4 #include "types/report_types.hpp" 5 #include "utils/conversion.hpp" 6 #include "utils/generate_id.hpp" 7 #include "utils/transform.hpp" 8 9 #include <phosphor-logging/log.hpp> 10 #include <sdbusplus/exception.hpp> 11 12 #include <stdexcept> 13 #include <system_error> 14 15 ReadingParameters 16 convertToReadingParameters(ReadingParametersPastVersion params) 17 { 18 return utils::transform(params, [](const auto& param) { 19 using namespace std::chrono_literals; 20 21 const auto& [sensorPath, operationType, id, metadata] = param; 22 23 return ReadingParameters::value_type( 24 std::vector< 25 std::tuple<sdbusplus::message::object_path, std::string>>{ 26 {sensorPath, metadata}}, 27 operationType, id, utils::enumToString(CollectionTimeScope::point), 28 0u); 29 }); 30 } 31 32 ReportManager::ReportManager( 33 std::unique_ptr<interfaces::ReportFactory> reportFactoryIn, 34 std::unique_ptr<interfaces::JsonStorage> reportStorageIn, 35 const std::shared_ptr<sdbusplus::asio::object_server>& objServerIn) : 36 reportFactory(std::move(reportFactoryIn)), 37 reportStorage(std::move(reportStorageIn)), objServer(objServerIn) 38 { 39 reports.reserve(maxReports); 40 41 loadFromPersistent(); 42 43 reportManagerIface = objServer->add_unique_interface( 44 reportManagerPath, reportManagerIfaceName, [this](auto& dbusIface) { 45 dbusIface.register_property_r( 46 "MaxReports", size_t{}, sdbusplus::vtable::property_::const_, 47 [](const auto&) { return maxReports; }); 48 dbusIface.register_property_r( 49 "MaxReportIdLength", size_t{}, 50 sdbusplus::vtable::property_::const_, 51 [](const auto&) { return maxReportIdLength; }); 52 dbusIface.register_property_r( 53 "MinInterval", uint64_t{}, sdbusplus::vtable::property_::const_, 54 [](const auto&) -> uint64_t { return minInterval.count(); }); 55 56 dbusIface.register_method( 57 "AddReport", [this](boost::asio::yield_context& yield, 58 const std::string& reportId, 59 const std::string& reportingType, 60 const bool emitsReadingsUpdate, 61 const bool logToMetricReportsCollection, 62 const uint64_t interval, 63 ReadingParametersPastVersion metricParams) { 64 constexpr auto enabledDefault = true; 65 constexpr uint64_t appendLimitDefault = 0; 66 constexpr ReportUpdates reportUpdatesDefault = 67 ReportUpdates::overwrite; 68 69 std::vector<ReportAction> reportActions; 70 71 if (emitsReadingsUpdate) 72 { 73 reportActions.emplace_back( 74 ReportAction::emitsReadingsUpdate); 75 } 76 if (logToMetricReportsCollection) 77 { 78 reportActions.emplace_back( 79 ReportAction::logToMetricReportsCollection); 80 } 81 82 return addReport(yield, reportId, reportId, 83 utils::toReportingType(reportingType), 84 reportActions, Milliseconds(interval), 85 appendLimitDefault, reportUpdatesDefault, 86 convertToReadingParameters( 87 std::move(metricParams)), 88 enabledDefault) 89 .getPath(); 90 }); 91 92 dbusIface.register_method( 93 "AddReportFutureVersion", 94 [this]( 95 boost::asio::yield_context& yield, 96 const std::string& reportId, const std::string& reportName, 97 const std::string& reportingType, 98 const std::string& reportUpdates, 99 const uint64_t appendLimit, 100 const std::vector<std::string>& reportActions, 101 const uint64_t interval, ReadingParameters metricParams) { 102 constexpr auto enabledDefault = true; 103 return addReport(yield, reportId, reportName, 104 utils::toReportingType(reportingType), 105 utils::transform( 106 reportActions, 107 [](const auto& reportAction) { 108 return utils::toReportAction( 109 reportAction); 110 }), 111 Milliseconds(interval), appendLimit, 112 utils::toReportUpdates(reportUpdates), 113 std::move(metricParams), enabledDefault) 114 .getPath(); 115 }); 116 }); 117 } 118 119 void ReportManager::removeReport(const interfaces::Report* report) 120 { 121 reports.erase( 122 std::remove_if(reports.begin(), reports.end(), 123 [report](const auto& x) { return report == x.get(); }), 124 reports.end()); 125 } 126 127 void ReportManager::verifyReportIdLength(const std::string& reportId) 128 { 129 if (reportId.length() > maxReportIdLength) 130 { 131 throw sdbusplus::exception::SdBusError( 132 static_cast<int>(std::errc::invalid_argument), 133 "Report id exceeds maximum length"); 134 } 135 } 136 137 void ReportManager::verifyAddReport( 138 const std::string& reportId, const std::string& reportName, 139 const ReportingType reportingType, Milliseconds interval, 140 const ReportUpdates reportUpdates, 141 const std::vector<LabeledMetricParameters>& readingParams) 142 { 143 if (reportingType == ReportingType::onChange) 144 { 145 throw sdbusplus::exception::SdBusError( 146 static_cast<int>(std::errc::invalid_argument), 147 "Invalid reportingType"); 148 } 149 150 if (reports.size() >= maxReports) 151 { 152 throw sdbusplus::exception::SdBusError( 153 static_cast<int>(std::errc::too_many_files_open), 154 "Reached maximal report count"); 155 } 156 157 verifyReportIdLength(reportId); 158 utils::verifyIdCharacters(reportId); 159 160 for (const auto& report : reports) 161 { 162 if (report->getId() == reportId) 163 { 164 throw sdbusplus::exception::SdBusError( 165 static_cast<int>(std::errc::file_exists), "Duplicate report"); 166 } 167 } 168 169 verifyReportUpdates(reportUpdates); 170 171 if (reportingType == ReportingType::periodic && interval < minInterval) 172 { 173 throw sdbusplus::exception::SdBusError( 174 static_cast<int>(std::errc::invalid_argument), "Invalid interval"); 175 } 176 177 if (readingParams.size() > maxReadingParams) 178 179 { 180 throw sdbusplus::exception::SdBusError( 181 static_cast<int>(std::errc::argument_list_too_long), 182 "Too many reading parameters"); 183 } 184 185 try 186 { 187 namespace ts = utils::tstring; 188 189 for (const LabeledMetricParameters& item : readingParams) 190 { 191 utils::toOperationType( 192 utils::toUnderlying(item.at_label<ts::OperationType>())); 193 } 194 } 195 catch (const std::exception& e) 196 { 197 throw sdbusplus::exception::SdBusError( 198 static_cast<int>(std::errc::invalid_argument), e.what()); 199 } 200 } 201 202 interfaces::Report& ReportManager::addReport( 203 boost::asio::yield_context& yield, const std::string& reportId, 204 const std::string& reportName, const ReportingType reportingType, 205 const std::vector<ReportAction>& reportActions, Milliseconds interval, 206 const uint64_t appendLimit, const ReportUpdates reportUpdates, 207 ReadingParameters metricParams, const bool enabled) 208 { 209 auto labeledMetricParams = 210 reportFactory->convertMetricParams(yield, metricParams); 211 212 return addReport(reportId, reportName, reportingType, reportActions, 213 interval, appendLimit, reportUpdates, 214 std::move(labeledMetricParams), enabled); 215 } 216 217 interfaces::Report& ReportManager::addReport( 218 const std::string& reportId, const std::string& reportName, 219 const ReportingType reportingType, 220 const std::vector<ReportAction>& reportActions, Milliseconds interval, 221 const uint64_t appendLimit, const ReportUpdates reportUpdates, 222 std::vector<LabeledMetricParameters> labeledMetricParams, 223 const bool enabled) 224 { 225 std::string name = reportName; 226 if (name.empty()) 227 { 228 name = "Report"; 229 } 230 231 const auto existingReportIds = utils::transform( 232 reports, [](const auto& report) { return report->getId(); }); 233 234 std::string id = 235 utils::generateId(reportId, name, existingReportIds, maxReportIdLength); 236 237 verifyAddReport(id, name, reportingType, interval, reportUpdates, 238 labeledMetricParams); 239 240 reports.emplace_back(reportFactory->make( 241 id, name, reportingType, reportActions, interval, appendLimit, 242 reportUpdates, *this, *reportStorage, labeledMetricParams, enabled)); 243 return *reports.back(); 244 } 245 246 void ReportManager::loadFromPersistent() 247 { 248 std::vector<interfaces::JsonStorage::FilePath> paths = 249 reportStorage->list(); 250 251 for (const auto& path : paths) 252 { 253 std::optional<nlohmann::json> data = reportStorage->load(path); 254 try 255 { 256 size_t version = data->at("Version").get<size_t>(); 257 if (version != Report::reportVersion) 258 { 259 throw std::logic_error("Invalid version"); 260 } 261 bool enabled = data->at("Enabled").get<bool>(); 262 std::string& id = data->at("Id").get_ref<std::string&>(); 263 std::string& name = data->at("Name").get_ref<std::string&>(); 264 265 uint32_t reportingType = data->at("ReportingType").get<uint32_t>(); 266 std::vector<ReportAction> reportActions = utils::transform( 267 data->at("ReportActions").get<std::vector<uint32_t>>(), 268 [](const auto reportAction) { 269 return utils::toReportAction(reportAction); 270 }); 271 uint64_t interval = data->at("Interval").get<uint64_t>(); 272 uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>(); 273 uint32_t reportUpdates = data->at("ReportUpdates").get<uint32_t>(); 274 auto readingParameters = 275 data->at("ReadingParameters") 276 .get<std::vector<LabeledMetricParameters>>(); 277 278 addReport(id, name, utils::toReportingType(reportingType), 279 reportActions, Milliseconds(interval), appendLimit, 280 utils::toReportUpdates(reportUpdates), 281 std::move(readingParameters), enabled); 282 } 283 catch (const std::exception& e) 284 { 285 phosphor::logging::log<phosphor::logging::level::ERR>( 286 "Failed to load report from storage", 287 phosphor::logging::entry( 288 "FILENAME=%s", 289 static_cast<std::filesystem::path>(path).c_str()), 290 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what())); 291 reportStorage->remove(path); 292 } 293 } 294 } 295 296 void ReportManager::updateReport(const std::string& id) 297 { 298 for (auto& report : reports) 299 { 300 if (report->getId() == id) 301 { 302 report->updateReadings(); 303 return; 304 } 305 } 306 } 307 308 void ReportManager::verifyReportUpdates(const ReportUpdates reportUpdates) 309 { 310 if (std::find(supportedReportUpdates.begin(), supportedReportUpdates.end(), 311 reportUpdates) == supportedReportUpdates.end()) 312 { 313 throw sdbusplus::exception::SdBusError( 314 static_cast<int>(std::errc::invalid_argument), 315 "Invalid ReportUpdates"); 316 } 317 } 318