diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch | 913 |
1 files changed, 913 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 + |