summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry
diff options
context:
space:
mode:
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-Sync_ReadingUnit_with_Redfish_Sensor_Schema.patch227
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-POST-and-DELETE-in-MetricReportDefinitions.patch671
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README3
3 files changed, 0 insertions, 901 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Sync_ReadingUnit_with_Redfish_Sensor_Schema.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Sync_ReadingUnit_with_Redfish_Sensor_Schema.patch
deleted file mode 100644
index e3b25da90..000000000
--- a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Sync_ReadingUnit_with_Redfish_Sensor_Schema.patch
+++ /dev/null
@@ -1,227 +0,0 @@
-From 4b7e67d7200c0aba1b27478968d4f71449f1406a Mon Sep 17 00:00:00 2001
-From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
-Date: Mon, 8 Mar 2021 14:35:54 +0000
-Subject: [PATCH] Sync ReadingUnit with Redfish Sensor Schema
-
-Actual attribute "ReadingUnits" does not match with Redfish Sensor
-Schema. This change match "ReadingUnits" with Redfish Sensor Scheme
-1.0.0 and add missing "ReadingType" attribute. This change affect all
-users that depends on old units that does not match with Redfish
-standard. Added toReadingType and toReadingUnit function that uses
-values taken from Redfish Sensor Scheme 1.0.0. Latest version 1.2.0 of
-Sensor scheme defines units same units.
-
-Tested:
- - RedfishServiceValidator pass
-
-Change-Id: I0c8820eba7271022c427cd25dec321db36aa0176
-Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
-Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
----
- redfish-core/lib/power.hpp | 4 +-
- redfish-core/lib/sensors.hpp | 107 +++++++++++++++++++++++++++++------
- redfish-core/lib/thermal.hpp | 4 +-
- 3 files changed, 94 insertions(+), 21 deletions(-)
-
-diff --git a/redfish-core/lib/power.hpp b/redfish-core/lib/power.hpp
-index 1c7a009..99c45ef 100644
---- a/redfish-core/lib/power.hpp
-+++ b/redfish-core/lib/power.hpp
-@@ -153,7 +153,7 @@ class Power : public Node
- res.jsonValue["PowerControl"] = nlohmann::json::array();
-
- auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
-- res, chassisName, sensors::dbus::types.at(sensors::node::power),
-+ res, chassisName, sensors::dbus::paths.at(sensors::node::power),
- sensors::node::power);
-
- getChassisData(sensorAsyncResp);
-@@ -336,7 +336,7 @@ class Power : public Node
-
- const std::string& chassisName = params[0];
- auto asyncResp = std::make_shared<SensorsAsyncResp>(
-- res, chassisName, sensors::dbus::types.at(sensors::node::power),
-+ res, chassisName, sensors::dbus::paths.at(sensors::node::power),
- sensors::node::power);
-
- std::optional<std::vector<nlohmann::json>> voltageCollections;
-diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp
-index 35114bf..af030f0 100644
---- a/redfish-core/lib/sensors.hpp
-+++ b/redfish-core/lib/sensors.hpp
-@@ -54,9 +54,10 @@ static constexpr std::string_view thermal = "Thermal";
-
- namespace dbus
- {
-+
- static const boost::container::flat_map<std::string_view,
- std::vector<const char*>>
-- types = {{node::power,
-+ paths = {{node::power,
- {"/xyz/openbmc_project/sensors/voltage",
- "/xyz/openbmc_project/sensors/power"}},
- {node::sensors,
-@@ -67,6 +68,88 @@ static const boost::container::flat_map<std::string_view,
- {"/xyz/openbmc_project/sensors/fan_tach",
- "/xyz/openbmc_project/sensors/temperature",
- "/xyz/openbmc_project/sensors/fan_pwm"}}};
-+} // namespace dbus
-+
-+inline const char* toReadingType(const std::string& sensorType)
-+{
-+ if (sensorType == "voltage")
-+ {
-+ return "Voltage";
-+ }
-+ if (sensorType == "power")
-+ {
-+ return "Power";
-+ }
-+ if (sensorType == "current")
-+ {
-+ return "Current";
-+ }
-+ if (sensorType == "fan_tach")
-+ {
-+ return "Rotational";
-+ }
-+ if (sensorType == "temperature")
-+ {
-+ return "Temperature";
-+ }
-+ if (sensorType == "fan_pwm" || sensorType == "utilization")
-+ {
-+ return "Percent";
-+ }
-+ if (sensorType == "altitude")
-+ {
-+ return "Altitude";
-+ }
-+ if (sensorType == "airflow")
-+ {
-+ return "AirFlow";
-+ }
-+ if (sensorType == "energy")
-+ {
-+ return "EnergyJoules";
-+ }
-+ return "";
-+}
-+
-+inline const char* toReadingUnits(const std::string& sensorType)
-+{
-+ if (sensorType == "voltage")
-+ {
-+ return "V";
-+ }
-+ if (sensorType == "power")
-+ {
-+ return "W";
-+ }
-+ if (sensorType == "current")
-+ {
-+ return "A";
-+ }
-+ if (sensorType == "fan_tach")
-+ {
-+ return "RPM";
-+ }
-+ if (sensorType == "temperature")
-+ {
-+ return "Cel";
-+ }
-+ if (sensorType == "fan_pwm" || sensorType == "utilization")
-+ {
-+ return "%";
-+ }
-+ if (sensorType == "altitude")
-+ {
-+ return "m";
-+ }
-+ if (sensorType == "airflow")
-+ {
-+ return "cft_i/min";
-+ }
-+ if (sensorType == "energy")
-+ {
-+ return "J";
-+ }
-+ return "";
- }
- } // namespace sensors
-
-@@ -854,18 +937,8 @@ inline void objectInterfacesToJson(
- if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
- {
- sensorJson["@odata.type"] = "#Sensor.v1_0_0.Sensor";
-- if (sensorType == "power")
-- {
-- sensorJson["ReadingUnits"] = "Watts";
-- }
-- else if (sensorType == "current")
-- {
-- sensorJson["ReadingUnits"] = "Amperes";
-- }
-- else if (sensorType == "utilization")
-- {
-- sensorJson["ReadingUnits"] = "Percent";
-- }
-+ sensorJson["ReadingType"] = sensors::toReadingType(sensorType);
-+ sensorJson["ReadingUnits"] = sensors::toReadingUnits(sensorType);
- }
- else if (sensorType == "temperature")
- {
-@@ -2979,8 +3052,8 @@ inline void retrieveUriToDbusMap(const std::string& chassis,
- const std::string& node,
- SensorsAsyncResp::DataCompleteCb&& mapComplete)
- {
-- auto typesIt = sensors::dbus::types.find(node);
-- if (typesIt == sensors::dbus::types.end())
-+ auto pathIt = sensors::dbus::paths.find(node);
-+ if (pathIt == sensors::dbus::paths.end())
- {
- BMCWEB_LOG_ERROR << "Wrong node provided : " << node;
- mapComplete(boost::beast::http::status::bad_request, {});
-@@ -2995,7 +3068,7 @@ inline void retrieveUriToDbusMap(const std::string& chassis,
- uriToDbus) { mapCompleteCb(status, uriToDbus); };
-
- auto resp = std::make_shared<SensorsAsyncResp>(
-- *respBuffer, chassis, typesIt->second, node, std::move(callback));
-+ *respBuffer, chassis, pathIt->second, node, std::move(callback));
- getChassisData(resp);
- }
-
-@@ -3030,7 +3103,7 @@ class SensorCollection : public Node
- const std::string& chassisId = params[0];
- std::shared_ptr<SensorsAsyncResp> asyncResp =
- std::make_shared<SensorsAsyncResp>(
-- res, chassisId, sensors::dbus::types.at(sensors::node::sensors),
-+ res, chassisId, sensors::dbus::paths.at(sensors::node::sensors),
- sensors::node::sensors);
-
- auto getChassisCb =
-diff --git a/redfish-core/lib/thermal.hpp b/redfish-core/lib/thermal.hpp
-index 8e01bee..00acdf9 100644
---- a/redfish-core/lib/thermal.hpp
-+++ b/redfish-core/lib/thermal.hpp
-@@ -48,7 +48,7 @@ class Thermal : public Node
- }
- const std::string& chassisName = params[0];
- auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
-- res, chassisName, sensors::dbus::types.at(sensors::node::thermal),
-+ res, chassisName, sensors::dbus::paths.at(sensors::node::thermal),
- sensors::node::thermal);
-
- // TODO Need to get Chassis Redundancy information.
-@@ -71,7 +71,7 @@ class Thermal : public Node
- allCollections;
-
- auto asyncResp = std::make_shared<SensorsAsyncResp>(
-- res, chassisName, sensors::dbus::types.at(sensors::node::thermal),
-+ res, chassisName, sensors::dbus::paths.at(sensors::node::thermal),
- sensors::node::thermal);
-
- if (!json_util::readJson(req, asyncResp->res, "Temperatures",
---
-2.25.1
-
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-POST-and-DELETE-in-MetricReportDefinitions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-POST-and-DELETE-in-MetricReportDefinitions.patch
deleted file mode 100644
index aaf3f17c7..000000000
--- a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-POST-and-DELETE-in-MetricReportDefinitions.patch
+++ /dev/null
@@ -1,671 +0,0 @@
-From 03c4ece83b58b954323111a1a7b2bf2b61402d7e 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/4] Add POST and DELETE 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 Telemetry that serves as a backend
-for Redfish TelemetryService.
-Added DELETE request in MetricReportDefinitions node to allow user
-to remove report from Telemetry.
-Added conversion from string that represents duration format into
-its numeric equivalent.
-Added unit tests for conversion from and to Duration format.
-
-Tested:
- - Tested using witherspoon image on QEMU
- - Verified POST action in different cases:
- - all parameters are provided, new report is added to collection
- - some parameters are missing or invalid, user gets response with
- description of the issue
- - Verified that reports are removed on DELETE request
- - Verified that on invalid DELETE request user receives response
- with error
- - Verified time_utils::fromDurationString()
- - Succesfully passed RedfishServiceValidator.py
-
-Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
-Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
-Change-Id: I2fed96848594451e22fde686f8c066d7770cc65a
----
- meson.build | 1 +
- .../include/utils/telemetry_utils.hpp | 2 +
- redfish-core/include/utils/time_utils.hpp | 138 +++++++-
- redfish-core/lib/metric_report_definition.hpp | 328 ++++++++++++++++++
- redfish-core/ut/time_utils_test.cpp | 63 ++++
- 5 files changed, 530 insertions(+), 2 deletions(-)
- create mode 100644 redfish-core/ut/time_utils_test.cpp
-
-diff --git a/meson.build b/meson.build
-index 66a066b..22a8c4a 100644
---- a/meson.build
-+++ b/meson.build
-@@ -345,6 +345,7 @@ srcfiles_unittest = ['include/ut/dbus_utility_test.cpp',
- 'redfish-core/ut/privileges_test.cpp',
- 'redfish-core/ut/lock_test.cpp',
- 'redfish-core/ut/configfile_test.cpp',
-+ 'redfish-core/ut/time_utils_test.cpp',
- 'http/ut/utility_test.cpp']
-
- # Gather the Configuration data
-diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp
-index a3a8156..0a3af5f 100644
---- a/redfish-core/include/utils/telemetry_utils.hpp
-+++ b/redfish-core/include/utils/telemetry_utils.hpp
-@@ -1,5 +1,7 @@
- #pragma once
-
-+#include "dbus_utility.hpp"
-+
- namespace redfish
- {
-
-diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp
-index 4a87ba0..9965d4d 100644
---- a/redfish-core/include/utils/time_utils.hpp
-+++ b/redfish-core/include/utils/time_utils.hpp
-@@ -1,7 +1,13 @@
- #pragma once
-
-+#include "logging.hpp"
-+
-+#include <charconv>
- #include <chrono>
-+#include <cmath>
-+#include <optional>
- #include <string>
-+#include <system_error>
-
- namespace redfish
- {
-@@ -12,6 +18,8 @@ namespace time_utils
- namespace details
- {
-
-+using Days = std::chrono::duration<long long, std::ratio<24 * 60 * 60>>;
-+
- inline void leftZeroPadding(std::string& str, const std::size_t padding)
- {
- if (str.size() < padding)
-@@ -19,8 +27,135 @@ inline void leftZeroPadding(std::string& str, const std::size_t padding)
- str.insert(0, padding - str.size(), '0');
- }
- }
-+
-+template <typename FromTime>
-+bool fromDurationItem(std::string_view& fmt, const char postfix,
-+ std::chrono::milliseconds& out)
-+{
-+ const size_t pos = fmt.find(postfix);
-+ if (pos == std::string::npos)
-+ {
-+ return true;
-+ }
-+ if ((pos + 1U) > fmt.size())
-+ {
-+ return false;
-+ }
-+
-+ const char* end;
-+ std::chrono::milliseconds::rep ticks = 0;
-+ if constexpr (std::is_same_v<FromTime, std::chrono::milliseconds>)
-+ {
-+ end = fmt.data() + std::min<size_t>(pos, 3U);
-+ }
-+ else
-+ {
-+ end = fmt.data() + pos;
-+ }
-+
-+ auto [ptr, ec] = std::from_chars(fmt.data(), end, ticks);
-+ if (ptr != end || ec != std::errc())
-+ {
-+ BMCWEB_LOG_ERROR << "Failed to convert string to decimal with err: "
-+ << static_cast<int>(ec) << "("
-+ << std::make_error_code(ec).message() << "), ptr{"
-+ << static_cast<const void*>(ptr) << "} != end{"
-+ << static_cast<const void*>(end) << "})";
-+ return false;
-+ }
-+
-+ if constexpr (std::is_same_v<FromTime, std::chrono::milliseconds>)
-+ {
-+ ticks *= static_cast<std::chrono::milliseconds::rep>(
-+ std::pow(10, 3 - std::min<size_t>(pos, 3U)));
-+ }
-+ if (ticks < 0)
-+ {
-+ return false;
-+ }
-+
-+ out += FromTime(ticks);
-+ const auto maxConversionRange =
-+ std::chrono::duration_cast<FromTime>(std::chrono::milliseconds::max())
-+ .count();
-+ if (out < FromTime(ticks) || maxConversionRange < ticks)
-+ {
-+ return false;
-+ }
-+
-+ fmt.remove_prefix(pos + 1U);
-+ return true;
-+}
- } // namespace details
-
-+/**
-+ * @brief Convert string that represents value in Duration Format to its numeric
-+ * equivalent.
-+ */
-+std::optional<std::chrono::milliseconds>
-+ fromDurationString(const std::string& str)
-+{
-+ std::chrono::milliseconds out = std::chrono::milliseconds::zero();
-+ std::string_view v = str;
-+
-+ if (v.empty())
-+ {
-+ return out;
-+ }
-+ if (v.front() != 'P')
-+ {
-+ BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
-+ return std::nullopt;
-+ }
-+
-+ v.remove_prefix(1);
-+ if (!details::fromDurationItem<details::Days>(v, 'D', out))
-+ {
-+ BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
-+ return std::nullopt;
-+ }
-+
-+ if (v.empty())
-+ {
-+ return out;
-+ }
-+ if (v.front() != 'T')
-+ {
-+ BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
-+ return std::nullopt;
-+ }
-+
-+ v.remove_prefix(1);
-+ if (!details::fromDurationItem<std::chrono::hours>(v, 'H', out) ||
-+ !details::fromDurationItem<std::chrono::minutes>(v, 'M', out))
-+ {
-+ BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
-+ return std::nullopt;
-+ }
-+
-+ if (v.find('.') != std::string::npos && v.find('S') != std::string::npos)
-+ {
-+ if (!details::fromDurationItem<std::chrono::seconds>(v, '.', out) ||
-+ !details::fromDurationItem<std::chrono::milliseconds>(v, 'S', out))
-+ {
-+ BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
-+ return std::nullopt;
-+ }
-+ }
-+ else if (!details::fromDurationItem<std::chrono::seconds>(v, 'S', out))
-+ {
-+ BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
-+ return std::nullopt;
-+ }
-+
-+ if (!v.empty())
-+ {
-+ BMCWEB_LOG_ERROR << "Invalid duration format: " << str;
-+ return std::nullopt;
-+ }
-+ return out;
-+}
-+
- /**
- * @brief Convert time value into duration format that is based on ISO 8601.
- * Example output: "P12DT1M5.5S"
-@@ -36,8 +171,7 @@ inline std::string toDurationString(std::chrono::milliseconds ms)
- std::string fmt;
- fmt.reserve(sizeof("PxxxxxxxxxxxxDTxxHxxMxx.xxxxxxS"));
-
-- using Days = std::chrono::duration<long, std::ratio<24 * 60 * 60>>;
-- Days days = std::chrono::floor<Days>(ms);
-+ details::Days days = std::chrono::floor<details::Days>(ms);
- ms -= days;
-
- std::chrono::hours hours = std::chrono::floor<std::chrono::hours>(ms);
-diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
-index 59025d9..fcbc99c 100644
---- a/redfish-core/lib/metric_report_definition.hpp
-+++ b/redfish-core/lib/metric_report_definition.hpp
-@@ -1,9 +1,12 @@
- #pragma once
-
- #include "node.hpp"
-+#include "sensors.hpp"
- #include "utils/telemetry_utils.hpp"
- #include "utils/time_utils.hpp"
-
-+#include <boost/container/flat_map.hpp>
-+
- #include <tuple>
- #include <variant>
-
-@@ -95,6 +98,252 @@ inline void fillReportDefinition(
- asyncResp->res.jsonValue["Schedule"]["RecurrenceInterval"] =
- time_utils::toDurationString(std::chrono::milliseconds(*interval));
- }
-+
-+struct AddReportArgs
-+{
-+ std::string name;
-+ std::string reportingType;
-+ bool emitsReadingsUpdate = false;
-+ bool logToMetricReportsCollection = false;
-+ uint64_t interval = 0;
-+ std::vector<std::pair<std::string, std::vector<std::string>>> metrics;
-+};
-+
-+inline bool toDbusReportActions(crow::Response& res,
-+ std::vector<std::string>& actions,
-+ AddReportArgs& args)
-+{
-+ size_t index = 0;
-+ for (auto& action : actions)
-+ {
-+ if (action == "RedfishEvent")
-+ {
-+ args.emitsReadingsUpdate = true;
-+ }
-+ else if (action == "LogToMetricReportsCollection")
-+ {
-+ args.logToMetricReportsCollection = true;
-+ }
-+ else
-+ {
-+ messages::propertyValueNotInList(
-+ res, action, "ReportActions/" + std::to_string(index));
-+ return false;
-+ }
-+ index++;
-+ }
-+ return true;
-+}
-+
-+inline bool getUserParameters(crow::Response& res, const crow::Request& req,
-+ AddReportArgs& args)
-+{
-+ std::vector<nlohmann::json> metrics;
-+ std::vector<std::string> reportActions;
-+ std::optional<nlohmann::json> schedule;
-+ if (!json_util::readJson(req, res, "Id", args.name, "Metrics", metrics,
-+ "MetricReportDefinitionType", args.reportingType,
-+ "ReportActions", reportActions, "Schedule",
-+ schedule))
-+ {
-+ return false;
-+ }
-+
-+ constexpr const char* allowedCharactersInName =
-+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
-+ if (args.name.empty() || args.name.find_first_not_of(
-+ allowedCharactersInName) != std::string::npos)
-+ {
-+ BMCWEB_LOG_ERROR << "Failed to match " << args.name
-+ << " with allowed character "
-+ << allowedCharactersInName;
-+ messages::propertyValueIncorrect(res, "Id", args.name);
-+ return false;
-+ }
-+
-+ if (args.reportingType != "Periodic" && args.reportingType != "OnRequest")
-+ {
-+ messages::propertyValueNotInList(res, args.reportingType,
-+ "MetricReportDefinitionType");
-+ return false;
-+ }
-+
-+ if (!toDbusReportActions(res, reportActions, args))
-+ {
-+ return false;
-+ }
-+
-+ if (args.reportingType == "Periodic")
-+ {
-+ if (!schedule)
-+ {
-+ messages::createFailedMissingReqProperties(res, "Schedule");
-+ return false;
-+ }
-+
-+ std::string durationStr;
-+ if (!json_util::readJson(*schedule, res, "RecurrenceInterval",
-+ durationStr))
-+ {
-+ return false;
-+ }
-+
-+ std::optional<std::chrono::milliseconds> durationNum =
-+ time_utils::fromDurationString(durationStr);
-+ if (!durationNum)
-+ {
-+ messages::propertyValueIncorrect(res, "RecurrenceInterval",
-+ durationStr);
-+ return false;
-+ }
-+ args.interval = static_cast<uint64_t>(durationNum->count());
-+ }
-+
-+ args.metrics.reserve(metrics.size());
-+ for (auto& m : metrics)
-+ {
-+ std::string id;
-+ std::vector<std::string> uris;
-+ if (!json_util::readJson(m, res, "MetricId", id, "MetricProperties",
-+ uris))
-+ {
-+ return false;
-+ }
-+
-+ args.metrics.emplace_back(std::move(id), std::move(uris));
-+ }
-+
-+ return true;
-+}
-+
-+inline bool getChassisSensorNode(
-+ const std::shared_ptr<AsyncResp>& asyncResp,
-+ const std::vector<std::pair<std::string, std::vector<std::string>>>&
-+ metrics,
-+ boost::container::flat_set<std::pair<std::string, std::string>>& matched)
-+{
-+ for (const auto& [id, uris] : metrics)
-+ {
-+ for (size_t i = 0; i < uris.size(); i++)
-+ {
-+ const std::string& uri = uris[i];
-+ std::string chassis;
-+ std::string node;
-+
-+ if (!boost::starts_with(uri, "/redfish/v1/Chassis/") ||
-+ !dbus::utility::getNthStringFromPath(uri, 3, chassis) ||
-+ !dbus::utility::getNthStringFromPath(uri, 4, node))
-+ {
-+ BMCWEB_LOG_ERROR << "Failed to get chassis and sensor Node "
-+ "from "
-+ << uri;
-+ messages::propertyValueIncorrect(asyncResp->res, uri,
-+ "MetricProperties/" +
-+ std::to_string(i));
-+ return false;
-+ }
-+
-+ if (boost::ends_with(node, "#"))
-+ {
-+ node.pop_back();
-+ }
-+
-+ matched.emplace(std::move(chassis), std::move(node));
-+ }
-+ }
-+ return true;
-+}
-+
-+class AddReport
-+{
-+ public:
-+ AddReport(AddReportArgs argsIn, std::shared_ptr<AsyncResp> asyncResp) :
-+ asyncResp{std::move(asyncResp)}, args{std::move(argsIn)}
-+ {}
-+ ~AddReport()
-+ {
-+ if (asyncResp->res.result() != boost::beast::http::status::ok)
-+ {
-+ return;
-+ }
-+
-+ telemetry::ReadingParameters readingParams;
-+ readingParams.reserve(args.metrics.size());
-+
-+ for (const auto& [id, uris] : args.metrics)
-+ {
-+ for (size_t i = 0; i < uris.size(); i++)
-+ {
-+ const std::string& uri = uris[i];
-+ auto el = uriToDbus.find(uri);
-+ if (el == uriToDbus.end())
-+ {
-+ BMCWEB_LOG_ERROR << "Failed to find DBus sensor "
-+ "corresponding to URI "
-+ << uri;
-+ messages::propertyValueNotInList(asyncResp->res, uri,
-+ "MetricProperties/" +
-+ std::to_string(i));
-+ return;
-+ }
-+
-+ const std::string& dbusPath = el->second;
-+ readingParams.emplace_back(dbusPath, "SINGLE", id, uri);
-+ }
-+ }
-+
-+ crow::connections::systemBus->async_method_call(
-+ [asyncResp = std::move(asyncResp), name = args.name,
-+ uriToDbus = std::move(uriToDbus)](
-+ const boost::system::error_code ec, const std::string&) {
-+ 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 == boost::system::errc::argument_list_too_long)
-+ {
-+ nlohmann::json metricProperties = nlohmann::json::array();
-+ for (const auto& [uri, _] : uriToDbus)
-+ {
-+ metricProperties.emplace_back(uri);
-+ }
-+ messages::propertyValueIncorrect(
-+ asyncResp->res, metricProperties, "MetricProperties");
-+ return;
-+ }
-+ if (ec)
-+ {
-+ messages::internalError(asyncResp->res);
-+ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
-+ return;
-+ }
-+
-+ messages::created(asyncResp->res);
-+ },
-+ telemetry::service, "/xyz/openbmc_project/Telemetry/Reports",
-+ "xyz.openbmc_project.Telemetry.ReportManager", "AddReport",
-+ "TelemetryService/" + args.name, args.reportingType,
-+ args.emitsReadingsUpdate, args.logToMetricReportsCollection,
-+ args.interval, readingParams);
-+ }
-+
-+ void insert(const boost::container::flat_map<std::string, std::string>& el)
-+ {
-+ uriToDbus.insert(el.begin(), el.end());
-+ }
-+
-+ private:
-+ std::shared_ptr<AsyncResp> asyncResp;
-+ AddReportArgs args;
-+ boost::container::flat_map<std::string, std::string> uriToDbus{};
-+};
- } // namespace telemetry
-
- class MetricReportDefinitionCollection : public Node
-@@ -126,6 +375,46 @@ class MetricReportDefinitionCollection : public Node
- telemetry::getReportCollection(asyncResp,
- telemetry::metricReportDefinitionUri);
- }
-+
-+ void doPost(crow::Response& res, const crow::Request& req,
-+ const std::vector<std::string>&) override
-+ {
-+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+ telemetry::AddReportArgs args;
-+ if (!telemetry::getUserParameters(res, req, args))
-+ {
-+ return;
-+ }
-+
-+ boost::container::flat_set<std::pair<std::string, std::string>>
-+ chassisSensors;
-+ if (!telemetry::getChassisSensorNode(asyncResp, args.metrics,
-+ chassisSensors))
-+ {
-+ return;
-+ }
-+
-+ auto addReportReq =
-+ std::make_shared<telemetry::AddReport>(std::move(args), asyncResp);
-+ for (const auto& [chassis, sensorType] : chassisSensors)
-+ {
-+ retrieveUriToDbusMap(
-+ chassis, sensorType,
-+ [asyncResp, addReportReq](
-+ const boost::beast::http::status status,
-+ const boost::container::flat_map<std::string, std::string>&
-+ uriToDbus) {
-+ if (status != boost::beast::http::status::ok)
-+ {
-+ BMCWEB_LOG_ERROR << "Failed to retrieve URI to dbus "
-+ "sensors map with err "
-+ << static_cast<unsigned>(status);
-+ return;
-+ }
-+ addReportReq->insert(uriToDbus);
-+ });
-+ }
-+ }
- };
-
- class MetricReportDefinition : public Node
-@@ -184,5 +473,44 @@ class MetricReportDefinition : public Node
- "org.freedesktop.DBus.Properties", "GetAll",
- telemetry::reportInterface);
- }
-+
-+ void doDelete(crow::Response& res, const crow::Request&,
-+ const std::vector<std::string>& params) override
-+ {
-+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+ if (params.size() != 1)
-+ {
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ const std::string& id = params[0];
-+ const std::string reportPath = telemetry::getDbusReportPath(id);
-+
-+ crow::connections::systemBus->async_method_call(
-+ [asyncResp, id](const boost::system::error_code ec) {
-+ /*
-+ * boost::system::errc and std::errc are missing value for
-+ * EBADR error that is defined in Linux.
-+ */
-+ if (ec.value() == EBADR)
-+ {
-+ messages::resourceNotFound(asyncResp->res,
-+ "MetricReportDefinition", id);
-+ return;
-+ }
-+
-+ if (ec)
-+ {
-+ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ asyncResp->res.result(boost::beast::http::status::no_content);
-+ },
-+ telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete",
-+ "Delete");
-+ }
- };
- } // namespace redfish
-diff --git a/redfish-core/ut/time_utils_test.cpp b/redfish-core/ut/time_utils_test.cpp
-new file mode 100644
-index 0000000..70999ce
---- /dev/null
-+++ b/redfish-core/ut/time_utils_test.cpp
-@@ -0,0 +1,63 @@
-+#include "utils/time_utils.hpp"
-+
-+#include <gmock/gmock.h>
-+
-+using namespace testing;
-+
-+class FromDurationTest :
-+ public Test,
-+ public WithParamInterface<
-+ std::pair<std::string, std::optional<std::chrono::milliseconds>>>
-+{};
-+
-+INSTANTIATE_TEST_SUITE_P(
-+ _, FromDurationTest,
-+ Values(std::make_pair("PT12S", std::chrono::milliseconds(12000)),
-+ std::make_pair("PT0.204S", std::chrono::milliseconds(204)),
-+ std::make_pair("PT0.2S", std::chrono::milliseconds(200)),
-+ std::make_pair("PT50M", std::chrono::milliseconds(3000000)),
-+ std::make_pair("PT23H", std::chrono::milliseconds(82800000)),
-+ std::make_pair("P51D", std::chrono::milliseconds(4406400000)),
-+ std::make_pair("PT2H40M10.1S", std::chrono::milliseconds(9610100)),
-+ std::make_pair("P20DT2H40M10.1S",
-+ std::chrono::milliseconds(1737610100)),
-+ std::make_pair("", std::chrono::milliseconds(0)),
-+ std::make_pair("PTS", std::nullopt),
-+ std::make_pair("P1T", std::nullopt),
-+ std::make_pair("PT100M1000S100", std::nullopt),
-+ std::make_pair("PDTHMS", std::nullopt),
-+ std::make_pair("P99999999999999999DT", std::nullopt),
-+ std::make_pair("PD222T222H222M222.222S", std::nullopt),
-+ std::make_pair("PT99999H9999999999999999999999M99999999999S",
-+ std::nullopt),
-+ std::make_pair("PT-9H", std::nullopt)));
-+
-+TEST_P(FromDurationTest, convertToMilliseconds)
-+{
-+ const auto& [str, expected] = GetParam();
-+ EXPECT_THAT(redfish::time_utils::fromDurationString(str), Eq(expected));
-+}
-+
-+class ToDurationTest :
-+ public Test,
-+ public WithParamInterface<std::pair<std::chrono::milliseconds, std::string>>
-+{};
-+
-+INSTANTIATE_TEST_SUITE_P(
-+ _, ToDurationTest,
-+ Values(std::make_pair(std::chrono::milliseconds(12000), "PT12.000S"),
-+ std::make_pair(std::chrono::milliseconds(204), "PT0.204S"),
-+ std::make_pair(std::chrono::milliseconds(200), "PT0.200S"),
-+ std::make_pair(std::chrono::milliseconds(3000000), "PT50M"),
-+ std::make_pair(std::chrono::milliseconds(82800000), "PT23H"),
-+ std::make_pair(std::chrono::milliseconds(4406400000), "P51DT"),
-+ std::make_pair(std::chrono::milliseconds(9610100), "PT2H40M10.100S"),
-+ std::make_pair(std::chrono::milliseconds(1737610100),
-+ "P20DT2H40M10.100S"),
-+ std::make_pair(std::chrono::milliseconds(-250), "")));
-+
-+TEST_P(ToDurationTest, convertToDuration)
-+{
-+ const auto& [ms, expected] = GetParam();
-+ EXPECT_THAT(redfish::time_utils::toDurationString(ms), Eq(expected));
-+}
---
-2.17.1
-
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
index cfb47a49b..46128f7ae 100644
--- a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README
@@ -2,9 +2,6 @@ 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:
-- Add POST and DELETE in MetricReportDefinitions
- https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/32536/63
-
- Add support for MetricDefinition scheme
https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/33363/60