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 std::unique_ptr<interfaces::TriggerManager>& triggerManagerIn) : 37 reportFactory(std::move(reportFactoryIn)), 38 reportStorage(std::move(reportStorageIn)), objServer(objServerIn), 39 triggerManager(triggerManagerIn) 40 { 41 reports.reserve(maxReports); 42 43 loadFromPersistent(); 44 45 reportManagerIface = objServer->add_unique_interface( 46 reportManagerPath, reportManagerIfaceName, [this](auto& dbusIface) { 47 dbusIface.register_property_r( 48 "MaxReports", size_t{}, sdbusplus::vtable::property_::const_, 49 [](const auto&) { return maxReports; }); 50 dbusIface.register_property_r( 51 "MaxReportIdLength", size_t{}, 52 sdbusplus::vtable::property_::const_, 53 [](const auto&) { return maxReportIdLength; }); 54 dbusIface.register_property_r( 55 "MinInterval", uint64_t{}, sdbusplus::vtable::property_::const_, 56 [](const auto&) -> uint64_t { return minInterval.count(); }); 57 58 dbusIface.register_method( 59 "AddReport", [this](boost::asio::yield_context& yield, 60 const std::string& reportId, 61 const std::string& reportingType, 62 const bool emitsReadingsUpdate, 63 const bool logToMetricReportsCollection, 64 const uint64_t interval, 65 ReadingParametersPastVersion metricParams) { 66 constexpr auto enabledDefault = true; 67 constexpr uint64_t appendLimitDefault = 0; 68 constexpr ReportUpdates reportUpdatesDefault = 69 ReportUpdates::overwrite; 70 71 std::vector<ReportAction> reportActions; 72 73 if (emitsReadingsUpdate) 74 { 75 reportActions.emplace_back( 76 ReportAction::emitsReadingsUpdate); 77 } 78 if (logToMetricReportsCollection) 79 { 80 reportActions.emplace_back( 81 ReportAction::logToMetricReportsCollection); 82 } 83 84 return addReport(yield, reportId, reportId, 85 utils::toReportingType(reportingType), 86 reportActions, Milliseconds(interval), 87 appendLimitDefault, reportUpdatesDefault, 88 convertToReadingParameters( 89 std::move(metricParams)), 90 enabledDefault) 91 .getPath(); 92 }); 93 94 dbusIface.register_method( 95 "AddReportFutureVersion", 96 [this]( 97 boost::asio::yield_context& yield, 98 const std::string& reportId, const std::string& reportName, 99 const std::string& reportingType, 100 const std::string& reportUpdates, 101 const uint64_t appendLimit, 102 const std::vector<std::string>& reportActions, 103 const uint64_t interval, ReadingParameters metricParams) { 104 constexpr auto enabledDefault = true; 105 return addReport(yield, reportId, reportName, 106 utils::toReportingType(reportingType), 107 utils::transform( 108 reportActions, 109 [](const auto& reportAction) { 110 return utils::toReportAction( 111 reportAction); 112 }), 113 Milliseconds(interval), appendLimit, 114 utils::toReportUpdates(reportUpdates), 115 std::move(metricParams), enabledDefault) 116 .getPath(); 117 }); 118 }); 119 } 120 121 void ReportManager::removeReport(const interfaces::Report* report) 122 { 123 reports.erase( 124 std::remove_if(reports.begin(), reports.end(), 125 [report](const auto& x) { return report == x.get(); }), 126 reports.end()); 127 } 128 129 void ReportManager::verifyAddReport( 130 const std::string& reportId, const std::string& reportName, 131 const ReportingType reportingType, Milliseconds interval, 132 const ReportUpdates reportUpdates, const uint64_t appendLimit, 133 const std::vector<LabeledMetricParameters>& readingParams) 134 { 135 if (reportingType == ReportingType::onChange) 136 { 137 throw sdbusplus::exception::SdBusError( 138 static_cast<int>(std::errc::invalid_argument), 139 "Invalid reportingType"); 140 } 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 { 159 throw sdbusplus::exception::SdBusError( 160 static_cast<int>(std::errc::invalid_argument), "Invalid interval"); 161 } 162 163 size_t metricCount = 0; 164 for (auto metricParam : readingParams) 165 { 166 auto metricParamsVec = 167 metricParam.at_label<utils::tstring::SensorPath>(); 168 metricCount += metricParamsVec.size(); 169 } 170 171 if (readingParams.size() > maxNumberMetrics || 172 metricCount > maxNumberMetrics) 173 { 174 throw sdbusplus::exception::SdBusError( 175 static_cast<int>(std::errc::argument_list_too_long), 176 "Too many reading parameters"); 177 } 178 179 try 180 { 181 namespace ts = utils::tstring; 182 183 for (const LabeledMetricParameters& item : readingParams) 184 { 185 utils::toOperationType( 186 utils::toUnderlying(item.at_label<ts::OperationType>())); 187 } 188 } 189 catch (const std::exception& e) 190 { 191 throw sdbusplus::exception::SdBusError( 192 static_cast<int>(std::errc::invalid_argument), e.what()); 193 } 194 } 195 196 interfaces::Report& ReportManager::addReport( 197 boost::asio::yield_context& yield, const std::string& reportId, 198 const std::string& reportName, const ReportingType reportingType, 199 const std::vector<ReportAction>& reportActions, Milliseconds interval, 200 const uint64_t appendLimit, const ReportUpdates reportUpdates, 201 ReadingParameters metricParams, const bool enabled) 202 { 203 auto labeledMetricParams = 204 reportFactory->convertMetricParams(yield, metricParams); 205 206 return addReport(reportId, reportName, reportingType, reportActions, 207 interval, appendLimit, reportUpdates, 208 std::move(labeledMetricParams), enabled); 209 } 210 211 interfaces::Report& ReportManager::addReport( 212 const std::string& reportId, const std::string& reportName, 213 const ReportingType reportingType, 214 const std::vector<ReportAction>& reportActions, Milliseconds interval, 215 const uint64_t appendLimit, const ReportUpdates reportUpdates, 216 std::vector<LabeledMetricParameters> labeledMetricParams, 217 const bool enabled) 218 { 219 const auto existingReportIds = utils::transform( 220 reports, [](const auto& report) { return report->getId(); }); 221 222 auto [id, name] = utils::generateId(reportId, reportName, reportNameDefault, 223 existingReportIds, maxReportIdLength); 224 225 verifyAddReport(id, name, reportingType, interval, reportUpdates, 226 appendLimit, labeledMetricParams); 227 228 std::vector<std::string> triggerIds; 229 if (triggerManager) 230 { 231 triggerIds = triggerManager->getTriggerIdsForReport(id); 232 } 233 234 reports.emplace_back( 235 reportFactory->make(id, name, reportingType, reportActions, interval, 236 appendLimit, reportUpdates, *this, *reportStorage, 237 labeledMetricParams, enabled, triggerIds)); 238 return *reports.back(); 239 } 240 241 void ReportManager::loadFromPersistent() 242 { 243 std::vector<interfaces::JsonStorage::FilePath> paths = 244 reportStorage->list(); 245 246 for (const auto& path : paths) 247 { 248 std::optional<nlohmann::json> data = reportStorage->load(path); 249 try 250 { 251 size_t version = data->at("Version").get<size_t>(); 252 if (version != Report::reportVersion) 253 { 254 throw std::logic_error("Invalid version"); 255 } 256 bool enabled = data->at("Enabled").get<bool>(); 257 std::string& id = data->at("Id").get_ref<std::string&>(); 258 std::string& name = data->at("Name").get_ref<std::string&>(); 259 260 uint32_t reportingType = data->at("ReportingType").get<uint32_t>(); 261 std::vector<ReportAction> reportActions = utils::transform( 262 data->at("ReportActions").get<std::vector<uint32_t>>(), 263 [](const auto reportAction) { 264 return utils::toReportAction(reportAction); 265 }); 266 uint64_t interval = data->at("Interval").get<uint64_t>(); 267 uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>(); 268 uint32_t reportUpdates = data->at("ReportUpdates").get<uint32_t>(); 269 auto readingParameters = 270 data->at("ReadingParameters") 271 .get<std::vector<LabeledMetricParameters>>(); 272 273 addReport(id, name, utils::toReportingType(reportingType), 274 reportActions, Milliseconds(interval), appendLimit, 275 utils::toReportUpdates(reportUpdates), 276 std::move(readingParameters), enabled); 277 } 278 catch (const std::exception& e) 279 { 280 phosphor::logging::log<phosphor::logging::level::ERR>( 281 "Failed to load report from storage", 282 phosphor::logging::entry( 283 "FILENAME=%s", 284 static_cast<std::filesystem::path>(path).c_str()), 285 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what())); 286 reportStorage->remove(path); 287 } 288 } 289 } 290 291 void ReportManager::updateReport(const std::string& id) 292 { 293 for (auto& report : reports) 294 { 295 if (report->getId() == id) 296 { 297 report->updateReadings(); 298 return; 299 } 300 } 301 } 302 303 void ReportManager::updateTriggerIds(const std::string& reportId, 304 const std::string& triggerId, 305 TriggerIdUpdate updateType) 306 { 307 if (auto res = std::find_if(reports.begin(), reports.end(), 308 [&reportId](const auto& report) { 309 return report->getId() == reportId; 310 }); 311 res != reports.end()) 312 { 313 (*res)->updateTriggerIds(triggerId, updateType); 314 } 315 } 316