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