summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch
diff options
context:
space:
mode:
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.patch598
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
-