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.patch717
1 files changed, 0 insertions, 717 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
deleted file mode 100644
index 208831338..000000000
--- a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch
+++ /dev/null
@@ -1,717 +0,0 @@
-From d50e4ce193703c008d3293acd03e1c0542c0c215 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] Redfish TelemetryService schema implementation
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-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 - Telemetry.
-Added schemes attributes that are supported by Telemetry service
-design. User is able to fetch basic information about reports if
-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
- - 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
----
- redfish-core/include/redfish.hpp | 10 +
- .../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 | 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/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
-index dabf78e..b366e24 100644
---- a/redfish-core/include/redfish.hpp
-+++ b/redfish-core/include/redfish.hpp
-@@ -25,6 +25,8 @@
- #include "../lib/managers.hpp"
- #include "../lib/memory.hpp"
- #include "../lib/message_registries.hpp"
-+#include "../lib/metric_report.hpp"
-+#include "../lib/metric_report_definition.hpp"
- #include "../lib/network_protocol.hpp"
- #include "../lib/pcie.hpp"
- #include "../lib/power.hpp"
-@@ -36,6 +38,7 @@
- #include "../lib/storage.hpp"
- #include "../lib/systems.hpp"
- #include "../lib/task.hpp"
-+#include "../lib/telemetry_service.hpp"
- #include "../lib/thermal.hpp"
- #include "../lib/update_service.hpp"
- #ifdef BMCWEB_ENABLE_VM_NBDPROXY
-@@ -212,6 +215,13 @@ class RedfishService
- nodes.emplace_back(std::make_unique<HypervisorInterface>(app));
- nodes.emplace_back(std::make_unique<HypervisorSystem>(app));
-
-+ nodes.emplace_back(std::make_unique<TelemetryService>(app));
-+ nodes.emplace_back(
-+ std::make_unique<MetricReportDefinitionCollection>(app));
-+ nodes.emplace_back(std::make_unique<MetricReportDefinition>(app));
-+ nodes.emplace_back(std::make_unique<MetricReportCollection>(app));
-+ nodes.emplace_back(std::make_unique<MetricReport>(app));
-+
- for (const auto& node : nodes)
- {
- node->initPrivileges();
-diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp
-new file mode 100644
-index 0000000..8caee2d
---- /dev/null
-+++ b/redfish-core/include/utils/telemetry_utils.hpp
-@@ -0,0 +1,71 @@
-+#pragma once
-+
-+namespace redfish
-+{
-+
-+namespace telemetry
-+{
-+
-+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/";
-+constexpr const char* metricReportUri =
-+ "/redfish/v1/TelemetryService/MetricReports/";
-+
-+inline void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp,
-+ const std::string& uri)
-+{
-+ const std::array<const char*, 1> interfaces = {reportInterface};
-+
-+ crow::connections::systemBus->async_method_call(
-+ [asyncResp, uri](const boost::system::error_code ec,
-+ const std::vector<std::string>& reportPaths) {
-+ if (ec)
-+ {
-+ asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
-+ asyncResp->res.jsonValue["Members@odata.count"] = 0;
-+ return;
-+ }
-+
-+ nlohmann::json& members = asyncResp->res.jsonValue["Members"];
-+ members = nlohmann::json::array();
-+
-+ for (const std::string& path : reportPaths)
-+ {
-+ 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)}});
-+ }
-+
-+ asyncResp->res.jsonValue["Members@odata.count"] = members.size();
-+ },
-+ "xyz.openbmc_project.ObjectMapper",
-+ "/xyz/openbmc_project/object_mapper",
-+ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
-+ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService", 1,
-+ interfaces);
-+}
-+
-+inline std::string getDbusReportPath(const std::string& id)
-+{
-+ 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..dd4ea75
---- /dev/null
-+++ b/redfish-core/include/utils/time_utils.hpp
-@@ -0,0 +1,78 @@
-+#pragma once
-+
-+#include <chrono>
-+#include <string>
-+
-+namespace redfish
-+{
-+
-+namespace time_utils
-+{
-+
-+namespace details
-+{
-+
-+inline void leftZeroPadding(std::string& str, const std::size_t padding)
-+{
-+ if (str.size() < padding)
-+ {
-+ str.insert(0, padding - str.size(), '0');
-+ }
-+}
-+} // namespace details
-+
-+/**
-+ * @brief Convert time value into duration format that is based on ISO 8601.
-+ * Example output: "P12DT1M5.5S"
-+ * Ref: Redfish Specification, Section 9.4.4. Duration values
-+ */
-+std::string toDurationString(std::chrono::milliseconds ms)
-+{
-+ if (ms < std::chrono::milliseconds::zero())
-+ {
-+ return "";
-+ }
-+
-+ std::string fmt;
-+ 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;
-+
-+ std::chrono::seconds seconds = std::chrono::floor<std::chrono::seconds>(ms);
-+ ms -= seconds;
-+
-+ fmt = "P";
-+ if (days.count() > 0)
-+ {
-+ fmt += std::to_string(days.count()) + "D";
-+ }
-+ fmt += "T";
-+ 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;
-+}
-+
-+} // namespace time_utils
-+} // namespace redfish
-diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp
-new file mode 100644
-index 0000000..050304c
---- /dev/null
-+++ b/redfish-core/lib/metric_report.hpp
-@@ -0,0 +1,162 @@
-+#pragma once
-+
-+#include "node.hpp"
-+#include "utils/telemetry_utils.hpp"
-+
-+namespace redfish
-+{
-+
-+class MetricReportCollection : public Node
-+{
-+ public:
-+ MetricReportCollection(App& app) :
-+ Node(app, "/redfish/v1/TelemetryService/MetricReports/")
-+ {
-+ entityPrivileges = {
-+ {boost::beast::http::verb::get, {{"Login"}}},
-+ {boost::beast::http::verb::head, {{"Login"}}},
-+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
-+ }
-+
-+ private:
-+ void doGet(crow::Response& res, const crow::Request&,
-+ const std::vector<std::string>&) override
-+ {
-+ res.jsonValue["@odata.type"] =
-+ "#MetricReportCollection.MetricReportCollection";
-+ res.jsonValue["@odata.id"] =
-+ "/redfish/v1/TelemetryService/MetricReports";
-+ res.jsonValue["Name"] = "Metric Report Collection";
-+
-+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+ telemetry::getReportCollection(asyncResp, telemetry::metricReportUri);
-+ }
-+};
-+
-+class MetricReport : public Node
-+{
-+ public:
-+ MetricReport(App& app) :
-+ Node(app, "/redfish/v1/TelemetryService/MetricReports/<str>/",
-+ std::string())
-+ {
-+ entityPrivileges = {
-+ {boost::beast::http::verb::get, {{"Login"}}},
-+ {boost::beast::http::verb::head, {{"Login"}}},
-+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
-+ }
-+
-+ private:
-+ void doGet(crow::Response& res, const crow::Request&,
-+ const std::vector<std::string>& params) override
-+ {
-+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+
-+ if (params.size() != 1)
-+ {
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ const std::string& id = params[0];
-+ 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, uint64_t>>;
-+ using TimestampReadings = std::tuple<uint64_t, Readings>;
-+
-+ static nlohmann::json toMetricValues(const Readings& readings)
-+ {
-+ 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},
-+ {"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(static_cast<time_t>(timestamp))},
-+ });
-+ }
-+
-+ return metricValues;
-+ }
-+
-+ 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;
-+ asyncResp->res.jsonValue["Id"] = id;
-+ asyncResp->res.jsonValue["Name"] = id;
-+ asyncResp->res.jsonValue["MetricReportDefinition"]["@odata.id"] =
-+ telemetry::metricReportDefinitionUri + id;
-+
-+ 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;
-+ }
-+
-+ 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 =
-+ "#MetricReport.v1_3_0.MetricReport";
-+};
-+} // 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..48c56e6
---- /dev/null
-+++ b/redfish-core/lib/metric_report_definition.hpp
-@@ -0,0 +1,186 @@
-+#pragma once
-+
-+#include "node.hpp"
-+#include "utils/telemetry_utils.hpp"
-+#include "utils/time_utils.hpp"
-+
-+#include <tuple>
-+#include <variant>
-+
-+namespace redfish
-+{
-+
-+class MetricReportDefinitionCollection : public Node
-+{
-+ public:
-+ MetricReportDefinitionCollection(App& app) :
-+ Node(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
-+ {
-+ entityPrivileges = {
-+ {boost::beast::http::verb::get, {{"Login"}}},
-+ {boost::beast::http::verb::head, {{"Login"}}},
-+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
-+ }
-+
-+ private:
-+ void doGet(crow::Response& res, const crow::Request&,
-+ const std::vector<std::string>&) override
-+ {
-+ res.jsonValue["@odata.type"] = "#MetricReportDefinitionCollection."
-+ "MetricReportDefinitionCollection";
-+ res.jsonValue["@odata.id"] =
-+ "/redfish/v1/TelemetryService/MetricReportDefinitions";
-+ res.jsonValue["Name"] = "Metric Definition Collection";
-+
-+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+ telemetry::getReportCollection(asyncResp,
-+ telemetry::metricReportDefinitionUri);
-+ }
-+};
-+
-+class MetricReportDefinition : public Node
-+{
-+ public:
-+ MetricReportDefinition(App& app) :
-+ Node(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/",
-+ std::string())
-+ {
-+ entityPrivileges = {
-+ {boost::beast::http::verb::get, {{"Login"}}},
-+ {boost::beast::http::verb::head, {{"Login"}}},
-+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
-+ }
-+
-+ private:
-+ void doGet(crow::Response& res, const crow::Request&,
-+ const std::vector<std::string>& params) override
-+ {
-+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+
-+ if (params.size() != 1)
-+ {
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ 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;
-+ }
-+
-+ 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 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"] =
-+ telemetry::metricReportDefinitionUri + id;
-+ asyncResp->res.jsonValue["Id"] = id;
-+ asyncResp->res.jsonValue["Name"] = id;
-+ 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;
-+ }
-+
-+ std::vector<std::string> redfishReportActions;
-+ redfishReportActions.reserve(2);
-+ if (*emitsReadingsUpdate)
-+ {
-+ redfishReportActions.emplace_back("RedfishEvent");
-+ }
-+ if (*logToMetricReportsCollection)
-+ {
-+ redfishReportActions.emplace_back("LogToMetricReportsCollection");
-+ }
-+
-+ 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));
-+ }
-+
-+ static constexpr const char* schemaType =
-+ "#MetricReportDefinition.v1_3_0.MetricReportDefinition";
-+};
-+} // namespace redfish
-diff --git a/redfish-core/lib/service_root.hpp b/redfish-core/lib/service_root.hpp
-index 629280c..3df5ec5 100644
---- a/redfish-core/lib/service_root.hpp
-+++ b/redfish-core/lib/service_root.hpp
-@@ -68,6 +68,8 @@ class ServiceRoot : public Node
- res.jsonValue["Tasks"] = {{"@odata.id", "/redfish/v1/TaskService"}};
- res.jsonValue["EventService"] = {
- {"@odata.id", "/redfish/v1/EventService"}};
-+ res.jsonValue["TelemetryService"] = {
-+ {"@odata.id", "/redfish/v1/TelemetryService"}};
- res.end();
- }
-
-diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp
-new file mode 100644
-index 0000000..a6acc34
---- /dev/null
-+++ b/redfish-core/lib/telemetry_service.hpp
-@@ -0,0 +1,93 @@
-+#pragma once
-+
-+#include "node.hpp"
-+#include "utils/telemetry_utils.hpp"
-+
-+#include <variant>
-+
-+namespace redfish
-+{
-+
-+class TelemetryService : public Node
-+{
-+ public:
-+ TelemetryService(App& app) : Node(app, "/redfish/v1/TelemetryService/")
-+ {
-+ entityPrivileges = {
-+ {boost::beast::http::verb::get, {{"Login"}}},
-+ {boost::beast::http::verb::head, {{"Login"}}},
-+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
-+ }
-+
-+ private:
-+ void doGet(crow::Response& res, const crow::Request&,
-+ const std::vector<std::string>&) override
-+ {
-+ res.jsonValue["@odata.type"] =
-+ "#TelemetryService.v1_2_1.TelemetryService";
-+ res.jsonValue["@odata.id"] = "/redfish/v1/TelemetryService";
-+ res.jsonValue["Id"] = "TelemetryService";
-+ res.jsonValue["Name"] = "Telemetry Service";
-+
-+ res.jsonValue["LogService"]["@odata.id"] =
-+ "/redfish/v1/Managers/bmc/LogServices/Journal";
-+ res.jsonValue["MetricReportDefinitions"]["@odata.id"] =
-+ "/redfish/v1/TelemetryService/MetricReportDefinitions";
-+ res.jsonValue["MetricReports"]["@odata.id"] =
-+ "/redfish/v1/TelemetryService/MetricReports";
-+
-+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+ crow::connections::systemBus->async_method_call(
-+ [asyncResp](
-+ const boost::system::error_code 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";
-+
-+ 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)));
-+ },
-+ telemetry::service, "/xyz/openbmc_project/Telemetry/Reports",
-+ "org.freedesktop.DBus.Properties", "GetAll",
-+ "xyz.openbmc_project.Telemetry.ReportManager");
-+ }
-+};
-+} // namespace redfish
---
-2.17.1
-