summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb
diff options
context:
space:
mode:
authorJason M. Bills <jason.m.bills@linux.intel.com>2021-03-26 01:45:09 +0300
committerJason M. Bills <jason.m.bills@linux.intel.com>2021-03-27 01:41:36 +0300
commit12bef3e1bf292dec5ac15af9fb41e86f7bcfb0cb (patch)
tree608a905372f3f545d2686e29512ae3f5f5b4a78c /meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb
parent0a1ece0568a37de9f17fd6e0bcdfd2cad2c6503f (diff)
downloadopenbmc-12bef3e1bf292dec5ac15af9fb41e86f7bcfb0cb.tar.xz
Update to internal 0.43
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Add-ConnectedVia-property-to-virtual-media-item-temp.patch25
-rwxr-xr-xmeta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch10
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Change-InsertMedia-action-response-for-POST-in-proxy.patch30
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0003-Set-Inserted-redfish-property-for-not-inserted-resou.patch40
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0036-Fix-wrong-check-in-EventService-events.patch35
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0037-Add-state-sensor-messages-to-the-registry.patch98
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch717
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-POST-and-DELETE-in-MetricReportDefinitions.patch769
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-MetricDefinition-scheme.patch457
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Sync-Telmetry-service-with-EventService.patch159
-rw-r--r--meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README9
11 files changed, 807 insertions, 1542 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Add-ConnectedVia-property-to-virtual-media-item-temp.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Add-ConnectedVia-property-to-virtual-media-item-temp.patch
new file mode 100644
index 000000000..ec6d70df1
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Add-ConnectedVia-property-to-virtual-media-item-temp.patch
@@ -0,0 +1,25 @@
+From 4af788655c5b5a5fae4d85b365a70dc619810fe0 Mon Sep 17 00:00:00 2001
+From: Karol Wachowski <karol.wachowski@intel.com>
+Date: Thu, 11 Feb 2021 08:35:41 +0000
+Subject: [PATCH] Add ConnectedVia property to virtual media item template
+
+Tested: Verified that ConnectedVia property is returned and set to
+ "NotConnected" for disconnected media.
+
+Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 188248a..80e7315 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -192,6 +192,7 @@ static nlohmann::json vmItemTemplate(const std::string& name,
+ item["@odata.id"] =
+ "/redfish/v1/Managers/" + name + "/VirtualMedia/" + resName;
+ item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
++ item["ConnectedVia"] = "NotConnected";
+ item["Name"] = "Virtual Removable Media";
+ item["Id"] = resName;
+ item["WriteProtected"] = true;
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch
index a802095d5..193461baf 100755
--- a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0001-Firmware-update-configuration-changes.patch
@@ -1,4 +1,4 @@
-From b831fbaf5c3ca346d2e701b021307ba219ca2ef8 Mon Sep 17 00:00:00 2001
+From 10cb7cb14974725a29b3ead4c543ca5e58234c07 Mon Sep 17 00:00:00 2001
From: Vikram Bodireddy <vikram.bodireddy@intel.com>
Date: Wed, 18 Nov 2020 17:14:41 +0530
Subject: [PATCH] Firmware update configuration changes
@@ -55,7 +55,7 @@ Signed-off-by: Helen Huang <he.huang@intel.com>
create mode 100644 static/redfish/v1/schema/OemUpdateService_v1.xml
diff --git a/redfish-core/lib/update_service.hpp b/redfish-core/lib/update_service.hpp
-index 9e382ce..7dff5c9 100644
+index 6d44171..8eda265 100644
--- a/redfish-core/lib/update_service.hpp
+++ b/redfish-core/lib/update_service.hpp
@@ -32,6 +32,17 @@ static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateErrorMatcher;
@@ -287,7 +287,7 @@ index 9e382ce..7dff5c9 100644
// Setup callback for when new software detected
// Give TFTP 10 minutes to complete
monitorForSoftwareAvailable(
- nullptr, req,
+ asyncResp, req,
"/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate",
- 600);
+ httpUriTargets, 600);
@@ -557,10 +557,10 @@ index 9e382ce..7dff5c9 100644
};
diff --git a/static/redfish/v1/$metadata/index.xml b/static/redfish/v1/$metadata/index.xml
-index e7f9d6d..bedc9e5 100644
+index 514f3dd..c068d4f 100644
--- a/static/redfish/v1/$metadata/index.xml
+++ b/static/redfish/v1/$metadata/index.xml
-@@ -2700,6 +2700,9 @@
+@@ -2142,6 +2142,9 @@
<edmx:Reference Uri="/redfish/v1/schema/OemManager_v1.xml">
<edmx:Include Namespace="OemManager"/>
</edmx:Reference>
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Change-InsertMedia-action-response-for-POST-in-proxy.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Change-InsertMedia-action-response-for-POST-in-proxy.patch
new file mode 100644
index 000000000..d04220b89
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0002-Change-InsertMedia-action-response-for-POST-in-proxy.patch
@@ -0,0 +1,30 @@
+From dcc94627aac5b8e4ad181c8548391c53d27b8896 Mon Sep 17 00:00:00 2001
+From: Karol Wachowski <karol.wachowski@intel.com>
+Date: Tue, 16 Feb 2021 06:47:11 +0000
+Subject: [PATCH] Change InsertMedia action response for POST in proxy mode
+
+Set boost::beast::http::status::method_not_allowed as a response
+for POST request to Virtual Media Insert Media action to keep
+consistency with other non existing requests.
+---
+ redfish-core/lib/virtual_media.hpp | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 80e7315..76e8c4a 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -611,10 +611,9 @@ class VirtualMediaActionInsertMedia : public Node
+ // Not possible in proxy mode
+ BMCWEB_LOG_DEBUG << "InsertMedia not "
+ "allowed in proxy mode";
+- messages::resourceNotFound(
+- aResp->res, "VirtualMedia.InsertMedia",
+- resName);
+-
++ aResp->res.result(
++ boost::beast::http::status::
++ method_not_allowed);
+ return;
+ }
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0003-Set-Inserted-redfish-property-for-not-inserted-resou.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0003-Set-Inserted-redfish-property-for-not-inserted-resou.patch
new file mode 100644
index 000000000..eaba041d5
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0003-Set-Inserted-redfish-property-for-not-inserted-resou.patch
@@ -0,0 +1,40 @@
+From dab4adbf211b6867f86fcf6080b34a0e41f6f4a1 Mon Sep 17 00:00:00 2001
+From: Karol Wachowski <karol.wachowski@intel.com>
+Date: Tue, 23 Feb 2021 15:53:16 +0000
+Subject: [PATCH] Set Inserted redfish property for not inserted resources
+
+Tested: Verified that Inserted property is returned and set to
+ "false" for not inserted media.
+Signed-off-by: Karol Wachowski <karol.wachowski@intel.com>
+---
+ redfish-core/lib/virtual_media.hpp | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/redfish-core/lib/virtual_media.hpp b/redfish-core/lib/virtual_media.hpp
+index 188248a..f477f63 100644
+--- a/redfish-core/lib/virtual_media.hpp
++++ b/redfish-core/lib/virtual_media.hpp
+@@ -95,6 +95,7 @@ static void vmParseInterfaceObject(const DbusInterfaceType& interface,
+ BMCWEB_LOG_DEBUG << "Value Active not found";
+ return;
+ }
++ aResp->res.jsonValue["Inserted"] = *activeValue;
+
+ const std::string* endpointIdValue =
+ std::get_if<std::string>(&endpointIdProperty->second);
+@@ -106,7 +107,6 @@ static void vmParseInterfaceObject(const DbusInterfaceType& interface,
+ aResp->res.jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] =
+ *endpointIdValue;
+ aResp->res.jsonValue["TransferProtocolType"] = "OEM";
+- aResp->res.jsonValue["Inserted"] = *activeValue;
+ if (*activeValue == true)
+ {
+ aResp->res.jsonValue["ConnectedVia"] = "Applet";
+@@ -137,7 +137,6 @@ static void vmParseInterfaceObject(const DbusInterfaceType& interface,
+ }
+
+ aResp->res.jsonValue["Image"] = *imageUrlValue;
+- aResp->res.jsonValue["Inserted"] = *activeValue;
+ aResp->res.jsonValue["TransferProtocolType"] =
+ getTransferProtocolTypeFromUri(*imageUrlValue);
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0036-Fix-wrong-check-in-EventService-events.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0036-Fix-wrong-check-in-EventService-events.patch
deleted file mode 100644
index 115a48112..000000000
--- a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0036-Fix-wrong-check-in-EventService-events.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From 6a9f85f9050e2c0f38148e295d7e25f56d05c6de Mon Sep 17 00:00:00 2001
-From: AppaRao Puli <apparao.puli@linux.intel.com>
-Date: Mon, 1 Feb 2021 23:45:53 +0000
-Subject: [PATCH] Fix wrong check in EventService events
-
-Sending async event logs to event listener is
-broken due to commit 23a21a1cbed23ace4174664950e595df961e9e69.
-Correct the check to make EventService back to functional
-state.
-
-Tested:
- - Redfish event logs are properly sent to subscribers.
-
-Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
-Change-Id: If232846a2b0ac694205731a801e55dc4bd5e928a
----
- redfish-core/include/event_service_manager.hpp | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
-index 54dafb4..3db9f0c 100644
---- a/redfish-core/include/event_service_manager.hpp
-+++ b/redfish-core/include/event_service_manager.hpp
-@@ -1208,7 +1208,7 @@ class EventServiceManager
-
- static void watchRedfishEventLogFile()
- {
-- if (inotifyConn)
-+ if (!inotifyConn)
- {
- return;
- }
---
-2.17.1
-
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0037-Add-state-sensor-messages-to-the-registry.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0037-Add-state-sensor-messages-to-the-registry.patch
new file mode 100644
index 000000000..b171a8b2c
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0037-Add-state-sensor-messages-to-the-registry.patch
@@ -0,0 +1,98 @@
+From df571ddf0596f73c0318da3a90b9813e6df19dd9 Mon Sep 17 00:00:00 2001
+From: "Arun P. Mohanan" <arun.p.m@linux.intel.com>
+Date: Wed, 27 Jan 2021 18:22:58 +0530
+Subject: [PATCH] Add state sensor messages to the registry
+
+Add messages to registry to indicate state sensor state change.
+
+Tested:
+Build and redfish validator passes.
+Logged these events and confirmed that they appear as expected on
+Redfish.
+GET: https://<BMC IP>/redfish/v1/Systems/system/LogServices/EventLog/Entries/1612528180
+{
+ "@odata.id": "/redfish/v1/Systems/system/LogServices/EventLog/Entries/1612528180",
+ "@odata.type": "#LogEntry.v1_4_0.LogEntry",
+ "Created": "2021-02-05T12:29:40+00:00",
+ "EntryType": "Event",
+ "Id": "1612528180",
+ "Message": "Operational Fault Status of Card_health_1 state sensor changed from Error to Normal.",
+ "MessageArgs": [
+ "Operational Fault Status",
+ "Card_health_1",
+ "Error",
+ "Normal"
+ ],
+ "MessageId": "OpenBMC.0.1.StateSensorNormal",
+ "Name": "System Event Log Entry",
+ "Severity": "OK"
+}
+
+Signed-off-by: Arun P. Mohanan <arun.p.m@linux.intel.com>
+---
+ .../registries/openbmc_message_registry.hpp | 36 +++++++++++++++++--
+ 1 file changed, 34 insertions(+), 2 deletions(-)
+
+diff --git a/redfish-core/include/registries/openbmc_message_registry.hpp b/redfish-core/include/registries/openbmc_message_registry.hpp
+index 5eb9380..dbea97c 100644
+--- a/redfish-core/include/registries/openbmc_message_registry.hpp
++++ b/redfish-core/include/registries/openbmc_message_registry.hpp
+@@ -29,7 +29,7 @@ const Header header = {
+ "0.1.0",
+ "OpenBMC",
+ };
+-constexpr std::array<MessageEntry, 187> registry = {
++constexpr std::array<MessageEntry, 190> registry = {
+ MessageEntry{
+ "ADDDCCorrectable",
+ {
+@@ -2318,6 +2318,39 @@ constexpr std::array<MessageEntry, 187> registry = {
+ {},
+ "None.",
+ }},
++ MessageEntry{
++ "StateSensorNormal",
++ {
++ "Indicates that a state sensor has changed state to normal.",
++ "%1 of %2 state sensor changed from %3 to %4.",
++ "OK",
++ "OK",
++ 4,
++ {"string", "string", "string", "string"},
++ "None.",
++ }},
++ MessageEntry{
++ "StateSensorWarning",
++ {
++ "Indicates that a state sensor has changed state to warning.",
++ "%1 of %2 state sensor changed from %3 to %4.",
++ "Warning",
++ "Warning",
++ 4,
++ {"string", "string", "string", "string"},
++ "Check sensor subsystem for errors.",
++ }},
++ MessageEntry{
++ "StateSensorCritical",
++ {
++ "Indicates that a state sensor has changed state to critical.",
++ "%1 of %2 state sensor changed from %3 to %4.",
++ "Critical",
++ "Critical",
++ 4,
++ {"string", "string", "string", "string"},
++ "Check sensor subsystem for errors.",
++ }},
+ MessageEntry{"SystemInterfaceDisabledProvisioned",
+ {
+ "Indicates that the system interface is in the disabled "
+@@ -2410,6 +2443,5 @@ constexpr std::array<MessageEntry, 187> registry = {
+ {"string"},
+ "None.",
+ }},
+-
+ };
+ } // namespace redfish::message_registries::openbmc
+--
+2.17.1
+
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch
deleted file mode 100644
index 208831338..000000000
--- a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Redfish-TelemetryService-schema-implementation.patch
+++ /dev/null
@@ -1,717 +0,0 @@
-From d50e4ce193703c008d3293acd03e1c0542c0c215 Mon Sep 17 00:00:00 2001
-From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
-Date: Mon, 27 Apr 2020 17:24:15 +0200
-Subject: [PATCH] Redfish TelemetryService schema implementation
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Added TelemetryService, MetricReports, MetricReportCollection,
-MetricReportDefinition and MetricReportDefinitionCollection schemas
-with GET method support. Added TelemetryService URI to root service.
-Implemented communication with backend - Telemetry.
-Added schemes attributes that are supported by Telemetry service
-design. User is able to fetch basic information about reports if
-Telemetry service is present in OpenBMC.
-Added util function that converts decimal value into duration format
-that is described by ISO 8601 and Redfish specification.
-
-Tested:
- - Succesfully passed RedfishServiceValidator.py
- - Verified DBus method calls to Telemetry service
- - Verified all possible pages that are displayed to user when:
- - Reports are fully defined in Telemetry
- - Reports are partially available in Telemetry
- - Telemetry is disabled
- - Verified time_utils::toDurationString() output
-
-Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
-Signed-off-by: Adrian Ambrożewicz <adrian.ambrozewicz@linux.intel.com>
-Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
-Change-Id: Ie6b0b49f4ef5eeaef07d1209b6c349270c04d570
----
- redfish-core/include/redfish.hpp | 10 +
- .../include/utils/telemetry_utils.hpp | 71 +++++++
- redfish-core/include/utils/time_utils.hpp | 78 ++++++++
- redfish-core/lib/metric_report.hpp | 162 +++++++++++++++
- redfish-core/lib/metric_report_definition.hpp | 186 ++++++++++++++++++
- redfish-core/lib/service_root.hpp | 2 +
- redfish-core/lib/telemetry_service.hpp | 93 +++++++++
- 7 files changed, 602 insertions(+)
- create mode 100644 redfish-core/include/utils/telemetry_utils.hpp
- create mode 100644 redfish-core/include/utils/time_utils.hpp
- create mode 100644 redfish-core/lib/metric_report.hpp
- create mode 100644 redfish-core/lib/metric_report_definition.hpp
- create mode 100644 redfish-core/lib/telemetry_service.hpp
-
-diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
-index dabf78e..b366e24 100644
---- a/redfish-core/include/redfish.hpp
-+++ b/redfish-core/include/redfish.hpp
-@@ -25,6 +25,8 @@
- #include "../lib/managers.hpp"
- #include "../lib/memory.hpp"
- #include "../lib/message_registries.hpp"
-+#include "../lib/metric_report.hpp"
-+#include "../lib/metric_report_definition.hpp"
- #include "../lib/network_protocol.hpp"
- #include "../lib/pcie.hpp"
- #include "../lib/power.hpp"
-@@ -36,6 +38,7 @@
- #include "../lib/storage.hpp"
- #include "../lib/systems.hpp"
- #include "../lib/task.hpp"
-+#include "../lib/telemetry_service.hpp"
- #include "../lib/thermal.hpp"
- #include "../lib/update_service.hpp"
- #ifdef BMCWEB_ENABLE_VM_NBDPROXY
-@@ -212,6 +215,13 @@ class RedfishService
- nodes.emplace_back(std::make_unique<HypervisorInterface>(app));
- nodes.emplace_back(std::make_unique<HypervisorSystem>(app));
-
-+ nodes.emplace_back(std::make_unique<TelemetryService>(app));
-+ nodes.emplace_back(
-+ std::make_unique<MetricReportDefinitionCollection>(app));
-+ nodes.emplace_back(std::make_unique<MetricReportDefinition>(app));
-+ nodes.emplace_back(std::make_unique<MetricReportCollection>(app));
-+ nodes.emplace_back(std::make_unique<MetricReport>(app));
-+
- for (const auto& node : nodes)
- {
- node->initPrivileges();
-diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp
-new file mode 100644
-index 0000000..8caee2d
---- /dev/null
-+++ b/redfish-core/include/utils/telemetry_utils.hpp
-@@ -0,0 +1,71 @@
-+#pragma once
-+
-+namespace redfish
-+{
-+
-+namespace telemetry
-+{
-+
-+constexpr const char* service = "xyz.openbmc_project.Telemetry";
-+constexpr const char* reportInterface = "xyz.openbmc_project.Telemetry.Report";
-+constexpr const char* metricReportDefinitionUri =
-+ "/redfish/v1/TelemetryService/MetricReportDefinitions/";
-+constexpr const char* metricReportUri =
-+ "/redfish/v1/TelemetryService/MetricReports/";
-+
-+inline void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp,
-+ const std::string& uri)
-+{
-+ const std::array<const char*, 1> interfaces = {reportInterface};
-+
-+ crow::connections::systemBus->async_method_call(
-+ [asyncResp, uri](const boost::system::error_code ec,
-+ const std::vector<std::string>& reportPaths) {
-+ if (ec)
-+ {
-+ asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
-+ asyncResp->res.jsonValue["Members@odata.count"] = 0;
-+ return;
-+ }
-+
-+ nlohmann::json& members = asyncResp->res.jsonValue["Members"];
-+ members = nlohmann::json::array();
-+
-+ for (const std::string& path : reportPaths)
-+ {
-+ std::size_t pos = path.rfind('/');
-+ if (pos == std::string::npos)
-+ {
-+ BMCWEB_LOG_ERROR << "Failed to find '/' in " << path;
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+ if (path.size() <= (pos + 1))
-+ {
-+ BMCWEB_LOG_ERROR << "Failed to parse path " << path;
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ members.push_back({{"@odata.id", uri + path.substr(pos + 1)}});
-+ }
-+
-+ asyncResp->res.jsonValue["Members@odata.count"] = members.size();
-+ },
-+ "xyz.openbmc_project.ObjectMapper",
-+ "/xyz/openbmc_project/object_mapper",
-+ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
-+ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService", 1,
-+ interfaces);
-+}
-+
-+inline std::string getDbusReportPath(const std::string& id)
-+{
-+ std::string path =
-+ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id;
-+ dbus::utility::escapePathForDbus(path);
-+ return path;
-+}
-+
-+} // namespace telemetry
-+} // namespace redfish
-diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp
-new file mode 100644
-index 0000000..dd4ea75
---- /dev/null
-+++ b/redfish-core/include/utils/time_utils.hpp
-@@ -0,0 +1,78 @@
-+#pragma once
-+
-+#include <chrono>
-+#include <string>
-+
-+namespace redfish
-+{
-+
-+namespace time_utils
-+{
-+
-+namespace details
-+{
-+
-+inline void leftZeroPadding(std::string& str, const std::size_t padding)
-+{
-+ if (str.size() < padding)
-+ {
-+ str.insert(0, padding - str.size(), '0');
-+ }
-+}
-+} // namespace details
-+
-+/**
-+ * @brief Convert time value into duration format that is based on ISO 8601.
-+ * Example output: "P12DT1M5.5S"
-+ * Ref: Redfish Specification, Section 9.4.4. Duration values
-+ */
-+std::string toDurationString(std::chrono::milliseconds ms)
-+{
-+ if (ms < std::chrono::milliseconds::zero())
-+ {
-+ return "";
-+ }
-+
-+ std::string fmt;
-+ fmt.reserve(sizeof("PxxxxxxxxxxxxDTxxHxxMxx.xxxxxxS"));
-+
-+ using Days = std::chrono::duration<long, std::ratio<24 * 60 * 60>>;
-+ Days days = std::chrono::floor<Days>(ms);
-+ ms -= days;
-+
-+ std::chrono::hours hours = std::chrono::floor<std::chrono::hours>(ms);
-+ ms -= hours;
-+
-+ std::chrono::minutes minutes = std::chrono::floor<std::chrono::minutes>(ms);
-+ ms -= minutes;
-+
-+ std::chrono::seconds seconds = std::chrono::floor<std::chrono::seconds>(ms);
-+ ms -= seconds;
-+
-+ fmt = "P";
-+ if (days.count() > 0)
-+ {
-+ fmt += std::to_string(days.count()) + "D";
-+ }
-+ fmt += "T";
-+ if (hours.count() > 0)
-+ {
-+ fmt += std::to_string(hours.count()) + "H";
-+ }
-+ if (minutes.count() > 0)
-+ {
-+ fmt += std::to_string(minutes.count()) + "M";
-+ }
-+ if (seconds.count() != 0 || ms.count() != 0)
-+ {
-+ fmt += std::to_string(seconds.count()) + ".";
-+ std::string msStr = std::to_string(ms.count());
-+ details::leftZeroPadding(msStr, 3);
-+ fmt += msStr + "S";
-+ }
-+
-+ return fmt;
-+}
-+
-+} // namespace time_utils
-+} // namespace redfish
-diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp
-new file mode 100644
-index 0000000..050304c
---- /dev/null
-+++ b/redfish-core/lib/metric_report.hpp
-@@ -0,0 +1,162 @@
-+#pragma once
-+
-+#include "node.hpp"
-+#include "utils/telemetry_utils.hpp"
-+
-+namespace redfish
-+{
-+
-+class MetricReportCollection : public Node
-+{
-+ public:
-+ MetricReportCollection(App& app) :
-+ Node(app, "/redfish/v1/TelemetryService/MetricReports/")
-+ {
-+ entityPrivileges = {
-+ {boost::beast::http::verb::get, {{"Login"}}},
-+ {boost::beast::http::verb::head, {{"Login"}}},
-+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
-+ }
-+
-+ private:
-+ void doGet(crow::Response& res, const crow::Request&,
-+ const std::vector<std::string>&) override
-+ {
-+ res.jsonValue["@odata.type"] =
-+ "#MetricReportCollection.MetricReportCollection";
-+ res.jsonValue["@odata.id"] =
-+ "/redfish/v1/TelemetryService/MetricReports";
-+ res.jsonValue["Name"] = "Metric Report Collection";
-+
-+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+ telemetry::getReportCollection(asyncResp, telemetry::metricReportUri);
-+ }
-+};
-+
-+class MetricReport : public Node
-+{
-+ public:
-+ MetricReport(App& app) :
-+ Node(app, "/redfish/v1/TelemetryService/MetricReports/<str>/",
-+ std::string())
-+ {
-+ entityPrivileges = {
-+ {boost::beast::http::verb::get, {{"Login"}}},
-+ {boost::beast::http::verb::head, {{"Login"}}},
-+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
-+ }
-+
-+ private:
-+ void doGet(crow::Response& res, const crow::Request&,
-+ const std::vector<std::string>& params) override
-+ {
-+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+
-+ if (params.size() != 1)
-+ {
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ const std::string& id = params[0];
-+ const std::string reportPath = telemetry::getDbusReportPath(id);
-+
-+ crow::connections::systemBus->async_method_call(
-+ [asyncResp, id, reportPath](const boost::system::error_code& ec) {
-+ if (ec.value() == EBADR)
-+ {
-+ messages::resourceNotFound(asyncResp->res, schemaType, id);
-+ return;
-+ }
-+ if (ec)
-+ {
-+ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ crow::connections::systemBus->async_method_call(
-+ [asyncResp,
-+ id](const boost::system::error_code ec,
-+ const std::variant<TimestampReadings>& ret) {
-+ if (ec)
-+ {
-+ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ fillReport(asyncResp, id, ret);
-+ },
-+ telemetry::service, reportPath,
-+ "org.freedesktop.DBus.Properties", "Get",
-+ telemetry::reportInterface, "Readings");
-+ },
-+ telemetry::service, reportPath, telemetry::reportInterface,
-+ "Update");
-+ }
-+
-+ using Readings =
-+ std::vector<std::tuple<std::string, std::string, double, uint64_t>>;
-+ using TimestampReadings = std::tuple<uint64_t, Readings>;
-+
-+ static nlohmann::json toMetricValues(const Readings& readings)
-+ {
-+ nlohmann::json metricValues = nlohmann::json::array_t();
-+
-+ for (auto& [id, metadata, sensorValue, timestamp] : readings)
-+ {
-+ nlohmann::json metadataJson = nlohmann::json::parse(metadata);
-+ metricValues.push_back({
-+ {"MetricId", id},
-+ {"MetricDefinition", metadataJson.contains("MetricDefinition")
-+ ? metadataJson["MetricDefinition"]
-+ : nlohmann::json()},
-+ {"MetricProperty", metadataJson.contains("MetricProperty")
-+ ? metadataJson["MetricProperty"]
-+ : nlohmann::json()},
-+ {"MetricValue", std::to_string(sensorValue)},
-+ {"Timestamp",
-+ crow::utility::getDateTime(static_cast<time_t>(timestamp))},
-+ });
-+ }
-+
-+ return metricValues;
-+ }
-+
-+ static void fillReport(const std::shared_ptr<AsyncResp>& asyncResp,
-+ const std::string& id,
-+ const std::variant<TimestampReadings>& var)
-+ {
-+ asyncResp->res.jsonValue["@odata.type"] = schemaType;
-+ asyncResp->res.jsonValue["@odata.id"] = telemetry::metricReportUri + id;
-+ asyncResp->res.jsonValue["Id"] = id;
-+ asyncResp->res.jsonValue["Name"] = id;
-+ asyncResp->res.jsonValue["MetricReportDefinition"]["@odata.id"] =
-+ telemetry::metricReportDefinitionUri + id;
-+
-+ const TimestampReadings* timestampReadings =
-+ std::get_if<TimestampReadings>(&var);
-+ if (!timestampReadings)
-+ {
-+ BMCWEB_LOG_ERROR << "Property type mismatch or property is missing";
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ const auto& [timestamp, readings] = *timestampReadings;
-+ asyncResp->res.jsonValue["Timestamp"] =
-+ crow::utility::getDateTime(static_cast<time_t>(timestamp));
-+ asyncResp->res.jsonValue["MetricValues"] = toMetricValues(readings);
-+ }
-+
-+ static constexpr const char* schemaType =
-+ "#MetricReport.v1_3_0.MetricReport";
-+};
-+} // namespace redfish
-diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
-new file mode 100644
-index 0000000..48c56e6
---- /dev/null
-+++ b/redfish-core/lib/metric_report_definition.hpp
-@@ -0,0 +1,186 @@
-+#pragma once
-+
-+#include "node.hpp"
-+#include "utils/telemetry_utils.hpp"
-+#include "utils/time_utils.hpp"
-+
-+#include <tuple>
-+#include <variant>
-+
-+namespace redfish
-+{
-+
-+class MetricReportDefinitionCollection : public Node
-+{
-+ public:
-+ MetricReportDefinitionCollection(App& app) :
-+ Node(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
-+ {
-+ entityPrivileges = {
-+ {boost::beast::http::verb::get, {{"Login"}}},
-+ {boost::beast::http::verb::head, {{"Login"}}},
-+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
-+ }
-+
-+ private:
-+ void doGet(crow::Response& res, const crow::Request&,
-+ const std::vector<std::string>&) override
-+ {
-+ res.jsonValue["@odata.type"] = "#MetricReportDefinitionCollection."
-+ "MetricReportDefinitionCollection";
-+ res.jsonValue["@odata.id"] =
-+ "/redfish/v1/TelemetryService/MetricReportDefinitions";
-+ res.jsonValue["Name"] = "Metric Definition Collection";
-+
-+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+ telemetry::getReportCollection(asyncResp,
-+ telemetry::metricReportDefinitionUri);
-+ }
-+};
-+
-+class MetricReportDefinition : public Node
-+{
-+ public:
-+ MetricReportDefinition(App& app) :
-+ Node(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/",
-+ std::string())
-+ {
-+ entityPrivileges = {
-+ {boost::beast::http::verb::get, {{"Login"}}},
-+ {boost::beast::http::verb::head, {{"Login"}}},
-+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
-+ }
-+
-+ private:
-+ void doGet(crow::Response& res, const crow::Request&,
-+ const std::vector<std::string>& params) override
-+ {
-+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+
-+ if (params.size() != 1)
-+ {
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ const std::string& id = params[0];
-+ crow::connections::systemBus->async_method_call(
-+ [asyncResp,
-+ id](const boost::system::error_code ec,
-+ const std::vector<std::pair<
-+ std::string, std::variant<bool, ReadingParameters,
-+ std::string, uint64_t>>>& ret) {
-+ if (ec.value() == EBADR)
-+ {
-+ messages::resourceNotFound(asyncResp->res, schemaType, id);
-+ return;
-+ }
-+ if (ec)
-+ {
-+ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ fillReportDefinition(asyncResp, id, ret);
-+ },
-+ telemetry::service, telemetry::getDbusReportPath(id),
-+ "org.freedesktop.DBus.Properties", "GetAll",
-+ telemetry::reportInterface);
-+ }
-+
-+ using ReadingParameters =
-+ std::vector<std::tuple<std::vector<sdbusplus::message::object_path>,
-+ std::string, std::string, std::string>>;
-+
-+ static void fillReportDefinition(
-+ const std::shared_ptr<AsyncResp>& asyncResp, const std::string& id,
-+ const std::vector<
-+ std::pair<std::string, std::variant<bool, ReadingParameters,
-+ std::string, uint64_t>>>& ret)
-+ {
-+ asyncResp->res.jsonValue["@odata.type"] = schemaType;
-+ asyncResp->res.jsonValue["@odata.id"] =
-+ telemetry::metricReportDefinitionUri + id;
-+ asyncResp->res.jsonValue["Id"] = id;
-+ asyncResp->res.jsonValue["Name"] = id;
-+ asyncResp->res.jsonValue["MetricReport"]["@odata.id"] =
-+ telemetry::metricReportUri + id;
-+ asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
-+ asyncResp->res.jsonValue["ReportUpdates"] = "Overwrite";
-+
-+ const bool* emitsReadingsUpdate = nullptr;
-+ const bool* logToMetricReportsCollection = nullptr;
-+ const ReadingParameters* readingParams = nullptr;
-+ const std::string* reportingType = nullptr;
-+ const uint64_t* interval = nullptr;
-+ for (const auto& [key, var] : ret)
-+ {
-+ if (key == "EmitsReadingsUpdate")
-+ {
-+ emitsReadingsUpdate = std::get_if<bool>(&var);
-+ }
-+ else if (key == "LogToMetricReportsCollection")
-+ {
-+ logToMetricReportsCollection = std::get_if<bool>(&var);
-+ }
-+ else if (key == "ReadingParameters")
-+ {
-+ readingParams = std::get_if<ReadingParameters>(&var);
-+ }
-+ else if (key == "ReportingType")
-+ {
-+ reportingType = std::get_if<std::string>(&var);
-+ }
-+ else if (key == "Interval")
-+ {
-+ interval = std::get_if<uint64_t>(&var);
-+ }
-+ }
-+ if (!emitsReadingsUpdate || !logToMetricReportsCollection ||
-+ !readingParams || !reportingType || !interval)
-+ {
-+ BMCWEB_LOG_ERROR << "Property type mismatch or property is missing";
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ std::vector<std::string> redfishReportActions;
-+ redfishReportActions.reserve(2);
-+ if (*emitsReadingsUpdate)
-+ {
-+ redfishReportActions.emplace_back("RedfishEvent");
-+ }
-+ if (*logToMetricReportsCollection)
-+ {
-+ redfishReportActions.emplace_back("LogToMetricReportsCollection");
-+ }
-+
-+ nlohmann::json metrics = nlohmann::json::array();
-+ for (auto& [sensorPaths, operationType, id, metadata] : *readingParams)
-+ {
-+ nlohmann::json metadataJson = nlohmann::json::parse(metadata);
-+ metrics.push_back({
-+ {"MetricId", id},
-+ {"MetricProperties", metadataJson.contains("MetricProperties")
-+ ? metadataJson["MetricProperties"]
-+ : nlohmann::json()},
-+ });
-+ }
-+ asyncResp->res.jsonValue["Metrics"] = metrics;
-+ asyncResp->res.jsonValue["MetricReportDefinitionType"] = *reportingType;
-+ asyncResp->res.jsonValue["ReportActions"] = redfishReportActions;
-+ asyncResp->res.jsonValue["Schedule"]["RecurrenceInterval"] =
-+ time_utils::toDurationString(std::chrono::milliseconds(*interval));
-+ }
-+
-+ static constexpr const char* schemaType =
-+ "#MetricReportDefinition.v1_3_0.MetricReportDefinition";
-+};
-+} // namespace redfish
-diff --git a/redfish-core/lib/service_root.hpp b/redfish-core/lib/service_root.hpp
-index 629280c..3df5ec5 100644
---- a/redfish-core/lib/service_root.hpp
-+++ b/redfish-core/lib/service_root.hpp
-@@ -68,6 +68,8 @@ class ServiceRoot : public Node
- res.jsonValue["Tasks"] = {{"@odata.id", "/redfish/v1/TaskService"}};
- res.jsonValue["EventService"] = {
- {"@odata.id", "/redfish/v1/EventService"}};
-+ res.jsonValue["TelemetryService"] = {
-+ {"@odata.id", "/redfish/v1/TelemetryService"}};
- res.end();
- }
-
-diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp
-new file mode 100644
-index 0000000..a6acc34
---- /dev/null
-+++ b/redfish-core/lib/telemetry_service.hpp
-@@ -0,0 +1,93 @@
-+#pragma once
-+
-+#include "node.hpp"
-+#include "utils/telemetry_utils.hpp"
-+
-+#include <variant>
-+
-+namespace redfish
-+{
-+
-+class TelemetryService : public Node
-+{
-+ public:
-+ TelemetryService(App& app) : Node(app, "/redfish/v1/TelemetryService/")
-+ {
-+ entityPrivileges = {
-+ {boost::beast::http::verb::get, {{"Login"}}},
-+ {boost::beast::http::verb::head, {{"Login"}}},
-+ {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::put, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
-+ {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
-+ }
-+
-+ private:
-+ void doGet(crow::Response& res, const crow::Request&,
-+ const std::vector<std::string>&) override
-+ {
-+ res.jsonValue["@odata.type"] =
-+ "#TelemetryService.v1_2_1.TelemetryService";
-+ res.jsonValue["@odata.id"] = "/redfish/v1/TelemetryService";
-+ res.jsonValue["Id"] = "TelemetryService";
-+ res.jsonValue["Name"] = "Telemetry Service";
-+
-+ res.jsonValue["LogService"]["@odata.id"] =
-+ "/redfish/v1/Managers/bmc/LogServices/Journal";
-+ res.jsonValue["MetricReportDefinitions"]["@odata.id"] =
-+ "/redfish/v1/TelemetryService/MetricReportDefinitions";
-+ res.jsonValue["MetricReports"]["@odata.id"] =
-+ "/redfish/v1/TelemetryService/MetricReports";
-+
-+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+ crow::connections::systemBus->async_method_call(
-+ [asyncResp](
-+ const boost::system::error_code ec,
-+ const std::vector<std::pair<
-+ std::string, std::variant<uint32_t, uint64_t>>>& ret) {
-+ if (ec == boost::system::errc::host_unreachable)
-+ {
-+ asyncResp->res.jsonValue["Status"]["State"] = "Absent";
-+ return;
-+ }
-+ if (ec)
-+ {
-+ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
-+
-+ const size_t* maxReports = nullptr;
-+ const uint64_t* minInterval = nullptr;
-+ for (const auto& [key, var] : ret)
-+ {
-+ if (key == "MaxReports")
-+ {
-+ maxReports = std::get_if<size_t>(&var);
-+ }
-+ else if (key == "MinInterval")
-+ {
-+ minInterval = std::get_if<uint64_t>(&var);
-+ }
-+ }
-+ if (!maxReports || !minInterval)
-+ {
-+ BMCWEB_LOG_ERROR
-+ << "Property type mismatch or property is missing";
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ asyncResp->res.jsonValue["MaxReports"] = *maxReports;
-+ asyncResp->res.jsonValue["MinCollectionInterval"] =
-+ time_utils::toDurationString(std::chrono::milliseconds(
-+ static_cast<time_t>(*minInterval)));
-+ },
-+ telemetry::service, "/xyz/openbmc_project/Telemetry/Reports",
-+ "org.freedesktop.DBus.Properties", "GetAll",
-+ "xyz.openbmc_project.Telemetry.ReportManager");
-+ }
-+};
-+} // namespace redfish
---
-2.17.1
-
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
index f40058ad8..fd7e8a445 100644
--- 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
@@ -1,7 +1,7 @@
-From 433358330c7f7d2fba99f6e488d67b314224317f Mon Sep 17 00:00:00 2001
+From bc1635622e122f307fb3b8eb9bbd66ea576a8f0c 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] Add POST and DELETE in MetricReportDefinitions
+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
@@ -12,64 +12,62 @@ 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:
- - Succesfully passed RedfishServiceValidator.py
- - Validated good cases with different parameters for POST action
- - Validated bad cases with different parameters for POST action
- - Verified time_utils::fromDurationString()
+ - 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
---
- .../include/utils/telemetry_utils.hpp | 5 +-
- redfish-core/include/utils/time_utils.hpp | 145 ++++++-
- redfish-core/lib/metric_report_definition.hpp | 382 +++++++++++++++++-
- 3 files changed, 516 insertions(+), 16 deletions(-)
+ meson.build | 1 +
+ redfish-core/include/utils/time_utils.hpp | 138 ++++++++++-
+ redfish-core/lib/metric_report_definition.hpp | 328 ++++++++++++++++++++++++++
+ redfish-core/ut/time_utils_test.cpp | 63 +++++
+ 4 files changed, 528 insertions(+), 2 deletions(-)
+ create mode 100644 redfish-core/ut/time_utils_test.cpp
-diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp
-index 8caee2d..acb739d 100644
---- a/redfish-core/include/utils/telemetry_utils.hpp
-+++ b/redfish-core/include/utils/telemetry_utils.hpp
-@@ -12,6 +12,8 @@ constexpr const char* metricReportDefinitionUri =
- "/redfish/v1/TelemetryService/MetricReportDefinitions/";
- constexpr const char* metricReportUri =
- "/redfish/v1/TelemetryService/MetricReports/";
-+constexpr const char* reportDir =
-+ "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/";
+diff --git a/meson.build b/meson.build
+index 7a16e91..3d65b01 100644
+--- a/meson.build
++++ b/meson.build
+@@ -330,6 +330,7 @@ srcfiles_bmcweb = ['src/webserver_main.cpp','redfish-core/src/error_messages.cpp
+ srcfiles_unittest = ['include/ut/dbus_utility_test.cpp',
+ 'redfish-core/ut/privileges_test.cpp',
+ 'redfish-core/ut/lock_test.cpp',
++ 'redfish-core/ut/time_utils_test.cpp',
+ 'http/ut/utility_test.cpp']
- inline void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp,
- const std::string& uri)
-@@ -61,8 +63,7 @@ inline void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp,
-
- inline std::string getDbusReportPath(const std::string& id)
- {
-- std::string path =
-- "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/" + id;
-+ std::string path = reportDir + id;
- dbus::utility::escapePathForDbus(path);
- return path;
- }
+ # Gather the Configuration data
diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp
-index dd4ea75..d8985ab 100644
+index dd4ea75..e94801b 100644
--- a/redfish-core/include/utils/time_utils.hpp
+++ b/redfish-core/include/utils/time_utils.hpp
-@@ -1,7 +1,12 @@
+@@ -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 +17,8 @@ namespace time_utils
+@@ -12,6 +18,8 @@ namespace time_utils
namespace details
{
@@ -78,32 +76,12 @@ index dd4ea75..d8985ab 100644
inline void leftZeroPadding(std::string& str, const std::size_t padding)
{
if (str.size() < padding)
-@@ -19,8 +26,143 @@ inline void leftZeroPadding(std::string& str, const std::size_t padding)
+@@ -19,8 +27,135 @@ inline void leftZeroPadding(std::string& str, const std::size_t padding)
str.insert(0, padding - str.size(), '0');
}
}
+
-+inline bool fromChars(const char* start, const char* end,
-+ std::chrono::milliseconds::rep& val)
-+{
-+ auto [ptr, ec] = std::from_chars(start, end, val);
-+ if (ptr != end)
-+ {
-+ BMCWEB_LOG_ERROR
-+ << "Failed to convert string to decimal because of unexpected sign";
-+ return false;
-+ }
-+ if (ec != std::errc())
-+ {
-+ BMCWEB_LOG_ERROR << "Failed to convert string to decimal with err: "
-+ << static_cast<int>(ec) << "("
-+ << std::make_error_code(ec).message() << ")";
-+ return false;
-+ }
-+ return true;
-+}
-+
-+template <typename T>
++template <typename FromTime>
+bool fromDurationItem(std::string_view& fmt, const char postfix,
+ std::chrono::milliseconds& out)
+{
@@ -117,31 +95,43 @@ index dd4ea75..d8985ab 100644
+ return false;
+ }
+
-+ std::chrono::milliseconds::rep v = 0;
-+ if constexpr (std::is_same_v<T, std::chrono::milliseconds>)
++ const char* end;
++ std::chrono::milliseconds::rep ticks = 0;
++ if constexpr (std::is_same_v<FromTime, std::chrono::milliseconds>)
+ {
-+ std::string str(fmt.data(), std::min<size_t>(pos, 3U));
-+ while (str.size() < 3U)
-+ {
-+ str += '0';
-+ }
-+ if (!fromChars(str.data(), str.data() + str.size(), v))
-+ {
-+ return false;
-+ }
++ end = fmt.data() + std::min<size_t>(pos, 3U);
+ }
+ else
+ {
-+ if (!fromChars(fmt.data(), fmt.data() + pos, v))
-+ {
-+ return false;
-+ }
++ end = fmt.data() + pos;
+ }
+
-+ out += T(v);
-+ if (out < T(v) ||
-+ std::chrono::duration_cast<T>(std::chrono::milliseconds::max())
-+ .count() < v)
++ 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;
+ }
@@ -222,7 +212,7 @@ index dd4ea75..d8985ab 100644
/**
* @brief Convert time value into duration format that is based on ISO 8601.
* Example output: "P12DT1M5.5S"
-@@ -36,8 +178,7 @@ std::string toDurationString(std::chrono::milliseconds ms)
+@@ -36,8 +171,7 @@ std::string toDurationString(std::chrono::milliseconds ms)
std::string fmt;
fmt.reserve(sizeof("PxxxxxxxxxxxxDTxxHxxMxx.xxxxxxS"));
@@ -233,10 +223,10 @@ index dd4ea75..d8985ab 100644
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 48c56e6..d5a540d 100644
+index 59025d9..fcbc99c 100644
--- a/redfish-core/lib/metric_report_definition.hpp
+++ b/redfish-core/lib/metric_report_definition.hpp
-@@ -1,15 +1,26 @@
+@@ -1,9 +1,12 @@
#pragma once
#include "node.hpp"
@@ -249,394 +239,311 @@ index 48c56e6..d5a540d 100644
#include <tuple>
#include <variant>
- namespace redfish
- {
-
-+namespace telemetry
-+{
-+
-+using ReadingParameters =
-+ std::vector<std::tuple<std::vector<sdbusplus::message::object_path>,
-+ std::string, std::string, std::string>>;
-+} // namespace telemetry
-+
- class MetricReportDefinitionCollection : public Node
- {
- public:
-@@ -39,6 +50,318 @@ class MetricReportDefinitionCollection : public Node
- telemetry::getReportCollection(asyncResp,
- telemetry::metricReportDefinitionUri);
- }
-+
-+ 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;
-+ };
+@@ -95,6 +98,252 @@ inline void fillReportDefinition(
+ asyncResp->res.jsonValue["Schedule"]["RecurrenceInterval"] =
+ time_utils::toDurationString(std::chrono::milliseconds(*interval));
+ }
+
-+ void doPost(crow::Response& res, const crow::Request& req,
-+ const std::vector<std::string>&) override
++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)
+ {
-+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+ AddReportArgs args;
-+ if (!getUserParameters(res, req, args))
++ if (action == "RedfishEvent")
+ {
-+ return;
++ args.emitsReadingsUpdate = true;
+ }
-+
-+ boost::container::flat_set<std::pair<std::string, std::string>>
-+ chassisSensors;
-+ if (!getChassisSensorNode(asyncResp, args.metrics, chassisSensors))
++ else if (action == "LogToMetricReportsCollection")
+ {
-+ return;
++ args.logToMetricReportsCollection = true;
+ }
-+
-+ auto addReportReq =
-+ std::make_shared<AddReport>(std::move(args), asyncResp);
-+ for (const auto& [chassis, sensorType] : chassisSensors)
++ else
+ {
-+ 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);
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+ addReportReq->insert(uriToDbus);
-+ });
++ messages::propertyValueNotInList(
++ res, action, "ReportActions/" + std::to_string(index));
++ return false;
+ }
++ index++;
+ }
++ return true;
++}
+
-+ static bool toDbusReportActions(crow::Response& res,
-+ std::vector<std::string>& actions,
-+ AddReportArgs& args)
++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))
+ {
-+ 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;
++ 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;
+ }
+
-+ static bool getUserParameters(crow::Response& res, const crow::Request& req,
-+ AddReportArgs& args)
++ if (!toDbusReportActions(res, reportActions, 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;
++ }
++
++ if (args.reportingType == "Periodic")
++ {
++ if (!schedule)
+ {
++ messages::createFailedMissingReqProperties(res, "Schedule");
+ return false;
+ }
+
-+ constexpr const char* allowedCharactersInName =
-+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
-+ if (args.name.empty() ||
-+ args.name.find_first_not_of(allowedCharactersInName) !=
-+ std::string::npos)
++ std::string durationStr;
++ if (!json_util::readJson(*schedule, res, "RecurrenceInterval",
++ durationStr))
+ {
-+ 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")
++ std::optional<std::chrono::milliseconds> durationNum =
++ time_utils::fromDurationString(durationStr);
++ if (!durationNum)
+ {
-+ messages::propertyValueNotInList(res, args.reportingType,
-+ "MetricReportDefinitionType");
++ messages::propertyValueIncorrect(res, "RecurrenceInterval",
++ durationStr);
+ return false;
+ }
++ args.interval = static_cast<uint64_t>(durationNum->count());
++ }
+
-+ if (!toDbusReportActions(res, reportActions, args))
++ 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;
+ }
+
-+ if (args.reportingType == "Periodic")
++ 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++)
+ {
-+ if (!schedule)
-+ {
-+ messages::createFailedMissingReqProperties(res, "Schedule");
-+ return false;
-+ }
++ const std::string& uri = uris[i];
++ std::string chassis;
++ std::string node;
+
-+ std::string durationStr;
-+ if (!json_util::readJson(*schedule, res, "RecurrenceInterval",
-+ durationStr))
++ 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;
+ }
+
-+ std::optional<std::chrono::milliseconds> durationNum =
-+ time_utils::fromDurationString(durationStr);
-+ if (!durationNum)
++ if (boost::ends_with(node, "#"))
+ {
-+ messages::propertyValueIncorrect(res, "RecurrenceInterval",
-+ durationStr);
-+ return false;
++ node.pop_back();
+ }
-+ args.interval = static_cast<uint64_t>(durationNum->count());
++
++ matched.emplace(std::move(chassis), std::move(node));
+ }
++ }
++ return true;
++}
+
-+ args.metrics.reserve(metrics.size());
-+ for (auto& m : metrics)
++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)
+ {
-+ 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;
+ }
+
-+ return true;
-+ }
++ telemetry::ReadingParameters readingParams;
++ readingParams.reserve(args.metrics.size());
+
-+ static 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 (const auto& [id, uris] : args.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))
++ auto el = uriToDbus.find(uri);
++ if (el == uriToDbus.end())
+ {
-+ BMCWEB_LOG_ERROR << "Failed to get chassis and sensor Node "
-+ "from "
++ BMCWEB_LOG_ERROR << "Failed to find DBus sensor "
++ "corresponding to URI "
+ << uri;
-+ messages::propertyValueIncorrect(asyncResp->res, uri,
++ messages::propertyValueNotInList(asyncResp->res, uri,
+ "MetricProperties/" +
+ std::to_string(i));
-+ return false;
-+ }
-+
-+ if (boost::ends_with(node, "#"))
-+ {
-+ node.pop_back();
++ return;
+ }
+
-+ matched.emplace(std::move(chassis), std::move(node));
++ const std::string& dbusPath = el->second;
++ readingParams.emplace_back(dbusPath, "SINGLE", id, uri);
+ }
+ }
-+ 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)
-+ {
-+ std::vector<sdbusplus::message::object_path> dbusPaths;
-+ dbusPaths.reserve(uris.size());
-+
-+ for (size_t i = 0; i < uris.size(); i++)
++ 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)
+ {
-+ const std::string& uri = uris[i];
-+ auto el = uriToDbus.find(uri);
-+ if (el == uriToDbus.end())
++ 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)
+ {
-+ BMCWEB_LOG_ERROR << "Failed to find DBus sensor "
-+ "corresponding to URI "
-+ << uri;
-+ messages::propertyValueNotInList(asyncResp->res, uri,
-+ "MetricProperties/" +
-+ std::to_string(i));
-+ return;
++ metricProperties.emplace_back(uri);
+ }
-+
-+ dbusPaths.emplace_back(el->second);
++ messages::propertyValueIncorrect(
++ asyncResp->res, metricProperties, "MetricProperties");
++ return;
+ }
-+
-+ nlohmann::json metadata;
-+ metadata["MetricProperties"] = uris;
-+ if (uris.size() == 1)
++ if (ec)
+ {
-+ metadata["MetricProperty"] = uris[0];
++ messages::internalError(asyncResp->res);
++ BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
++ return;
+ }
-+ readingParams.emplace_back(std::move(dbusPaths), "SINGLE", id,
-+ metadata.dump());
-+ }
+
-+ crow::connections::systemBus->async_method_call(
-+ [asyncResp = asyncResp, name = args.name](
-+ 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)
-+ {
-+ messages::propertyValueNotInList(
-+ asyncResp->res, "/Exceeds supported size/",
-+ "Metrics");
-+ return;
-+ }
-+ if (ec == boost::system::errc::not_supported)
-+ {
-+ messages::propertyValueNotInList(
-+ asyncResp->res,
-+ "/Only single property per metric is supported/",
-+ "MetricProperties");
-+ return;
-+ }
-+ if (ec == boost::system::errc::invalid_argument)
-+ {
-+ messages::propertyValueNotInList(
-+ asyncResp->res, "/Less then MinInterval/",
-+ "RecurrenceInterval");
-+ 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());
++ }
+
-+ 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);
++ 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;
+ }
+
-+ void insert(
-+ const boost::container::flat_map<std::string, std::string>& el)
++ boost::container::flat_set<std::pair<std::string, std::string>>
++ chassisSensors;
++ if (!telemetry::getChassisSensorNode(asyncResp, args.metrics,
++ chassisSensors))
+ {
-+ uriToDbus.insert(el.begin(), el.end());
++ return;
+ }
+
-+ private:
-+ std::shared_ptr<AsyncResp> asyncResp;
-+ AddReportArgs args;
-+ boost::container::flat_map<std::string, std::string> uriToDbus{};
-+ };
++ 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
-@@ -73,9 +396,10 @@ class MetricReportDefinition : public Node
- crow::connections::systemBus->async_method_call(
- [asyncResp,
- id](const boost::system::error_code ec,
-- const std::vector<std::pair<
-- std::string, std::variant<bool, ReadingParameters,
-- std::string, uint64_t>>>& ret) {
-+ const std::vector<
-+ std::pair<std::string,
-+ std::variant<bool, telemetry::ReadingParameters,
-+ std::string, uint64_t>>>& ret) {
- if (ec.value() == EBADR)
- {
- messages::resourceNotFound(asyncResp->res, schemaType, id);
-@@ -95,15 +419,11 @@ class MetricReportDefinition : public Node
+@@ -184,5 +473,44 @@ class MetricReportDefinition : public Node
+ "org.freedesktop.DBus.Properties", "GetAll",
telemetry::reportInterface);
}
-
-- using ReadingParameters =
-- std::vector<std::tuple<std::vector<sdbusplus::message::object_path>,
-- std::string, std::string, std::string>>;
--
- static void fillReportDefinition(
- const std::shared_ptr<AsyncResp>& asyncResp, const std::string& id,
-- const std::vector<
-- std::pair<std::string, std::variant<bool, ReadingParameters,
-- std::string, uint64_t>>>& ret)
-+ const std::vector<std::pair<
-+ std::string, std::variant<bool, telemetry::ReadingParameters,
-+ std::string, uint64_t>>>& ret)
- {
- asyncResp->res.jsonValue["@odata.type"] = schemaType;
- asyncResp->res.jsonValue["@odata.id"] =
-@@ -117,7 +437,7 @@ class MetricReportDefinition : public Node
-
- const bool* emitsReadingsUpdate = nullptr;
- const bool* logToMetricReportsCollection = nullptr;
-- const ReadingParameters* readingParams = nullptr;
-+ const telemetry::ReadingParameters* readingParams = nullptr;
- const std::string* reportingType = nullptr;
- const uint64_t* interval = nullptr;
- for (const auto& [key, var] : ret)
-@@ -132,7 +452,7 @@ class MetricReportDefinition : public Node
- }
- else if (key == "ReadingParameters")
- {
-- readingParams = std::get_if<ReadingParameters>(&var);
-+ readingParams = std::get_if<telemetry::ReadingParameters>(&var);
- }
- else if (key == "ReportingType")
- {
-@@ -180,6 +500,44 @@ class MetricReportDefinition : public Node
- time_utils::toDurationString(std::chrono::milliseconds(*interval));
- }
-
++
+ void doDelete(crow::Response& res, const crow::Request&,
+ const std::vector<std::string>& params) override
+ {
@@ -658,7 +565,8 @@ index 48c56e6..d5a540d 100644
+ */
+ if (ec.value() == EBADR)
+ {
-+ messages::resourceNotFound(asyncResp->res, schemaType, id);
++ messages::resourceNotFound(asyncResp->res,
++ "MetricReportDefinition", id);
+ return;
+ }
+
@@ -674,10 +582,77 @@ index 48c56e6..d5a540d 100644
+ telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete",
+ "Delete");
+ }
-+
- static constexpr const char* schemaType =
- "#MetricReportDefinition.v1_3_0.MetricReportDefinition";
};
+ } // 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
+2.16.6
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-MetricDefinition-scheme.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-MetricDefinition-scheme.patch
index 7c3e4c804..99af0ab86 100644
--- a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-MetricDefinition-scheme.patch
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0003-Add-support-for-MetricDefinition-scheme.patch
@@ -1,35 +1,41 @@
-From d9016c8064f5732fb6d24d07a990ddfa294a8a9d Mon Sep 17 00:00:00 2001
+From 462b2e814698e12a18b4956eb3c6421c34a3a4ba 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] Add support for MetricDefinition scheme
+Date: Tue, 15 Dec 2020 12:28:17 +0100
+Subject: [PATCH 3/4] Add support for MetricDefinition scheme
+
+Added MetricDefinition node to Redfish code. Now user is able
+to list all available metrics in OpenBMC that are supported
+by Telemetry service. Metrics are grouped by following
+categories: temperature, power, voltage, current, fan_tach,
+fan_pwm, utilization.
-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.
Added generic function to fill ReadingUnits and ReadingType
-in Sensor scheme.
+in Sensor node.
Tested:
- - Succesfully passed RedfishServiceValidator.py
- - Validated a presence of MetricDefinition members
+ - MetricDefinitions response is filled with existing sensors,
+ it works with and without Telemetry service
+ - Validated a presence of MetricDefinition members and it
+ attributes
+ - Succesfully passed RedfishServiceValidator.py using
+ witherspoon image on QEMU
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 +
- .../include/utils/telemetry_utils.hpp | 56 ++--
- redfish-core/lib/metric_definition.hpp | 269 ++++++++++++++++++
- redfish-core/lib/metric_report_definition.hpp | 22 ++
- redfish-core/lib/power.hpp | 4 +-
- redfish-core/lib/sensors.hpp | 96 +++++--
- redfish-core/lib/telemetry_service.hpp | 2 +
- redfish-core/lib/thermal.hpp | 4 +-
- 8 files changed, 406 insertions(+), 50 deletions(-)
+ redfish-core/include/redfish.hpp | 3 +
+ redfish-core/include/utils/telemetry_utils.hpp | 2 +
+ redfish-core/lib/metric_definition.hpp | 283 +++++++++++++++++++++++++
+ redfish-core/lib/power.hpp | 4 +-
+ redfish-core/lib/sensors.hpp | 85 ++++++--
+ redfish-core/lib/telemetry_service.hpp | 2 +
+ redfish-core/lib/thermal.hpp | 4 +-
+ 7 files changed, 361 insertions(+), 22 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 b366e24..a938d43 100644
+index e94c0f3..83f2300 100644
--- a/redfish-core/include/redfish.hpp
+++ b/redfish-core/include/redfish.hpp
@@ -25,6 +25,7 @@
@@ -40,7 +46,7 @@ index b366e24..a938d43 100644
#include "../lib/metric_report.hpp"
#include "../lib/metric_report_definition.hpp"
#include "../lib/network_protocol.hpp"
-@@ -216,6 +217,8 @@ class RedfishService
+@@ -213,6 +214,8 @@ class RedfishService
nodes.emplace_back(std::make_unique<HypervisorSystem>(app));
nodes.emplace_back(std::make_unique<TelemetryService>(app));
@@ -50,7 +56,7 @@ index b366e24..a938d43 100644
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 acb739d..c13a79b 100644
+index a3a8156..c1b7639 100644
--- a/redfish-core/include/utils/telemetry_utils.hpp
+++ b/redfish-core/include/utils/telemetry_utils.hpp
@@ -8,6 +8,8 @@ namespace telemetry
@@ -62,80 +68,12 @@ index acb739d..c13a79b 100644
constexpr const char* metricReportDefinitionUri =
"/redfish/v1/TelemetryService/MetricReportDefinitions/";
constexpr const char* metricReportUri =
-@@ -15,6 +17,36 @@ constexpr const char* metricReportUri =
- constexpr const char* reportDir =
- "/xyz/openbmc_project/Telemetry/Reports/TelemetryService/";
-
-+inline void dbusPathsToMembers(const std::shared_ptr<AsyncResp>& asyncResp,
-+ const std::vector<std::string>& paths,
-+ const std::string& uri)
-+{
-+ nlohmann::json& members = asyncResp->res.jsonValue["Members"];
-+ members = nlohmann::json::array();
-+
-+ for (const std::string& path : paths)
-+ {
-+ std::size_t pos = path.rfind('/');
-+ if (pos == std::string::npos)
-+ {
-+ BMCWEB_LOG_ERROR << "Failed to find '/' in " << path;
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ if (path.size() <= (pos + 1))
-+ {
-+ BMCWEB_LOG_ERROR << "Failed to parse path " << path;
-+ messages::internalError(asyncResp->res);
-+ return;
-+ }
-+
-+ members.push_back({{"@odata.id", uri + path.substr(pos + 1)}});
-+ }
-+
-+ asyncResp->res.jsonValue["Members@odata.count"] = members.size();
-+}
-+
- inline void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp,
- const std::string& uri)
- {
-@@ -30,29 +62,7 @@ inline void getReportCollection(const std::shared_ptr<AsyncResp>& asyncResp,
- return;
- }
-
-- nlohmann::json& members = asyncResp->res.jsonValue["Members"];
-- members = nlohmann::json::array();
--
-- for (const std::string& path : reportPaths)
-- {
-- std::size_t pos = path.rfind('/');
-- if (pos == std::string::npos)
-- {
-- BMCWEB_LOG_ERROR << "Failed to find '/' in " << path;
-- messages::internalError(asyncResp->res);
-- return;
-- }
-- if (path.size() <= (pos + 1))
-- {
-- BMCWEB_LOG_ERROR << "Failed to parse path " << path;
-- messages::internalError(asyncResp->res);
-- return;
-- }
--
-- members.push_back({{"@odata.id", uri + path.substr(pos + 1)}});
-- }
--
-- asyncResp->res.jsonValue["Members@odata.count"] = members.size();
-+ dbusPathsToMembers(asyncResp, reportPaths, uri);
- },
- "xyz.openbmc_project.ObjectMapper",
- "/xyz/openbmc_project/object_mapper",
diff --git a/redfish-core/lib/metric_definition.hpp b/redfish-core/lib/metric_definition.hpp
new file mode 100644
-index 0000000..f037ed2
+index 0000000..4a40af5
--- /dev/null
+++ b/redfish-core/lib/metric_definition.hpp
-@@ -0,0 +1,269 @@
+@@ -0,0 +1,283 @@
+#pragma once
+
+#include "node.hpp"
@@ -149,31 +87,36 @@ index 0000000..f037ed2
+{
+
+template <typename F>
-+inline void getChassisNames(F&& cb)
++inline void getChassisNames(F&& cb, const std::shared_ptr<AsyncResp>& asyncResp)
+{
+ const std::array<const char*, 2> interfaces = {
+ "xyz.openbmc_project.Inventory.Item.Board",
+ "xyz.openbmc_project.Inventory.Item.Chassis"};
+
+ crow::connections::systemBus->async_method_call(
-+ [callback = std::move(cb)](const boost::system::error_code ec,
-+ std::vector<std::string>& chassisList) {
++ [asyncResp,
++ callback = std::move(cb)](const boost::system::error_code ec,
++ std::vector<std::string>& chassises) {
+ if (ec)
+ {
++ messages::internalError(asyncResp->res);
+ BMCWEB_LOG_DEBUG << "DBus call error: " << ec.value();
+ return;
+ }
+
+ std::vector<std::string> chassisNames;
-+ chassisNames.reserve(chassisList.size());
-+ for (const std::string& chassisPath : chassisList)
++ chassisNames.reserve(chassises.size());
++ for (const std::string& chassis : chassises)
+ {
-+ size_t pos = chassisPath.rfind('/');
-+ if (pos == std::string::npos)
++ sdbusplus::message::object_path path(chassis);
++ std::string name = path.filename();
++ if (name.empty())
+ {
-+ continue;
++ messages::internalError(asyncResp->res);
++ BMCWEB_LOG_ERROR << "Invalid chassis: " << chassis;
++ return;
+ }
-+ chassisNames.push_back(chassisPath.substr(pos + 1));
++ chassisNames.push_back(name);
+ }
+
+ callback(chassisNames);
@@ -185,6 +128,109 @@ index 0000000..f037ed2
+}
+} // namespace utils
+
++namespace telemetry
++{
++
++class DefinitionCollectionReduce
++{
++ public:
++ DefinitionCollectionReduce(const std::shared_ptr<AsyncResp>& asyncResp) :
++ asyncResp{asyncResp}
++ {}
++
++ ~DefinitionCollectionReduce()
++ {
++ if (asyncResp->res.result() != boost::beast::http::status::ok)
++ {
++ return;
++ }
++
++ nlohmann::json& members = asyncResp->res.jsonValue["Members"];
++ members = nlohmann::json::array();
++
++ for (const std::string& type : dbusTypes)
++ {
++ members.push_back(
++ {{"@odata.id", telemetry::metricDefinitionUri + type}});
++ }
++ asyncResp->res.jsonValue["Members@odata.count"] = members.size();
++ }
++
++ void insert(const boost::container::flat_map<std::string, std::string>& el)
++ {
++ for (const auto& [_, dbusSensor] : el)
++ {
++ sdbusplus::message::object_path path(dbusSensor);
++ sdbusplus::message::object_path parentPath = path.parent_path();
++ std::string type = parentPath.filename();
++ if (type.empty())
++ {
++ BMCWEB_LOG_ERROR << "Received invalid DBus Sensor Path = "
++ << dbusSensor;
++ continue;
++ }
++
++ dbusTypes.insert(std::move(type));
++ }
++ }
++
++ private:
++ const std::shared_ptr<AsyncResp> asyncResp;
++ boost::container::flat_set<std::string> dbusTypes;
++};
++
++class DefinitionReduce
++{
++ public:
++ DefinitionReduce(const std::shared_ptr<AsyncResp>& asyncResp,
++ const std::string& id) :
++ id(id),
++ pattern{'/' + id + '/'}, asyncResp{asyncResp}
++ {}
++ ~DefinitionReduce()
++ {
++ if (asyncResp->res.result() != boost::beast::http::status::ok)
++ {
++ return;
++ }
++ if (redfishSensors.empty())
++ {
++ messages::resourceNotFound(asyncResp->res, "MetricDefinition", id);
++ return;
++ }
++
++ asyncResp->res.jsonValue["MetricProperties"] = redfishSensors;
++ asyncResp->res.jsonValue["Id"] = id;
++ asyncResp->res.jsonValue["Name"] = id;
++ asyncResp->res.jsonValue["@odata.id"] =
++ telemetry::metricDefinitionUri + id;
++ asyncResp->res.jsonValue["@odata.type"] =
++ "#MetricDefinition.v1_0_3.MetricDefinition";
++ asyncResp->res.jsonValue["MetricDataType"] = "Decimal";
++ asyncResp->res.jsonValue["MetricType"] = "Numeric";
++ asyncResp->res.jsonValue["IsLinear"] = true;
++ asyncResp->res.jsonValue["Units"] = sensors::toReadingUnits(id);
++ }
++
++ void insert(const boost::container::flat_map<std::string, std::string>& el)
++ {
++ for (const auto& [redfishSensor, dbusSensor] : el)
++ {
++ if (dbusSensor.find(pattern) != std::string::npos)
++ {
++ redfishSensors.push_back(redfishSensor);
++ }
++ }
++ }
++
++ private:
++ const std::string id;
++ const std::string pattern;
++ const std::shared_ptr<AsyncResp> asyncResp;
++ std::vector<std::string> redfishSensors;
++};
++} // namespace telemetry
++
+class MetricDefinitionCollection : public Node
+{
+ public:
@@ -213,7 +259,8 @@ index 0000000..f037ed2
+ res.jsonValue["Members@odata.count"] = 0;
+
+ auto asyncResp = std::make_shared<AsyncResp>(res);
-+ auto collectionReduce = std::make_shared<CollectionGather>(asyncResp);
++ auto collectionReduce =
++ std::make_shared<telemetry::DefinitionCollectionReduce>(asyncResp);
+ utils::getChassisNames(
+ [asyncResp,
+ collectionReduce](const std::vector<std::string>& chassisNames) {
@@ -242,50 +289,9 @@ index 0000000..f037ed2
+ });
+ }
+ }
-+ });
++ },
++ asyncResp);
+ }
-+
-+ class CollectionGather
-+ {
-+ public:
-+ CollectionGather(const std::shared_ptr<AsyncResp>& asyncResp) :
-+ asyncResp{asyncResp}
-+ {}
-+
-+ ~CollectionGather()
-+ {
-+ if (asyncResp->res.result() != boost::beast::http::status::ok)
-+ {
-+ return;
-+ }
-+
-+ telemetry::dbusPathsToMembers(
-+ asyncResp,
-+ std::vector<std::string>(dbusTypes.begin(), dbusTypes.end()),
-+ telemetry::metricDefinitionUri);
-+ }
-+
-+ void insert(
-+ const boost::container::flat_map<std::string, std::string>& el)
-+ {
-+ for (const auto& [_, dbusSensor] : el)
-+ {
-+ size_t pos = dbusSensor.rfind('/');
-+ if (pos == std::string::npos)
-+ {
-+ BMCWEB_LOG_ERROR << "Received invalid DBus Sensor Path = "
-+ << dbusSensor;
-+ continue;
-+ }
-+
-+ dbusTypes.insert(dbusSensor.substr(0, pos));
-+ }
-+ }
-+
-+ private:
-+ const std::shared_ptr<AsyncResp> asyncResp;
-+ boost::container::flat_set<std::string> dbusTypes;
-+ };
+};
+
+class MetricDefinition : public Node
@@ -317,7 +323,7 @@ index 0000000..f037ed2
+
+ const std::string& id = params[0];
+ auto definitionGather =
-+ std::make_shared<DefinitionGather>(asyncResp, id);
++ std::make_shared<telemetry::DefinitionReduce>(asyncResp, id);
+ utils::getChassisNames(
+ [asyncResp,
+ definitionGather](const std::vector<std::string>& chassisNames) {
@@ -345,113 +351,12 @@ index 0000000..f037ed2
+ });
+ }
+ }
-+ });
++ },
++ asyncResp);
+ }
-+
-+ class DefinitionGather
-+ {
-+ public:
-+ DefinitionGather(const std::shared_ptr<AsyncResp>& asyncResp,
-+ const std::string& id) :
-+ id(id),
-+ pattern{'/' + id + '/'}, asyncResp{asyncResp}
-+ {}
-+ ~DefinitionGather()
-+ {
-+ if (asyncResp->res.result() != boost::beast::http::status::ok)
-+ {
-+ return;
-+ }
-+ if (redfishSensors.empty())
-+ {
-+ messages::resourceNotFound(asyncResp->res, schemaType, id);
-+ return;
-+ }
-+
-+ asyncResp->res.jsonValue["MetricProperties"] = redfishSensors;
-+ 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["IsLinear"] = true;
-+ asyncResp->res.jsonValue["Units"] = sensors::toReadingUnits(id);
-+ }
-+
-+ void insert(
-+ const boost::container::flat_map<std::string, std::string>& el)
-+ {
-+ for (const auto& [redfishSensor, dbusSensor] : el)
-+ {
-+ if (dbusSensor.find(pattern) != std::string::npos)
-+ {
-+ redfishSensors.push_back(redfishSensor);
-+ }
-+ }
-+ }
-+
-+ const std::string id;
-+ const std::string pattern;
-+
-+ private:
-+ const std::shared_ptr<AsyncResp> asyncResp;
-+ std::vector<std::string> redfishSensors;
-+ };
-+
-+ static constexpr const char* schemaType =
-+ "#MetricDefinition.v1_0_3.MetricDefinition";
+};
+
+} // namespace redfish
-diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp
-index d5a540d..03f0b82 100644
---- a/redfish-core/lib/metric_report_definition.hpp
-+++ b/redfish-core/lib/metric_report_definition.hpp
-@@ -269,6 +269,8 @@ class MetricReportDefinitionCollection : public Node
- {
- std::vector<sdbusplus::message::object_path> dbusPaths;
- dbusPaths.reserve(uris.size());
-+ std::string sensorType;
-+ bool invalidType = false;
-
- for (size_t i = 0; i < uris.size(); i++)
- {
-@@ -286,6 +288,21 @@ class MetricReportDefinitionCollection : public Node
- }
-
- dbusPaths.emplace_back(el->second);
-+
-+ if (invalidType)
-+ {
-+ continue;
-+ }
-+ std::string tmp;
-+ dbus::utility::getNthStringFromPath(el->second, 3, tmp);
-+ if (sensorType.empty())
-+ {
-+ sensorType = std::move(tmp);
-+ }
-+ else if (sensorType != tmp)
-+ {
-+ invalidType = true;
-+ }
- }
-
- nlohmann::json metadata;
-@@ -294,6 +311,11 @@ class MetricReportDefinitionCollection : public Node
- {
- metadata["MetricProperty"] = uris[0];
- }
-+ if (!sensorType.empty() && !invalidType)
-+ {
-+ metadata["MetricDefinition"]["@odata.id"] =
-+ telemetry::metricDefinitionUri + sensorType;
-+ }
- readingParams.emplace_back(std::move(dbusPaths), "SINGLE", id,
- metadata.dump());
- }
diff --git a/redfish-core/lib/power.hpp b/redfish-core/lib/power.hpp
index 1c7a009..99c45ef 100644
--- a/redfish-core/lib/power.hpp
@@ -475,7 +380,7 @@ index 1c7a009..99c45ef 100644
std::optional<std::vector<nlohmann::json>> voltageCollections;
diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp
-index 14c9593..bf97540 100644
+index 14c9593..5080f77 100644
--- a/redfish-core/lib/sensors.hpp
+++ b/redfish-core/lib/sensors.hpp
@@ -54,9 +54,10 @@ static constexpr std::string_view thermal = "Thermal";
@@ -555,46 +460,12 @@ index 14c9593..bf97540 100644
}
} // namespace sensors
-@@ -90,19 +149,20 @@ class SensorsAsyncResp
- };
-
- SensorsAsyncResp(crow::Response& response, const std::string& chassisIdIn,
-- const std::vector<const char*>& typesIn,
-+ const std::vector<const char*>& matchPathsIn,
- const std::string_view& subNode) :
- res(response),
-- chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode)
-+ chassisId(chassisIdIn), matchPaths(matchPathsIn),
-+ chassisSubNode(subNode)
- {}
-
- // Store extra data about sensor mapping and return it in callback
- SensorsAsyncResp(crow::Response& response, const std::string& chassisIdIn,
-- const std::vector<const char*>& typesIn,
-+ const std::vector<const char*>& matchPathsIn,
- const std::string_view& subNode,
- DataCompleteCb&& creationComplete) :
- res(response),
-- chassisId(chassisIdIn), types(typesIn),
-+ chassisId(chassisIdIn), matchPaths(matchPathsIn),
- chassisSubNode(subNode), metadata{std::vector<SensorData>()},
- dataComplete{std::move(creationComplete)}
- {}
-@@ -161,7 +221,7 @@ class SensorsAsyncResp
-
- crow::Response& res;
- const std::string chassisId;
-- const std::vector<const char*> types;
-+ const std::vector<const char*> matchPaths;
- const std::string chassisSubNode;
-
- private:
-@@ -345,11 +405,11 @@ inline void reduceSensorList(
+@@ -345,11 +404,11 @@ inline void reduceSensorList(
return;
}
- for (const char* type : sensorsAsyncResp->types)
-+ for (const char* path : sensorsAsyncResp->matchPaths)
++ for (const char* path : sensorsAsyncResp->types)
{
for (const std::string& sensor : *allSensors)
{
@@ -603,7 +474,7 @@ index 14c9593..bf97540 100644
{
activeSensors->emplace(sensor);
}
-@@ -853,18 +913,8 @@ inline void objectInterfacesToJson(
+@@ -853,18 +912,8 @@ inline void objectInterfacesToJson(
if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors)
{
sensorJson["@odata.type"] = "#Sensor.v1_0_0.Sensor";
@@ -624,7 +495,7 @@ index 14c9593..bf97540 100644
}
else if (sensorType == "temperature")
{
-@@ -2976,8 +3026,8 @@ inline void retrieveUriToDbusMap(const std::string& chassis,
+@@ -2976,8 +3025,8 @@ inline void retrieveUriToDbusMap(const std::string& chassis,
const std::string& node,
SensorsAsyncResp::DataCompleteCb&& mapComplete)
{
@@ -635,7 +506,7 @@ index 14c9593..bf97540 100644
{
BMCWEB_LOG_ERROR << "Wrong node provided : " << node;
mapComplete(boost::beast::http::status::bad_request, {});
-@@ -3027,7 +3077,7 @@ class SensorCollection : public Node
+@@ -3027,7 +3076,7 @@ class SensorCollection : public Node
const std::string& chassisId = params[0];
std::shared_ptr<SensorsAsyncResp> asyncResp =
std::make_shared<SensorsAsyncResp>(
@@ -645,13 +516,13 @@ index 14c9593..bf97540 100644
auto getChassisCb =
diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp
-index a6acc34..8105d86 100644
+index 61ca891..a8c8b03 100644
--- a/redfish-core/lib/telemetry_service.hpp
+++ b/redfish-core/lib/telemetry_service.hpp
-@@ -34,6 +34,8 @@ class TelemetryService : public Node
+@@ -32,6 +32,8 @@ class TelemetryService : public Node
+ res.jsonValue["Id"] = "TelemetryService";
+ res.jsonValue["Name"] = "Telemetry Service";
- 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"] =
@@ -680,5 +551,5 @@ index 8e01bee..00acdf9 100644
if (!json_util::readJson(req, asyncResp->res, "Temperatures",
--
-2.17.1
+2.16.6
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Sync-Telmetry-service-with-EventService.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Sync-Telmetry-service-with-EventService.patch
index 06c50b31f..3df9fe5ae 100644
--- a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Sync-Telmetry-service-with-EventService.patch
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0004-Sync-Telmetry-service-with-EventService.patch
@@ -1,27 +1,28 @@
-From b6286fe6800ca402b013e57429025fd9e4d65cab Mon Sep 17 00:00:00 2001
+From 14a429586fd8ccb82c72611fe3310860db2e643b Mon Sep 17 00:00:00 2001
From: "Wludzik, Jozef" <jozef.wludzik@intel.com>
-Date: Fri, 4 Dec 2020 14:48:41 +0100
-Subject: [PATCH] Sync Telmetry service with EventService
+Date: Tue, 15 Dec 2020 12:30:31 +0100
+Subject: [PATCH 4/4] Sync Telmetry service with EventService
-Now assembling MetricReport is done properly and is
-covered in one place - MetricReport node.
-Updated method of fetching Readings from Telemetry by
-EventService. Using ReportUpdate signal is no longer
-supported.
+Synced the latest changes in Telemetry service with Event Service
+code. Now assembling MetricReport is covered in single place in
+code. Updated method of fetching Readings from Telemetry by
+Event Service. Using ReportUpdate signal is no longer
+supported. Now Event Service monitors for PropertiesChanged signal
+from /xyz/openbmc_project/Telemetry/Reports path.
Tested:
- - Received MetricReport in EventListener server after
- adding subscription to EventService.
+ - Verified that EventListener received MetricReport response from
+ Event Service in insecure http push style eventing mode
Change-Id: I2fc1841a6c9259a8bff30b34bddc0d4aabd41912
Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
---
- .../include/event_service_manager.hpp | 156 ++++++------------
- redfish-core/lib/metric_report.hpp | 35 ++--
- 2 files changed, 74 insertions(+), 117 deletions(-)
+ redfish-core/include/event_service_manager.hpp | 157 +++++++++----------------
+ redfish-core/lib/metric_report.hpp | 28 +++--
+ 2 files changed, 71 insertions(+), 114 deletions(-)
diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
-index f68ae1d..893a813 100644
+index 3db9f0c..5c5a6c1 100644
--- a/redfish-core/include/event_service_manager.hpp
+++ b/redfish-core/include/event_service_manager.hpp
@@ -14,6 +14,7 @@
@@ -41,7 +42,7 @@ index f68ae1d..893a813 100644
- const ReadingsObjType& readings)
+ void filterAndSendReports(
+ const std::string& id,
-+ const std::variant<MetricReport::TimestampReadings>& var)
++ const std::variant<telemetry::TimestampReadings>& var)
{
- std::string metricReportDef =
- "/redfish/v1/TelemetryService/MetricReportDefinitions/" + id2;
@@ -62,7 +63,7 @@ index f68ae1d..893a813 100644
- nlohmann::json metricValuesArray = nlohmann::json::array();
- for (const auto& it : readings)
+ nlohmann::json json;
-+ if (!MetricReport::fillReport(json, id, var))
++ if (!telemetry::fillReport(json, id, var))
{
- metricValuesArray.push_back({});
- nlohmann::json& entry = metricValuesArray.back();
@@ -90,7 +91,7 @@ index f68ae1d..893a813 100644
}
void updateRetryConfig(const uint32_t retryAttempts,
-@@ -1342,56 +1324,71 @@ class EventServiceManager
+@@ -1342,56 +1324,72 @@ class EventServiceManager
}
#endif
@@ -152,7 +153,7 @@ index f68ae1d..893a813 100644
- if (!timestampPtr)
+ std::string intf;
+ std::vector<std::pair<
-+ std::string, std::variant<MetricReport::TimestampReadings>>>
++ std::string, std::variant<telemetry::TimestampReadings>>>
+ props;
+ std::vector<std::string> invalidProp;
+
@@ -166,7 +167,7 @@ index f68ae1d..893a813 100644
- ReadingsObjType* readingsPtr =
- std::get_if<ReadingsObjType>(&resp["Readings"]);
- if (!readingsPtr)
-+ const std::variant<MetricReport::TimestampReadings>* varPtr =
++ const std::variant<telemetry::TimestampReadings>* varPtr =
+ nullptr;
+ for (const auto& [key, var] : props)
+ {
@@ -183,15 +184,16 @@ index f68ae1d..893a813 100644
}
- if (!readingsPtr->size())
-+ std::string id;
-+ if (!dbus::utility::getNthStringFromPath(msg.get_path(), 5, id))
++ sdbusplus::message::object_path path(msg.get_path());
++ std::string id = path.filename();
++ if (id.empty())
{
- BMCWEB_LOG_DEBUG << "No metrics report to be transferred";
+ BMCWEB_LOG_ERROR << "Failed to get Id from path";
return;
}
-@@ -1401,52 +1398,9 @@ class EventServiceManager
+@@ -1401,52 +1399,9 @@ class EventServiceManager
std::shared_ptr<Subscription> entry = it.second;
if (entry->eventFormatType == metricReportFormatType)
{
@@ -246,85 +248,64 @@ index f68ae1d..893a813 100644
}
diff --git a/redfish-core/lib/metric_report.hpp b/redfish-core/lib/metric_report.hpp
-index 050304c..c2013cc 100644
+index 9caf4a3..e79a41c 100644
--- a/redfish-core/lib/metric_report.hpp
+++ b/redfish-core/lib/metric_report.hpp
-@@ -52,6 +52,10 @@ class MetricReport : public Node
- {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
+@@ -31,16 +31,14 @@ inline nlohmann::json toMetricValues(const Readings& readings)
+ return metricValues;
+ }
+
+-inline void fillReport(const std::shared_ptr<AsyncResp>& asyncResp,
+- const std::string& id,
++inline bool fillReport(nlohmann::json& json, const std::string& id,
+ const std::variant<TimestampReadings>& var)
+ {
+- asyncResp->res.jsonValue["@odata.type"] =
+- "#MetricReport.v1_3_0.MetricReport";
+- asyncResp->res.jsonValue["@odata.id"] = telemetry::metricReportUri + id;
+- asyncResp->res.jsonValue["Id"] = id;
+- asyncResp->res.jsonValue["Name"] = id;
+- asyncResp->res.jsonValue["MetricReportDefinition"]["@odata.id"] =
++ json["@odata.type"] = "#MetricReport.v1_3_0.MetricReport";
++ json["@odata.id"] = telemetry::metricReportUri + id;
++ json["Id"] = id;
++ json["Name"] = id;
++ json["MetricReportDefinition"]["@odata.id"] =
+ telemetry::metricReportDefinitionUri + id;
+
+ const TimestampReadings* timestampReadings =
+@@ -48,14 +46,14 @@ inline void fillReport(const std::shared_ptr<AsyncResp>& asyncResp,
+ if (!timestampReadings)
+ {
+ BMCWEB_LOG_ERROR << "Property type mismatch or property is missing";
+- messages::internalError(asyncResp->res);
+- return;
++ return false;
}
-+ using Readings =
-+ std::vector<std::tuple<std::string, std::string, double, uint64_t>>;
-+ using TimestampReadings = std::tuple<uint64_t, Readings>;
-+
- private:
- void doGet(crow::Response& res, const crow::Request&,
- const std::vector<std::string>& params) override
-@@ -92,7 +96,10 @@ class MetricReport : public Node
+ const auto& [timestamp, readings] = *timestampReadings;
+- asyncResp->res.jsonValue["Timestamp"] =
++ json["Timestamp"] =
+ crow::utility::getDateTime(static_cast<time_t>(timestamp));
+- asyncResp->res.jsonValue["MetricValues"] = toMetricValues(readings);
++ json["MetricValues"] = toMetricValues(readings);
++ return true;
+ }
+ } // namespace telemetry
+
+@@ -146,7 +144,11 @@ class MetricReport : public Node
return;
}
-- fillReport(asyncResp, id, ret);
-+ if (!fillReport(asyncResp->res.jsonValue, id, ret))
+- telemetry::fillReport(asyncResp, id, ret);
++ if (!telemetry::fillReport(asyncResp->res.jsonValue, id,
++ ret))
+ {
+ messages::internalError(asyncResp->res);
+ }
},
telemetry::service, reportPath,
"org.freedesktop.DBus.Properties", "Get",
-@@ -102,10 +109,6 @@ class MetricReport : public Node
- "Update");
- }
-
-- using Readings =
-- std::vector<std::tuple<std::string, std::string, double, uint64_t>>;
-- using TimestampReadings = std::tuple<uint64_t, Readings>;
--
- static nlohmann::json toMetricValues(const Readings& readings)
- {
- nlohmann::json metricValues = nlohmann::json::array_t();
-@@ -130,15 +133,15 @@ class MetricReport : public Node
- return metricValues;
- }
-
-- static void fillReport(const std::shared_ptr<AsyncResp>& asyncResp,
-- const std::string& id,
-+ public:
-+ static bool fillReport(nlohmann::json& json, const std::string& id,
- const std::variant<TimestampReadings>& var)
- {
-- asyncResp->res.jsonValue["@odata.type"] = schemaType;
-- asyncResp->res.jsonValue["@odata.id"] = telemetry::metricReportUri + id;
-- asyncResp->res.jsonValue["Id"] = id;
-- asyncResp->res.jsonValue["Name"] = id;
-- asyncResp->res.jsonValue["MetricReportDefinition"]["@odata.id"] =
-+ json["@odata.type"] = schemaType;
-+ json["@odata.id"] = telemetry::metricReportUri + id;
-+ json["Id"] = id;
-+ json["Name"] = id;
-+ json["MetricReportDefinition"]["@odata.id"] =
- telemetry::metricReportDefinitionUri + id;
-
- const TimestampReadings* timestampReadings =
-@@ -146,14 +149,14 @@ class MetricReport : public Node
- if (!timestampReadings)
- {
- BMCWEB_LOG_ERROR << "Property type mismatch or property is missing";
-- messages::internalError(asyncResp->res);
-- return;
-+ return false;
- }
-
- const auto& [timestamp, readings] = *timestampReadings;
-- asyncResp->res.jsonValue["Timestamp"] =
-+ json["Timestamp"] =
- crow::utility::getDateTime(static_cast<time_t>(timestamp));
-- asyncResp->res.jsonValue["MetricValues"] = toMetricValues(readings);
-+ json["MetricValues"] = toMetricValues(readings);
-+ return true;
- }
-
- static constexpr const char* schemaType =
--
-2.17.1
+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
index 833fabfec..35c6e90bc 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,15 +2,12 @@ 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/54
-
- Add POST and DELETE in MetricReportDefinitions
- https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/32536/46
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/32536/58
- Add support for MetricDefinition scheme
- https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/33363/42
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/33363/54
- Sync Telmetry service with EventService
- https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/38798/9
+ https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/38798/21