summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry
diff options
context:
space:
mode:
authorJason M. Bills <jason.m.bills@linux.intel.com>2020-08-31 23:56:28 +0300
committerJason M. Bills <jason.m.bills@linux.intel.com>2020-09-02 00:21:46 +0300
commitf99301c1a626951ee7feee081a1494e795d0e243 (patch)
treeca75379d317be9cc1757a00e0352a048b5d3200b /meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry
parent40108db4434d8c2e0a1ad2d1dd3f5ae34b17352c (diff)
downloadopenbmc-f99301c1a626951ee7feee081a1494e795d0e243.tar.xz
Update to internal 0.74
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch913
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch594
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-DELETE-in-MetricReportDefinitions-st.patch68
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-OnRequest-in-MetricReportDefinition.patch169
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Add-support-for-MetricDefinition-scheme.patch535
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Fix-MetricReport-timestamp-for-EventService.patch78
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README21
7 files changed, 2378 insertions, 0 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
new file mode 100644
index 000000000..3850c8fa8
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch
@@ -0,0 +1,913 @@
+From 7820421433349df28bd393e8d610d1848af0f1c8 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 1/5] 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 - MonitoringService.
+Added schemes attributes that are supported by MonitoringService
+design. User is able to fetch basic information about reports if
+MonitoringService is present in OpenBMC.
+
+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
+
+Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
+Signed-off-by: Adrian Ambrożewicz <adrian.ambrozewicz@linux.intel.com>
+Change-Id: Ie6b0b49f4ef5eeaef07d1209b6c349270c04d570
+---
+ 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 | 193 +++++++++++++++++++++++++
+ redfish-core/lib/service_root.hpp | 2 +
+ redfish-core/lib/telemetry_service.hpp | 92 ++++++++++++
+ 9 files changed, 765 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 e1360f7..3df88d8 100644
+--- a/include/dbus_utility.hpp
++++ b/include/dbus_utility.hpp
+@@ -109,5 +109,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 cc98e1a..3d4c117 100644
+--- a/redfish-core/include/redfish.hpp
++++ b/redfish-core/include/redfish.hpp
+@@ -25,6 +25,8 @@
+ #include "../lib/log_services.hpp"
+ #include "../lib/managers.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"
+@@ -35,6 +37,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
+@@ -202,6 +205,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/json_utils.hpp b/redfish-core/include/utils/json_utils.hpp
+index d578de4..fbb259d 100644
+--- a/redfish-core/include/utils/json_utils.hpp
++++ b/redfish-core/include/utils/json_utils.hpp
+@@ -13,15 +13,19 @@
+ // See the License for the specific language governing permissions and
+ // limitations under the License.
+ */
++
+ #pragma once
+
+ #include <http_request.h>
+ #include <http_response.h>
+
++#include <boost/container/flat_map.hpp>
+ #include <error_messages.hpp>
+ #include <nlohmann/json.hpp>
+
+ #include <bitset>
++#include <string>
++#include <variant>
+
+ namespace redfish
+ {
+@@ -436,5 +440,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
+--- /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.
++*/
++
++#pragma once
++
++namespace redfish
++{
++
++namespace telemetry
++{
++
++static constexpr const char* metricReportDefinitionUri =
++ "/redfish/v1/TelemetryService/MetricReportDefinitions/";
++static 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)
++{
++ const std::array<const char*, 1> interfaces = {reportInterface};
++
++ dbus::utility::getSubTreePaths(
++ [asyncResp, uri](const boost::system::error_code ec,
++ const std::vector<std::string>& reports) {
++ if (ec == boost::system::errc::no_such_file_or_directory)
++ {
++ asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
++ asyncResp->res.jsonValue["Members@odata.count"] = 0;
++ return;
++ }
++
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
++ return;
++ }
++
++ json_util::dbusPathsToMembersArray(asyncResp->res, reports, uri);
++ },
++ telemetryPath, 1, interfaces);
++}
++
++template <typename Callback>
++static void getReport(const std::shared_ptr<AsyncResp>& asyncResp,
++ const std::string& id, const char* schemaType,
++ const Callback&& callback)
++{
++ 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);
++}
++} // 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
+--- /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.
++*/
++
++#pragma once
++
++#include <boost/algorithm/string/trim.hpp>
++
++#include <chrono>
++#include <cstdint>
++#include <string>
++#include <type_traits>
++
++namespace redfish
++{
++
++namespace time_utils
++{
++
++namespace details
++{
++
++template <typename T>
++std::string toDurationFormatItem(std::chrono::milliseconds& duration,
++ const char* postfix)
++{
++ 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)
++ {
++ ss << static_cast<float>(t.count()) /
++ static_cast<float>(std::chrono::milliseconds::period::den);
++ }
++ 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".
++ */
++std::string toDurationFormat(const uint32_t ms)
++{
++ std::chrono::milliseconds duration(ms);
++ if (duration.count() == 0)
++ {
++ return "PT0S";
++ }
++
++ std::string fmt;
++ fmt.reserve(sizeof("PxxxDTxxHxxMxx.xxxxxxS"));
++
++ using Days = std::chrono::duration<int, std::ratio<24 * 60 * 60>>;
++
++ fmt += "P";
++ fmt += details::toDurationFormatItem<Days>(duration, "D");
++ if (duration.count() == 0)
++ {
++ return fmt;
++ }
++
++ 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");
++
++ 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..a52d680
+--- /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.
++*/
++
++#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(CrowApp& app) : Node(app, telemetry::metricReportUri)
++ {
++ 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& req,
++ const std::vector<std::string>& params) 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(CrowApp& app) :
++ Node(app, std::string(telemetry::metricReportUri) + "<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& req,
++ 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];
++ telemetry::getReport(asyncResp, id, schemaType, getReportProperties);
++ }
++
++ using Readings =
++ std::vector<std::tuple<std::string, std::string, double, int32_t>>;
++ using MetricValues = std::vector<std::map<std::string, std::string>>;
++
++ static MetricValues toMetricValues(const Readings& readings)
++ {
++ MetricValues metricValues;
++
++ for (auto& [id, metadata, sensorValue, timestamp] : readings)
++ {
++ metricValues.push_back({
++ {"MetricId", id},
++ {"MetricProperty", metadata},
++ {"MetricValue", std::to_string(sensorValue)},
++ {"Timestamp", crow::utility::getDateTime(timestamp)},
++ });
++ }
++
++ return metricValues;
++ }
++
++ static void getReportProperties(const std::shared_ptr<AsyncResp> asyncResp,
++ const std::string& reportPath,
++ const std::string& id)
++ {
++ 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;
++
++ 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;
++ }
++
++ 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");
++ }
++
++ 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..d82ae59
+--- /dev/null
++++ b/redfish-core/lib/metric_report_definition.hpp
+@@ -0,0 +1,193 @@
++/*
++// 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.
++*/
++
++#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 <variant>
++
++namespace redfish
++{
++
++class MetricReportDefinitionCollection : public Node
++{
++ public:
++ MetricReportDefinitionCollection(CrowApp& app) :
++ Node(app, telemetry::metricReportDefinitionUri)
++ {
++ 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& req,
++ const std::vector<std::string>& params) 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(CrowApp& app) :
++ Node(app, std::string(telemetry::metricReportDefinitionUri) + "<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& req,
++ 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];
++
++ 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;
++ }
++
++ using ReadingParameters =
++ std::vector<std::tuple<std::vector<sdbusplus::message::object_path>,
++ std::string, std::string, std::string>>;
++ using Metrics = std::vector<std::map<
++ std::string, std::variant<std::string, std::vector<std::string>>>>;
++
++ static Metrics toMetrics(const ReadingParameters& params)
++ {
++ Metrics metrics;
++
++ 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)
++ {
++ 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";
++
++ 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;
++ }
++
++ 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");
++ }
++
++ public:
++ 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 b6bd6e0..3302390 100644
+--- a/redfish-core/lib/service_root.hpp
++++ b/redfish-core/lib/service_root.hpp
+@@ -69,6 +69,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..a410700
+--- /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.
++*/
++
++#pragma once
++
++#include "node.hpp"
++#include "utils/time_utils.hpp"
++
++#include <boost/container/flat_map.hpp>
++
++#include <variant>
++
++namespace redfish
++{
++
++class TelemetryService : public Node
++{
++ public:
++ TelemetryService(CrowApp& 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& req,
++ const std::vector<std::string>& params) override
++ {
++ res.jsonValue["@odata.type"] =
++ "#TelemetryService.v1_2_0.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";
++
++ getMonitoringServiceProperties(res);
++ }
++
++ void getMonitoringServiceProperties(crow::Response& res)
++ {
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ dbus::utility::getAllProperties(
++ [asyncResp](
++ const boost::system::error_code ec,
++ const boost::container::flat_map<std::string,
++ std::variant<uint32_t>>& ret) {
++ if (ec)
++ {
++ asyncResp->res.jsonValue["Status"]["State"] = "Absent";
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
++ 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);
++ },
++ "xyz.openbmc_project.MonitoringService",
++ "/xyz/openbmc_project/MonitoringService/Reports",
++ "xyz.openbmc_project.MonitoringService.ReportsManagement");
++ }
++};
++} // namespace redfish
+--
+2.16.6
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch
new file mode 100644
index 000000000..8a8690bf3
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-support-for-POST-in-MetricReportDefinitions.patch
@@ -0,0 +1,594 @@
+From 941be2c7d819b4a55d5a8b67948e53658d907789 Mon Sep 17 00:00:00 2001
+From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
+Date: Mon, 18 May 2020 11:56:57 +0200
+Subject: [PATCH 2/5] Add support for POST in MetricReportDefinitions
+
+Added POST action in MetricReportDefinitions node to allow user
+to add new MetricReportDefinition. Using minimal set of
+MetricReportDefinition parameters from user bmcweb converts it to
+DBus call "AddReport" to MonitoringService that serves as a backend
+for TelemetryService.
+
+Tested:
+ - Succesfully passed RedfishServiceValidator.py
+ - Validated good cases with different parameters for POST action
+ - Validated bad cases with different parameters for POST action
+ - Validated fromDurationFormat() with range of arguments starting
+ from PT0.0S up to P49D (it is an upper limit for uint32_t)
+
+Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
+Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Change-Id: I2fed96848594451e22fde686f8c066d7770cc65a
+---
+ redfish-core/include/utils/time_utils.hpp | 49 +++
+ .../include/utils/validate_params_length.hpp | 109 +++++++
+ redfish-core/lib/metric_report_definition.hpp | 347 +++++++++++++++++++++
+ 3 files changed, 505 insertions(+)
+ create mode 100644 redfish-core/include/utils/validate_params_length.hpp
+
+diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp
+index 0256b3f..c365585 100644
+--- a/redfish-core/include/utils/time_utils.hpp
++++ b/redfish-core/include/utils/time_utils.hpp
+@@ -57,6 +57,32 @@ std::string toDurationFormatItem(std::chrono::milliseconds& duration,
+ return ss.str();
+ }
+
++template <typename T>
++static long long fromDurationFormatItem(std::string_view& fmt,
++ const char* postfix)
++{
++ auto pos = fmt.find(postfix);
++ if (pos == std::string::npos)
++ {
++ return 0;
++ }
++
++ long out;
++ if constexpr (std::is_same<T, std::chrono::milliseconds>::value)
++ {
++ /* Half point is added to avoid numeric error on rounding */
++ out = static_cast<long>(std::strtof(fmt.data(), nullptr) *
++ std::chrono::milliseconds::period::den +
++ 0.5f);
++ }
++ else
++ {
++ out = std::strtol(fmt.data(), nullptr, 10);
++ }
++ fmt.remove_prefix(pos + 1);
++ return std::chrono::milliseconds(T(out)).count();
++}
++
+ } // namespace details
+
+ /**
+@@ -93,5 +119,28 @@ std::string toDurationFormat(const uint32_t ms)
+ return fmt;
+ }
+
++static uint32_t fromDurationFormat(std::string_view fmt)
++{
++ if (fmt.empty() || fmt[0] != 'P')
++ {
++ return 0;
++ }
++ using Days = std::chrono::duration<int, std::ratio<24 * 60 * 60>>;
++
++ fmt.remove_prefix(1);
++ auto out = details::fromDurationFormatItem<Days>(fmt, "D");
++ if (fmt[0] != 'T')
++ {
++ return static_cast<uint32_t>(out);
++ }
++
++ fmt.remove_prefix(1);
++ out += details::fromDurationFormatItem<std::chrono::hours>(fmt, "H");
++ out += details::fromDurationFormatItem<std::chrono::minutes>(fmt, "M");
++ out += details::fromDurationFormatItem<std::chrono::milliseconds>(fmt, "S");
++
++ return static_cast<uint32_t>(out);
++}
++
+ } // namespace time_utils
+ } // namespace redfish
+diff --git a/redfish-core/include/utils/validate_params_length.hpp b/redfish-core/include/utils/validate_params_length.hpp
+new file mode 100644
+index 0000000..c4e0569
+--- /dev/null
++++ b/redfish-core/include/utils/validate_params_length.hpp
+@@ -0,0 +1,109 @@
++#pragma once
++
++namespace redfish
++{
++namespace detail
++{
++template <class Limits, size_t... Seq>
++bool validateParamsLength(crow::Response& res, Limits&& limits,
++ std::index_sequence<Seq...>)
++{
++ return (... && std::get<Seq>(limits).validate(res));
++}
++} // namespace detail
++
++template <class T>
++struct ItemSizeValidator
++{
++ ItemSizeValidator(const T&& item, std::string_view name, size_t limit) :
++ item(std::forward<const T>(item)), name(name), limit(limit)
++ {}
++
++ bool validate(crow::Response& res) const
++ {
++ return ItemSizeValidator<T>::validateItem(res, item, name, limit);
++ }
++
++ private:
++ static bool validateItem(crow::Response& res, size_t item,
++ std::string_view name, size_t limit)
++ {
++ if (item > static_cast<size_t>(limit))
++ {
++ messages::stringValueTooLong(res, std::string(name),
++ static_cast<int>(limit));
++ return false;
++ }
++ return true;
++ }
++
++ static bool validateItem(crow::Response& res, std::string_view item,
++ std::string_view name, size_t limit)
++ {
++ return validateItem(res, item.size(), name, limit);
++ }
++
++ static bool validateItem(crow::Response& res, const std::string& item,
++ std::string_view name, size_t limit)
++ {
++ return validateItem(res, item.size(), name, limit);
++ }
++
++ static bool validateItem(crow::Response& res,
++ const sdbusplus::message::object_path& item,
++ std::string_view name, size_t limit)
++ {
++ return validateItem(res, item.str.size(), name, limit);
++ }
++
++ T item;
++ std::string_view name;
++ size_t limit;
++};
++
++template <class T>
++ItemSizeValidator(const T&, std::string_view, size_t)
++ -> ItemSizeValidator<const T&>;
++
++ItemSizeValidator(const char*, std::string_view, size_t)
++ ->ItemSizeValidator<std::string_view>;
++
++template <class ContainerT>
++struct ArrayItemsValidator
++{
++ ArrayItemsValidator(const ContainerT& item, std::string_view name,
++ size_t limit) :
++ item(item),
++ name(name), limit(limit)
++ {}
++
++ bool validate(crow::Response& res) const
++ {
++ return std::all_of(
++ item.begin(), item.end(), [&res, this](const auto& item) {
++ return ItemSizeValidator(item, name, limit).validate(res);
++ });
++ }
++
++ private:
++ const ContainerT& item;
++ std::string_view name;
++ size_t limit;
++};
++
++template <class T>
++bool validateParamLength(crow::Response& res, T&& item, std::string_view name,
++ size_t limit)
++{
++ return ItemSizeValidator(std::forward<T>(item), name, limit).validate(res);
++}
++
++template <class Limits>
++bool validateParamsLength(crow::Response& res, Limits&& limits)
++{
++ return detail::validateParamsLength(
++ res, std::forward<Limits>(limits),
++ std::make_index_sequence<std::tuple_size_v<std::decay_t<Limits>>>());
++}
++
++} // namespace redfish
+diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
+index d82ae59..ecbab0c 100644
+--- a/redfish-core/lib/metric_report_definition.hpp
++++ b/redfish-core/lib/metric_report_definition.hpp
+@@ -17,16 +17,29 @@
+ #pragma once
+
+ #include "node.hpp"
++#include "sensors.hpp"
+ #include "utils/telemetry_utils.hpp"
+ #include "utils/time_utils.hpp"
++#include "utils/validate_params_length.hpp"
+
++#include <boost/algorithm/string/join.hpp>
++#include <boost/algorithm/string/split.hpp>
+ #include <boost/container/flat_map.hpp>
+
++#include <regex>
+ #include <system_error>
++#include <tuple>
+ #include <variant>
+
+ namespace redfish
+ {
++static constexpr size_t maxShortParamLength = 255;
++static constexpr size_t maxLongParamLength = 1024;
++static constexpr size_t maxDbusNameLength = 255;
++static constexpr size_t maxArraySize = 100;
++static constexpr size_t maxReportIdLen =
++ maxDbusNameLength - std::string_view(telemetry::telemetryPath).size() -
++ std::string_view("/").size();
+
+ class MetricReportDefinitionCollection : public Node
+ {
+@@ -57,6 +70,339 @@ class MetricReportDefinitionCollection : public Node
+ telemetry::getReportCollection(asyncResp,
+ telemetry::metricReportDefinitionUri);
+ }
++
++ using ChassisSensorNode = std::pair<std::string, std::string>;
++ using DbusSensor = sdbusplus::message::object_path;
++ using DbusSensors = std::vector<DbusSensor>;
++ using MetricParam =
++ std::tuple<DbusSensors, std::string, std::string, std::string>;
++ using MetricParams = std::vector<MetricParam>;
++ /*
++ * AddReportArgs misses "Domain" parameter because it is constant for
++ * TelemetryService and equals "TelemetryService".
++ */
++ using AddReportArgs =
++ std::tuple<std::string, std::string, std::vector<std::string>, uint32_t,
++ MetricParams>;
++
++ void doPost(crow::Response& res, const crow::Request& req,
++ const std::vector<std::string>& params) override
++ {
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ AddReportArgs addReportArgs;
++ if (!getUserParameters(res, req, addReportArgs))
++ {
++ return;
++ }
++
++ boost::container::flat_set<ChassisSensorNode> chassisSensorSet;
++ auto unmatched = getChassisSensorNode(
++ std::get<MetricParams>(addReportArgs), chassisSensorSet);
++ if (unmatched)
++ {
++ messages::resourceNotFound(asyncResp->res, "MetricProperties",
++ *unmatched);
++ return;
++ }
++
++ auto addReportReq =
++ std::make_shared<AddReport>(addReportArgs, asyncResp);
++ for (auto& [chassis, sensorType] : chassisSensorSet)
++ {
++ retrieveUriToDbusMap(
++ chassis, sensorType,
++ [asyncResp, addReportReq](
++ const boost::beast::http::status status,
++ const boost::container::flat_map<std::string, std::string>&
++ uriToDbus) { *addReportReq += uriToDbus; });
++ }
++ }
++
++ static std::optional<std::string>
++ replaceReportActions(std::vector<std::string>& actions)
++ {
++ const boost::container::flat_map<std::string, std::string>
++ reportActions = {
++ {"RedfishEvent", "Event"},
++ {"LogToMetricReportsCollection", "Log"},
++ };
++
++ for (auto& action : actions)
++ {
++ auto found = reportActions.find(action);
++ if (found == reportActions.end())
++ {
++ return action;
++ }
++ action = found->second;
++ }
++ return std::nullopt;
++ }
++
++ static constexpr const std::array<const char*, 2> supportedDefinitionType =
++ {"Periodic", "OnRequest"};
++
++ static bool getUserParameters(crow::Response& res, const crow::Request& req,
++ AddReportArgs& params)
++ {
++ std::vector<nlohmann::json> metrics;
++ std::optional<nlohmann::json> schedule;
++ auto& [name, reportingType, reportActions, scanPeriod, metricParams] =
++ params;
++ if (!json_util::readJson(req, res, "Id", name, "Metrics", metrics,
++ "MetricReportDefinitionType", reportingType,
++ "ReportActions", reportActions, "Schedule",
++ schedule))
++ {
++ return false;
++ }
++
++ auto limits = std::make_tuple(
++ ItemSizeValidator(name, "Id", maxReportIdLen),
++ ItemSizeValidator(reportingType, "MetricReportDefinitionType",
++ maxShortParamLength),
++ ItemSizeValidator(reportActions.size(), "ReportActions.size()",
++ maxArraySize),
++ ArrayItemsValidator(reportActions, "ReportActions",
++ maxShortParamLength),
++ ItemSizeValidator(metrics.size(), "Metrics.size()", maxArraySize));
++
++ if (!validateParamsLength(res, std::move(limits)))
++ {
++ return false;
++ }
++
++ constexpr const char* allowedCharactersInName =
++ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
++ "_";
++ if (name.empty() || name.find_first_not_of(allowedCharactersInName) !=
++ std::string::npos)
++ {
++ BMCWEB_LOG_ERROR << "Failed to match " << name
++ << " with allowed character "
++ << allowedCharactersInName;
++ messages::propertyValueFormatError(res, name, "Id");
++ return false;
++ }
++
++ if (!std::any_of(
++ supportedDefinitionType.begin(), supportedDefinitionType.end(),
++ [reportingType](auto& x) { return reportingType == x; }))
++ {
++ messages::propertyValueNotInList(res, reportingType,
++ "MetricReportDefinitionType");
++ return false;
++ }
++
++ auto unmatched = replaceReportActions(reportActions);
++ if (unmatched)
++ {
++ messages::propertyValueNotInList(res, *unmatched, "ReportActions");
++ return false;
++ }
++
++ if (reportingType == "Periodic")
++ {
++ if (!schedule)
++ {
++ messages::createFailedMissingReqProperties(res, "Schedule");
++ return false;
++ }
++
++ std::string interval;
++ if (!json_util::readJson(*schedule, res, "RecurrenceInterval",
++ interval))
++ {
++ return false;
++ }
++
++ if (!validateParamLength(res, interval, "RecurrenceInterval",
++ maxShortParamLength))
++ {
++ return false;
++ }
++
++ constexpr const char* durationPattern =
++ "-?P(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(.\\d+)?S)?)?";
++ if (!std::regex_match(interval, std::regex(durationPattern)))
++ {
++ messages::propertyValueFormatError(res, interval,
++ "RecurrenceInterval");
++ return false;
++ }
++
++ scanPeriod = time_utils::fromDurationFormat(interval);
++ }
++
++ return fillMetricParams(metrics, res, metricParams);
++ }
++
++ static bool fillMetricParams(std::vector<nlohmann::json>& metrics,
++ crow::Response& res,
++ MetricParams& metricParams)
++ {
++ metricParams.reserve(metrics.size());
++ for (auto& m : metrics)
++ {
++ std::string metricId;
++ std::vector<std::string> metricProperties;
++ if (!json_util::readJson(m, res, "MetricId", metricId,
++ "MetricProperties", metricProperties))
++ {
++ return false;
++ }
++
++ auto limits = std::make_tuple(
++ ItemSizeValidator(metricId, "MetricId", maxLongParamLength),
++ ItemSizeValidator(metricProperties.size(),
++ "MetricProperties.size()", maxArraySize),
++ ArrayItemsValidator(metricProperties, "MetricProperties",
++ maxLongParamLength));
++
++ if (!validateParamsLength(res, std::move(limits)))
++ {
++ return false;
++ }
++
++ DbusSensors dbusSensors;
++ dbusSensors.reserve(metricProperties.size());
++ std::for_each(
++ metricProperties.begin(), metricProperties.end(),
++ [&dbusSensors](auto& x) { dbusSensors.emplace_back(x); });
++
++ metricParams.emplace_back(
++ dbusSensors, "SINGLE", metricId,
++ boost::algorithm::join(metricProperties, ", "));
++ }
++ return true;
++ }
++
++ static std::optional<std::string> getChassisSensorNode(
++ const MetricParams& metricParams,
++ boost::container::flat_set<ChassisSensorNode>& matched)
++ {
++ for (const auto& metricParam : metricParams)
++ {
++ const auto& sensors = std::get<DbusSensors>(metricParam);
++ for (const auto& sensor : sensors)
++ {
++ /*
++ * Support only following paths:
++ * "/redfish/v1/Chassis/<chassis>/Power#/..."
++ * "/redfish/v1/Chassis/<chassis>/Sensors/..."
++ * "/redfish/v1/Chassis/<chassis>/Thermal#/..."
++ */
++ constexpr const char* uriPattern =
++ "\\/redfish\\/v1\\/Chassis\\/(\\w+)\\/"
++ "(Power|Sensors|Thermal)[#]?\\/.*";
++ std::smatch m;
++ if (!std::regex_match(sensor.str, m, std::regex(uriPattern)) ||
++ m.size() != 3)
++ {
++ BMCWEB_LOG_ERROR << "Failed to match " << sensor.str
++ << " with pattern " << uriPattern;
++ return sensor;
++ }
++
++ BMCWEB_LOG_DEBUG << "Chassis=" << m[1] << ", Type=" << m[2];
++ matched.emplace(m[1], m[2]);
++ }
++ }
++ return std::nullopt;
++ }
++
++ static std::optional<std::string> replaceUriWithDbus(
++ const boost::container::flat_map<std::string, std::string>& uriToDbus,
++ MetricParams& metricParams)
++ {
++ for (auto& metricParam : metricParams)
++ {
++ auto& dbusSensors = std::get<DbusSensors>(metricParam);
++ for (auto& uri : dbusSensors)
++ {
++ auto dbus = uriToDbus.find(uri);
++ if (dbus == uriToDbus.end())
++ {
++ BMCWEB_LOG_ERROR << "Failed to find DBus sensor "
++ "corresponding to URI "
++ << uri.str;
++ return uri;
++ }
++ uri = dbus->second;
++ }
++ }
++ return std::nullopt;
++ }
++
++ static void addReport(std::shared_ptr<AsyncResp> asyncResp,
++ AddReportArgs args)
++ {
++ constexpr const char* domain = "TelemetryService";
++ auto& [name, reportingType, reportActions, scanPeriod, metricParams] =
++ args;
++
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, name](const boost::system::error_code ec,
++ const std::string ret) {
++ if (ec == boost::system::errc::file_exists)
++ {
++ messages::resourceAlreadyExists(
++ asyncResp->res, "MetricReportDefinition", "Id", name);
++ return;
++ }
++ if (ec == boost::system::errc::too_many_files_open)
++ {
++ messages::createLimitReachedForResource(asyncResp->res);
++ return;
++ }
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
++ return;
++ }
++
++ messages::created(asyncResp->res);
++ },
++ "xyz.openbmc_project.MonitoringService",
++ "/xyz/openbmc_project/MonitoringService/Reports",
++ "xyz.openbmc_project.MonitoringService.ReportsManagement",
++ "AddReport", name, domain, reportingType, reportActions, scanPeriod,
++ metricParams);
++ }
++
++ class AddReport
++ {
++ public:
++ AddReport(AddReportArgs& args, std::shared_ptr<AsyncResp>& asyncResp) :
++ asyncResp{asyncResp}, addReportArgs{args}
++ {}
++ ~AddReport()
++ {
++ auto unmatched = replaceUriWithDbus(
++ uriToDbus, std::get<MetricParams>(addReportArgs));
++ if (unmatched)
++ {
++ messages::resourceNotFound(asyncResp->res, "MetricProperties",
++ *unmatched);
++ return;
++ }
++
++ addReport(asyncResp, addReportArgs);
++ }
++
++ AddReport& operator+=(
++ const boost::container::flat_map<std::string, std::string>& rhs)
++ {
++ this->uriToDbus.insert(rhs.begin(), rhs.end());
++ return *this;
++ }
++
++ private:
++ std::shared_ptr<AsyncResp> asyncResp;
++ AddReportArgs addReportArgs;
++ boost::container::flat_map<std::string, std::string> uriToDbus{};
++ };
+ };
+
+ class MetricReportDefinition : public Node
+@@ -148,6 +494,7 @@ class MetricReportDefinition : public Node
+ asyncResp->res.jsonValue["MetricReport"]["@odata.id"] =
+ telemetry::metricReportUri + id;
+ asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
++ asyncResp->res.jsonValue["ReportUpdates"] = "Overwrite";
+
+ dbus::utility::getAllProperties(
+ [asyncResp](const boost::system::error_code ec,
+--
+2.16.6
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-DELETE-in-MetricReportDefinitions-st.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-DELETE-in-MetricReportDefinitions-st.patch
new file mode 100644
index 000000000..4c49b0cd3
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-DELETE-in-MetricReportDefinitions-st.patch
@@ -0,0 +1,68 @@
+From 8b2f4a6fe57bf2410cdb22f8c3c695e98d583040 Mon Sep 17 00:00:00 2001
+From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
+Date: Mon, 18 May 2020 12:40:15 +0200
+Subject: [PATCH 3/5] Add support for DELETE in MetricReportDefinitions/<str>
+
+Added support for DELETE action in MetricReportDefinitions/<str>
+node. It allows user to remove MetricReportDefinition together
+with MetricReport connected to it.
+
+Tested:
+ - Succesfully passed RedfishServiceValidator.py
+ - Validated DELETE action by removing exisiting
+ MetricReportDefinitions from MonitoringService
+ - Validated DELETE action with negative cases when
+ MetricReportDefinition does not exist
+
+Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
+Change-Id: Iffde9f7bbf2955376e9714ac8d833967bd25eaa3
+---
+ redfish-core/lib/metric_report_definition.hpp | 32 +++++++++++++++++++++++++++
+ 1 file changed, 32 insertions(+)
+
+diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
+index ecbab0c..8e04ac8 100644
+--- a/redfish-core/lib/metric_report_definition.hpp
++++ b/redfish-core/lib/metric_report_definition.hpp
+@@ -533,6 +533,38 @@ class MetricReportDefinition : public Node
+ "xyz.openbmc_project.MonitoringService.Report");
+ }
+
++ void doDelete(crow::Response& res, const crow::Request& req,
++ 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];
++ telemetry::getReport(asyncResp, id, schemaType, deleteReport);
++ }
++
++ static void deleteReport(const std::shared_ptr<AsyncResp>& asyncResp,
++ const std::string& path, const std::string& id)
++ {
++ crow::connections::systemBus->async_method_call(
++ [asyncResp](const boost::system::error_code ec) {
++ if (ec)
++ {
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ asyncResp->res.result(boost::beast::http::status::no_content);
++ },
++ "xyz.openbmc_project.MonitoringService", path,
++ "xyz.openbmc_project.Object.Delete", "Delete");
++ }
++
+ public:
+ static constexpr const char* schemaType =
+ "#MetricReportDefinition.v1_3_0.MetricReportDefinition";
+--
+2.16.6
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-OnRequest-in-MetricReportDefinition.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-OnRequest-in-MetricReportDefinition.patch
new file mode 100644
index 000000000..e996ac585
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Add-support-for-OnRequest-in-MetricReportDefinition.patch
@@ -0,0 +1,169 @@
+From 9fc7d722b3192df9940062185b40ebb0fabad518 Mon Sep 17 00:00:00 2001
+From: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Date: Mon, 8 Jun 2020 15:16:10 +0200
+Subject: [PATCH 4/5] Add support for "OnRequest" in MetricReportDefinition
+
+Added support for "OnRequest" of ReportingType property in
+MetricReportDefinition node. Now user is able to create
+MetricReportDefinition that is updated on every GET request
+on MetricReport.
+
+Tested:
+ - Succesfully passed RedfishServiceValidator.py
+ - Manually tested via curl
+
+Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Change-Id: I1cdfe47e56fdc5ec9753558145d0bf3645160aaf
+---
+ include/dbus_utility.hpp | 30 +++++++++++++++
+ redfish-core/include/utils/telemetry_utils.hpp | 8 ++--
+ redfish-core/lib/metric_report.hpp | 53 +++++++++++++++++++++++++-
+ 3 files changed, 87 insertions(+), 4 deletions(-)
+
+diff --git a/include/dbus_utility.hpp b/include/dbus_utility.hpp
+index 3df88d8..029d8d8 100644
+--- a/include/dbus_utility.hpp
++++ b/include/dbus_utility.hpp
+@@ -17,6 +17,7 @@
+
+ #include <sdbusplus/message.hpp>
+
++#include <functional>
+ #include <regex>
+
+ namespace dbus
+@@ -130,5 +131,34 @@ inline void getAllProperties(Callback&& callback, const std::string& service,
+ interface);
+ }
+
++template <typename T>
++static void getProperty(
++ std::function<void(const boost::system::error_code&, const T&)> callback,
++ const std::string& service, const std::string& path,
++ const std::string& interface, const std::string& property)
++{
++ crow::connections::systemBus->async_method_call(
++ [callback](const boost::system::error_code ec,
++ const std::variant<T>& value) {
++ if (ec)
++ {
++ callback(ec, T{});
++ return;
++ }
++
++ if (auto v = std::get_if<T>(&value))
++ {
++ callback(ec, *v);
++ return;
++ }
++
++ callback(boost::system::errc::make_error_code(
++ boost::system::errc::io_error),
++ T{});
++ },
++ service, path, "org.freedesktop.DBus.Properties", "Get", interface,
++ property);
++}
++
+ } // namespace utility
+ } // namespace dbus
+diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp
+index 05ed00f..6c4e810 100644
+--- a/redfish-core/include/utils/telemetry_utils.hpp
++++ b/redfish-core/include/utils/telemetry_utils.hpp
+@@ -26,6 +26,8 @@ static constexpr const char* metricReportDefinitionUri =
+ "/redfish/v1/TelemetryService/MetricReportDefinitions/";
+ static constexpr const char* metricReportUri =
+ "/redfish/v1/TelemetryService/MetricReports/";
++static constexpr const char* monitoringService =
++ "xyz.openbmc_project.MonitoringService";
+ static constexpr const char* reportInterface =
+ "xyz.openbmc_project.MonitoringService.Report";
+ static constexpr const char* telemetryPath =
+@@ -66,9 +68,9 @@ static void getReport(const std::shared_ptr<AsyncResp>& asyncResp,
+ 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) {
++ [asyncResp, id, schemaType, callback = std::move(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);
+diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp
+index a52d680..877e7f1 100644
+--- a/redfish-core/lib/metric_report.hpp
++++ b/redfish-core/lib/metric_report.hpp
+@@ -85,7 +85,7 @@ class MetricReport : public Node
+ }
+
+ const std::string& id = params[0];
+- telemetry::getReport(asyncResp, id, schemaType, getReportProperties);
++ telemetry::getReport(asyncResp, id, schemaType, updateReportIfRequired);
+ }
+
+ using Readings =
+@@ -143,6 +143,57 @@ class MetricReport : public Node
+ "xyz.openbmc_project.MonitoringService.Report");
+ }
+
++ template <typename Callback>
++ static void updateReport(Callback&& callback,
++ const std::shared_ptr<AsyncResp>& asyncResp,
++ const std::string& path)
++ {
++ crow::connections::systemBus->async_method_call(
++ [asyncResp, callback{std::move(callback)}](
++ const boost::system::error_code& ec) {
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ callback();
++ },
++ telemetry::monitoringService, path, telemetry::reportInterface,
++ "Update");
++ }
++
++ static void
++ updateReportIfRequired(const std::shared_ptr<AsyncResp> asyncResp,
++ const std::string& reportPath,
++ const std::string& id)
++ {
++ dbus::utility::getProperty<std::string>(
++ [asyncResp, id, reportPath](const boost::system::error_code& ec,
++ const std::string& reportingType) {
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ if (reportingType == "OnRequest")
++ {
++ updateReport(
++ [asyncResp, reportPath, id] {
++ getReportProperties(asyncResp, reportPath, id);
++ },
++ asyncResp, reportPath);
++ }
++ else
++ {
++ getReportProperties(asyncResp, reportPath, id);
++ }
++ },
++ telemetry::monitoringService, reportPath,
++ telemetry::reportInterface, "ReportingType");
++ }
++
+ static constexpr const char* schemaType =
+ "#MetricReport.v1_3_0.MetricReport";
+ };
+--
+2.16.6
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Add-support-for-MetricDefinition-scheme.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Add-support-for-MetricDefinition-scheme.patch
new file mode 100644
index 000000000..f7da8a556
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Add-support-for-MetricDefinition-scheme.patch
@@ -0,0 +1,535 @@
+From b1da8901b5985d6a77b63ca9eb0570b46528f0bd Mon Sep 17 00:00:00 2001
+From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
+Date: Mon, 8 Jun 2020 17:15:54 +0200
+Subject: [PATCH 5/5] Add support for MetricDefinition scheme
+
+Added MetricDefinition node to redfish core. Now user is able to
+get all possible metrics that are present in system and are
+supported by TelemetryService.
+
+Tested:
+ - Succesfully passed RedfishServiceValidator.py
+ - Validated a presence of MetricDefinition members
+
+Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
+Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
+Change-Id: I3086e1302e1ba2e5442d1367939fd5507a0cbc00
+---
+ redfish-core/include/redfish.hpp | 3 +
+ redfish-core/include/utils/telemetry_utils.hpp | 2 +
+ redfish-core/lib/metric_definition.hpp | 300 +++++++++++++++++++++++++
+ redfish-core/lib/metric_report.hpp | 65 +++++-
+ redfish-core/lib/sensors.hpp | 43 +++-
+ redfish-core/lib/telemetry_service.hpp | 2 +
+ 6 files changed, 402 insertions(+), 13 deletions(-)
+ create mode 100644 redfish-core/lib/metric_definition.hpp
+
+diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
+index 3d4c117..2a12bf9 100644
+--- a/redfish-core/include/redfish.hpp
++++ b/redfish-core/include/redfish.hpp
+@@ -25,6 +25,7 @@
+ #include "../lib/log_services.hpp"
+ #include "../lib/managers.hpp"
+ #include "../lib/message_registries.hpp"
++#include "../lib/metric_definition.hpp"
+ #include "../lib/metric_report.hpp"
+ #include "../lib/metric_report_definition.hpp"
+ #include "../lib/network_protocol.hpp"
+@@ -206,6 +207,8 @@ class RedfishService
+ nodes.emplace_back(std::make_unique<HypervisorSystem>(app));
+
+ nodes.emplace_back(std::make_unique<TelemetryService>(app));
++ nodes.emplace_back(std::make_unique<MetricDefinitionCollection>(app));
++ nodes.emplace_back(std::make_unique<MetricDefinition>(app));
+ nodes.emplace_back(
+ std::make_unique<MetricReportDefinitionCollection>(app));
+ nodes.emplace_back(std::make_unique<MetricReportDefinition>(app));
+diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp
+index 6c4e810..bb747c4 100644
+--- a/redfish-core/include/utils/telemetry_utils.hpp
++++ b/redfish-core/include/utils/telemetry_utils.hpp
+@@ -22,6 +22,8 @@ namespace redfish
+ namespace telemetry
+ {
+
++static constexpr const char* metricDefinitionUri =
++ "/redfish/v1/TelemetryService/MetricDefinitions/";
+ static constexpr const char* metricReportDefinitionUri =
+ "/redfish/v1/TelemetryService/MetricReportDefinitions/";
+ static constexpr const char* metricReportUri =
+diff --git a/redfish-core/lib/metric_definition.hpp b/redfish-core/lib/metric_definition.hpp
+new file mode 100644
+index 0000000..837a068
+--- /dev/null
++++ b/redfish-core/lib/metric_definition.hpp
+@@ -0,0 +1,300 @@
++/*
++// 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.
++*/
++
++#pragma once
++
++#include "node.hpp"
++#include "sensors.hpp"
++#include "utils/telemetry_utils.hpp"
++
++namespace redfish
++{
++
++namespace chassis
++{
++template <typename F>
++static inline void getChassisNames(F&& callback)
++{
++ const std::array<const char*, 2> interfaces = {
++ "xyz.openbmc_project.Inventory.Item.Board",
++ "xyz.openbmc_project.Inventory.Item.Chassis"};
++
++ dbus::utility::getSubTreePaths(
++ [callback{std::move(callback)}](const boost::system::error_code ec,
++ std::vector<std::string>& chassisList) {
++ if (ec)
++ {
++ return;
++ }
++
++ std::vector<std::string> chassisNames;
++ chassisNames.reserve(chassisList.size());
++ for (auto& chassisPath : chassisList)
++ {
++ auto pos = chassisPath.rfind("/");
++ if (pos == std::string::npos)
++ {
++ continue;
++ }
++ chassisNames.push_back(chassisPath.substr(pos + 1));
++ }
++
++ callback(chassisNames);
++ },
++ "/xyz/openbmc_project/inventory", 0, interfaces);
++}
++} // namespace chassis
++
++class MetricDefinitionCollection : public Node
++{
++ public:
++ MetricDefinitionCollection(CrowApp& app) :
++ Node(app, "/redfish/v1/TelemetryService/MetricDefinitions")
++ {
++ 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& req,
++ const std::vector<std::string>& params) override
++ {
++ res.jsonValue["@odata.type"] = "#MetricDefinitionCollection."
++ "MetricDefinitionCollection";
++ res.jsonValue["@odata.id"] =
++ "/redfish/v1/TelemetryService/MetricDefinitions";
++ res.jsonValue["Name"] = "Metric Definition Collection";
++ res.jsonValue["Members"] = nlohmann::json::array();
++ res.jsonValue["Members@odata.count"] = sensors::dbus::types.size();
++
++ auto asyncResp = std::make_shared<AsyncResp>(res);
++ auto collectionReduce = std::make_shared<CollectionGather>(asyncResp);
++ chassis::getChassisNames(
++ [asyncResp,
++ collectionReduce](const std::vector<std::string>& chassisNames) {
++ for (auto& chassisName : chassisNames)
++ {
++ for (auto& [sensorNode, _] : sensors::dbus::types)
++ {
++ BMCWEB_LOG_INFO << "Chassis: " << chassisName
++ << " sensor: " << sensorNode;
++ retrieveUriToDbusMap(
++ chassisName, sensorNode.data(),
++ [asyncResp, collectionReduce](
++ const boost::beast::http::status status,
++ const boost::container::flat_map<
++ std::string, std::string>& uriToDbus) {
++ *collectionReduce += uriToDbus;
++ });
++ }
++ }
++ });
++ }
++
++ class CollectionGather
++ {
++ public:
++ CollectionGather(const std::shared_ptr<AsyncResp>& asyncResp) :
++ asyncResp{asyncResp}
++ {
++ dbusTypes.reserve(sensors::dbus::paths.size());
++ }
++
++ ~CollectionGather()
++ {
++ json_util::dbusPathsToMembersArray(
++ asyncResp->res,
++ std::vector<std::string>(dbusTypes.begin(), dbusTypes.end()),
++ telemetry::metricDefinitionUri);
++ }
++
++ CollectionGather& operator+=(
++ const boost::container::flat_map<std::string, std::string>& rhs)
++ {
++ for (auto& [_, dbusSensor] : rhs)
++ {
++ auto pos = dbusSensor.rfind("/");
++ if (pos == std::string::npos)
++ {
++ BMCWEB_LOG_ERROR << "Received invalid DBus Sensor Path = "
++ << dbusSensor;
++ continue;
++ }
++
++ this->dbusTypes.insert(dbusSensor.substr(0, pos));
++ }
++ return *this;
++ }
++
++ private:
++ const std::shared_ptr<AsyncResp> asyncResp;
++ boost::container::flat_set<std::string> dbusTypes;
++ };
++};
++
++class MetricDefinition : public Node
++{
++ public:
++ MetricDefinition(CrowApp& app) :
++ Node(app, std::string(telemetry::metricDefinitionUri) + "<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& req,
++ 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];
++
++ size_t sensorIndex = 0;
++ for (auto& name : sensors::dbus::names)
++ {
++ if (name == id)
++ {
++ break;
++ }
++ sensorIndex++;
++ }
++ if (sensorIndex >= sensors::dbus::max)
++ {
++ messages::resourceNotFound(asyncResp->res, schemaType, id);
++ return;
++ }
++
++ auto definitionGather =
++ std::make_shared<DefinitionGather>(asyncResp, id);
++ chassis::getChassisNames(
++ [asyncResp, definitionGather,
++ sensorIndex](const std::vector<std::string>& chassisNames) {
++ for (auto& chassisName : chassisNames)
++ {
++ for (auto& [sensorNode, dbusPaths] : sensors::dbus::types)
++ {
++ auto found =
++ std::find(dbusPaths.begin(), dbusPaths.end(),
++ sensors::dbus::paths[sensorIndex]);
++ if (found == dbusPaths.end())
++ {
++ continue;
++ }
++
++ retrieveUriToDbusMap(
++ chassisName, sensorNode.data(),
++ [asyncResp, definitionGather](
++ const boost::beast::http::status status,
++ const boost::container::flat_map<
++ std::string, std::string>& uriToDbus) {
++ *definitionGather += uriToDbus;
++ });
++ }
++ }
++ });
++ }
++
++ class DefinitionGather
++ {
++ public:
++ DefinitionGather(const std::shared_ptr<AsyncResp>& asyncResp,
++ const std::string& id) :
++ id(id),
++ asyncResp{asyncResp}
++ {}
++ ~DefinitionGather()
++ {
++ if (redfishSensors.empty())
++ {
++ messages::resourceNotFound(asyncResp->res, schemaType, id);
++ return;
++ }
++
++ asyncResp->res.jsonValue["MetricProperties"] =
++ nlohmann::json::array();
++ auto& members = asyncResp->res.jsonValue["MetricProperties"];
++ for (auto& redfishSensor : redfishSensors)
++ {
++ members.push_back(redfishSensor);
++ }
++
++ asyncResp->res.jsonValue["Id"] = id;
++ asyncResp->res.jsonValue["Name"] = id;
++ asyncResp->res.jsonValue["@odata.id"] =
++ telemetry::metricDefinitionUri + id;
++ asyncResp->res.jsonValue["@odata.type"] = schemaType;
++ asyncResp->res.jsonValue["MetricDataType"] = "Decimal";
++ asyncResp->res.jsonValue["MetricType"] = "Numeric";
++ asyncResp->res.jsonValue["Implementation"] = "PhysicalSensor";
++ asyncResp->res.jsonValue["IsLinear"] = true;
++ asyncResp->res.jsonValue["TimestampAccuracy"] = "PT0.1S";
++ auto unit = sensorUnits.find(id);
++ if (unit != sensorUnits.end())
++ {
++ asyncResp->res.jsonValue["Units"] = unit->second;
++ }
++ }
++
++ DefinitionGather& operator+=(
++ const boost::container::flat_map<std::string, std::string>& rhs)
++ {
++ for (auto& [redfishSensor, dbusSensor] : rhs)
++ {
++ if (dbusSensor.find(id) != std::string::npos)
++ {
++ this->redfishSensors.push_back(redfishSensor);
++ }
++ }
++ return *this;
++ }
++
++ const std::string id;
++
++ private:
++ const std::shared_ptr<AsyncResp> asyncResp;
++ std::vector<std::string> redfishSensors;
++ const boost::container::flat_map<std::string, std::string> sensorUnits =
++ {{sensors::dbus::names[sensors::dbus::voltage], "V"},
++ {sensors::dbus::names[sensors::dbus::power], "W"},
++ {sensors::dbus::names[sensors::dbus::current], "A"},
++ {sensors::dbus::names[sensors::dbus::fan_tach], "RPM"},
++ {sensors::dbus::names[sensors::dbus::temperature], "Cel"},
++ {sensors::dbus::names[sensors::dbus::utilization], "%"},
++ {sensors::dbus::names[sensors::dbus::fan_pwm], "Duty cycle"}};
++ };
++
++ static constexpr const char* schemaType =
++ "#MetricDefinition.v1_0_3.MetricDefinition";
++};
++
++} // namespace redfish
+diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp
+index 877e7f1..be72b18 100644
+--- a/redfish-core/lib/metric_report.hpp
++++ b/redfish-core/lib/metric_report.hpp
+@@ -91,6 +91,9 @@ class MetricReport : public Node
+ using Readings =
+ std::vector<std::tuple<std::string, std::string, double, int32_t>>;
+ using MetricValues = std::vector<std::map<std::string, std::string>>;
++ using ReadingParameters =
++ std::vector<std::tuple<std::vector<sdbusplus::message::object_path>,
++ std::string, std::string, std::string>>;
+
+ static MetricValues toMetricValues(const Readings& readings)
+ {
+@@ -109,6 +112,49 @@ class MetricReport : public Node
+ return metricValues;
+ }
+
++ static void addMetricDefinition(nlohmann::json& metrics,
++ const ReadingParameters& params)
++ {
++ for (auto& metric : metrics)
++ {
++ if (!metric.contains("MetricId"))
++ {
++ continue;
++ }
++
++ auto& id = metric["MetricId"].get_ref<std::string&>();
++ auto param =
++ std::find_if(params.begin(), params.end(), [id](const auto& x) {
++ return id == std::get<2>(x);
++ });
++ if (param == params.end())
++ {
++ continue;
++ }
++
++ auto& dbusPaths =
++ std::get<std::vector<sdbusplus::message::object_path>>(*param);
++ if (dbusPaths.size() > 1)
++ {
++ continue;
++ }
++
++ auto dbusPath = dbusPaths.begin();
++ for (size_t i = 0; i < sensors::dbus::paths.size(); i++)
++ {
++ if (dbusPath->str.find(sensors::dbus::paths[i]) ==
++ std::string::npos)
++ {
++ continue;
++ }
++ metric["MetricDefinition"]["@odata.id"] =
++ telemetry::metricDefinitionUri +
++ std::string(sensors::dbus::names[i]);
++ break;
++ }
++ }
++ }
++
+ static void getReportProperties(const std::shared_ptr<AsyncResp> asyncResp,
+ const std::string& reportPath,
+ const std::string& id)
+@@ -124,7 +170,8 @@ class MetricReport : public Node
+ [asyncResp](
+ const boost::system::error_code ec,
+ const boost::container::flat_map<
+- std::string, std::variant<Readings, int32_t>>& ret) {
++ std::string,
++ std::variant<Readings, int32_t, ReadingParameters>>& ret) {
+ if (ec)
+ {
+ messages::internalError(asyncResp->res);
+@@ -138,6 +185,22 @@ class MetricReport : public Node
+ json_util::assignIfPresent<Readings>(
+ ret, "Readings", asyncResp->res.jsonValue["MetricValues"],
+ toMetricValues);
++
++ auto found = ret.find("ReadingParameters");
++ if (found != ret.end())
++ {
++ auto params =
++ std::get_if<ReadingParameters>(&found->second);
++ if (params)
++ {
++ auto& jsonValue = asyncResp->res.jsonValue;
++ if (jsonValue.contains("MetricValues"))
++ {
++ addMetricDefinition(jsonValue["MetricValues"],
++ *params);
++ }
++ }
++ }
+ },
+ "xyz.openbmc_project.MonitoringService", reportPath,
+ "xyz.openbmc_project.MonitoringService.Report");
+diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp
+index f12bbe0..1fa1009 100644
+--- a/redfish-core/lib/sensors.hpp
++++ b/redfish-core/lib/sensors.hpp
+@@ -53,20 +53,39 @@ static constexpr std::string_view thermal = "Thermal";
+
+ namespace dbus
+ {
++
++enum Index
++{
++ voltage = 0,
++ power,
++ current,
++ fan_tach,
++ temperature,
++ fan_pwm,
++ utilization,
++ max
++};
++
++static constexpr std::array<const char*, max> names = {
++ "voltage", "power", "current", "fan_tach",
++ "temperature", "fan_pwm", "utilization"};
++
++static constexpr std::array<const char*, max> paths = {
++ "/xyz/openbmc_project/sensors/voltage",
++ "/xyz/openbmc_project/sensors/power",
++ "/xyz/openbmc_project/sensors/current",
++ "/xyz/openbmc_project/sensors/fan_tach",
++ "/xyz/openbmc_project/sensors/temperature",
++ "/xyz/openbmc_project/sensors/fan_pwm",
++ "/xyz/openbmc_project/sensors/utilization"};
++
+ static const boost::container::flat_map<std::string_view,
+ std::vector<const char*>>
+- types = {{node::power,
+- {"/xyz/openbmc_project/sensors/voltage",
+- "/xyz/openbmc_project/sensors/power"}},
+- {node::sensors,
+- {"/xyz/openbmc_project/sensors/power",
+- "/xyz/openbmc_project/sensors/current",
+- "/xyz/openbmc_project/sensors/utilization"}},
+- {node::thermal,
+- {"/xyz/openbmc_project/sensors/fan_tach",
+- "/xyz/openbmc_project/sensors/temperature",
+- "/xyz/openbmc_project/sensors/fan_pwm"}}};
+-}
++ types = {
++ {node::power, {paths[voltage], paths[power]}},
++ {node::sensors, {paths[power], paths[current], paths[utilization]}},
++ {node::thermal, {paths[fan_tach], paths[temperature], paths[fan_pwm]}}};
++} // namespace dbus
+ } // namespace sensors
+
+ /**
+diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp
+index a410700..79e4154 100644
+--- a/redfish-core/lib/telemetry_service.hpp
++++ b/redfish-core/lib/telemetry_service.hpp
+@@ -52,6 +52,8 @@ class TelemetryService : public Node
+
+ res.jsonValue["LogService"]["@odata.id"] =
+ "/redfish/v1/Managers/bmc/LogServices/Journal";
++ res.jsonValue["MetricDefinitions"]["@odata.id"] =
++ "/redfish/v1/TelemetryService/MetricDefinitions";
+ res.jsonValue["MetricReportDefinitions"]["@odata.id"] =
+ "/redfish/v1/TelemetryService/MetricReportDefinitions";
+ res.jsonValue["MetricReports"]["@odata.id"] =
+--
+2.16.6
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Fix-MetricReport-timestamp-for-EventService.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Fix-MetricReport-timestamp-for-EventService.patch
new file mode 100644
index 000000000..75d49b6d6
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0006-Fix-MetricReport-timestamp-for-EventService.patch
@@ -0,0 +1,78 @@
+From b71f087a173c36a16526156fa34581673e2b860c Mon Sep 17 00:00:00 2001
+From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
+Date: Fri, 24 Jul 2020 17:05:38 +0200
+Subject: [PATCH 6/6] Fix MetricReport timestamp for EventService
+
+Changed MetricReport timestamp type from std::string to int32_t.
+
+Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
+Change-Id: I0a52b6963e7bedda89a216256f64764cd8799bf1
+---
+ redfish-core/include/event_service_manager.hpp | 23 +++++++++++++----------
+ 1 file changed, 13 insertions(+), 10 deletions(-)
+
+diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
+index d2f4f2a..dc04ccb 100644
+--- a/redfish-core/include/event_service_manager.hpp
++++ b/redfish-core/include/event_service_manager.hpp
+@@ -38,7 +38,7 @@ namespace redfish
+ {
+
+ using ReadingsObjType =
+- std::vector<std::tuple<std::string, std::string, double, std::string>>;
++ std::vector<std::tuple<std::string, std::string, double, int32_t>>;
+ using EventServiceConfig = std::tuple<bool, uint32_t, uint32_t>;
+
+ static constexpr const char* eventFormatType = "Event";
+@@ -532,10 +532,12 @@ class Subscription
+ metricValuesArray.push_back({});
+ nlohmann::json& entry = metricValuesArray.back();
+
+- entry = {{"MetricId", std::get<0>(it)},
+- {"MetricProperty", std::get<1>(it)},
+- {"MetricValue", std::to_string(std::get<2>(it))},
+- {"Timestamp", std::get<3>(it)}};
++ auto& [id, property, value, timestamp] = it;
++
++ entry = {{"MetricId", id},
++ {"MetricProperty", property},
++ {"MetricValue", value},
++ {"Timestamp", crow::utility::getDateTime(timestamp)}};
+ }
+
+ nlohmann::json msg = {
+@@ -1266,7 +1268,7 @@ class EventServiceManager
+ [idStr{std::move(idStr)}](
+ const boost::system::error_code ec,
+ boost::container::flat_map<
+- std::string, std::variant<std::string, ReadingsObjType>>&
++ std::string, std::variant<int32_t, ReadingsObjType>>&
+ resp) {
+ if (ec)
+ {
+@@ -1275,8 +1277,8 @@ class EventServiceManager
+ return;
+ }
+
+- const std::string* timestampPtr =
+- std::get_if<std::string>(&resp["Timestamp"]);
++ const int32_t* timestampPtr =
++ std::get_if<int32_t>(&resp["Timestamp"]);
+ if (!timestampPtr)
+ {
+ BMCWEB_LOG_DEBUG << "Failed to Get timestamp.";
+@@ -1303,8 +1305,9 @@ class EventServiceManager
+ std::shared_ptr<Subscription> entry = it.second;
+ if (entry->eventFormatType == metricReportFormatType)
+ {
+- entry->filterAndSendReports(idStr, *timestampPtr,
+- *readingsPtr);
++ entry->filterAndSendReports(
++ idStr, crow::utility::getDateTime(*timestampPtr),
++ *readingsPtr);
+ }
+ }
+ },
+--
+2.16.6
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README
new file mode 100644
index 000000000..2929b0aec
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README
@@ -0,0 +1,21 @@
+These patches are mirror of upstream TelemetryService implementation.
+Until change is integrated they will be manually merged here to enable feature in Intel builds.
+
+Current revisions:
+- Redfish TelemetryService schema implementation
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/31692/29
+
+- Add support for POST in MetricReportDefinitions
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/32536/24
+
+- Add support for DELETE in MetricReportDefinitions/<str>
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/32537/23
+
+- Add support for "OnRequest" in MetricReportDefinition
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/33358/17
+
+- Add support for MetricDefinition scheme
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/33363/20
+
+- Temporary patch for EventService because of change in design
+ 0006-Fix-MetricReport-timestamp-for-EventService.patch