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 dbusIface.register_property_r( 56 "SupportedOperationTypes", std::vector<std::string>{}, 57 sdbusplus::vtable::property_::const_, 58 [](const auto&) -> std::vector<std::string> { 59 return utils::transform<std::vector>( 60 utils::convDataOperationType, [](const auto& item) { 61 return std::string(item.first); 62 }); 63 }); 64 65 dbusIface.register_method( 66 "AddReport", [this](boost::asio::yield_context& yield, 67 const std::string& reportId, 68 const std::string& reportingType, 69 const bool emitsReadingsUpdate, 70 const bool logToMetricReportsCollection, 71 const uint64_t interval, 72 ReadingParametersPastVersion metricParams) { 73 constexpr auto enabledDefault = true; 74 constexpr uint64_t appendLimitDefault = 0; 75 constexpr ReportUpdates reportUpdatesDefault = 76 ReportUpdates::overwrite; 77 78 std::vector<ReportAction> reportActions; 79 80 if (emitsReadingsUpdate) 81 { 82 reportActions.emplace_back( 83 ReportAction::emitsReadingsUpdate); 84 } 85 if (logToMetricReportsCollection) 86 { 87 reportActions.emplace_back( 88 ReportAction::logToMetricReportsCollection); 89 } 90 91 return addReport(yield, reportId, reportId, 92 utils::toReportingType(reportingType), 93 reportActions, Milliseconds(interval), 94 appendLimitDefault, reportUpdatesDefault, 95 convertToReadingParameters( 96 std::move(metricParams)), 97 enabledDefault) 98 .getPath(); 99 }); 100 101 dbusIface.register_method( 102 "AddReportFutureVersion", 103 [this]( 104 boost::asio::yield_context& yield, 105 const std::string& reportId, const std::string& reportName, 106 const std::string& reportingType, 107 const std::string& reportUpdates, 108 const uint64_t appendLimit, 109 const std::vector<std::string>& reportActions, 110 const uint64_t interval, ReadingParameters metricParams) { 111 constexpr auto enabledDefault = true; 112 return addReport(yield, reportId, reportName, 113 utils::toReportingType(reportingType), 114 utils::transform( 115 reportActions, 116 [](const auto& reportAction) { 117 return utils::toReportAction( 118 reportAction); 119 }), 120 Milliseconds(interval), appendLimit, 121 utils::toReportUpdates(reportUpdates), 122 std::move(metricParams), enabledDefault) 123 .getPath(); 124 }); 125 }); 126 } 127 128 void ReportManager::removeReport(const interfaces::Report* report) 129 { 130 reports.erase( 131 std::remove_if(reports.begin(), reports.end(), 132 [report](const auto& x) { return report == x.get(); }), 133 reports.end()); 134 } 135 136 void ReportManager::verifyAddReport( 137 const std::string& reportId, const std::string& reportName, 138 const ReportingType reportingType, Milliseconds interval, 139 const ReportUpdates reportUpdates, const uint64_t appendLimit, 140 const std::vector<LabeledMetricParameters>& readingParams) 141 { 142 if (reports.size() >= maxReports) 143 { 144 throw sdbusplus::exception::SdBusError( 145 static_cast<int>(std::errc::too_many_files_open), 146 "Reached maximal report count"); 147 } 148 149 if (appendLimit > maxAppendLimit && 150 appendLimit != std::numeric_limits<uint64_t>::max()) 151 { 152 throw sdbusplus::exception::SdBusError( 153 static_cast<int>(std::errc::invalid_argument), 154 "Append limit out of range"); 155 } 156 157 if ((reportingType == ReportingType::periodic && interval < minInterval) || 158 (reportingType != ReportingType::periodic && 159 interval != Milliseconds{0})) 160 { 161 throw sdbusplus::exception::SdBusError( 162 static_cast<int>(std::errc::invalid_argument), "Invalid interval"); 163 } 164 165 size_t metricCount = 0; 166 for (auto metricParam : readingParams) 167 { 168 auto metricParamsVec = 169 metricParam.at_label<utils::tstring::SensorPath>(); 170 metricCount += metricParamsVec.size(); 171 } 172 173 if (readingParams.size() > maxNumberMetrics || 174 metricCount > maxNumberMetrics) 175 { 176 throw sdbusplus::exception::SdBusError( 177 static_cast<int>(std::errc::argument_list_too_long), 178 "Too many reading parameters"); 179 } 180 181 try 182 { 183 namespace ts = utils::tstring; 184 185 for (const LabeledMetricParameters& item : readingParams) 186 { 187 utils::toOperationType( 188 utils::toUnderlying(item.at_label<ts::OperationType>())); 189 } 190 } 191 catch (const std::exception& e) 192 { 193 throw sdbusplus::exception::SdBusError( 194 static_cast<int>(std::errc::invalid_argument), e.what()); 195 } 196 } 197 198 interfaces::Report& ReportManager::addReport( 199 boost::asio::yield_context& yield, const std::string& reportId, 200 const std::string& reportName, const ReportingType reportingType, 201 const std::vector<ReportAction>& reportActions, Milliseconds interval, 202 const uint64_t appendLimit, const ReportUpdates reportUpdates, 203 ReadingParameters metricParams, const bool enabled) 204 { 205 auto labeledMetricParams = 206 reportFactory->convertMetricParams(yield, metricParams); 207 208 return addReport(reportId, reportName, reportingType, reportActions, 209 interval, appendLimit, reportUpdates, 210 std::move(labeledMetricParams), enabled, Readings{}); 211 } 212 213 interfaces::Report& ReportManager::addReport( 214 const std::string& reportId, const std::string& reportName, 215 const ReportingType reportingType, 216 const std::vector<ReportAction>& reportActions, Milliseconds interval, 217 const uint64_t appendLimit, const ReportUpdates reportUpdates, 218 std::vector<LabeledMetricParameters> labeledMetricParams, 219 const bool enabled, Readings readings) 220 { 221 const auto existingReportIds = utils::transform( 222 reports, [](const auto& report) { return report->getId(); }); 223 224 auto [id, name] = utils::generateId(reportId, reportName, reportNameDefault, 225 existingReportIds, maxReportIdLength); 226 227 verifyAddReport(id, name, reportingType, interval, reportUpdates, 228 appendLimit, labeledMetricParams); 229 230 reports.emplace_back( 231 reportFactory->make(id, name, reportingType, reportActions, interval, 232 appendLimit, reportUpdates, *this, *reportStorage, 233 labeledMetricParams, enabled, std::move(readings))); 234 return *reports.back(); 235 } 236 237 void ReportManager::loadFromPersistent() 238 { 239 std::vector<interfaces::JsonStorage::FilePath> paths = 240 reportStorage->list(); 241 242 for (const auto& path : paths) 243 { 244 std::optional<nlohmann::json> data = reportStorage->load(path); 245 try 246 { 247 size_t version = data->at("Version").get<size_t>(); 248 if (version != Report::reportVersion) 249 { 250 throw std::logic_error("Invalid version"); 251 } 252 bool enabled = data->at("Enabled").get<bool>(); 253 std::string& id = data->at("Id").get_ref<std::string&>(); 254 std::string& name = data->at("Name").get_ref<std::string&>(); 255 256 uint32_t reportingType = data->at("ReportingType").get<uint32_t>(); 257 std::vector<ReportAction> reportActions = utils::transform( 258 data->at("ReportActions").get<std::vector<uint32_t>>(), 259 [](const auto reportAction) { 260 return utils::toReportAction(reportAction); 261 }); 262 uint64_t interval = data->at("Interval").get<uint64_t>(); 263 uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>(); 264 uint32_t reportUpdates = data->at("ReportUpdates").get<uint32_t>(); 265 auto readingParameters = 266 data->at("ReadingParameters") 267 .get<std::vector<LabeledMetricParameters>>(); 268 269 Readings readings = {}; 270 271 if (auto it = data->find("MetricValues"); it != data->end()) 272 { 273 const auto labeledReadings = it->get<LabeledReadings>(); 274 readings = utils::toReadings(labeledReadings); 275 } 276 277 addReport(id, name, utils::toReportingType(reportingType), 278 reportActions, Milliseconds(interval), appendLimit, 279 utils::toReportUpdates(reportUpdates), 280 std::move(readingParameters), enabled, 281 std::move(readings)); 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