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 ReportManager::ReportManager( 19 std::unique_ptr<interfaces::ReportFactory> reportFactoryIn, 20 std::unique_ptr<interfaces::JsonStorage> reportStorageIn, 21 const std::shared_ptr<sdbusplus::asio::object_server>& objServerIn) : 22 reportFactory(std::move(reportFactoryIn)), 23 reportStorage(std::move(reportStorageIn)), objServer(objServerIn) 24 { 25 reports.reserve(maxReports); 26 27 loadFromPersistent(); 28 29 reportManagerIface = objServer->add_unique_interface( 30 reportManagerPath, reportManagerIfaceName, 31 [this](auto& dbusIface) { 32 dbusIface.register_property_r("MaxReports", size_t{}, 33 sdbusplus::vtable::property_::const_, 34 [](const auto&) { return maxReports; }); 35 dbusIface.register_property_r( 36 "MinInterval", uint64_t{}, sdbusplus::vtable::property_::const_, 37 [](const auto&) -> uint64_t { return minInterval.count(); }); 38 dbusIface.register_property_r( 39 "SupportedOperationTypes", std::vector<std::string>{}, 40 sdbusplus::vtable::property_::const_, 41 [](const auto&) -> std::vector<std::string> { 42 return utils::transform<std::vector>( 43 utils::convDataOperationType, 44 [](const auto& item) { return std::string(item.first); }); 45 }); 46 dbusIface.register_method( 47 "AddReport", 48 [this](boost::asio::yield_context& yield, std::string reportId, 49 std::string reportName, std::string reportingType, 50 std::string reportUpdates, uint64_t appendLimit, 51 std::vector<std::string> reportActions, uint64_t interval, 52 ReadingParameters readingParameters, bool enabled) { 53 if (reportingType.empty()) 54 { 55 reportingType = utils::enumToString(ReportingType::onRequest); 56 } 57 58 if (reportUpdates.empty()) 59 { 60 reportUpdates = utils::enumToString(ReportUpdates::overwrite); 61 } 62 63 if (appendLimit == std::numeric_limits<uint64_t>::max()) 64 { 65 appendLimit = maxAppendLimit; 66 } 67 68 if (interval == std::numeric_limits<uint64_t>::max()) 69 { 70 interval = 0; 71 } 72 73 return addReport(yield, reportId, reportName, 74 utils::toReportingType(reportingType), 75 utils::transform( 76 reportActions, 77 [](const auto& reportAction) { 78 return utils::toReportAction(reportAction); 79 }), 80 Milliseconds(interval), appendLimit, 81 utils::toReportUpdates(reportUpdates), 82 readingParameters, enabled) 83 .getPath(); 84 }); 85 }); 86 } 87 88 void ReportManager::removeReport(const interfaces::Report* report) 89 { 90 reports.erase( 91 std::remove_if(reports.begin(), reports.end(), 92 [report](const auto& x) { return report == x.get(); }), 93 reports.end()); 94 } 95 96 void ReportManager::verifyAddReport( 97 const std::string& reportId, const std::string& reportName, 98 const ReportingType reportingType, Milliseconds interval, 99 const ReportUpdates reportUpdates, const uint64_t appendLimit, 100 const std::vector<LabeledMetricParameters>& readingParams) 101 { 102 namespace ts = utils::tstring; 103 104 if (reports.size() >= maxReports) 105 { 106 throw sdbusplus::exception::SdBusError( 107 static_cast<int>(std::errc::too_many_files_open), 108 "Reached maximal report count"); 109 } 110 111 if (appendLimit > maxAppendLimit && 112 appendLimit != std::numeric_limits<uint64_t>::max()) 113 { 114 throw errors::InvalidArgument("AppendLimit", "Out of range."); 115 } 116 117 if ((reportingType == ReportingType::periodic && interval < minInterval) || 118 (reportingType != ReportingType::periodic && 119 interval != Milliseconds{0})) 120 { 121 throw errors::InvalidArgument("Interval"); 122 } 123 124 size_t metricCount = 0; 125 for (auto metricParam : readingParams) 126 { 127 auto metricParamsVec = 128 metricParam.at_label<utils::tstring::SensorPath>(); 129 metricCount += metricParamsVec.size(); 130 } 131 132 if (readingParams.size() > maxNumberMetrics || 133 metricCount > maxNumberMetrics) 134 { 135 throw errors::InvalidArgument("MetricParams", "Too many."); 136 } 137 138 for (const LabeledMetricParameters& item : readingParams) 139 { 140 utils::toOperationType( 141 utils::toUnderlying(item.at_label<ts::OperationType>())); 142 } 143 } 144 145 interfaces::Report& ReportManager::addReport( 146 boost::asio::yield_context& yield, const std::string& reportId, 147 const std::string& reportName, const ReportingType reportingType, 148 const std::vector<ReportAction>& reportActions, Milliseconds interval, 149 const uint64_t appendLimit, const ReportUpdates reportUpdates, 150 ReadingParameters metricParams, const bool enabled) 151 { 152 auto labeledMetricParams = reportFactory->convertMetricParams(yield, 153 metricParams); 154 155 return addReport(reportId, reportName, reportingType, reportActions, 156 interval, appendLimit, reportUpdates, 157 std::move(labeledMetricParams), enabled, Readings{}); 158 } 159 160 interfaces::Report& ReportManager::addReport( 161 const std::string& reportId, const std::string& reportName, 162 const ReportingType reportingType, 163 const std::vector<ReportAction>& reportActions, Milliseconds interval, 164 const uint64_t appendLimit, const ReportUpdates reportUpdates, 165 std::vector<LabeledMetricParameters> labeledMetricParams, 166 const bool enabled, Readings readings) 167 { 168 const auto existingReportIds = utils::transform( 169 reports, [](const auto& report) { return report->getId(); }); 170 171 auto [id, name] = utils::makeIdName(reportId, reportName, reportNameDefault, 172 existingReportIds); 173 174 verifyAddReport(id, name, reportingType, interval, reportUpdates, 175 appendLimit, labeledMetricParams); 176 177 reports.emplace_back( 178 reportFactory->make(id, name, reportingType, reportActions, interval, 179 appendLimit, reportUpdates, *this, *reportStorage, 180 labeledMetricParams, enabled, std::move(readings))); 181 return *reports.back(); 182 } 183 184 void ReportManager::loadFromPersistent() 185 { 186 std::vector<interfaces::JsonStorage::FilePath> paths = 187 reportStorage->list(); 188 189 for (const auto& path : paths) 190 { 191 std::optional<nlohmann::json> data = reportStorage->load(path); 192 try 193 { 194 size_t version = data->at("Version").get<size_t>(); 195 if (version != Report::reportVersion) 196 { 197 throw std::logic_error("Invalid version"); 198 } 199 bool enabled = data->at("Enabled").get<bool>(); 200 std::string& id = data->at("Id").get_ref<std::string&>(); 201 std::string& name = data->at("Name").get_ref<std::string&>(); 202 203 uint32_t reportingType = data->at("ReportingType").get<uint32_t>(); 204 std::vector<ReportAction> reportActions = utils::transform( 205 data->at("ReportActions").get<std::vector<uint32_t>>(), 206 [](const auto reportAction) { 207 return utils::toReportAction(reportAction); 208 }); 209 uint64_t interval = data->at("Interval").get<uint64_t>(); 210 uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>(); 211 uint32_t reportUpdates = data->at("ReportUpdates").get<uint32_t>(); 212 auto readingParameters = 213 data->at("ReadingParameters") 214 .get<std::vector<LabeledMetricParameters>>(); 215 216 Readings readings = {}; 217 218 if (auto it = data->find("MetricValues"); it != data->end()) 219 { 220 const auto labeledReadings = it->get<LabeledReadings>(); 221 readings = utils::toReadings(labeledReadings); 222 } 223 224 addReport(id, name, utils::toReportingType(reportingType), 225 reportActions, Milliseconds(interval), appendLimit, 226 utils::toReportUpdates(reportUpdates), 227 std::move(readingParameters), enabled, 228 std::move(readings)); 229 } 230 catch (const std::exception& e) 231 { 232 phosphor::logging::log<phosphor::logging::level::ERR>( 233 "Failed to load report from storage", 234 phosphor::logging::entry( 235 "FILENAME=%s", 236 static_cast<std::filesystem::path>(path).c_str()), 237 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what())); 238 reportStorage->remove(path); 239 } 240 } 241 } 242