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