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