summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch
diff options
context:
space:
mode:
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.patch813
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