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