diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch | 598 |
1 files changed, 0 insertions, 598 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch deleted file mode 100644 index c24352de5..000000000 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch +++ /dev/null @@ -1,598 +0,0 @@ -From 00806052b1e9440809ce727523ffcc66083f6417 Mon Sep 17 00:00:00 2001 -From: "Wludzik, Jozef" <jozef.wludzik@intel.com> -Date: Mon, 18 May 2020 11:56:57 +0200 -Subject: [PATCH 06/10] Add support for POST in MetricReportDefinitions - -Added POST action in MetricReportDefinitions node to allow user -to add new MetricReportDefinition. Using minimal set of -MetricReportDefinition parameters from user bmcweb converts it to -DBus call "AddReport" to MonitoringService that serves as a backend -for TelemetryService. - -Tested: - - Succesfully passed RedfishServiceValidator.py - - Validated good cases with different parameters for POST action - - Validated bad cases with different parameters for POST action - - Validated fromDurationFormat() with range of arguments starting - from PT0.0S up to P49D (it is an upper limit for uint32_t) - -Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com> -Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com> -Change-Id: I2fed96848594451e22fde686f8c066d7770cc65a - -%% original patch: 0002-Add-support-for-POST-in-MetricReportDefinitions.patch - -Change-Id: I55032bc1086b60800d19bd1c0fa14fdb891f5a5b ---- - redfish-core/include/utils/time_utils.hpp | 49 +++ - .../include/utils/validate_params_length.hpp | 109 +++++++ - redfish-core/lib/metric_report_definition.hpp | 347 +++++++++++++++++++++ - 3 files changed, 505 insertions(+) - create mode 100644 redfish-core/include/utils/validate_params_length.hpp - -diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp -index 0256b3f..c365585 100644 ---- a/redfish-core/include/utils/time_utils.hpp -+++ b/redfish-core/include/utils/time_utils.hpp -@@ -57,6 +57,32 @@ std::string toDurationFormatItem(std::chrono::milliseconds& duration, - return ss.str(); - } - -+template <typename T> -+static long long fromDurationFormatItem(std::string_view& fmt, -+ const char* postfix) -+{ -+ auto pos = fmt.find(postfix); -+ if (pos == std::string::npos) -+ { -+ return 0; -+ } -+ -+ long out; -+ if constexpr (std::is_same<T, std::chrono::milliseconds>::value) -+ { -+ /* Half point is added to avoid numeric error on rounding */ -+ out = static_cast<long>(std::strtof(fmt.data(), nullptr) * -+ std::chrono::milliseconds::period::den + -+ 0.5f); -+ } -+ else -+ { -+ out = std::strtol(fmt.data(), nullptr, 10); -+ } -+ fmt.remove_prefix(pos + 1); -+ return std::chrono::milliseconds(T(out)).count(); -+} -+ - } // namespace details - - /** -@@ -93,5 +119,28 @@ std::string toDurationFormat(const uint32_t ms) - return fmt; - } - -+static uint32_t fromDurationFormat(std::string_view fmt) -+{ -+ if (fmt.empty() || fmt[0] != 'P') -+ { -+ return 0; -+ } -+ using Days = std::chrono::duration<int, std::ratio<24 * 60 * 60>>; -+ -+ fmt.remove_prefix(1); -+ auto out = details::fromDurationFormatItem<Days>(fmt, "D"); -+ if (fmt[0] != 'T') -+ { -+ return static_cast<uint32_t>(out); -+ } -+ -+ fmt.remove_prefix(1); -+ out += details::fromDurationFormatItem<std::chrono::hours>(fmt, "H"); -+ out += details::fromDurationFormatItem<std::chrono::minutes>(fmt, "M"); -+ out += details::fromDurationFormatItem<std::chrono::milliseconds>(fmt, "S"); -+ -+ return static_cast<uint32_t>(out); -+} -+ - } // namespace time_utils - } // namespace redfish -diff --git a/redfish-core/include/utils/validate_params_length.hpp b/redfish-core/include/utils/validate_params_length.hpp -new file mode 100644 -index 0000000..c4e0569 ---- /dev/null -+++ b/redfish-core/include/utils/validate_params_length.hpp -@@ -0,0 +1,109 @@ -+#pragma once -+ -+namespace redfish -+{ -+namespace detail -+{ -+template <class Limits, size_t... Seq> -+bool validateParamsLength(crow::Response& res, Limits&& limits, -+ std::index_sequence<Seq...>) -+{ -+ return (... && std::get<Seq>(limits).validate(res)); -+} -+} // namespace detail -+ -+template <class T> -+struct ItemSizeValidator -+{ -+ ItemSizeValidator(const T&& item, std::string_view name, size_t limit) : -+ item(std::forward<const T>(item)), name(name), limit(limit) -+ {} -+ -+ bool validate(crow::Response& res) const -+ { -+ return ItemSizeValidator<T>::validateItem(res, item, name, limit); -+ } -+ -+ private: -+ static bool validateItem(crow::Response& res, size_t item, -+ std::string_view name, size_t limit) -+ { -+ if (item > static_cast<size_t>(limit)) -+ { -+ messages::stringValueTooLong(res, std::string(name), -+ static_cast<int>(limit)); -+ return false; -+ } -+ return true; -+ } -+ -+ static bool validateItem(crow::Response& res, std::string_view item, -+ std::string_view name, size_t limit) -+ { -+ return validateItem(res, item.size(), name, limit); -+ } -+ -+ static bool validateItem(crow::Response& res, const std::string& item, -+ std::string_view name, size_t limit) -+ { -+ return validateItem(res, item.size(), name, limit); -+ } -+ -+ static bool validateItem(crow::Response& res, -+ const sdbusplus::message::object_path& item, -+ std::string_view name, size_t limit) -+ { -+ return validateItem(res, item.str.size(), name, limit); -+ } -+ -+ T item; -+ std::string_view name; -+ size_t limit; -+}; -+ -+template <class T> -+ItemSizeValidator(const T&, std::string_view, size_t) -+ -> ItemSizeValidator<const T&>; -+ -+ItemSizeValidator(const char*, std::string_view, size_t) -+ ->ItemSizeValidator<std::string_view>; -+ -+template <class ContainerT> -+struct ArrayItemsValidator -+{ -+ ArrayItemsValidator(const ContainerT& item, std::string_view name, -+ size_t limit) : -+ item(item), -+ name(name), limit(limit) -+ {} -+ -+ bool validate(crow::Response& res) const -+ { -+ return std::all_of( -+ item.begin(), item.end(), [&res, this](const auto& item) { -+ return ItemSizeValidator(item, name, limit).validate(res); -+ }); -+ } -+ -+ private: -+ const ContainerT& item; -+ std::string_view name; -+ size_t limit; -+}; -+ -+template <class T> -+bool validateParamLength(crow::Response& res, T&& item, std::string_view name, -+ size_t limit) -+{ -+ return ItemSizeValidator(std::forward<T>(item), name, limit).validate(res); -+} -+ -+template <class Limits> -+bool validateParamsLength(crow::Response& res, Limits&& limits) -+{ -+ return detail::validateParamsLength( -+ res, std::forward<Limits>(limits), -+ std::make_index_sequence<std::tuple_size_v<std::decay_t<Limits>>>()); -+} -+ -+} // namespace redfish -diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp -index 72e62e9..c6b09f8 100644 ---- a/redfish-core/lib/metric_report_definition.hpp -+++ b/redfish-core/lib/metric_report_definition.hpp -@@ -17,16 +17,29 @@ - #pragma once - - #include "node.hpp" -+#include "sensors.hpp" - #include "utils/telemetry_utils.hpp" - #include "utils/time_utils.hpp" -+#include "utils/validate_params_length.hpp" - -+#include <boost/algorithm/string/join.hpp> -+#include <boost/algorithm/string/split.hpp> - #include <boost/container/flat_map.hpp> - -+#include <regex> - #include <system_error> -+#include <tuple> - #include <variant> - - namespace redfish - { -+static constexpr size_t maxShortParamLength = 255; -+static constexpr size_t maxLongParamLength = 1024; -+static constexpr size_t maxDbusNameLength = 255; -+static constexpr size_t maxArraySize = 100; -+static constexpr size_t maxReportIdLen = -+ maxDbusNameLength - std::string_view(telemetry::telemetryPath).size() - -+ std::string_view("/").size(); - - class MetricReportDefinitionCollection : public Node - { -@@ -57,6 +70,339 @@ class MetricReportDefinitionCollection : public Node - telemetry::getReportCollection(asyncResp, - telemetry::metricReportDefinitionUri); - } -+ -+ using ChassisSensorNode = std::pair<std::string, std::string>; -+ using DbusSensor = sdbusplus::message::object_path; -+ using DbusSensors = std::vector<DbusSensor>; -+ using MetricParam = -+ std::tuple<DbusSensors, std::string, std::string, std::string>; -+ using MetricParams = std::vector<MetricParam>; -+ /* -+ * AddReportArgs misses "Domain" parameter because it is constant for -+ * TelemetryService and equals "TelemetryService". -+ */ -+ using AddReportArgs = -+ std::tuple<std::string, std::string, std::vector<std::string>, uint32_t, -+ MetricParams>; -+ -+ void doPost(crow::Response& res, const crow::Request& req, -+ const std::vector<std::string>&) override -+ { -+ auto asyncResp = std::make_shared<AsyncResp>(res); -+ AddReportArgs addReportArgs; -+ if (!getUserParameters(res, req, addReportArgs)) -+ { -+ return; -+ } -+ -+ boost::container::flat_set<ChassisSensorNode> chassisSensorSet; -+ auto unmatched = getChassisSensorNode( -+ std::get<MetricParams>(addReportArgs), chassisSensorSet); -+ if (unmatched) -+ { -+ messages::resourceNotFound(asyncResp->res, "MetricProperties", -+ *unmatched); -+ return; -+ } -+ -+ auto addReportReq = -+ std::make_shared<AddReport>(addReportArgs, asyncResp); -+ for (auto& [chassis, sensorType] : chassisSensorSet) -+ { -+ retrieveUriToDbusMap( -+ chassis, sensorType, -+ [asyncResp, addReportReq]( -+ const boost::beast::http::status, -+ const boost::container::flat_map<std::string, std::string>& -+ uriToDbus) { *addReportReq += uriToDbus; }); -+ } -+ } -+ -+ static std::optional<std::string> -+ replaceReportActions(std::vector<std::string>& actions) -+ { -+ const boost::container::flat_map<std::string, std::string> -+ reportActions = { -+ {"RedfishEvent", "Event"}, -+ {"LogToMetricReportsCollection", "Log"}, -+ }; -+ -+ for (auto& action : actions) -+ { -+ auto found = reportActions.find(action); -+ if (found == reportActions.end()) -+ { -+ return action; -+ } -+ action = found->second; -+ } -+ return std::nullopt; -+ } -+ -+ static constexpr const std::array<const char*, 2> supportedDefinitionType = -+ {"Periodic", "OnRequest"}; -+ -+ static bool getUserParameters(crow::Response& res, const crow::Request& req, -+ AddReportArgs& params) -+ { -+ std::vector<nlohmann::json> metrics; -+ std::optional<nlohmann::json> schedule; -+ auto& [name, reportingType, reportActions, scanPeriod, metricParams] = -+ params; -+ if (!json_util::readJson(req, res, "Id", name, "Metrics", metrics, -+ "MetricReportDefinitionType", reportingType, -+ "ReportActions", reportActions, "Schedule", -+ schedule)) -+ { -+ return false; -+ } -+ -+ auto limits = std::make_tuple( -+ ItemSizeValidator(name, "Id", maxReportIdLen), -+ ItemSizeValidator(reportingType, "MetricReportDefinitionType", -+ maxShortParamLength), -+ ItemSizeValidator(reportActions.size(), "ReportActions.size()", -+ maxArraySize), -+ ArrayItemsValidator(reportActions, "ReportActions", -+ maxShortParamLength), -+ ItemSizeValidator(metrics.size(), "Metrics.size()", maxArraySize)); -+ -+ if (!validateParamsLength(res, std::move(limits))) -+ { -+ return false; -+ } -+ -+ constexpr const char* allowedCharactersInName = -+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" -+ "_"; -+ if (name.empty() || name.find_first_not_of(allowedCharactersInName) != -+ std::string::npos) -+ { -+ BMCWEB_LOG_ERROR << "Failed to match " << name -+ << " with allowed character " -+ << allowedCharactersInName; -+ messages::propertyValueFormatError(res, name, "Id"); -+ return false; -+ } -+ -+ if (!std::any_of( -+ supportedDefinitionType.begin(), supportedDefinitionType.end(), -+ [reportingType](auto& x) { return reportingType == x; })) -+ { -+ messages::propertyValueNotInList(res, reportingType, -+ "MetricReportDefinitionType"); -+ return false; -+ } -+ -+ auto unmatched = replaceReportActions(reportActions); -+ if (unmatched) -+ { -+ messages::propertyValueNotInList(res, *unmatched, "ReportActions"); -+ return false; -+ } -+ -+ if (reportingType == "Periodic") -+ { -+ if (!schedule) -+ { -+ messages::createFailedMissingReqProperties(res, "Schedule"); -+ return false; -+ } -+ -+ std::string interval; -+ if (!json_util::readJson(*schedule, res, "RecurrenceInterval", -+ interval)) -+ { -+ return false; -+ } -+ -+ if (!validateParamLength(res, interval, "RecurrenceInterval", -+ maxShortParamLength)) -+ { -+ return false; -+ } -+ -+ constexpr const char* durationPattern = -+ "-?P(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(.\\d+)?S)?)?"; -+ if (!std::regex_match(interval, std::regex(durationPattern))) -+ { -+ messages::propertyValueFormatError(res, interval, -+ "RecurrenceInterval"); -+ return false; -+ } -+ -+ scanPeriod = time_utils::fromDurationFormat(interval); -+ } -+ -+ return fillMetricParams(metrics, res, metricParams); -+ } -+ -+ static bool fillMetricParams(std::vector<nlohmann::json>& metrics, -+ crow::Response& res, -+ MetricParams& metricParams) -+ { -+ metricParams.reserve(metrics.size()); -+ for (auto& m : metrics) -+ { -+ std::string metricId; -+ std::vector<std::string> metricProperties; -+ if (!json_util::readJson(m, res, "MetricId", metricId, -+ "MetricProperties", metricProperties)) -+ { -+ return false; -+ } -+ -+ auto limits = std::make_tuple( -+ ItemSizeValidator(metricId, "MetricId", maxLongParamLength), -+ ItemSizeValidator(metricProperties.size(), -+ "MetricProperties.size()", maxArraySize), -+ ArrayItemsValidator(metricProperties, "MetricProperties", -+ maxLongParamLength)); -+ -+ if (!validateParamsLength(res, std::move(limits))) -+ { -+ return false; -+ } -+ -+ DbusSensors dbusSensors; -+ dbusSensors.reserve(metricProperties.size()); -+ std::for_each( -+ metricProperties.begin(), metricProperties.end(), -+ [&dbusSensors](auto& x) { dbusSensors.emplace_back(x); }); -+ -+ metricParams.emplace_back( -+ dbusSensors, "SINGLE", metricId, -+ boost::algorithm::join(metricProperties, ", ")); -+ } -+ return true; -+ } -+ -+ static std::optional<std::string> getChassisSensorNode( -+ const MetricParams& metricParams, -+ boost::container::flat_set<ChassisSensorNode>& matched) -+ { -+ for (const auto& metricParam : metricParams) -+ { -+ const auto& sensors = std::get<DbusSensors>(metricParam); -+ for (const auto& sensor : sensors) -+ { -+ /* -+ * Support only following paths: -+ * "/redfish/v1/Chassis/<chassis>/Power#/..." -+ * "/redfish/v1/Chassis/<chassis>/Sensors/..." -+ * "/redfish/v1/Chassis/<chassis>/Thermal#/..." -+ */ -+ constexpr const char* uriPattern = -+ "\\/redfish\\/v1\\/Chassis\\/(\\w+)\\/" -+ "(Power|Sensors|Thermal)[#]?\\/.*"; -+ std::smatch m; -+ if (!std::regex_match(sensor.str, m, std::regex(uriPattern)) || -+ m.size() != 3) -+ { -+ BMCWEB_LOG_ERROR << "Failed to match " << sensor.str -+ << " with pattern " << uriPattern; -+ return sensor; -+ } -+ -+ BMCWEB_LOG_DEBUG << "Chassis=" << m[1] << ", Type=" << m[2]; -+ matched.emplace(m[1], m[2]); -+ } -+ } -+ return std::nullopt; -+ } -+ -+ static std::optional<std::string> replaceUriWithDbus( -+ const boost::container::flat_map<std::string, std::string>& uriToDbus, -+ MetricParams& metricParams) -+ { -+ for (auto& metricParam : metricParams) -+ { -+ auto& dbusSensors = std::get<DbusSensors>(metricParam); -+ for (auto& uri : dbusSensors) -+ { -+ auto dbus = uriToDbus.find(uri); -+ if (dbus == uriToDbus.end()) -+ { -+ BMCWEB_LOG_ERROR << "Failed to find DBus sensor " -+ "corresponding to URI " -+ << uri.str; -+ return uri; -+ } -+ uri = dbus->second; -+ } -+ } -+ return std::nullopt; -+ } -+ -+ static void addReport(std::shared_ptr<AsyncResp> asyncResp, -+ AddReportArgs args) -+ { -+ constexpr const char* domain = "TelemetryService"; -+ auto& [name, reportingType, reportActions, scanPeriod, metricParams] = -+ args; -+ -+ crow::connections::systemBus->async_method_call( -+ [asyncResp, name](const boost::system::error_code ec, -+ const std::string) { -+ if (ec == boost::system::errc::file_exists) -+ { -+ messages::resourceAlreadyExists( -+ asyncResp->res, "MetricReportDefinition", "Id", name); -+ return; -+ } -+ if (ec == boost::system::errc::too_many_files_open) -+ { -+ messages::createLimitReachedForResource(asyncResp->res); -+ return; -+ } -+ if (ec) -+ { -+ messages::internalError(asyncResp->res); -+ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; -+ return; -+ } -+ -+ messages::created(asyncResp->res); -+ }, -+ "xyz.openbmc_project.MonitoringService", -+ "/xyz/openbmc_project/MonitoringService/Reports", -+ "xyz.openbmc_project.MonitoringService.ReportsManagement", -+ "AddReport", name, domain, reportingType, reportActions, scanPeriod, -+ metricParams); -+ } -+ -+ class AddReport -+ { -+ public: -+ AddReport(AddReportArgs& args, std::shared_ptr<AsyncResp>& asyncResp) : -+ asyncResp{asyncResp}, addReportArgs{args} -+ {} -+ ~AddReport() -+ { -+ auto unmatched = replaceUriWithDbus( -+ uriToDbus, std::get<MetricParams>(addReportArgs)); -+ if (unmatched) -+ { -+ messages::resourceNotFound(asyncResp->res, "MetricProperties", -+ *unmatched); -+ return; -+ } -+ -+ addReport(asyncResp, addReportArgs); -+ } -+ -+ AddReport& operator+=( -+ const boost::container::flat_map<std::string, std::string>& rhs) -+ { -+ this->uriToDbus.insert(rhs.begin(), rhs.end()); -+ return *this; -+ } -+ -+ private: -+ std::shared_ptr<AsyncResp> asyncResp; -+ AddReportArgs addReportArgs; -+ boost::container::flat_map<std::string, std::string> uriToDbus{}; -+ }; - }; - - class MetricReportDefinition : public Node -@@ -146,6 +492,7 @@ class MetricReportDefinition : public Node - asyncResp->res.jsonValue["MetricReport"]["@odata.id"] = - telemetry::metricReportUri + id; - asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; -+ asyncResp->res.jsonValue["ReportUpdates"] = "Overwrite"; - - dbus::utility::getAllProperties( - [asyncResp](const boost::system::error_code ec, --- -2.16.6 - |