diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch | 813 |
1 files changed, 308 insertions, 505 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch index 9157f1bf1..b1334a420 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch @@ -1,7 +1,7 @@ -From d8b7e2f4eae85cd76d480970e888a50548523fc2 Mon Sep 17 00:00:00 2001 +From c7fce288802ece4a6e1ff71ee060a44e0b8fe992 Mon Sep 17 00:00:00 2001 From: "Wludzik, Jozef" <jozef.wludzik@intel.com> Date: Mon, 27 Apr 2020 17:24:15 +0200 -Subject: [PATCH 05/10] Redfish TelemetryService schema implementation +Subject: [PATCH 1/4] Redfish TelemetryService schema implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -9,73 +9,41 @@ Content-Transfer-Encoding: 8bit Added TelemetryService, MetricReports, MetricReportCollection, MetricReportDefinition and MetricReportDefinitionCollection schemas with GET method support. Added TelemetryService URI to root service. -Implemented communication with backend - MonitoringService. -Added schemes attributes that are supported by MonitoringService +Implemented communication with backend - Telemetry. +Added schemes attributes that are supported by Telemetry service design. User is able to fetch basic information about reports if -MonitoringService is present in OpenBMC. +Telemetry service is present in OpenBMC. +Added util function that converts decimal value into duration format +that is described by ISO 8601 and Redfish specification. Tested: - - Succesfully passed RedfishServiceValidator.py - - Validated conversion to duration format using whole - range of uint32_t type - - Validated assigning value to JSON response using different - closures and std::functions types + - Succesfully passed RedfishServiceValidator.py + - Verified DBus method calls to Telemetry service + - Verified all possible pages that are displayed to user when: + - Reports are fully defined in Telemetry + - Reports are partially available in Telemetry + - Telemetry is disabled + - Verified time_utils::toDurationString() output Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com> Signed-off-by: Adrian Ambrożewicz <adrian.ambrozewicz@linux.intel.com> +Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com> Change-Id: Ie6b0b49f4ef5eeaef07d1209b6c349270c04d570 - -%% original patch: 0001-Redfish-TelemetryService-schema-implementation.patch - -Change-Id: I547073faef9228e8dc5350ea28d06cdd3c5341f6 --- - include/dbus_utility.hpp | 21 +++ redfish-core/include/redfish.hpp | 10 ++ - redfish-core/include/utils/json_utils.hpp | 101 +++++++++++++ - redfish-core/include/utils/telemetry_utils.hpp | 100 +++++++++++++ - redfish-core/include/utils/time_utils.hpp | 97 +++++++++++++ - redfish-core/lib/metric_report.hpp | 149 +++++++++++++++++++ - redfish-core/lib/metric_report_definition.hpp | 191 +++++++++++++++++++++++++ + redfish-core/include/utils/telemetry_utils.hpp | 71 ++++++++++ + redfish-core/include/utils/time_utils.hpp | 78 +++++++++++ + redfish-core/lib/metric_report.hpp | 162 +++++++++++++++++++++ + redfish-core/lib/metric_report_definition.hpp | 186 +++++++++++++++++++++++++ redfish-core/lib/service_root.hpp | 2 + - redfish-core/lib/telemetry_service.hpp | 92 ++++++++++++ - 9 files changed, 763 insertions(+) + redfish-core/lib/telemetry_service.hpp | 93 +++++++++++++ + 7 files changed, 602 insertions(+) create mode 100644 redfish-core/include/utils/telemetry_utils.hpp create mode 100644 redfish-core/include/utils/time_utils.hpp create mode 100644 redfish-core/lib/metric_report.hpp create mode 100644 redfish-core/lib/metric_report_definition.hpp create mode 100644 redfish-core/lib/telemetry_service.hpp -diff --git a/include/dbus_utility.hpp b/include/dbus_utility.hpp -index 8ba9a57..ef3438b 100644 ---- a/include/dbus_utility.hpp -+++ b/include/dbus_utility.hpp -@@ -99,5 +99,26 @@ inline void checkDbusPathExists(const std::string& path, Callback&& callback) - std::array<std::string, 0>()); - } - -+template <typename Array, typename Callback> -+inline void getSubTreePaths(Callback&& callback, const std::string& path, -+ int depth, Array& interfaces) -+{ -+ crow::connections::systemBus->async_method_call( -+ callback, "xyz.openbmc_project.ObjectMapper", -+ "/xyz/openbmc_project/object_mapper", -+ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", path, depth, -+ interfaces); -+} -+ -+template <typename Callback> -+inline void getAllProperties(Callback&& callback, const std::string& service, -+ const std::string& path, -+ const std::string& interface) -+{ -+ crow::connections::systemBus->async_method_call( -+ callback, service, path, "org.freedesktop.DBus.Properties", "GetAll", -+ interface); -+} -+ - } // namespace utility - } // namespace dbus diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp index 54d5d0e..2587b37 100644 --- a/redfish-core/include/redfish.hpp @@ -111,154 +79,12 @@ index 54d5d0e..2587b37 100644 for (const auto& node : nodes) { node->initPrivileges(); -diff --git a/redfish-core/include/utils/json_utils.hpp b/redfish-core/include/utils/json_utils.hpp -index c355000..c866a2f 100644 ---- a/redfish-core/include/utils/json_utils.hpp -+++ b/redfish-core/include/utils/json_utils.hpp -@@ -13,14 +13,18 @@ - // See the License for the specific language governing permissions and - // limitations under the License. - */ -+ - #pragma once - -+#include <boost/container/flat_map.hpp> - #include <error_messages.hpp> - #include <http_request.hpp> - #include <http_response.hpp> - #include <nlohmann/json.hpp> - - #include <bitset> -+#include <string> -+#include <variant> - - namespace redfish - { -@@ -425,5 +429,102 @@ bool getValueFromJsonObject(nlohmann::json& jsonData, const std::string& key, - return details::unpackValue(jsonValue, key, value); - } - -+template <class T> -+struct IsStdFunction -+{ -+ static constexpr bool value = false; -+}; -+ -+template <class T> -+struct IsStdFunction<std::function<T>> -+{ -+ static constexpr bool value = true; -+}; -+ -+template <class T> -+constexpr bool is_std_function_v = IsStdFunction<T>::value; -+ -+/** -+ * @brief Assign dbus property to http response attribute if property is stored -+ * on the map. -+ */ -+template <typename T, typename S, typename... V> -+bool assignIfPresent( -+ const boost::container::flat_map<std::string, std::variant<V...>>& ret, -+ const char* propertyName, nlohmann::json& attribute, const S& convert) -+{ -+ if constexpr (is_std_function_v<S>) -+ { -+ if (!convert) -+ { -+ BMCWEB_LOG_ERROR << "Passed empty target as convert argument"; -+ return false; -+ } -+ } -+ -+ auto found = ret.find(propertyName); -+ if (found != ret.end()) -+ { -+ auto property = std::get_if<T>(&found->second); -+ if (property) -+ { -+ attribute = convert(*property); -+ return true; -+ } -+ else -+ { -+ BMCWEB_LOG_ERROR << "Variant does not contain this type"; -+ } -+ } -+ else -+ { -+ BMCWEB_LOG_ERROR << "Element not found in map"; -+ } -+ -+ return false; -+} -+ -+template <typename T, typename... V> -+bool assignIfPresent( -+ const boost::container::flat_map<std::string, std::variant<V...>>& ret, -+ const char* propertyName, nlohmann::json& attribute) -+{ -+ return assignIfPresent<T>(ret, propertyName, attribute, -+ [](const T& v) -> T { return v; }); -+} -+ -+template <typename T, typename... V> -+bool assignIfPresent( -+ const boost::container::flat_map<std::string, std::variant<V...>>& ret, -+ const char* attributeName, crow::Response& res) -+{ -+ return assignIfPresent<T>(ret, attributeName, res.jsonValue[attributeName]); -+} -+ -+/** -+ * @brief Translate dbusPaths received from ObjectMapper into Redfish -+ * collection members and fill http response with those information. -+ */ -+inline void dbusPathsToMembersArray(crow::Response& res, -+ const std::vector<std::string>& reports, -+ const char* path) -+{ -+ nlohmann::json& members = res.jsonValue["Members"]; -+ members = nlohmann::json::array(); -+ -+ for (const std::string& objpath : reports) -+ { -+ std::size_t lastPos = objpath.rfind("/"); -+ if (lastPos == std::string::npos) -+ { -+ BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath; -+ continue; -+ } -+ members.push_back({{"@odata.id", path + objpath.substr(lastPos + 1)}}); -+ } -+ -+ res.jsonValue["Members@odata.count"] = members.size(); -+} -+ - } // namespace json_util - } // namespace redfish diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp new file mode 100644 -index 0000000..05ed00f +index 0000000..8caee2d --- /dev/null +++ b/redfish-core/include/utils/telemetry_utils.hpp -@@ -0,0 +1,100 @@ -+/* -+// Copyright (c) 2018-2020 Intel Corporation -+// -+// Licensed under the Apache License, Version 2.0 (the "License"); -+// you may not use this file except in compliance with the License. -+// You may obtain a copy of the License at -+// -+// http://www.apache.org/licenses/LICENSE-2.0 -+// -+// Unless required by applicable law or agreed to in writing, software -+// distributed under the License is distributed on an "AS IS" BASIS, -+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+// See the License for the specific language governing permissions and -+// limitations under the License. -+*/ -+ +@@ -0,0 +1,71 @@ +#pragma once + +namespace redfish @@ -267,112 +93,79 @@ index 0000000..05ed00f +namespace telemetry +{ + -+static constexpr const char* metricReportDefinitionUri = ++constexpr const char* service = "xyz.openbmc_project.Telemetry"; ++constexpr const char* reportInterface = "xyz.openbmc_project.Telemetry.Report"; ++constexpr const char* metricReportDefinitionUri = + "/redfish/v1/TelemetryService/MetricReportDefinitions/"; -+static constexpr const char* metricReportUri = ++constexpr const char* metricReportUri = + "/redfish/v1/TelemetryService/MetricReports/"; -+static constexpr const char* reportInterface = -+ "xyz.openbmc_project.MonitoringService.Report"; -+static constexpr const char* telemetryPath = -+ "/xyz/openbmc_project/MonitoringService/Reports/TelemetryService"; + -+static void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp, -+ const char* uri) ++inline void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp, ++ const std::string& uri) +{ + const std::array<const char*, 1> interfaces = {reportInterface}; + -+ dbus::utility::getSubTreePaths( ++ crow::connections::systemBus->async_method_call( + [asyncResp, uri](const boost::system::error_code ec, -+ const std::vector<std::string>& reports) { -+ if (ec == boost::system::errc::no_such_file_or_directory) ++ const std::vector<std::string>& reportPaths) { ++ if (ec) + { + asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); + asyncResp->res.jsonValue["Members@odata.count"] = 0; + return; + } + -+ if (ec) ++ nlohmann::json& members = asyncResp->res.jsonValue["Members"]; ++ members = nlohmann::json::array(); ++ ++ for (const std::string& path : reportPaths) + { -+ messages::internalError(asyncResp->res); -+ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; -+ return; ++ std::size_t pos = path.rfind('/'); ++ if (pos == std::string::npos) ++ { ++ BMCWEB_LOG_ERROR << "Failed to find '/' in " << path; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ if (path.size() <= (pos + 1)) ++ { ++ BMCWEB_LOG_ERROR << "Failed to parse path " << path; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ members.push_back({{"@odata.id", uri + path.substr(pos + 1)}}); + } + -+ json_util::dbusPathsToMembersArray(asyncResp->res, reports, uri); ++ asyncResp->res.jsonValue["Members@odata.count"] = members.size(); + }, -+ telemetryPath, 1, interfaces); ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", ++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService", 1, ++ interfaces); +} + -+template <typename Callback> -+static void getReport(const std::shared_ptr<AsyncResp>& asyncResp, -+ const std::string& id, const char* schemaType, -+ const Callback&& callback) ++inline std::string getDbusReportPath(const std::string& id) +{ -+ const std::array<const char*, 1> interfaces = {reportInterface}; -+ -+ dbus::utility::getSubTreePaths( -+ [asyncResp, id, schemaType, -+ callback](const boost::system::error_code ec, -+ const std::vector<std::string>& reports) { -+ if (ec == boost::system::errc::no_such_file_or_directory) -+ { -+ messages::resourceNotFound(asyncResp->res, schemaType, id); -+ return; -+ } -+ -+ if (ec) -+ { -+ messages::internalError(asyncResp->res); -+ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; -+ return; -+ } -+ -+ const std::string target = "/xyz/openbmc_project/" -+ "MonitoringService/Reports/" -+ "TelemetryService/" + -+ id; -+ auto path = std::find(reports.begin(), reports.end(), target); -+ if (path == std::end(reports)) -+ { -+ messages::resourceNotFound(asyncResp->res, schemaType, id); -+ return; -+ } -+ callback(asyncResp, *path, id); -+ }, -+ telemetryPath, 1, interfaces); ++ std::string path = ++ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id; ++ dbus::utility::escapePathForDbus(path); ++ return path; +} ++ +} // namespace telemetry +} // namespace redfish diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp new file mode 100644 -index 0000000..0256b3f +index 0000000..dd4ea75 --- /dev/null +++ b/redfish-core/include/utils/time_utils.hpp -@@ -0,0 +1,97 @@ -+/* -+// Copyright (c) 2020 Intel Corporation -+// -+// Licensed under the Apache License, Version 2.0 (the "License"); -+// you may not use this file except in compliance with the License. -+// You may obtain a copy of the License at -+// -+// http://www.apache.org/licenses/LICENSE-2.0 -+// -+// Unless required by applicable law or agreed to in writing, software -+// distributed under the License is distributed on an "AS IS" BASIS, -+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+// See the License for the specific language governing permissions and -+// limitations under the License. -+*/ -+ +@@ -0,0 +1,78 @@ +#pragma once + -+#include <boost/algorithm/string/trim.hpp> -+ +#include <chrono> -+#include <cstdint> +#include <string> -+#include <type_traits> + +namespace redfish +{ @@ -383,63 +176,64 @@ index 0000000..0256b3f +namespace details +{ + -+template <typename T> -+std::string toDurationFormatItem(std::chrono::milliseconds& duration, -+ const char* postfix) ++inline void leftZeroPadding(std::string& str, const std::size_t padding) +{ -+ const auto t = std::chrono::duration_cast<T>(duration); -+ if (t.count() == 0) -+ { -+ return ""; -+ } -+ -+ std::stringstream ss; -+ if constexpr (std::is_same<T, std::chrono::milliseconds>::value) ++ if (str.size() < padding) + { -+ ss << static_cast<float>(t.count()) / -+ static_cast<float>(std::chrono::milliseconds::period::den); ++ str.insert(0, padding - str.size(), '0'); + } -+ else -+ { -+ ss << t.count(); -+ } -+ ss << postfix; -+ duration -= t; -+ return ss.str(); +} -+ +} // namespace details + +/** + * @brief Convert time value into duration format that is based on ISO 8601. -+ * Pattern: "-?P(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(.\\d+)?S)?)?" -+ * Reference: "Redfish Telemetry White Paper". ++ * Example output: "P12DT1M5.5S" ++ * Ref: Redfish Specification, Section 9.4.4. Duration values + */ -+std::string toDurationFormat(const uint32_t ms) ++std::string toDurationString(std::chrono::milliseconds ms) +{ -+ std::chrono::milliseconds duration(ms); -+ if (duration.count() == 0) ++ if (ms < std::chrono::milliseconds::zero()) + { -+ return "PT0S"; ++ return ""; + } + + std::string fmt; -+ fmt.reserve(sizeof("PxxxDTxxHxxMxx.xxxxxxS")); ++ fmt.reserve(sizeof("PxxxxxxxxxxxxDTxxHxxMxx.xxxxxxS")); ++ ++ using Days = std::chrono::duration<long, std::ratio<24 * 60 * 60>>; ++ Days days = std::chrono::floor<Days>(ms); ++ ms -= days; ++ ++ std::chrono::hours hours = std::chrono::floor<std::chrono::hours>(ms); ++ ms -= hours; ++ ++ std::chrono::minutes minutes = std::chrono::floor<std::chrono::minutes>(ms); ++ ms -= minutes; + -+ using Days = std::chrono::duration<int, std::ratio<24 * 60 * 60>>; ++ std::chrono::seconds seconds = std::chrono::floor<std::chrono::seconds>(ms); ++ ms -= seconds; + -+ fmt += "P"; -+ fmt += details::toDurationFormatItem<Days>(duration, "D"); -+ if (duration.count() == 0) ++ fmt = "P"; ++ if (days.count() > 0) + { -+ return fmt; ++ fmt += std::to_string(days.count()) + "D"; + } -+ + fmt += "T"; -+ fmt += details::toDurationFormatItem<std::chrono::hours>(duration, "H"); -+ fmt += details::toDurationFormatItem<std::chrono::minutes>(duration, "M"); -+ fmt += -+ details::toDurationFormatItem<std::chrono::milliseconds>(duration, "S"); ++ if (hours.count() > 0) ++ { ++ fmt += std::to_string(hours.count()) + "H"; ++ } ++ if (minutes.count() > 0) ++ { ++ fmt += std::to_string(minutes.count()) + "M"; ++ } ++ if (seconds.count() != 0 || ms.count() != 0) ++ { ++ fmt += std::to_string(seconds.count()) + "."; ++ std::string msStr = std::to_string(ms.count()); ++ details::leftZeroPadding(msStr, 3); ++ fmt += msStr + "S"; ++ } + + return fmt; +} @@ -448,43 +242,23 @@ index 0000000..0256b3f +} // namespace redfish diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp new file mode 100644 -index 0000000..4d1c4e5 +index 0000000..050304c --- /dev/null +++ b/redfish-core/lib/metric_report.hpp -@@ -0,0 +1,149 @@ -+/* -+// Copyright (c) 2018-2020 Intel Corporation -+// -+// Licensed under the Apache License, Version 2.0 (the "License"); -+// you may not use this file except in compliance with the License. -+// You may obtain a copy of the License at -+// -+// http://www.apache.org/licenses/LICENSE-2.0 -+// -+// Unless required by applicable law or agreed to in writing, software -+// distributed under the License is distributed on an "AS IS" BASIS, -+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+// See the License for the specific language governing permissions and -+// limitations under the License. -+*/ -+ +@@ -0,0 +1,162 @@ +#pragma once + +#include "node.hpp" +#include "utils/telemetry_utils.hpp" + -+#include <boost/container/flat_map.hpp> -+ -+#include <system_error> -+#include <variant> -+ +namespace redfish +{ + +class MetricReportCollection : public Node +{ + public: -+ MetricReportCollection(App& app) : Node(app, telemetry::metricReportUri) ++ MetricReportCollection(App& app) : ++ Node(app, "/redfish/v1/TelemetryService/MetricReports/") + { + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, @@ -514,7 +288,7 @@ index 0000000..4d1c4e5 +{ + public: + MetricReport(App& app) : -+ Node(app, std::string(telemetry::metricReportUri) + "<str>/", ++ Node(app, "/redfish/v1/TelemetryService/MetricReports/<str>/", + std::string()) + { + entityPrivileges = { @@ -539,33 +313,74 @@ index 0000000..4d1c4e5 + } + + const std::string& id = params[0]; -+ telemetry::getReport(asyncResp, id, schemaType, getReportProperties); ++ const std::string reportPath = telemetry::getDbusReportPath(id); ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, id, reportPath](const boost::system::error_code& ec) { ++ if (ec.value() == EBADR) ++ { ++ messages::resourceNotFound(asyncResp->res, schemaType, id); ++ return; ++ } ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, ++ id](const boost::system::error_code ec, ++ const std::variant<TimestampReadings>& ret) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ fillReport(asyncResp, id, ret); ++ }, ++ telemetry::service, reportPath, ++ "org.freedesktop.DBus.Properties", "Get", ++ telemetry::reportInterface, "Readings"); ++ }, ++ telemetry::service, reportPath, telemetry::reportInterface, ++ "Update"); + } + + using Readings = -+ std::vector<std::tuple<std::string, std::string, double, int32_t>>; -+ using MetricValues = std::vector<std::map<std::string, std::string>>; ++ std::vector<std::tuple<std::string, std::string, double, uint64_t>>; ++ using TimestampReadings = std::tuple<uint64_t, Readings>; + -+ static MetricValues toMetricValues(const Readings& readings) ++ static nlohmann::json toMetricValues(const Readings& readings) + { -+ MetricValues metricValues; ++ nlohmann::json metricValues = nlohmann::json::array_t(); + + for (auto& [id, metadata, sensorValue, timestamp] : readings) + { ++ nlohmann::json metadataJson = nlohmann::json::parse(metadata); + metricValues.push_back({ + {"MetricId", id}, -+ {"MetricProperty", metadata}, ++ {"MetricDefinition", metadataJson.contains("MetricDefinition") ++ ? metadataJson["MetricDefinition"] ++ : nlohmann::json()}, ++ {"MetricProperty", metadataJson.contains("MetricProperty") ++ ? metadataJson["MetricProperty"] ++ : nlohmann::json()}, + {"MetricValue", std::to_string(sensorValue)}, -+ {"Timestamp", crow::utility::getDateTime(timestamp)}, ++ {"Timestamp", ++ crow::utility::getDateTime(static_cast<time_t>(timestamp))}, + }); + } + + return metricValues; + } + -+ static void getReportProperties(const std::shared_ptr<AsyncResp> asyncResp, -+ const std::string& reportPath, -+ const std::string& id) ++ static void fillReport(const std::shared_ptr<AsyncResp>& asyncResp, ++ const std::string& id, ++ const std::variant<TimestampReadings>& var) + { + asyncResp->res.jsonValue["@odata.type"] = schemaType; + asyncResp->res.jsonValue["@odata.id"] = telemetry::metricReportUri + id; @@ -574,27 +389,19 @@ index 0000000..4d1c4e5 + asyncResp->res.jsonValue["MetricReportDefinition"]["@odata.id"] = + telemetry::metricReportDefinitionUri + id; + -+ dbus::utility::getAllProperties( -+ [asyncResp]( -+ const boost::system::error_code ec, -+ const boost::container::flat_map< -+ std::string, std::variant<Readings, int32_t>>& ret) { -+ if (ec) -+ { -+ messages::internalError(asyncResp->res); -+ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; -+ return; -+ } ++ const TimestampReadings* timestampReadings = ++ std::get_if<TimestampReadings>(&var); ++ if (!timestampReadings) ++ { ++ BMCWEB_LOG_ERROR << "Property type mismatch or property is missing"; ++ messages::internalError(asyncResp->res); ++ return; ++ } + -+ json_util::assignIfPresent<int32_t>( -+ ret, "Timestamp", asyncResp->res.jsonValue["Timestamp"], -+ crow::utility::getDateTime); -+ json_util::assignIfPresent<Readings>( -+ ret, "Readings", asyncResp->res.jsonValue["MetricValues"], -+ toMetricValues); -+ }, -+ "xyz.openbmc_project.MonitoringService", reportPath, -+ "xyz.openbmc_project.MonitoringService.Report"); ++ const auto& [timestamp, readings] = *timestampReadings; ++ asyncResp->res.jsonValue["Timestamp"] = ++ crow::utility::getDateTime(static_cast<time_t>(timestamp)); ++ asyncResp->res.jsonValue["MetricValues"] = toMetricValues(readings); + } + + static constexpr const char* schemaType = @@ -603,35 +410,17 @@ index 0000000..4d1c4e5 +} // namespace redfish diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp new file mode 100644 -index 0000000..72e62e9 +index 0000000..48c56e6 --- /dev/null +++ b/redfish-core/lib/metric_report_definition.hpp -@@ -0,0 +1,191 @@ -+/* -+// Copyright (c) 2018-2020 Intel Corporation -+// -+// Licensed under the Apache License, Version 2.0 (the "License"); -+// you may not use this file except in compliance with the License. -+// You may obtain a copy of the License at -+// -+// http://www.apache.org/licenses/LICENSE-2.0 -+// -+// Unless required by applicable law or agreed to in writing, software -+// distributed under the License is distributed on an "AS IS" BASIS, -+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+// See the License for the specific language governing permissions and -+// limitations under the License. -+*/ -+ +@@ -0,0 +1,186 @@ +#pragma once + +#include "node.hpp" +#include "utils/telemetry_utils.hpp" +#include "utils/time_utils.hpp" + -+#include <boost/container/flat_map.hpp> -+ -+#include <system_error> ++#include <tuple> +#include <variant> + +namespace redfish @@ -641,7 +430,7 @@ index 0000000..72e62e9 +{ + public: + MetricReportDefinitionCollection(App& app) : -+ Node(app, telemetry::metricReportDefinitionUri) ++ Node(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") + { + entityPrivileges = { + {boost::beast::http::verb::get, {{"Login"}}}, @@ -672,7 +461,7 @@ index 0000000..72e62e9 +{ + public: + MetricReportDefinition(App& app) : -+ Node(app, std::string(telemetry::metricReportDefinitionUri) + "<str>/", ++ Node(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/", + std::string()) + { + entityPrivileges = { @@ -697,55 +486,40 @@ index 0000000..72e62e9 + } + + const std::string& id = params[0]; ++ crow::connections::systemBus->async_method_call( ++ [asyncResp, ++ id](const boost::system::error_code ec, ++ const std::vector<std::pair< ++ std::string, std::variant<bool, ReadingParameters, ++ std::string, uint64_t>>>& ret) { ++ if (ec.value() == EBADR) ++ { ++ messages::resourceNotFound(asyncResp->res, schemaType, id); ++ return; ++ } ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; ++ messages::internalError(asyncResp->res); ++ return; ++ } + -+ telemetry::getReport(asyncResp, id, schemaType, -+ getReportDefinitonProperties); -+ } -+ -+ static std::vector<std::string> -+ toReportActions(const std::vector<std::string>& actions) -+ { -+ const boost::container::flat_map<std::string, std::string> -+ reportActions = { -+ {"Event", "RedfishEvent"}, -+ {"Log", "LogToMetricReportsCollection"}, -+ }; -+ -+ std::vector<std::string> out; -+ for (auto& action : actions) -+ { -+ auto found = reportActions.find(action); -+ if (found != reportActions.end()) -+ { -+ out.emplace_back(found->second); -+ } -+ } -+ return out; ++ fillReportDefinition(asyncResp, id, ret); ++ }, ++ telemetry::service, telemetry::getDbusReportPath(id), ++ "org.freedesktop.DBus.Properties", "GetAll", ++ telemetry::reportInterface); + } + + using ReadingParameters = + std::vector<std::tuple<std::vector<sdbusplus::message::object_path>, + std::string, std::string, std::string>>; + -+ static nlohmann::json toMetrics(const ReadingParameters& params) -+ { -+ nlohmann::json metrics = nlohmann::json::array(); -+ -+ for (auto& [sensorPaths, operationType, id, metadata] : params) -+ { -+ metrics.push_back({ -+ {"MetricId", id}, -+ {"MetricProperties", std::vector<std::string>() = {metadata}}, -+ }); -+ } -+ -+ return metrics; -+ } -+ -+ static void -+ getReportDefinitonProperties(const std::shared_ptr<AsyncResp> asyncResp, -+ const std::string& reportPath, -+ const std::string& id) ++ static void fillReportDefinition( ++ const std::shared_ptr<AsyncResp>& asyncResp, const std::string& id, ++ const std::vector< ++ std::pair<std::string, std::variant<bool, ReadingParameters, ++ std::string, uint64_t>>>& ret) + { + asyncResp->res.jsonValue["@odata.type"] = schemaType; + asyncResp->res.jsonValue["@odata.id"] = @@ -755,45 +529,73 @@ index 0000000..72e62e9 + asyncResp->res.jsonValue["MetricReport"]["@odata.id"] = + telemetry::metricReportUri + id; + asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; ++ asyncResp->res.jsonValue["ReportUpdates"] = "Overwrite"; ++ ++ const bool* emitsReadingsUpdate = nullptr; ++ const bool* logToMetricReportsCollection = nullptr; ++ const ReadingParameters* readingParams = nullptr; ++ const std::string* reportingType = nullptr; ++ const uint64_t* interval = nullptr; ++ for (const auto& [key, var] : ret) ++ { ++ if (key == "EmitsReadingsUpdate") ++ { ++ emitsReadingsUpdate = std::get_if<bool>(&var); ++ } ++ else if (key == "LogToMetricReportsCollection") ++ { ++ logToMetricReportsCollection = std::get_if<bool>(&var); ++ } ++ else if (key == "ReadingParameters") ++ { ++ readingParams = std::get_if<ReadingParameters>(&var); ++ } ++ else if (key == "ReportingType") ++ { ++ reportingType = std::get_if<std::string>(&var); ++ } ++ else if (key == "Interval") ++ { ++ interval = std::get_if<uint64_t>(&var); ++ } ++ } ++ if (!emitsReadingsUpdate || !logToMetricReportsCollection || ++ !readingParams || !reportingType || !interval) ++ { ++ BMCWEB_LOG_ERROR << "Property type mismatch or property is missing"; ++ messages::internalError(asyncResp->res); ++ return; ++ } + -+ dbus::utility::getAllProperties( -+ [asyncResp](const boost::system::error_code ec, -+ const boost::container::flat_map< -+ std::string, -+ std::variant<std::string, std::vector<std::string>, -+ uint32_t, ReadingParameters>>& ret) { -+ if (ec) -+ { -+ messages::internalError(asyncResp->res); -+ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; -+ return; -+ } ++ std::vector<std::string> redfishReportActions; ++ redfishReportActions.reserve(2); ++ if (*emitsReadingsUpdate) ++ { ++ redfishReportActions.emplace_back("RedfishEvent"); ++ } ++ if (*logToMetricReportsCollection) ++ { ++ redfishReportActions.emplace_back("LogToMetricReportsCollection"); ++ } + -+ json_util::assignIfPresent<std::vector<std::string>>( -+ ret, "ReportAction", -+ asyncResp->res.jsonValue["ReportActions"], toReportActions); -+ auto assigned = json_util::assignIfPresent<std::string>( -+ ret, "ReportingType", -+ asyncResp->res.jsonValue["MetricReportDefinitionType"]); -+ if (assigned && -+ asyncResp->res.jsonValue["MetricReportDefinitionType"] == -+ "Periodic") -+ { -+ json_util::assignIfPresent<uint32_t>( -+ ret, "ScanPeriod", -+ asyncResp->res -+ .jsonValue["Schedule"]["RecurrenceInterval"], -+ time_utils::toDurationFormat); -+ } -+ json_util::assignIfPresent<ReadingParameters>( -+ ret, "ReadingParameters", -+ asyncResp->res.jsonValue["Metrics"], toMetrics); -+ }, -+ "xyz.openbmc_project.MonitoringService", reportPath, -+ "xyz.openbmc_project.MonitoringService.Report"); ++ nlohmann::json metrics = nlohmann::json::array(); ++ for (auto& [sensorPaths, operationType, id, metadata] : *readingParams) ++ { ++ nlohmann::json metadataJson = nlohmann::json::parse(metadata); ++ metrics.push_back({ ++ {"MetricId", id}, ++ {"MetricProperties", metadataJson.contains("MetricProperties") ++ ? metadataJson["MetricProperties"] ++ : nlohmann::json()}, ++ }); ++ } ++ asyncResp->res.jsonValue["Metrics"] = metrics; ++ asyncResp->res.jsonValue["MetricReportDefinitionType"] = *reportingType; ++ asyncResp->res.jsonValue["ReportActions"] = redfishReportActions; ++ asyncResp->res.jsonValue["Schedule"]["RecurrenceInterval"] = ++ time_utils::toDurationString(std::chrono::milliseconds(*interval)); + } + -+ public: + static constexpr const char* schemaType = + "#MetricReportDefinition.v1_3_0.MetricReportDefinition"; +}; @@ -813,32 +615,14 @@ index 629280c..3df5ec5 100644 diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp new file mode 100644 -index 0000000..b849781 +index 0000000..a6acc34 --- /dev/null +++ b/redfish-core/lib/telemetry_service.hpp -@@ -0,0 +1,92 @@ -+/* -+// Copyright (c) 2018-2020 Intel Corporation -+// -+// Licensed under the Apache License, Version 2.0 (the "License"); -+// you may not use this file except in compliance with the License. -+// You may obtain a copy of the License at -+// -+// http://www.apache.org/licenses/LICENSE-2.0 -+// -+// Unless required by applicable law or agreed to in writing, software -+// distributed under the License is distributed on an "AS IS" BASIS, -+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+// See the License for the specific language governing permissions and -+// limitations under the License. -+*/ -+ +@@ -0,0 +1,93 @@ +#pragma once + +#include "node.hpp" -+#include "utils/time_utils.hpp" -+ -+#include <boost/container/flat_map.hpp> ++#include "utils/telemetry_utils.hpp" + +#include <variant> + @@ -864,7 +648,7 @@ index 0000000..b849781 + const std::vector<std::string>&) override + { + res.jsonValue["@odata.type"] = -+ "#TelemetryService.v1_2_0.TelemetryService"; ++ "#TelemetryService.v1_2_1.TelemetryService"; + res.jsonValue["@odata.id"] = "/redfish/v1/TelemetryService"; + res.jsonValue["Id"] = "TelemetryService"; + res.jsonValue["Name"] = "Telemetry Service"; @@ -876,36 +660,55 @@ index 0000000..b849781 + res.jsonValue["MetricReports"]["@odata.id"] = + "/redfish/v1/TelemetryService/MetricReports"; + -+ getMonitoringServiceProperties(res); -+ } -+ -+ void getMonitoringServiceProperties(crow::Response& res) -+ { + auto asyncResp = std::make_shared<AsyncResp>(res); -+ dbus::utility::getAllProperties( ++ crow::connections::systemBus->async_method_call( + [asyncResp]( + const boost::system::error_code ec, -+ const boost::container::flat_map<std::string, -+ std::variant<uint32_t>>& ret) { -+ if (ec) ++ const std::vector<std::pair< ++ std::string, std::variant<uint32_t, uint64_t>>>& ret) { ++ if (ec == boost::system::errc::host_unreachable) + { + asyncResp->res.jsonValue["Status"]["State"] = "Absent"; ++ return; ++ } ++ if (ec) ++ { + BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; ++ messages::internalError(asyncResp->res); + return; + } + + asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; + -+ json_util::assignIfPresent<uint32_t>(ret, "MaxReports", -+ asyncResp->res); -+ json_util::assignIfPresent<uint32_t>( -+ ret, "PollRateResolution", -+ asyncResp->res.jsonValue["MinCollectionInterval"], -+ time_utils::toDurationFormat); ++ const size_t* maxReports = nullptr; ++ const uint64_t* minInterval = nullptr; ++ for (const auto& [key, var] : ret) ++ { ++ if (key == "MaxReports") ++ { ++ maxReports = std::get_if<size_t>(&var); ++ } ++ else if (key == "MinInterval") ++ { ++ minInterval = std::get_if<uint64_t>(&var); ++ } ++ } ++ if (!maxReports || !minInterval) ++ { ++ BMCWEB_LOG_ERROR ++ << "Property type mismatch or property is missing"; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ asyncResp->res.jsonValue["MaxReports"] = *maxReports; ++ asyncResp->res.jsonValue["MinCollectionInterval"] = ++ time_utils::toDurationString(std::chrono::milliseconds( ++ static_cast<time_t>(*minInterval))); + }, -+ "xyz.openbmc_project.MonitoringService", -+ "/xyz/openbmc_project/MonitoringService/Reports", -+ "xyz.openbmc_project.MonitoringService.ReportsManagement"); ++ telemetry::service, "/xyz/openbmc_project/Telemetry/Reports", ++ "org.freedesktop.DBus.Properties", "GetAll", ++ "xyz.openbmc_project.Telemetry.ReportManager"); + } +}; +} // namespace redfish |