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