From e370fd750e2821620ec427f26f8efab0069824ff Mon Sep 17 00:00:00 2001 From: "Jason M. Bills" Date: Mon, 3 May 2021 10:49:59 -0700 Subject: Update to internal 0.47 Signed-off-by: Jason M. Bills --- meta-openbmc-mods/conf/layer.conf | 2 +- .../recipes-core/interfaces/libmctp_git.bb | 2 +- .../meta-common/recipes-core/safec/safec_3.4.bb | 2 +- .../recipes-intel/intel-pfr/pfr-manager_%.bbappend | 2 +- .../recipes-kernel/linux/linux-aspeed_%.bbappend | 2 +- .../fans/phosphor-pid-control_%.bbappend | 4 +- .../host/phosphor-host-postd_git.bbappend | 2 +- ...8-Revert-Disable-nbd-proxy-from-the-build.patch | 50 ++ ...ervice-Fix-retry-handling-for-http-client.patch | 543 ++++++++++++++++++ .../0002-EventService-https-client-support.patch | 401 +++++++++++++ ...003-Move-EventService-init-to-later-stage.patch | 50 ++ .../0004-Add-Server-Sent-Events-support.patch | 477 ++++++++++++++++ ...tyle-subscription-support-to-eventservice.patch | 627 +++++++++++++++++++++ .../0006-Add-EventService-SSE-filter-support.patch | 243 ++++++++ .../interfaces/bmcweb/eventservice/README | 22 + ...nc_ReadingUnit_with_Redfish_Sensor_Schema.patch | 227 ++++++++ ...OST-and-DELETE-in-MetricReportDefinitions.patch | 35 +- ...3-Add-support-for-MetricDefinition-scheme.patch | 256 ++------- ...4-Sync-Telmetry-service-with-EventService.patch | 224 ++++---- .../interfaces/bmcweb/telemetry/README | 6 +- .../recipes-phosphor/interfaces/bmcweb_%.bbappend | 18 +- .../recipes-phosphor/peci/peci-pcie_%.bbappend | 2 +- .../recipes-phosphor/pmci/libmctp-intel_git.bb | 2 +- .../recipes-phosphor/pmci/libpldm-intel_git.bb | 2 +- .../recipes-phosphor/pmci/mctp-emulator.bb | 2 +- .../recipes-phosphor/pmci/mctp-wrapper.bb | 2 +- .../meta-common/recipes-phosphor/pmci/mctpd.bb | 2 +- .../meta-common/recipes-phosphor/pmci/mctpwplus.bb | 2 +- .../recipes-phosphor/pmci/nvmemi-daemon.bb | 6 +- .../meta-common/recipes-phosphor/pmci/pldmd.bb | 2 +- .../recipes-phosphor/pmci/pmci-launcher.bb | 2 +- .../sensors/dbus-sensors_%.bbappend | 2 +- .../telemetry/telemetry_%.bbappend | 2 +- .../recipes-phosphor/webui/webui-vue_%.bbappend | 2 +- .../chassis/x86-power-control_%.bbappend | 2 +- .../meta-wht/conf/bblayers.conf.sample | 2 +- .../meta-wolfpass/conf/bblayers.conf.sample | 2 +- 37 files changed, 2847 insertions(+), 384 deletions(-) create mode 100644 meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0038-Revert-Disable-nbd-proxy-from-the-build.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-EventService-Fix-retry-handling-for-http-client.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0003-Move-EventService-init-to-later-stage.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README create mode 100644 meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Sync_ReadingUnit_with_Redfish_Sensor_Schema.patch diff --git a/meta-openbmc-mods/conf/layer.conf b/meta-openbmc-mods/conf/layer.conf index 1f69c2c1c..fc30a04ef 100644 --- a/meta-openbmc-mods/conf/layer.conf +++ b/meta-openbmc-mods/conf/layer.conf @@ -17,6 +17,6 @@ USERADDEXTENSION = "useradd-staticids" USERADD_UID_TABLES = "files/passwd" USERADD_GID_TABLES = "files/group" -LAYER_CONF_VERSION = "13" +LAYER_CONF_VERSION = "14" INTELBASE = '${@os.path.normpath("${LAYERDIR}/")}' diff --git a/meta-openbmc-mods/meta-common/recipes-core/interfaces/libmctp_git.bb b/meta-openbmc-mods/meta-common/recipes-core/interfaces/libmctp_git.bb index 18ab8cb9f..ead001a7c 100644 --- a/meta-openbmc-mods/meta-common/recipes-core/interfaces/libmctp_git.bb +++ b/meta-openbmc-mods/meta-common/recipes-core/interfaces/libmctp_git.bb @@ -2,7 +2,7 @@ SUMMARY = "libmctp" DESCRIPTION = "Implementation of MCTP (DTMF DSP0236)" SRC_URI = "git://github.com/openbmc/libmctp.git" -SRCREV = "e889b19f4b349cd5c4ff186ff3c3b604c8f9c7b6" +SRCREV = "eba19a3b122a39ef2b5dbda49b418a202f78a48d" PV = "0.1+git${SRCPV}" diff --git a/meta-openbmc-mods/meta-common/recipes-core/safec/safec_3.4.bb b/meta-openbmc-mods/meta-common/recipes-core/safec/safec_3.4.bb index d6de78175..d8bfc68c0 100644 --- a/meta-openbmc-mods/meta-common/recipes-core/safec/safec_3.4.bb +++ b/meta-openbmc-mods/meta-common/recipes-core/safec/safec_3.4.bb @@ -7,7 +7,7 @@ SECTION = "lib" inherit autotools pkgconfig S = "${WORKDIR}/git" -SRCREV = "0234bec46da4863f849f100c2f5336412ab2f69b" +SRCREV = "a4abce065446410d137cece7cd017ff80251713d" SRC_URI = "git://github.com/rurban/safeclib.git" COMPATIBLE_HOST = '(x86_64|i.86|powerpc|powerpc64|arm|aarch64).*-linux' diff --git a/meta-openbmc-mods/meta-common/recipes-intel/intel-pfr/pfr-manager_%.bbappend b/meta-openbmc-mods/meta-common/recipes-intel/intel-pfr/pfr-manager_%.bbappend index 6b769b941..f89a2fbb0 100644 --- a/meta-openbmc-mods/meta-common/recipes-intel/intel-pfr/pfr-manager_%.bbappend +++ b/meta-openbmc-mods/meta-common/recipes-intel/intel-pfr/pfr-manager_%.bbappend @@ -1,5 +1,5 @@ # Enable downstream autobump SRC_URI = "git://github.com/openbmc/pfr-manager" -SRCREV = "00acaffb3840d019bf0853d1ee93fdde947f47d2" +SRCREV = "2dfaf507da581ed4c71d41c02976e4efafc1d634" DEPENDS += " libgpiod \ " diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend index 7e79ac14b..4af0acc49 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed_%.bbappend @@ -4,7 +4,7 @@ KBRANCH = "dev-5.10-intel" KSRC = "git://github.com/Intel-BMC/linux;protocol=ssh;branch=${KBRANCH}" # Include this as a comment only for downstream auto-bump # SRC_URI = "git://github.com/Intel-BMC/linux;protocol=ssh;branch=dev-5.10-intel" -SRCREV="e51019aa5cea44aa55b922261df9c7c7a4e5bcbc" +SRCREV="807fd9e1636097ca70957a3ff373bd1280737e46" do_compile_prepend(){ # device tree compiler flags diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend index 7e25bb26e..d988b7c93 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/fans/phosphor-pid-control_%.bbappend @@ -4,7 +4,7 @@ inherit obmc-phosphor-systemd SYSTEMD_SERVICE_${PN} = "phosphor-pid-control.service" EXTRA_OECONF = "--enable-configure-dbus=yes" -SRC_URI = "git://github.com/openbmc/phosphor-pid-control.git;nobranch=1" -SRCREV = "1277543ac599de45d15db99d15bd0e89d3653c9b" +SRC_URI = "git://github.com/openbmc/phosphor-pid-control.git" +SRCREV = "d11a732a802cc281f4c1583275871fbc5f5ecced" FILES_${PN} = "${bindir}/swampd ${bindir}/setsensor" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/host/phosphor-host-postd_git.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/host/phosphor-host-postd_git.bbappend index ed48da4c0..a294ac520 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/host/phosphor-host-postd_git.bbappend +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/host/phosphor-host-postd_git.bbappend @@ -1,4 +1,4 @@ DEPENDS += " gtest" #SRC_URI = "git://github.com/openbmc/phosphor-host-postd.git" -SRCREV = "ba5258f7158e8a0e61043290aff0873358509158" +SRCREV = "e2abf4429059b7ee3f90cc07f233a66b3022a176" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0038-Revert-Disable-nbd-proxy-from-the-build.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0038-Revert-Disable-nbd-proxy-from-the-build.patch new file mode 100644 index 000000000..3e3f69d1c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/0038-Revert-Disable-nbd-proxy-from-the-build.patch @@ -0,0 +1,50 @@ +From 95f002dc969d7d6d64dbf2ee0db7dc1c1c6a9173 Mon Sep 17 00:00:00 2001 +From: Przemyslaw Czarnowski +Date: Thu, 18 Mar 2021 11:30:28 +0100 +Subject: [PATCH] Revert "Disable nbd proxy from the build" + +NBD Proxy has been disabled upstream. Reenable as we use it for Virtual +Media + +This reverts commit efb8062c306474942bc94f15d748b2eb0b58fbb6. +--- + meson.build | 2 +- + meson_options.txt | 9 +-------- + 2 files changed, 2 insertions(+), 9 deletions(-) + +diff --git a/meson.build b/meson.build +index 66a066b..cef0a49 100644 +--- a/meson.build ++++ b/meson.build +@@ -81,7 +81,7 @@ feature_map = { + 'static-hosting' : '-DBMCWEB_ENABLE_STATIC_HOSTING', + 'insecure-tftp-update' : '-DBMCWEB_INSECURE_ENABLE_REDFISH_FW_TFTP_UPDATE', + 'validate-unsecure-feature' : '-DBMCWEB_ENABLE_VALIDATION_UNSECURE_FEATURE', +-#'vm-nbdproxy' : '-DBMCWEB_ENABLE_VM_NBDPROXY', ++'vm-nbdproxy' : '-DBMCWEB_ENABLE_VM_NBDPROXY', + 'vm-websocket' : '-DBMCWEB_ENABLE_VM_WEBSOCKET', + } + +diff --git a/meson_options.txt b/meson_options.txt +index 9611631..7ee3ebb 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -3,14 +3,7 @@ option('yocto-deps', type: 'feature', value: 'disabled', description : 'Use YOCT + option('kvm', type : 'feature',value : 'enabled', description : 'Enable the KVM host video WebSocket. Path is \'/kvm/0\'. Video is from the BMC\'s \'/dev/video\' device.') + option ('tests', type : 'feature', value : 'enabled', description : 'Enable Unit tests for bmcweb') + option('vm-websocket', type : 'feature', value : 'enabled', description : '''Enable the Virtual Media WebSocket. Path is \'/vm/0/0\'to open the websocket. See https://github.com/openbmc/jsnbd/blob/master/README.''') +- +-# if you use this option and are seeing this comment, please comment here: +-# https://github.com/openbmc/bmcweb/issues/188 and put forward your intentions +-# for this code. At this point, no daemon has been upstreamed that implements +-# this interface, so for the moment this appears to be dead code; In leiu of +-# removing it, it has been disabled to try to give those that use it the +-# opportunity to upstream their backend implementation +-#option('vm-nbdproxy', type: 'feature', value : 'disabled', description : 'Enable the Virtual Media WebSocket.') ++option('vm-nbdproxy', type: 'feature', value : 'disabled', description : 'Enable the Virtual Media WebSocket.') + option('rest', type : 'feature', value : 'enabled', description : '''Enable Phosphor REST (D-Bus) APIs. Paths directly map Phosphor D-Bus object paths, for example, \'/xyz/openbmc_project/logging/entry/enumerate\'. See https://github.com/openbmc/docs/blob/master/rest-api.md.''') + option('redfish', type : 'feature',value : 'enabled', description: 'Enable Redfish APIs. Paths are under \'/redfish/v1/\'. See https://github.com/openbmc/bmcweb/blob/master/DEVELOPING.md#redfish.') + option('host-serial-socket', type : 'feature', value : 'enabled', description : 'Enable host serial console WebSocket. Path is \'/console0\'. See https://github.com/openbmc/docs/blob/master/console.md.') +-- +2.26.2 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-EventService-Fix-retry-handling-for-http-client.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-EventService-Fix-retry-handling-for-http-client.patch new file mode 100644 index 000000000..b46d30149 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0001-EventService-Fix-retry-handling-for-http-client.patch @@ -0,0 +1,543 @@ +From ae55e89c14ea5abef0895409c956f5f4c38f330f Mon Sep 17 00:00:00 2001 +From: Sunitha Harish +Date: Fri, 19 Feb 2021 13:38:31 +0530 +Subject: [PATCH 1/2] EventService : Fix retry handling for http-client + +When the event send/receive is failed, the bmcweb does not handle +the failure to tear-down the complete connection and start a fresh + +The keep-alive header from the event listener is read to update +the connection states, so that the connection will be kept alive +or closed as per the subscriber's specifications + +Updated the connection state machine to handle retry logic properly. +Avoided multiple simultaneous async calls which crashes the bmcweb. So +added connBusy flag which protects simultaneous async calls. + +Used boost http response parser as parser for producing the response +message. Set the parser skip option to handle the empty response message +from listening server. + +Tested by: + - Subscribe for the events at BMC using DMTF event listener + - Generate an event and see the same is received at the listener's console + - Update the listner to change the keep-alive to true/false and + observe the http-client connection states at bmcweb + +Change-Id: Ibb45691f139916ba2954da37beda9d4f91c7cef3 +Signed-off-by: Sunitha Harish +Signed-off-by: AppaRao Puli +--- + http/http_client.hpp | 289 ++++++++++-------- + .../include/event_service_manager.hpp | 2 +- + 2 files changed, 163 insertions(+), 128 deletions(-) + +diff --git a/http/http_client.hpp b/http/http_client.hpp +index 992ac2b..d116f6d 100644 +--- a/http/http_client.hpp ++++ b/http/http_client.hpp +@@ -34,22 +34,28 @@ namespace crow + { + + static constexpr uint8_t maxRequestQueueSize = 50; ++static constexpr unsigned int httpReadBodyLimit = 8192; + + enum class ConnState + { + initialized, + resolveInProgress, + resolveFailed, ++ resolved, + connectInProgress, + connectFailed, + connected, + sendInProgress, + sendFailed, ++ recvInProgress, + recvFailed, + idle, +- suspended, ++ closeInProgress, + closed, +- terminated ++ suspended, ++ terminated, ++ abortConnection, ++ retry + }; + + class HttpClient : public std::enable_shared_from_this +@@ -58,11 +64,14 @@ class HttpClient : public std::enable_shared_from_this + crow::async_resolve::Resolver resolver; + boost::beast::tcp_stream conn; + boost::asio::steady_timer timer; +- boost::beast::flat_buffer buffer; ++ boost::beast::flat_static_buffer buffer; + boost::beast::http::request req; +- boost::beast::http::response res; +- std::vector> headers; +- std::queue requestDataQueue; ++ std::optional< ++ boost::beast::http::response_parser> ++ parser; ++ boost::asio::ip::tcp::endpoint endpoint; ++ boost::circular_buffer_space_optimized requestDataQueue{}; ++ std::vector endPoints; + ConnState state; + std::string subId; + std::string host; +@@ -76,12 +85,7 @@ class HttpClient : public std::enable_shared_from_this + + void doResolve() + { +- if (state == ConnState::resolveInProgress) +- { +- return; +- } + state = ConnState::resolveInProgress; +- + BMCWEB_LOG_DEBUG << "Trying to resolve: " << host << ":" << port; + + auto respHandler = +@@ -89,78 +93,56 @@ class HttpClient : public std::enable_shared_from_this + const boost::beast::error_code ec, + const std::vector& + endpointList) { +- if (ec) ++ if (ec || (endpointList.size() == 0)) + { + BMCWEB_LOG_ERROR << "Resolve failed: " << ec.message(); + self->state = ConnState::resolveFailed; +- self->checkQueue(); ++ self->handleConnState(); + return; + } + BMCWEB_LOG_DEBUG << "Resolved"; +- self->doConnect(endpointList); ++ self->endPoints.assign(endpointList.begin(), ++ endpointList.end()); ++ self->state = ConnState::resolved; ++ self->handleConnState(); + }; + resolver.asyncResolve(host, port, std::move(respHandler)); + } + +- void doConnect( +- const std::vector& endpointList) ++ void doConnect() + { +- if (state == ConnState::connectInProgress) +- { +- return; +- } + state = ConnState::connectInProgress; + + BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":" << port; + + conn.expires_after(std::chrono::seconds(30)); + conn.async_connect( +- endpointList, [self(shared_from_this())]( +- const boost::beast::error_code ec, +- const boost::asio::ip::tcp::endpoint& endpoint) { ++ endPoints, [self(shared_from_this())]( ++ const boost::beast::error_code ec, ++ const boost::asio::ip::tcp::endpoint& endpoint) { + if (ec) + { + BMCWEB_LOG_ERROR << "Connect " << endpoint + << " failed: " << ec.message(); + self->state = ConnState::connectFailed; +- self->checkQueue(); ++ self->handleConnState(); + return; + } +- self->state = ConnState::connected; + BMCWEB_LOG_DEBUG << "Connected to: " << endpoint; +- +- self->checkQueue(); ++ self->state = ConnState::connected; ++ self->handleConnState(); + }); + } + + void sendMessage(const std::string& data) + { +- if (state == ConnState::sendInProgress) +- { +- return; +- } + state = ConnState::sendInProgress; + + BMCWEB_LOG_DEBUG << __FUNCTION__ << "(): " << host << ":" << port; + +- req.version(static_cast(11)); // HTTP 1.1 +- req.target(uri); +- req.method(boost::beast::http::verb::post); +- +- // Set headers +- for (const auto& [key, value] : headers) +- { +- req.set(key, value); +- } +- req.set(boost::beast::http::field::host, host); +- req.keep_alive(true); +- + req.body() = data; + req.prepare_payload(); + +- // Set a timeout on the operation +- conn.expires_after(std::chrono::seconds(30)); +- + // Send the HTTP request to the remote host + boost::beast::http::async_write( + conn, req, +@@ -171,7 +153,7 @@ class HttpClient : public std::enable_shared_from_this + BMCWEB_LOG_ERROR << "sendMessage() failed: " + << ec.message(); + self->state = ConnState::sendFailed; +- self->checkQueue(); ++ self->handleConnState(); + return; + } + BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: " +@@ -184,9 +166,18 @@ class HttpClient : public std::enable_shared_from_this + + void recvMessage() + { ++ state = ConnState::recvInProgress; ++ ++ parser.emplace(std::piecewise_construct, std::make_tuple()); ++ parser->body_limit(httpReadBodyLimit); ++ // Since these are all push style eventing, we are not ++ // bothered about response body parsing. ++ // Check only for the response header ++ parser->skip(true); ++ + // Receive the HTTP response + boost::beast::http::async_read( +- conn, buffer, res, ++ conn, buffer, *parser, + [self(shared_from_this())](const boost::beast::error_code& ec, + const std::size_t& bytesTransferred) { + if (ec) +@@ -194,30 +185,46 @@ class HttpClient : public std::enable_shared_from_this + BMCWEB_LOG_ERROR << "recvMessage() failed: " + << ec.message(); + self->state = ConnState::recvFailed; +- self->checkQueue(); ++ self->handleConnState(); + return; + } + BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: " + << bytesTransferred; +- boost::ignore_unused(bytesTransferred); +- +- // Discard received data. We are not interested. +- BMCWEB_LOG_DEBUG << "recvMessage() data: " << self->res; ++ BMCWEB_LOG_DEBUG << "recvMessage() data: " ++ << self->parser->get(); + + // Send is successful, Lets remove data from queue + // check for next request data in queue. +- self->requestDataQueue.pop(); ++ if (!self->requestDataQueue.empty()) ++ { ++ self->requestDataQueue.pop_front(); ++ } + self->state = ConnState::idle; +- self->checkQueue(); ++ ++ // Keep the connection alive if server supports it ++ // Else close the connection ++ BMCWEB_LOG_DEBUG << "recvMessage() keepalive : " ++ << self->parser->keep_alive(); ++ if (!self->parser->keep_alive()) ++ { ++ // Abort the connection since server is not keep-alive ++ // enabled ++ self->state = ConnState::abortConnection; ++ } ++ // Transfer ownership of the response ++ self->parser->release(); ++ ++ self->handleConnState(); + }); + } + + void doClose() + { ++ state = ConnState::closeInProgress; + boost::beast::error_code ec; + conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); ++ conn.close(); + +- state = ConnState::closed; + // not_connected happens sometimes so don't bother reporting it. + if (ec && ec != boost::beast::errc::not_connected) + { +@@ -225,112 +232,139 @@ class HttpClient : public std::enable_shared_from_this + return; + } + BMCWEB_LOG_DEBUG << "Connection closed gracefully"; +- } +- +- void checkQueue(const bool newRecord = false) +- { +- if (requestDataQueue.empty()) ++ if ((state != ConnState::suspended) && (state != ConnState::terminated)) + { +- // TODO: Having issue in keeping connection alive. So lets close if +- // nothing to be transferred. +- doClose(); +- +- BMCWEB_LOG_DEBUG << "requestDataQueue is empty\n"; +- return; ++ state = ConnState::closed; ++ handleConnState(); + } ++ } + ++ void waitAndRetry() ++ { + if (retryCount >= maxRetryAttempts) + { +- BMCWEB_LOG_ERROR << "Maximum number of retries is reached."; ++ BMCWEB_LOG_ERROR << "Maximum number of retries reached."; + + // Clear queue. + while (!requestDataQueue.empty()) + { +- requestDataQueue.pop(); ++ requestDataQueue.pop_front(); + } + +- BMCWEB_LOG_DEBUG << "Retry policy is set to " << retryPolicyAction; ++ BMCWEB_LOG_DEBUG << "Retry policy: " << retryPolicyAction; + if (retryPolicyAction == "TerminateAfterRetries") + { + // TODO: delete subscription + state = ConnState::terminated; +- return; + } + if (retryPolicyAction == "SuspendRetries") + { + state = ConnState::suspended; +- return; + } +- // keep retrying, reset count and continue. ++ // Reset the retrycount to zero so that client can try connecting ++ // again if needed + retryCount = 0; ++ handleConnState(); ++ return; + } + +- if ((state == ConnState::connectFailed) || +- (state == ConnState::sendFailed) || +- (state == ConnState::recvFailed)) ++ if (runningTimer) + { +- if (newRecord) +- { +- // We are already running async wait and retry. +- // Since record is added to queue, it gets the +- // turn in FIFO. +- return; +- } +- +- if (runningTimer) +- { +- BMCWEB_LOG_DEBUG << "Retry timer is already running."; +- return; +- } +- runningTimer = true; +- +- retryCount++; +- +- BMCWEB_LOG_DEBUG << "Attempt retry after " << retryIntervalSecs +- << " seconds. RetryCount = " << retryCount; +- timer.expires_after(std::chrono::seconds(retryIntervalSecs)); +- timer.async_wait( +- [self = shared_from_this()](const boost::system::error_code&) { +- self->runningTimer = false; +- self->connStateCheck(); +- }); ++ BMCWEB_LOG_DEBUG << "Retry timer is already running."; + return; + } +- // reset retry count. +- retryCount = 0; +- connStateCheck(); ++ runningTimer = true; + ++ retryCount++; ++ ++ BMCWEB_LOG_DEBUG << "Attempt retry after " << retryIntervalSecs ++ << " seconds. RetryCount = " << retryCount; ++ timer.expires_after(std::chrono::seconds(retryIntervalSecs)); ++ timer.async_wait( ++ [self = shared_from_this()](const boost::system::error_code ec) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "async_wait failed: " << ec.message(); ++ // Ignore the error and continue the retry loop to attempt ++ // sending the event as per the retry policy ++ } ++ self->runningTimer = false; ++ ++ // Lets close connection and start from resolve. ++ self->doClose(); ++ }); + return; + } + +- void connStateCheck() ++ void handleConnState() + { + switch (state) + { + case ConnState::resolveInProgress: + case ConnState::connectInProgress: + case ConnState::sendInProgress: +- case ConnState::suspended: +- case ConnState::terminated: +- // do nothing ++ case ConnState::recvInProgress: ++ case ConnState::closeInProgress: ++ { ++ BMCWEB_LOG_DEBUG << "Async operation is already in progress"; + break; ++ } + case ConnState::initialized: + case ConnState::closed: ++ { ++ if (requestDataQueue.empty()) ++ { ++ BMCWEB_LOG_DEBUG << "requestDataQueue is empty"; ++ return; ++ } ++ doResolve(); ++ break; ++ } ++ case ConnState::resolved: ++ { ++ doConnect(); ++ break; ++ } ++ case ConnState::suspended: ++ case ConnState::terminated: ++ { ++ doClose(); ++ break; ++ } ++ case ConnState::resolveFailed: + case ConnState::connectFailed: + case ConnState::sendFailed: + case ConnState::recvFailed: +- case ConnState::resolveFailed: ++ case ConnState::retry: + { +- doResolve(); ++ // In case of failures during connect and handshake ++ // the retry policy will be applied ++ waitAndRetry(); + break; + } + case ConnState::connected: + case ConnState::idle: + { ++ // State idle means, previous attempt is successful ++ // State connected means, client connection is established ++ // successfully ++ if (requestDataQueue.empty()) ++ { ++ BMCWEB_LOG_DEBUG << "requestDataQueue is empty"; ++ return; ++ } + std::string data = requestDataQueue.front(); + sendMessage(data); + break; + } ++ case ConnState::abortConnection: ++ { ++ // Server did not want to keep alive the session ++ doClose(); ++ break; ++ } ++ default: ++ break; + } + } + +@@ -339,37 +373,38 @@ class HttpClient : public std::enable_shared_from_this + const std::string& destIP, const std::string& destPort, + const std::string& destUri) : + conn(ioc), +- timer(ioc), subId(id), host(destIP), port(destPort), uri(destUri), +- retryCount(0), maxRetryAttempts(5), retryIntervalSecs(0), ++ timer(ioc), req(boost::beast::http::verb::post, destUri, 11), ++ state(ConnState::initialized), subId(id), host(destIP), port(destPort), ++ uri(destUri), retryCount(0), maxRetryAttempts(5), retryIntervalSecs(0), + retryPolicyAction("TerminateAfterRetries"), runningTimer(false) + { +- state = ConnState::initialized; ++ // Set the request header ++ req.set(boost::beast::http::field::host, host); ++ req.set(boost::beast::http::field::content_type, "application/json"); ++ req.keep_alive(true); ++ ++ requestDataQueue.set_capacity(maxRequestQueueSize); + } + + void sendData(const std::string& data) + { +- if (state == ConnState::suspended) ++ if ((state == ConnState::suspended) || (state == ConnState::terminated)) + { + return; + } +- +- if (requestDataQueue.size() <= maxRequestQueueSize) +- { +- requestDataQueue.push(data); +- checkQueue(true); +- } +- else +- { +- BMCWEB_LOG_ERROR << "Request queue is full. So ignoring data."; +- } +- ++ requestDataQueue.push_back(data); ++ handleConnState(); + return; + } + +- void setHeaders( ++ void addHeaders( + const std::vector>& httpHeaders) + { +- headers = httpHeaders; ++ // Set custom headers ++ for (const auto& [key, value] : httpHeaders) ++ { ++ req.set(key, value); ++ } + } + + void setRetryConfig(const uint32_t retryAttempts, +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index 148c703..bffa68f 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -412,7 +412,7 @@ class Subscription + reqHeaders.emplace_back(std::pair(key, val)); + } + } +- conn->setHeaders(reqHeaders); ++ conn->addHeaders(reqHeaders); + conn->sendData(msg); + this->eventSeqNum++; + } +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch new file mode 100644 index 000000000..b1f61c6fd --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0002-EventService-https-client-support.patch @@ -0,0 +1,401 @@ +From 579fda953ec991b4e7f9d7194b08f6aa103fa0ec Mon Sep 17 00:00:00 2001 +From: AppaRao Puli +Date: Mon, 22 Feb 2021 17:07:47 +0000 +Subject: [PATCH 2/2] EventService: https client support + +Add https client support for push style +eventing. Using this BMC can push the event +logs/telemetry data to event listener over +secure http channel. + +Tested: + - Created subscription with https destination + url. Using SubmitTestEvent action set the + event and can see event on event listener. + - Validator passed. + +Change-Id: I44c3918b39baa2eb5fddda9d635f99aa280a422a +Signed-off-by: AppaRao Puli +--- + http/http_client.hpp | 255 ++++++++++++------ + .../include/event_service_manager.hpp | 2 +- + 2 files changed, 175 insertions(+), 82 deletions(-) + +diff --git a/http/http_client.hpp b/http/http_client.hpp +index d116f6d..cebc857 100644 +--- a/http/http_client.hpp ++++ b/http/http_client.hpp +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -44,6 +45,8 @@ enum class ConnState + resolved, + connectInProgress, + connectFailed, ++ handshakeInProgress, ++ handshakeFailed, + connected, + sendInProgress, + sendFailed, +@@ -62,7 +65,9 @@ class HttpClient : public std::enable_shared_from_this + { + private: + crow::async_resolve::Resolver resolver; ++ boost::asio::ssl::context ctx{boost::asio::ssl::context::tlsv12_client}; + boost::beast::tcp_stream conn; ++ std::optional> sslConn; + boost::asio::steady_timer timer; + boost::beast::flat_static_buffer buffer; + boost::beast::http::request req; +@@ -112,23 +117,52 @@ class HttpClient : public std::enable_shared_from_this + void doConnect() + { + state = ConnState::connectInProgress; ++ sslConn.emplace(conn, ctx); + + BMCWEB_LOG_DEBUG << "Trying to connect to: " << host << ":" << port; ++ auto respHandler = [self(shared_from_this())]( ++ const boost::beast::error_code ec, ++ const boost::asio::ip::tcp::endpoint& endpoint) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "Connect " << endpoint ++ << " failed: " << ec.message(); ++ self->state = ConnState::connectFailed; ++ self->handleConnState(); ++ return; ++ } + ++ BMCWEB_LOG_DEBUG << "Connected to: " << endpoint; ++ if (self->sslConn) ++ { ++ self->performHandshake(); ++ } ++ else ++ { ++ self->handleConnState(); ++ } ++ }; + conn.expires_after(std::chrono::seconds(30)); +- conn.async_connect( +- endPoints, [self(shared_from_this())]( +- const boost::beast::error_code ec, +- const boost::asio::ip::tcp::endpoint& endpoint) { ++ conn.async_connect(endPoints, std::move(respHandler)); ++ } ++ ++ void performHandshake() ++ { ++ state = ConnState::handshakeInProgress; ++ ++ sslConn->async_handshake( ++ boost::asio::ssl::stream_base::client, ++ [self(shared_from_this())](const boost::beast::error_code ec) { + if (ec) + { +- BMCWEB_LOG_ERROR << "Connect " << endpoint +- << " failed: " << ec.message(); +- self->state = ConnState::connectFailed; ++ BMCWEB_LOG_ERROR << "SSL handshake failed: " ++ << ec.message(); ++ self->state = ConnState::handshakeFailed; + self->handleConnState(); + return; + } +- BMCWEB_LOG_DEBUG << "Connected to: " << endpoint; ++ ++ BMCWEB_LOG_DEBUG << "SSL Handshake successfull"; + self->state = ConnState::connected; + self->handleConnState(); + }); +@@ -136,106 +170,159 @@ class HttpClient : public std::enable_shared_from_this + + void sendMessage(const std::string& data) + { +- state = ConnState::sendInProgress; +- + BMCWEB_LOG_DEBUG << __FUNCTION__ << "(): " << host << ":" << port; ++ state = ConnState::sendInProgress; + + req.body() = data; + req.prepare_payload(); + +- // Send the HTTP request to the remote host +- boost::beast::http::async_write( +- conn, req, +- [self(shared_from_this())](const boost::beast::error_code& ec, +- const std::size_t& bytesTransferred) { +- if (ec) +- { +- BMCWEB_LOG_ERROR << "sendMessage() failed: " +- << ec.message(); +- self->state = ConnState::sendFailed; +- self->handleConnState(); +- return; +- } +- BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: " +- << bytesTransferred; +- boost::ignore_unused(bytesTransferred); ++ auto respHandler = [self(shared_from_this())]( ++ const boost::beast::error_code ec, ++ const std::size_t& bytesTransferred) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "sendMessage() failed: " << ec.message(); ++ self->state = ConnState::sendFailed; ++ self->handleConnState(); ++ return; ++ } + +- self->recvMessage(); +- }); +- } ++ BMCWEB_LOG_DEBUG << "sendMessage() bytes transferred: " ++ << bytesTransferred; ++ boost::ignore_unused(bytesTransferred); ++ self->recvMessage(); ++ }; + ++ conn.expires_after(std::chrono::seconds(30)); ++ if (sslConn) ++ { ++ boost::beast::http::async_write(*sslConn, req, ++ std::move(respHandler)); ++ } ++ else ++ { ++ boost::beast::http::async_write(conn, req, std::move(respHandler)); ++ } ++ } + void recvMessage() + { + state = ConnState::recvInProgress; + ++ auto respHandler = [self(shared_from_this())]( ++ const boost::beast::error_code ec, ++ const std::size_t& bytesTransferred) { ++ if (ec && ec != boost::asio::ssl::error::stream_truncated) ++ { ++ BMCWEB_LOG_ERROR << "recvMessage() failed: " << ec.message(); ++ ++ self->state = ConnState::recvFailed; ++ self->handleConnState(); ++ return; ++ } ++ ++ BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: " ++ << bytesTransferred; ++ boost::ignore_unused(bytesTransferred); ++ // Send is successful, Lets remove data from queue ++ // check for next request data in queue. ++ if (!self->requestDataQueue.empty()) ++ { ++ self->requestDataQueue.pop_front(); ++ } ++ self->state = ConnState::idle; ++ // Keep the connection alive if server supports it ++ // Else close the connection ++ BMCWEB_LOG_DEBUG << "recvMessage() keepalive : " ++ << self->parser->keep_alive(); ++ if (!self->parser->keep_alive()) ++ { ++ // Abort the connection since server is not keep-alive enabled ++ self->state = ConnState::abortConnection; ++ } ++ // Transfer ownership of the response ++ self->parser->release(); ++ ++ self->handleConnState(); ++ }; + parser.emplace(std::piecewise_construct, std::make_tuple()); + parser->body_limit(httpReadBodyLimit); + // Since these are all push style eventing, we are not + // bothered about response body parsing. + // Check only for the response header + parser->skip(true); ++ conn.expires_after(std::chrono::seconds(30)); ++ if (sslConn) ++ { ++ boost::beast::http::async_read(*sslConn, buffer, *parser, ++ std::move(respHandler)); ++ } ++ else ++ { ++ boost::beast::http::async_read(conn, buffer, *parser, ++ std::move(respHandler)); ++ } ++ } ++ void doClose() ++ { ++ state = ConnState::closeInProgress; + +- // Receive the HTTP response +- boost::beast::http::async_read( +- conn, buffer, *parser, +- [self(shared_from_this())](const boost::beast::error_code& ec, +- const std::size_t& bytesTransferred) { ++ // Set the timeout on the tcp stream socket for the async operation ++ conn.expires_after(std::chrono::seconds(30)); ++ if (sslConn) ++ { ++ sslConn->async_shutdown([self = shared_from_this()]( ++ const boost::system::error_code ec) { + if (ec) + { +- BMCWEB_LOG_ERROR << "recvMessage() failed: " +- << ec.message(); +- self->state = ConnState::recvFailed; +- self->handleConnState(); +- return; ++ // Many https server closes connection abruptly ++ // i.e witnout close_notify. More details are at ++ // https://github.com/boostorg/beast/issues/824 ++ if (ec == boost::asio::ssl::error::stream_truncated) ++ { ++ BMCWEB_LOG_INFO << "doClose(): Connection " ++ "closed by server. "; ++ } ++ else ++ { ++ BMCWEB_LOG_ERROR << "doClose() failed: " ++ << ec.message(); ++ } + } +- BMCWEB_LOG_DEBUG << "recvMessage() bytes transferred: " +- << bytesTransferred; +- BMCWEB_LOG_DEBUG << "recvMessage() data: " +- << self->parser->get(); +- +- // Send is successful, Lets remove data from queue +- // check for next request data in queue. +- if (!self->requestDataQueue.empty()) ++ else + { +- self->requestDataQueue.pop_front(); ++ BMCWEB_LOG_DEBUG << "Connection closed gracefully..."; + } +- self->state = ConnState::idle; ++ self->conn.close(); + +- // Keep the connection alive if server supports it +- // Else close the connection +- BMCWEB_LOG_DEBUG << "recvMessage() keepalive : " +- << self->parser->keep_alive(); +- if (!self->parser->keep_alive()) ++ if ((self->state != ConnState::suspended) && ++ (self->state != ConnState::terminated)) + { +- // Abort the connection since server is not keep-alive +- // enabled +- self->state = ConnState::abortConnection; ++ self->state = ConnState::closed; ++ self->handleConnState(); + } +- // Transfer ownership of the response +- self->parser->release(); +- +- self->handleConnState(); + }); +- } +- +- void doClose() +- { +- state = ConnState::closeInProgress; +- boost::beast::error_code ec; +- conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); +- conn.close(); +- +- // not_connected happens sometimes so don't bother reporting it. +- if (ec && ec != boost::beast::errc::not_connected) +- { +- BMCWEB_LOG_ERROR << "shutdown failed: " << ec.message(); +- return; + } +- BMCWEB_LOG_DEBUG << "Connection closed gracefully"; +- if ((state != ConnState::suspended) && (state != ConnState::terminated)) ++ else + { +- state = ConnState::closed; +- handleConnState(); ++ boost::beast::error_code ec; ++ conn.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ++ ec); ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "doClose() failed: " << ec.message(); ++ } ++ else ++ { ++ BMCWEB_LOG_DEBUG << "Connection closed gracefully..."; ++ } ++ conn.close(); ++ ++ if ((state != ConnState::suspended) && ++ (state != ConnState::terminated)) ++ { ++ state = ConnState::closed; ++ handleConnState(); ++ } + } + } + +@@ -302,6 +389,7 @@ class HttpClient : public std::enable_shared_from_this + { + case ConnState::resolveInProgress: + case ConnState::connectInProgress: ++ case ConnState::handshakeInProgress: + case ConnState::sendInProgress: + case ConnState::recvInProgress: + case ConnState::closeInProgress: +@@ -333,6 +421,7 @@ class HttpClient : public std::enable_shared_from_this + } + case ConnState::resolveFailed: + case ConnState::connectFailed: ++ case ConnState::handshakeFailed: + case ConnState::sendFailed: + case ConnState::recvFailed: + case ConnState::retry: +@@ -371,7 +460,8 @@ class HttpClient : public std::enable_shared_from_this + public: + explicit HttpClient(boost::asio::io_context& ioc, const std::string& id, + const std::string& destIP, const std::string& destPort, +- const std::string& destUri) : ++ const std::string& destUri, ++ const std::string& uriProto) : + conn(ioc), + timer(ioc), req(boost::beast::http::verb::post, destUri, 11), + state(ConnState::initialized), subId(id), host(destIP), port(destPort), +@@ -384,8 +474,11 @@ class HttpClient : public std::enable_shared_from_this + req.keep_alive(true); + + requestDataQueue.set_capacity(maxRequestQueueSize); ++ if (uriProto == "https") ++ { ++ sslConn.emplace(conn, ctx); ++ } + } +- + void sendData(const std::string& data) + { + if ((state == ConnState::suspended) || (state == ConnState::terminated)) +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index bffa68f..1e6f496 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -387,7 +387,7 @@ class Subscription + { + conn = std::make_shared( + crow::connections::systemBus->get_io_context(), id, host, port, +- path); ++ path, uriProto); + } + + Subscription(const std::shared_ptr& adaptor) : +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0003-Move-EventService-init-to-later-stage.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0003-Move-EventService-init-to-later-stage.patch new file mode 100644 index 000000000..f910915ce --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0003-Move-EventService-init-to-later-stage.patch @@ -0,0 +1,50 @@ +From 2dcfef0170ca5ba1f934de6934db64b545ab3b55 Mon Sep 17 00:00:00 2001 +From: AppaRao Puli +Date: Tue, 23 Mar 2021 23:36:40 +0000 +Subject: [PATCH] Move EventService init to later stage + +The bmcweb crash issue seen when there is eventservice +config with subscriptions in persistent file. + +During EventService instantiation, it uses the "get_io_context()" +from systemBus, so it should be called after systemBus init. So +moved the EventService instantiation after systemBus initialization. + +Tested: + - bmcweb crash issue resolved. + +Signed-off-by: AppaRao Puli +Change-Id: Iab52f0e89478e306af475066fb5691153a05677d +--- + src/webserver_main.cpp | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/src/webserver_main.cpp b/src/webserver_main.cpp +index 3b9631a..f0e2207 100644 +--- a/src/webserver_main.cpp ++++ b/src/webserver_main.cpp +@@ -80,9 +80,6 @@ int main(int /*argc*/, char** /*argv*/) + #ifdef BMCWEB_ENABLE_REDFISH + crow::redfish::requestRoutes(app); + redfish::RedfishService redfish(app); +- +- // Create EventServiceManager instance and initialize Config +- redfish::EventServiceManager::getInstance(); + #endif + + #ifdef BMCWEB_ENABLE_DBUS_REST +@@ -116,6 +113,11 @@ int main(int /*argc*/, char** /*argv*/) + crow::connections::systemBus = + std::make_shared(*io); + ++#ifdef BMCWEB_ENABLE_REDFISH ++ // Create EventServiceManager instance and initialize Config ++ redfish::EventServiceManager::getInstance(); ++#endif ++ + #ifdef BMCWEB_ENABLE_VM_NBDPROXY + crow::nbd_proxy::requestRoutes(app); + #endif +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch new file mode 100644 index 000000000..0d31fbc72 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0004-Add-Server-Sent-Events-support.patch @@ -0,0 +1,477 @@ +From e93a6a02b0fba3371144d474422fadacc3f25fde Mon Sep 17 00:00:00 2001 +From: AppaRao Puli +Date: Fri, 12 Mar 2021 18:53:25 +0000 +Subject: [PATCH 4/6] Add Server-Sent-Events support + +Server-Sent Events is a standard describing how servers can +initiate data transmission towards clients once an initial +client connection has been established. Unlike websockets +(which are bidirectional), Server-Sent Events are +unidirectional and commonly used to send message updates or +continuous data streams to a browser client. + +This is base patch for adding Server-Sent events support to +bmcweb. Redfish eventservice SSE style subscription uses +this and will be loaded on top of this commit. + +Tested: + - Tested using follow-up patch on top which adds + support for Redfish EventService SSE style subscription + and observed events are getting sent periodically. + +Change-Id: I36956565cbba30c2007852c9471f477f6d1736e9 +Signed-off-by: AppaRao Puli +--- + http/http_connection.hpp | 14 +- + http/http_response.hpp | 7 +- + http/routing.hpp | 70 ++++++++++ + http/server_sent_event.hpp | 279 +++++++++++++++++++++++++++++++++++++ + 4 files changed, 364 insertions(+), 6 deletions(-) + create mode 100644 http/server_sent_event.hpp + +diff --git a/http/http_connection.hpp b/http/http_connection.hpp +index 4482f8d..2c8bf40 100644 +--- a/http/http_connection.hpp ++++ b/http/http_connection.hpp +@@ -326,7 +326,7 @@ class Connection : + BMCWEB_LOG_INFO << "Request: " + << " " << this << " HTTP/" << req->version() / 10 << "." + << req->version() % 10 << ' ' << req->methodString() +- << " " << req->target() << " " << req->ipAddress; ++ << " " << req->url << " " << req->ipAddress; + + needToCallAfterHandlers = false; + +@@ -345,11 +345,15 @@ class Connection : + boost::asio::post(self->adaptor.get_executor(), + [self] { self->completeRequest(); }); + }; +- if (req->isUpgrade() && +- boost::iequals( +- req->getHeaderValue(boost::beast::http::field::upgrade), +- "websocket")) ++ ++ if ((req->isUpgrade() && ++ boost::iequals(req->getHeaderValue( ++ boost::beast::http::field::upgrade), ++ "websocket")) || ++ (req->url == "/sse")) + { ++ BMCWEB_LOG_DEBUG << "Request: " << this ++ << " is getting upgraded"; + handler->handleUpgrade(*req, res, std::move(adaptor)); + // delete lambda with self shared_ptr + // to enable connection destruction +diff --git a/http/http_response.hpp b/http/http_response.hpp +index cd00ec8..ffd6dda 100644 +--- a/http/http_response.hpp ++++ b/http/http_response.hpp +@@ -13,10 +13,15 @@ namespace crow + template + class Connection; + ++template ++class SseConnectionImpl; ++ + struct Response + { + template + friend class crow::Connection; ++ template ++ friend class crow::SseConnectionImpl; + using response_type = + boost::beast::http::response; + +@@ -136,8 +141,8 @@ struct Response + + private: + bool completed{}; +- std::function completeRequestHandler; + std::function isAliveHelper; ++ std::function completeRequestHandler; + + // In case of a JSON object, set the Content-Type header + void jsonMode() +diff --git a/http/routing.hpp b/http/routing.hpp +index 65c7b70..0824939 100644 +--- a/http/routing.hpp ++++ b/http/routing.hpp +@@ -6,6 +6,7 @@ + #include "http_response.hpp" + #include "logging.hpp" + #include "privileges.hpp" ++#include "server_sent_event.hpp" + #include "sessions.hpp" + #include "utility.hpp" + #include "websocket.hpp" +@@ -390,6 +391,67 @@ class WebSocketRule : public BaseRule + std::function errorHandler; + }; + ++class SseSocketRule : public BaseRule ++{ ++ using self_t = SseSocketRule; ++ ++ public: ++ SseSocketRule(const std::string& ruleIn) : BaseRule(ruleIn) ++ {} ++ ++ void validate() override ++ {} ++ ++ void handle(const Request&, Response& res, const RoutingParams&) override ++ { ++ res.result(boost::beast::http::status::not_found); ++ res.end(); ++ } ++ ++ void handleUpgrade(const Request& req, Response&, ++ boost::asio::ip::tcp::socket&& adaptor) override ++ { ++ std::shared_ptr> ++ myConnection = std::make_shared< ++ crow::SseConnectionImpl>( ++ req, std::move(adaptor), openHandler, closeHandler); ++ myConnection->start(); ++ } ++#ifdef BMCWEB_ENABLE_SSL ++ void handleUpgrade(const Request& req, Response&, ++ boost::beast::ssl_stream&& ++ adaptor) override ++ { ++ std::shared_ptr>> ++ myConnection = std::make_shared>>( ++ req, std::move(adaptor), openHandler, closeHandler); ++ myConnection->start(); ++ } ++#endif ++ ++ template ++ self_t& onopen(Func f) ++ { ++ openHandler = f; ++ return *this; ++ } ++ ++ template ++ self_t& onclose(Func f) ++ { ++ closeHandler = f; ++ return *this; ++ } ++ ++ private: ++ std::function&, ++ const crow::Request&, crow::Response&)> ++ openHandler; ++ std::function&)> closeHandler; ++}; ++ + template + struct RuleParameterTraits + { +@@ -402,6 +464,14 @@ struct RuleParameterTraits + return *p; + } + ++ SseSocketRule& serverSentEvent() ++ { ++ self_t* self = static_cast(this); ++ SseSocketRule* p = new SseSocketRule(self->rule); ++ self->ruleToUpgrade.reset(p); ++ return *p; ++ } ++ + self_t& name(const std::string_view name) noexcept + { + self_t* self = static_cast(this); +diff --git a/http/server_sent_event.hpp b/http/server_sent_event.hpp +new file mode 100644 +index 0000000..41d18ed +--- /dev/null ++++ b/http/server_sent_event.hpp +@@ -0,0 +1,279 @@ ++#pragma once ++#include "http_request.hpp" ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#ifdef BMCWEB_ENABLE_SSL ++#include ++#endif ++ ++namespace crow ++{ ++ ++struct SseConnection : std::enable_shared_from_this ++{ ++ public: ++ SseConnection(const crow::Request& reqIn) : req(reqIn) ++ {} ++ virtual ~SseConnection() = default; ++ ++ virtual boost::asio::io_context& getIoContext() = 0; ++ virtual void sendSSEHeader() = 0; ++ virtual void completeRequest() = 0; ++ virtual void close(const std::string_view msg = "quit") = 0; ++ virtual void sendEvent(const std::string_view id, ++ const std::string_view msg) = 0; ++ ++ crow::Request req; ++ crow::Response res; ++}; ++ ++template ++class SseConnectionImpl : public SseConnection ++{ ++ public: ++ SseConnectionImpl( ++ const crow::Request& reqIn, Adaptor adaptorIn, ++ std::function&, ++ const crow::Request&, crow::Response&)> ++ openHandler, ++ std::function&)> closeHandler) : ++ SseConnection(reqIn), ++ adaptor(std::move(adaptorIn)), openHandler(std::move(openHandler)), ++ closeHandler(std::move(closeHandler)) ++ { ++ BMCWEB_LOG_DEBUG << "SseConnectionImpl: SSE constructor " << this; ++ } ++ ++ ~SseConnectionImpl() override ++ { ++ res.completeRequestHandler = nullptr; ++ BMCWEB_LOG_DEBUG << "SseConnectionImpl: SSE destructor " << this; ++ } ++ ++ boost::asio::io_context& getIoContext() override ++ { ++ return static_cast( ++ adaptor.get_executor().context()); ++ } ++ ++ void start() ++ { ++ // Register for completion callback. ++ res.completeRequestHandler = [this, self(shared_from_this())] { ++ boost::asio::post(this->adaptor.get_executor(), ++ [self] { self->completeRequest(); }); ++ }; ++ ++ if (openHandler) ++ { ++ std::shared_ptr self = this->shared_from_this(); ++ openHandler(self, req, res); ++ } ++ } ++ ++ void close(const std::string_view msg) override ++ { ++ BMCWEB_LOG_DEBUG << "Closing SSE connection " << this << " - " << msg; ++ boost::beast::get_lowest_layer(adaptor).close(); ++ ++ // send notification to handler for cleanup ++ if (closeHandler) ++ { ++ std::shared_ptr self = this->shared_from_this(); ++ closeHandler(self); ++ } ++ } ++ ++ void sendSSEHeader() override ++ { ++ BMCWEB_LOG_DEBUG << "Starting SSE connection"; ++ using BodyType = boost::beast::http::buffer_body; ++ auto response = ++ std::make_shared>( ++ boost::beast::http::status::ok, 11); ++ auto serializer = ++ std::make_shared>( ++ *response); ++ ++ response->set(boost::beast::http::field::server, "bmcweb"); ++ response->set(boost::beast::http::field::content_type, ++ "text/event-stream"); ++ response->body().data = nullptr; ++ response->body().size = 0; ++ response->body().more = true; ++ ++ boost::beast::http::async_write_header( ++ adaptor, *serializer, ++ [this, self(shared_from_this()), response, serializer]( ++ const boost::beast::error_code& ec, const std::size_t&) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "Error sending header" << ec; ++ close("async_write_header failed"); ++ return; ++ } ++ BMCWEB_LOG_DEBUG << "SSE header sent - Connection established"; ++ ++ // SSE stream header sent, So lets setup monitor. ++ // Any read data on this stream will be error in case of SSE. ++ setupRead(); ++ }); ++ } ++ ++ void setupRead() ++ { ++ adaptor.async_read_some( ++ outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()), ++ [this](const boost::system::error_code& ec, std::size_t bytesRead) { ++ BMCWEB_LOG_DEBUG << "async_read_some: Read " << bytesRead ++ << " bytes"; ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "Read error: " << ec; ++ } ++ outputBuffer.commit(bytesRead); ++ outputBuffer.consume(bytesRead); ++ ++ // After establishing SSE stream, Reading data on this ++ // stream means client is disobeys the SSE protocol. ++ // Read the data to avoid buffer attacks and close connection. ++ close("Close SSE connection"); ++ return; ++ }); ++ } ++ ++ void doWrite() ++ { ++ if (doingWrite) ++ { ++ return; ++ } ++ if (inputBuffer.size() == 0) ++ { ++ BMCWEB_LOG_DEBUG << "inputBuffer is empty... Bailing out"; ++ return; ++ } ++ doingWrite = true; ++ ++ adaptor.async_write_some( ++ inputBuffer.data(), [this, self(shared_from_this())]( ++ boost::beast::error_code ec, ++ const std::size_t& bytesTransferred) { ++ doingWrite = false; ++ inputBuffer.consume(bytesTransferred); ++ ++ if (ec == boost::asio::error::eof) ++ { ++ BMCWEB_LOG_ERROR << "async_write_some() SSE stream closed"; ++ close("SSE stream closed"); ++ return; ++ } ++ ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "async_write_some() failed: " ++ << ec.message(); ++ close("async_write_some failed"); ++ return; ++ } ++ BMCWEB_LOG_DEBUG << "async_write_some() bytes transferred: " ++ << bytesTransferred; ++ ++ doWrite(); ++ }); ++ } ++ ++ void completeRequest() override ++ { ++ BMCWEB_LOG_DEBUG << "SSE completeRequest() handler"; ++ if (res.body().empty() && !res.jsonValue.empty()) ++ { ++ res.addHeader("Content-Type", "application/json"); ++ res.body() = res.jsonValue.dump( ++ 2, ' ', true, nlohmann::json::error_handler_t::replace); ++ } ++ ++ res.preparePayload(); ++ auto serializer = ++ std::make_shared>(*res.stringResponse); ++ ++ boost::beast::http::async_write( ++ adaptor, *serializer, ++ [this, self(shared_from_this()), ++ serializer](const boost::system::error_code& ec, ++ std::size_t bytesTransferred) { ++ BMCWEB_LOG_DEBUG << this << " async_write " << bytesTransferred ++ << " bytes"; ++ if (ec) ++ { ++ BMCWEB_LOG_DEBUG << this << " from async_write failed"; ++ return; ++ } ++ res.clear(); ++ ++ BMCWEB_LOG_DEBUG << this ++ << " Closing SSE connection - Request invalid"; ++ close("Request invalid"); ++ }); ++ ++ // delete lambda with self shared_ptr ++ // to enable connection destruction ++ res.completeRequestHandler = nullptr; ++ } ++ ++ void sendEvent(const std::string_view id, ++ const std::string_view msg) override ++ { ++ if (msg.empty()) ++ { ++ BMCWEB_LOG_DEBUG << "Empty data, bailing out."; ++ return; ++ } ++ ++ std::string rawData; ++ if (!id.empty()) ++ { ++ rawData += "id: "; ++ rawData.append(id.begin(), id.end()); ++ rawData += "\n"; ++ } ++ ++ rawData += "data: "; ++ for (char character : msg) ++ { ++ rawData += character; ++ if (character == '\n') ++ { ++ rawData += "data: "; ++ } ++ } ++ rawData += "\n\n"; ++ ++ boost::asio::buffer_copy(inputBuffer.prepare(rawData.size()), ++ boost::asio::buffer(rawData)); ++ inputBuffer.commit(rawData.size()); ++ ++ doWrite(); ++ } ++ ++ private: ++ Adaptor adaptor; ++ ++ boost::beast::flat_static_buffer<1024U * 8U> outputBuffer; ++ boost::beast::flat_static_buffer<1024U * 64U> inputBuffer; ++ bool doingWrite = false; ++ ++ std::function&, const crow::Request&, ++ crow::Response&)> ++ openHandler; ++ std::function&)> closeHandler; ++}; ++} // namespace crow +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch new file mode 100644 index 000000000..e2eaf1761 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch @@ -0,0 +1,627 @@ +From dbaf8c1dc402da97fec723d050e063cd283d8736 Mon Sep 17 00:00:00 2001 +From: AppaRao Puli +Date: Tue, 16 Mar 2021 15:37:24 +0000 +Subject: [PATCH 5/6] Add SSE style subscription support to eventservice + +This commit adds the SSE style eventservice subscription +style event. Using this, end user can subscribe for +Redfish event logs using GET on SSE usri from +browser. +URI: /redfish/v1/EventService/Subscriptions/SSE + +Tested: + - From Browser did GET on above SSE URI and + generated some Redfish event logs(power cycle) + and saw redfish event logs streaming on browser. + - After SSE registration, Check Subscription collections + and GET on individual subscription and saw desired + response. + - Ran RedfishValidation and its passed. + +Signed-off-by: AppaRao Puli +Change-Id: I7f4b7a34974080739c4ba968ed570489af0474de +--- + http/http_connection.hpp | 2 +- + include/eventservice_sse.hpp | 75 +++++ + .../include/event_service_manager.hpp | 77 ++++- + redfish-core/include/server_sent_events.hpp | 291 ------------------ + redfish-core/lib/event_service.hpp | 4 +- + src/webserver_main.cpp | 2 + + 6 files changed, 149 insertions(+), 302 deletions(-) + create mode 100644 include/eventservice_sse.hpp + delete mode 100644 redfish-core/include/server_sent_events.hpp + +diff --git a/http/http_connection.hpp b/http/http_connection.hpp +index 2c8bf40..1ab776c 100644 +--- a/http/http_connection.hpp ++++ b/http/http_connection.hpp +@@ -350,7 +350,7 @@ class Connection : + boost::iequals(req->getHeaderValue( + boost::beast::http::field::upgrade), + "websocket")) || +- (req->url == "/sse")) ++ (req->url == "/redfish/v1/EventService/Subscriptions/SSE")) + { + BMCWEB_LOG_DEBUG << "Request: " << this + << " is getting upgraded"; +diff --git a/include/eventservice_sse.hpp b/include/eventservice_sse.hpp +new file mode 100644 +index 0000000..6c98e6e +--- /dev/null ++++ b/include/eventservice_sse.hpp +@@ -0,0 +1,75 @@ ++#pragma once ++ ++#include ++#include ++ ++namespace redfish ++{ ++namespace eventservice_sse ++{ ++ ++static bool createSubscription(std::shared_ptr& conn, ++ const crow::Request& req, crow::Response& res) ++{ ++ if ((EventServiceManager::getInstance().getNumberOfSubscriptions() >= ++ maxNoOfSubscriptions) || ++ EventServiceManager::getInstance().getNumberOfSSESubscriptions() >= ++ maxNoOfSSESubscriptions) ++ { ++ BMCWEB_LOG_ERROR << "Max SSE subscriptions reached"; ++ messages::eventSubscriptionLimitExceeded(res); ++ res.end(); ++ return false; ++ } ++ BMCWEB_LOG_DEBUG << "Request query param size: " << req.urlParams.size(); ++ ++ std::shared_ptr subValue = ++ std::make_shared(std::move(conn)); ++ ++ // GET on this URI means, Its SSE subscriptionType. ++ subValue->subscriptionType = redfish::subscriptionTypeSSE; ++ ++ // TODO: parse $filter query params and fill config. ++ subValue->protocol = "Redfish"; ++ subValue->retryPolicy = "TerminateAfterRetries"; ++ subValue->eventFormatType = "Event"; ++ ++ std::string id = ++ redfish::EventServiceManager::getInstance().addSubscription(subValue, ++ false); ++ if (id.empty()) ++ { ++ messages::internalError(res); ++ res.end(); ++ return false; ++ } ++ ++ return true; ++} ++ ++static void deleteSubscription(std::shared_ptr& conn) ++{ ++ redfish::EventServiceManager::getInstance().deleteSubscription(conn); ++} ++ ++inline void requestRoutes(App& app) ++{ ++ BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/SSE") ++ .privileges({"ConfigureComponents", "ConfigureManager"}) ++ .serverSentEvent() ++ .onopen([](std::shared_ptr& conn, ++ const crow::Request& req, crow::Response& res) { ++ BMCWEB_LOG_DEBUG << "Connection " << conn << " opened."; ++ if (createSubscription(conn, req, res)) ++ { ++ // All success, lets send SSE haader ++ conn->sendSSEHeader(); ++ } ++ }) ++ .onclose([](std::shared_ptr& conn) { ++ BMCWEB_LOG_DEBUG << "Connection " << conn << " closed"; ++ deleteSubscription(conn); ++ }); ++} ++} // namespace eventservice_sse ++} // namespace redfish +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index 148c703..e3eba86 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -22,13 +22,15 @@ + #include + + #include ++#include + #include + #include + #include + #include +-#include ++#include + #include + ++#include + #include + #include + #include +@@ -45,9 +47,13 @@ using EventServiceConfig = std::tuple; + static constexpr const char* eventFormatType = "Event"; + static constexpr const char* metricReportFormatType = "MetricReport"; + ++static constexpr const char* subscriptionTypeSSE = "SSE"; + static constexpr const char* eventServiceFile = + "/var/lib/bmcweb/eventservice_config.json"; + ++static constexpr const uint8_t maxNoOfSubscriptions = 20; ++static constexpr const uint8_t maxNoOfSSESubscriptions = 10; ++ + #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES + static std::optional inotifyConn; + static constexpr const char* redfishEventLogDir = "/var/log"; +@@ -390,11 +396,9 @@ class Subscription + path); + } + +- Subscription(const std::shared_ptr& adaptor) : +- eventSeqNum(1) +- { +- sseConn = std::make_shared(adaptor); +- } ++ Subscription(const std::shared_ptr& adaptor) : ++ sseConn(adaptor), eventSeqNum(1) ++ {} + + ~Subscription() = default; + +@@ -419,7 +423,7 @@ class Subscription + + if (sseConn != nullptr) + { +- sseConn->sendData(eventSeqNum, msg); ++ sseConn->sendEvent(std::to_string(eventSeqNum), msg); + } + } + +@@ -509,6 +513,7 @@ class Subscription + + this->sendEvent( + msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace)); ++ this->eventSeqNum++; + } + #endif + +@@ -579,14 +584,39 @@ class Subscription + return eventSeqNum; + } + ++ void setSubscriptionId(const std::string& id) ++ { ++ BMCWEB_LOG_DEBUG << "Subscription ID: " << id; ++ subId = id; ++ } ++ ++ std::string getSubscriptionId() ++ { ++ return subId; ++ } ++ ++ std::optional ++ getSubscriptionId(const std::shared_ptr& connPtr) ++ { ++ if (sseConn != nullptr && connPtr == sseConn) ++ { ++ BMCWEB_LOG_DEBUG << __FUNCTION__ ++ << " conn matched, subId: " << subId; ++ return subId; ++ } ++ ++ return std::nullopt; ++ } ++ + private: ++ std::shared_ptr sseConn = nullptr; + uint64_t eventSeqNum; + std::string host; + std::string port; + std::string path; + std::string uriProto; + std::shared_ptr conn = nullptr; +- std::shared_ptr sseConn = nullptr; ++ std::string subId; + }; + + static constexpr const bool defaultEnabledState = true; +@@ -977,6 +1007,8 @@ class EventServiceManager + subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval); + subValue->updateRetryPolicy(); + ++ // Set Subscription ID for back trace ++ subValue->setSubscriptionId(id); + return id; + } + +@@ -1001,11 +1033,40 @@ class EventServiceManager + } + } + ++ void deleteSubscription(const std::shared_ptr& connPtr) ++ { ++ for (const auto& it : this->subscriptionsMap) ++ { ++ std::shared_ptr entry = it.second; ++ if (entry->subscriptionType == subscriptionTypeSSE) ++ { ++ std::optional id = ++ entry->getSubscriptionId(connPtr); ++ if (id) ++ { ++ deleteSubscription(*id); ++ return; ++ } ++ } ++ } ++ } ++ + size_t getNumberOfSubscriptions() + { + return subscriptionsMap.size(); + } + ++ size_t getNumberOfSSESubscriptions() const ++ { ++ auto count = std::count_if( ++ subscriptionsMap.begin(), subscriptionsMap.end(), ++ [this](const std::pair>& ++ entry) { ++ return (entry.second->subscriptionType == subscriptionTypeSSE); ++ }); ++ return static_cast(count); ++ } ++ + std::vector getAllIDs() + { + std::vector idList; +diff --git a/redfish-core/include/server_sent_events.hpp b/redfish-core/include/server_sent_events.hpp +deleted file mode 100644 +index 578fa19..0000000 +--- a/redfish-core/include/server_sent_events.hpp ++++ /dev/null +@@ -1,291 +0,0 @@ +- +-/* +-// Copyright (c) 2020 Intel Corporation +-// +-// Licensed under the Apache License, Version 2.0 (the "License"); +-// you may not use this file except in compliance with the License. +-// You may obtain a copy of the License at +-// +-// http://www.apache.org/licenses/LICENSE-2.0 +-// +-// Unless required by applicable law or agreed to in writing, software +-// distributed under the License is distributed on an "AS IS" BASIS, +-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-// See the License for the specific language governing permissions and +-// limitations under the License. +-*/ +-#pragma once +-#include "node.hpp" +- +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-namespace crow +-{ +- +-static constexpr uint8_t maxReqQueueSize = 50; +- +-enum class SseConnState +-{ +- startInit, +- initInProgress, +- initialized, +- initFailed, +- sendInProgress, +- sendFailed, +- idle, +- suspended, +- closed +-}; +- +-class ServerSentEvents : public std::enable_shared_from_this +-{ +- private: +- std::shared_ptr sseConn; +- std::queue> requestDataQueue; +- std::string outBuffer; +- SseConnState state; +- int retryCount; +- int maxRetryAttempts; +- +- void sendEvent(const std::string& id, const std::string& msg) +- { +- if (msg.empty()) +- { +- BMCWEB_LOG_DEBUG << "Empty data, bailing out."; +- return; +- } +- +- if (state == SseConnState::sendInProgress) +- { +- return; +- } +- state = SseConnState::sendInProgress; +- +- if (!id.empty()) +- { +- outBuffer += "id: "; +- outBuffer.append(id.begin(), id.end()); +- outBuffer += "\n"; +- } +- +- outBuffer += "data: "; +- for (char character : msg) +- { +- outBuffer += character; +- if (character == '\n') +- { +- outBuffer += "data: "; +- } +- } +- outBuffer += "\n\n"; +- +- doWrite(); +- } +- +- void doWrite() +- { +- if (outBuffer.empty()) +- { +- BMCWEB_LOG_DEBUG << "All data sent successfully."; +- // Send is successful, Lets remove data from queue +- // check for next request data in queue. +- requestDataQueue.pop(); +- state = SseConnState::idle; +- checkQueue(); +- return; +- } +- +- sseConn->async_write_some( +- boost::asio::buffer(outBuffer.data(), outBuffer.size()), +- [self(shared_from_this())]( +- boost::beast::error_code ec, +- [[maybe_unused]] const std::size_t& bytesTransferred) { +- self->outBuffer.erase(0, bytesTransferred); +- +- if (ec == boost::asio::error::eof) +- { +- // Send is successful, Lets remove data from queue +- // check for next request data in queue. +- self->requestDataQueue.pop(); +- self->state = SseConnState::idle; +- self->checkQueue(); +- return; +- } +- +- if (ec) +- { +- BMCWEB_LOG_ERROR << "async_write_some() failed: " +- << ec.message(); +- self->state = SseConnState::sendFailed; +- self->checkQueue(); +- return; +- } +- BMCWEB_LOG_DEBUG << "async_write_some() bytes transferred: " +- << bytesTransferred; +- +- self->doWrite(); +- }); +- } +- +- void startSSE() +- { +- if (state == SseConnState::initInProgress) +- { +- return; +- } +- state = SseConnState::initInProgress; +- +- BMCWEB_LOG_DEBUG << "starting SSE connection "; +- using BodyType = boost::beast::http::buffer_body; +- auto response = +- std::make_shared>( +- boost::beast::http::status::ok, 11); +- auto serializer = +- std::make_shared>( +- *response); +- +- // TODO: Add hostname in http header. +- response->set(boost::beast::http::field::server, "iBMC"); +- response->set(boost::beast::http::field::content_type, +- "text/event-stream"); +- response->body().data = nullptr; +- response->body().size = 0; +- response->body().more = true; +- +- boost::beast::http::async_write_header( +- *sseConn, *serializer, +- [this, response, +- serializer](const boost::beast::error_code& ec, +- [[maybe_unused]] const std::size_t& bytesTransferred) { +- if (ec) +- { +- BMCWEB_LOG_ERROR << "Error sending header" << ec; +- state = SseConnState::initFailed; +- checkQueue(); +- return; +- } +- +- BMCWEB_LOG_DEBUG << "startSSE Header sent."; +- state = SseConnState::initialized; +- checkQueue(); +- }); +- } +- +- void checkQueue(const bool newRecord = false) +- { +- if (requestDataQueue.empty()) +- { +- BMCWEB_LOG_DEBUG << "requestDataQueue is empty\n"; +- return; +- } +- +- if (retryCount >= maxRetryAttempts) +- { +- BMCWEB_LOG_ERROR << "Maximum number of retries is reached."; +- +- // Clear queue. +- while (!requestDataQueue.empty()) +- { +- requestDataQueue.pop(); +- } +- +- // TODO: Take 'DeliveryRetryPolicy' action. +- // For now, doing 'SuspendRetries' action. +- state = SseConnState::suspended; +- return; +- } +- +- if ((state == SseConnState::initFailed) || +- (state == SseConnState::sendFailed)) +- { +- if (newRecord) +- { +- // We are already running async wait and retry. +- // Since record is added to queue, it gets the +- // turn in FIFO. +- return; +- } +- +- retryCount++; +- // TODO: Perform async wait for retryTimeoutInterval before proceed. +- } +- else +- { +- // reset retry count. +- retryCount = 0; +- } +- +- switch (state) +- { +- case SseConnState::initInProgress: +- case SseConnState::sendInProgress: +- case SseConnState::suspended: +- case SseConnState::startInit: +- case SseConnState::closed: +- // do nothing +- break; +- case SseConnState::initFailed: +- { +- startSSE(); +- break; +- } +- case SseConnState::initialized: +- case SseConnState::idle: +- case SseConnState::sendFailed: +- { +- std::pair reqData = +- requestDataQueue.front(); +- sendEvent(std::to_string(reqData.first), reqData.second); +- break; +- } +- } +- +- return; +- } +- +- public: +- ServerSentEvents(const ServerSentEvents&) = delete; +- ServerSentEvents& operator=(const ServerSentEvents&) = delete; +- ServerSentEvents(ServerSentEvents&&) = delete; +- ServerSentEvents& operator=(ServerSentEvents&&) = delete; +- +- ServerSentEvents(const std::shared_ptr& adaptor) : +- sseConn(adaptor), state(SseConnState::startInit), retryCount(0), +- maxRetryAttempts(5) +- { +- startSSE(); +- } +- +- ~ServerSentEvents() = default; +- +- void sendData(const uint64_t& id, const std::string& data) +- { +- if (state == SseConnState::suspended) +- { +- return; +- } +- +- if (requestDataQueue.size() <= maxReqQueueSize) +- { +- requestDataQueue.push(std::pair(id, data)); +- checkQueue(true); +- } +- else +- { +- BMCWEB_LOG_ERROR << "Request queue is full. So ignoring data."; +- } +- } +-}; +- +-} // namespace crow +diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp +index be6f04d..1875ec9 100644 +--- a/redfish-core/lib/event_service.hpp ++++ b/redfish-core/lib/event_service.hpp +@@ -34,8 +34,6 @@ static constexpr const std::array supportedResourceTypes = { + "Task"}; + #endif + +-static constexpr const uint8_t maxNoOfSubscriptions = 20; +- + class EventService : public Node + { + public: +@@ -59,6 +57,8 @@ class EventService : public Node + {"@odata.type", "#EventService.v1_5_0.EventService"}, + {"Id", "EventService"}, + {"Name", "Event Service"}, ++ {"ServerSentEventUri", ++ "/redfish/v1/EventService/Subscriptions/SSE"}, + {"Subscriptions", + {{"@odata.id", "/redfish/v1/EventService/Subscriptions"}}}, + {"Actions", +diff --git a/src/webserver_main.cpp b/src/webserver_main.cpp +index 3b9631a..bf8e705 100644 +--- a/src/webserver_main.cpp ++++ b/src/webserver_main.cpp +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -79,6 +80,7 @@ int main(int /*argc*/, char** /*argv*/) + + #ifdef BMCWEB_ENABLE_REDFISH + crow::redfish::requestRoutes(app); ++ redfish::eventservice_sse::requestRoutes(app); + redfish::RedfishService redfish(app); + + // Create EventServiceManager instance and initialize Config +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch new file mode 100644 index 000000000..e83e05c96 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/0006-Add-EventService-SSE-filter-support.patch @@ -0,0 +1,243 @@ +From 693de7b901e7618585e0e8d848fc37fa0cccd18e Mon Sep 17 00:00:00 2001 +From: AppaRao Puli +Date: Wed, 17 Mar 2021 01:16:50 +0000 +Subject: [PATCH 6/6] Add EventService SSE filter support + +This commit implements the Event Service SSE stream +filters support. As per redfish specification: +The SSE streams have these formats: + - Metric report SSE stream + - Event message SSE stream + +To reduce the amount of data, service supports $filter +query parameter in SSE URI. +Below properties support as filter criteria: + - EventFormatType( Event & MetricReport) + - MessageId + - RegistryPrefix + - MetricReportDefinition + +For more details, refer Redfish specification section 13.5.2 + +Tested: + Created SSE stream with different filters and observed + desired events on SSE stream client(browser), some examples + - To get all Redfish events, + URI: /redfish/v1/EventService/Subscriptions/SSE?$filter=(EventFormatType%20eq%20Event) + - To get Redfish events with RegistryPrefix "OpenBMC" + URi: /redfish/v1/EventService/Subscriptions/SSE?$filter=(RegistryPrefix%20eq%20OpenBMC) + - To get only DC power of Events, + URI: /redfish/v1/EventService/Subscriptions/SSE?$filter=(EventFormatType%20eq%20Event)%20and%20(MessageId%20eq%20DCPowerOff) + +Signed-off-by: AppaRao Puli +Change-Id: I55c6f53bb5e57aa1f2d1601f1a16525a33b13bd2 +--- + include/eventservice_sse.hpp | 94 ++++++++++++++++++- + redfish-core/include/error_messages.hpp | 9 ++ + .../include/event_service_manager.hpp | 5 + + redfish-core/lib/event_service.hpp | 4 - + redfish-core/src/error_messages.cpp | 26 +++++ + 5 files changed, 130 insertions(+), 8 deletions(-) + +diff --git a/include/eventservice_sse.hpp b/include/eventservice_sse.hpp +index 6c98e6e..ff72c4d 100644 +--- a/include/eventservice_sse.hpp ++++ b/include/eventservice_sse.hpp +@@ -23,16 +23,102 @@ static bool createSubscription(std::shared_ptr& conn, + } + BMCWEB_LOG_DEBUG << "Request query param size: " << req.urlParams.size(); + ++ // EventService SSE supports only "$filter" query param. ++ if (req.urlParams.size() > 1) ++ { ++ messages::invalidQueryFilter(res); ++ res.end(); ++ return false; ++ } ++ std::string eventFormatType; ++ std::string queryFilters; ++ if (req.urlParams.size()) ++ { ++ boost::urls::url_view::params_type::iterator it = ++ req.urlParams.find("$filter"); ++ if (it == req.urlParams.end()) ++ { ++ messages::invalidQueryFilter(res); ++ res.end(); ++ return false; ++ } ++ queryFilters = it->value(); ++ } ++ else ++ { ++ eventFormatType = "Event"; ++ } ++ ++ std::vector msgIds; ++ std::vector regPrefixes; ++ std::vector mrdsArray; ++ if (!queryFilters.empty()) ++ { ++ // Reading from query params. ++ bool status = readSSEQueryParams(queryFilters, eventFormatType, msgIds, ++ regPrefixes, mrdsArray); ++ if (!status) ++ { ++ messages::invalidObject(res, queryFilters); ++ res.end(); ++ return false; ++ } ++ ++ // RegsitryPrefix and messageIds are mutuly exclusive as per redfish ++ // specification. ++ if (regPrefixes.size() && msgIds.size()) ++ { ++ messages::mutualExclusiveProperties(res, "RegistryPrefix", ++ "MessageId"); ++ res.end(); ++ return false; ++ } ++ ++ if (!eventFormatType.empty()) ++ { ++ if (std::find(supportedEvtFormatTypes.begin(), ++ supportedEvtFormatTypes.end(), ++ eventFormatType) == supportedEvtFormatTypes.end()) ++ { ++ messages::propertyValueNotInList(res, eventFormatType, ++ "EventFormatType"); ++ res.end(); ++ return false; ++ } ++ } ++ else ++ { ++ // If nothing specified, using default "Event" ++ eventFormatType = "Event"; ++ } ++ ++ if (!regPrefixes.empty()) ++ { ++ for (const std::string& it : regPrefixes) ++ { ++ if (std::find(supportedRegPrefixes.begin(), ++ supportedRegPrefixes.end(), ++ it) == supportedRegPrefixes.end()) ++ { ++ messages::propertyValueNotInList(res, it, "RegistryPrefix"); ++ res.end(); ++ return false; ++ } ++ } ++ } ++ } ++ + std::shared_ptr subValue = + std::make_shared(std::move(conn)); + + // GET on this URI means, Its SSE subscriptionType. +- subValue->subscriptionType = redfish::subscriptionTypeSSE; +- +- // TODO: parse $filter query params and fill config. ++ subValue->subscriptionType = subscriptionTypeSSE; + subValue->protocol = "Redfish"; + subValue->retryPolicy = "TerminateAfterRetries"; +- subValue->eventFormatType = "Event"; ++ subValue->eventFormatType = eventFormatType; ++ subValue->registryMsgIds = msgIds; ++ subValue->registryPrefixes = regPrefixes; ++ subValue->metricReportDefinitions = mrdsArray; + + std::string id = + redfish::EventServiceManager::getInstance().addSubscription(subValue, +diff --git a/redfish-core/include/error_messages.hpp b/redfish-core/include/error_messages.hpp +index 7dfdc80..922dae9 100644 +--- a/redfish-core/include/error_messages.hpp ++++ b/redfish-core/include/error_messages.hpp +@@ -959,6 +959,15 @@ nlohmann::json mutualExclusiveProperties(const std::string& arg1, + void mutualExclusiveProperties(crow::Response& res, const std::string& arg1, + const std::string& arg2); + ++/** ++ * @brief Formats InvalidQueryFilter message into JSON ++ * Message body: "The requested URL contains the invalid query filters" ++ * ++ * @returns Message InvalidQueryFilter formatted to JSON */ ++nlohmann::json invalidQueryFilter(); ++ ++void invalidQueryFilter(crow::Response& res); ++ + } // namespace messages + + } // namespace redfish +diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp +index e3eba86..8f120b1 100644 +--- a/redfish-core/include/event_service_manager.hpp ++++ b/redfish-core/include/event_service_manager.hpp +@@ -54,6 +54,11 @@ static constexpr const char* eventServiceFile = + static constexpr const uint8_t maxNoOfSubscriptions = 20; + static constexpr const uint8_t maxNoOfSSESubscriptions = 10; + ++static constexpr const std::array supportedEvtFormatTypes = { ++ eventFormatType, metricReportFormatType}; ++static constexpr const std::array supportedRegPrefixes = { ++ "Base", "OpenBMC", "Task"}; ++ + #ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES + static std::optional inotifyConn; + static constexpr const char* redfishEventLogDir = "/var/log"; +diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp +index 1875ec9..4d1ac9f 100644 +--- a/redfish-core/lib/event_service.hpp ++++ b/redfish-core/lib/event_service.hpp +@@ -19,10 +19,6 @@ + namespace redfish + { + +-static constexpr const std::array supportedEvtFormatTypes = { +- eventFormatType, metricReportFormatType}; +-static constexpr const std::array supportedRegPrefixes = { +- "Base", "OpenBMC", "Task"}; + static constexpr const std::array supportedRetryPolicies = { + "TerminateAfterRetries", "SuspendRetries", "RetryForever"}; + +diff --git a/redfish-core/src/error_messages.cpp b/redfish-core/src/error_messages.cpp +index cfbc9c2..3493132 100644 +--- a/redfish-core/src/error_messages.cpp ++++ b/redfish-core/src/error_messages.cpp +@@ -2147,6 +2147,32 @@ void mutualExclusiveProperties(crow::Response& res, const std::string& arg1, + addMessageToErrorJson(res.jsonValue, mutualExclusiveProperties(arg1, arg2)); + } + ++/** ++ * @internal ++ * @brief Formats InvalidQueryFilter into JSON ++ * ++ * See header file for more information ++ * @endinternal ++ */ ++nlohmann::json invalidQueryFilter() ++{ ++ return nlohmann::json{ ++ {"@odata.type", "#Message.v1_0_0.Message"}, ++ {"MessageId", "Base.1.5.0.InvalidQueryFilter"}, ++ {"Message", "The requested url contains the invalid query filter."}, ++ {"MessageArgs", nlohmann::json::array()}, ++ {"Severity", "Warning"}, ++ {"Resolution", ++ "Ensure the correct query filter is specified in requested url " ++ "and resubmit the request."}}; ++} ++ ++void invalidQueryFilter(crow::Response& res) ++{ ++ res.result(boost::beast::http::status::bad_request); ++ addMessageToErrorJson(res.jsonValue, invalidQueryFilter()); ++} ++ + } // namespace messages + + } // namespace redfish +-- +2.17.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README new file mode 100644 index 000000000..55340bb5c --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/eventservice/README @@ -0,0 +1,22 @@ +Eventservice specific patches: Temporary pulling down +the upstream patches. These will be remove as soon as +thee gets merged upstream. + +Upstream revision information: + - EventService : Fix retry handling for http-client + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/40731/18 + + - EventService: https client support + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/31735/38 + + - Move EventService init to later stage + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/41516 + + - Add Server-Sent-Events support + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/41258/5 + + - Add SSE style subscription support to eventservice + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/41319/5 + + - Add EventService SSE filter support + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/41349/2 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Sync_ReadingUnit_with_Redfish_Sensor_Schema.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Sync_ReadingUnit_with_Redfish_Sensor_Schema.patch new file mode 100644 index 000000000..e3b25da90 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Sync_ReadingUnit_with_Redfish_Sensor_Schema.patch @@ -0,0 +1,227 @@ +From 4b7e67d7200c0aba1b27478968d4f71449f1406a Mon Sep 17 00:00:00 2001 +From: "Wludzik, Jozef" +Date: Mon, 8 Mar 2021 14:35:54 +0000 +Subject: [PATCH] Sync ReadingUnit with Redfish Sensor Schema + +Actual attribute "ReadingUnits" does not match with Redfish Sensor +Schema. This change match "ReadingUnits" with Redfish Sensor Scheme +1.0.0 and add missing "ReadingType" attribute. This change affect all +users that depends on old units that does not match with Redfish +standard. Added toReadingType and toReadingUnit function that uses +values taken from Redfish Sensor Scheme 1.0.0. Latest version 1.2.0 of +Sensor scheme defines units same units. + +Tested: + - RedfishServiceValidator pass + +Change-Id: I0c8820eba7271022c427cd25dec321db36aa0176 +Signed-off-by: Wludzik, Jozef +Signed-off-by: Krzysztof Grobelny +--- + redfish-core/lib/power.hpp | 4 +- + redfish-core/lib/sensors.hpp | 107 +++++++++++++++++++++++++++++------ + redfish-core/lib/thermal.hpp | 4 +- + 3 files changed, 94 insertions(+), 21 deletions(-) + +diff --git a/redfish-core/lib/power.hpp b/redfish-core/lib/power.hpp +index 1c7a009..99c45ef 100644 +--- a/redfish-core/lib/power.hpp ++++ b/redfish-core/lib/power.hpp +@@ -153,7 +153,7 @@ class Power : public Node + res.jsonValue["PowerControl"] = nlohmann::json::array(); + + auto sensorAsyncResp = std::make_shared( +- res, chassisName, sensors::dbus::types.at(sensors::node::power), ++ res, chassisName, sensors::dbus::paths.at(sensors::node::power), + sensors::node::power); + + getChassisData(sensorAsyncResp); +@@ -336,7 +336,7 @@ class Power : public Node + + const std::string& chassisName = params[0]; + auto asyncResp = std::make_shared( +- res, chassisName, sensors::dbus::types.at(sensors::node::power), ++ res, chassisName, sensors::dbus::paths.at(sensors::node::power), + sensors::node::power); + + std::optional> voltageCollections; +diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp +index 35114bf..af030f0 100644 +--- a/redfish-core/lib/sensors.hpp ++++ b/redfish-core/lib/sensors.hpp +@@ -54,9 +54,10 @@ static constexpr std::string_view thermal = "Thermal"; + + namespace dbus + { ++ + static const boost::container::flat_map> +- types = {{node::power, ++ paths = {{node::power, + {"/xyz/openbmc_project/sensors/voltage", + "/xyz/openbmc_project/sensors/power"}}, + {node::sensors, +@@ -67,6 +68,88 @@ static const boost::container::flat_mapchassisSubNode == sensors::node::sensors) + { + sensorJson["@odata.type"] = "#Sensor.v1_0_0.Sensor"; +- if (sensorType == "power") +- { +- sensorJson["ReadingUnits"] = "Watts"; +- } +- else if (sensorType == "current") +- { +- sensorJson["ReadingUnits"] = "Amperes"; +- } +- else if (sensorType == "utilization") +- { +- sensorJson["ReadingUnits"] = "Percent"; +- } ++ sensorJson["ReadingType"] = sensors::toReadingType(sensorType); ++ sensorJson["ReadingUnits"] = sensors::toReadingUnits(sensorType); + } + else if (sensorType == "temperature") + { +@@ -2979,8 +3052,8 @@ inline void retrieveUriToDbusMap(const std::string& chassis, + const std::string& node, + SensorsAsyncResp::DataCompleteCb&& mapComplete) + { +- auto typesIt = sensors::dbus::types.find(node); +- if (typesIt == sensors::dbus::types.end()) ++ auto pathIt = sensors::dbus::paths.find(node); ++ if (pathIt == sensors::dbus::paths.end()) + { + BMCWEB_LOG_ERROR << "Wrong node provided : " << node; + mapComplete(boost::beast::http::status::bad_request, {}); +@@ -2995,7 +3068,7 @@ inline void retrieveUriToDbusMap(const std::string& chassis, + uriToDbus) { mapCompleteCb(status, uriToDbus); }; + + auto resp = std::make_shared( +- *respBuffer, chassis, typesIt->second, node, std::move(callback)); ++ *respBuffer, chassis, pathIt->second, node, std::move(callback)); + getChassisData(resp); + } + +@@ -3030,7 +3103,7 @@ class SensorCollection : public Node + const std::string& chassisId = params[0]; + std::shared_ptr asyncResp = + std::make_shared( +- res, chassisId, sensors::dbus::types.at(sensors::node::sensors), ++ res, chassisId, sensors::dbus::paths.at(sensors::node::sensors), + sensors::node::sensors); + + auto getChassisCb = +diff --git a/redfish-core/lib/thermal.hpp b/redfish-core/lib/thermal.hpp +index 8e01bee..00acdf9 100644 +--- a/redfish-core/lib/thermal.hpp ++++ b/redfish-core/lib/thermal.hpp +@@ -48,7 +48,7 @@ class Thermal : public Node + } + const std::string& chassisName = params[0]; + auto sensorAsyncResp = std::make_shared( +- res, chassisName, sensors::dbus::types.at(sensors::node::thermal), ++ res, chassisName, sensors::dbus::paths.at(sensors::node::thermal), + sensors::node::thermal); + + // TODO Need to get Chassis Redundancy information. +@@ -71,7 +71,7 @@ class Thermal : public Node + allCollections; + + auto asyncResp = std::make_shared( +- res, chassisName, sensors::dbus::types.at(sensors::node::thermal), ++ res, chassisName, sensors::dbus::paths.at(sensors::node::thermal), + sensors::node::thermal); + + if (!json_util::readJson(req, asyncResp->res, "Temperatures", +-- +2.25.1 + diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-POST-and-DELETE-in-MetricReportDefinitions.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0002-Add-POST-and-DELETE-in-MetricReportDefinitions.patch index fd7e8a445..aaf3f17c7 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,4 +1,4 @@ -From bc1635622e122f307fb3b8eb9bbd66ea576a8f0c Mon Sep 17 00:00:00 2001 +From 03c4ece83b58b954323111a1a7b2bf2b61402d7e Mon Sep 17 00:00:00 2001 From: "Wludzik, Jozef" Date: Mon, 18 May 2020 11:56:57 +0200 Subject: [PATCH 2/4] Add POST and DELETE in MetricReportDefinitions @@ -31,26 +31,39 @@ Signed-off-by: Krzysztof Grobelny Change-Id: I2fed96848594451e22fde686f8c066d7770cc65a --- 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(-) + .../include/utils/telemetry_utils.hpp | 2 + + redfish-core/include/utils/time_utils.hpp | 138 +++++++- + redfish-core/lib/metric_report_definition.hpp | 328 ++++++++++++++++++ + redfish-core/ut/time_utils_test.cpp | 63 ++++ + 5 files changed, 530 insertions(+), 2 deletions(-) create mode 100644 redfish-core/ut/time_utils_test.cpp diff --git a/meson.build b/meson.build -index 7a16e91..3d65b01 100644 +index 66a066b..22a8c4a 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', +@@ -345,6 +345,7 @@ srcfiles_unittest = ['include/ut/dbus_utility_test.cpp', 'redfish-core/ut/privileges_test.cpp', 'redfish-core/ut/lock_test.cpp', + 'redfish-core/ut/configfile_test.cpp', + 'redfish-core/ut/time_utils_test.cpp', 'http/ut/utility_test.cpp'] # Gather the Configuration data +diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp +index a3a8156..0a3af5f 100644 +--- a/redfish-core/include/utils/telemetry_utils.hpp ++++ b/redfish-core/include/utils/telemetry_utils.hpp +@@ -1,5 +1,7 @@ + #pragma once + ++#include "dbus_utility.hpp" ++ + namespace redfish + { + diff --git a/redfish-core/include/utils/time_utils.hpp b/redfish-core/include/utils/time_utils.hpp -index dd4ea75..e94801b 100644 +index 4a87ba0..9965d4d 100644 --- a/redfish-core/include/utils/time_utils.hpp +++ b/redfish-core/include/utils/time_utils.hpp @@ -1,7 +1,13 @@ @@ -212,7 +225,7 @@ index dd4ea75..e94801b 100644 /** * @brief Convert time value into duration format that is based on ISO 8601. * Example output: "P12DT1M5.5S" -@@ -36,8 +171,7 @@ std::string toDurationString(std::chrono::milliseconds ms) +@@ -36,8 +171,7 @@ inline std::string toDurationString(std::chrono::milliseconds ms) std::string fmt; fmt.reserve(sizeof("PxxxxxxxxxxxxDTxxHxxMxx.xxxxxxS")); @@ -654,5 +667,5 @@ index 0000000..70999ce + EXPECT_THAT(redfish::time_utils::toDurationString(ms), Eq(expected)); +} -- -2.16.6 +2.17.1 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 99af0ab86..03feec633 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,4 +1,4 @@ -From 462b2e814698e12a18b4956eb3c6421c34a3a4ba Mon Sep 17 00:00:00 2001 +From b6ccf463b4cfb8df4a904f06c5f4852029a96c50 Mon Sep 17 00:00:00 2001 From: "Wludzik, Jozef" Date: Tue, 15 Dec 2020 12:28:17 +0100 Subject: [PATCH 3/4] Add support for MetricDefinition scheme @@ -9,9 +9,6 @@ by Telemetry service. Metrics are grouped by following categories: temperature, power, voltage, current, fan_tach, fan_pwm, utilization. -Added generic function to fill ReadingUnits and ReadingType -in Sensor node. - Tested: - MetricDefinitions response is filled with existing sensors, it works with and without Telemetry service @@ -24,18 +21,15 @@ Signed-off-by: Wludzik, Jozef Signed-off-by: Krzysztof Grobelny Change-Id: I3086e1302e1ba2e5442d1367939fd5507a0cbc00 --- - 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(-) + redfish-core/include/redfish.hpp | 3 + + .../include/utils/telemetry_utils.hpp | 2 + + redfish-core/lib/metric_definition.hpp | 283 ++++++++++++++++++ + redfish-core/lib/telemetry_service.hpp | 2 + + 4 files changed, 290 insertions(+) create mode 100644 redfish-core/lib/metric_definition.hpp diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp -index e94c0f3..83f2300 100644 +index aad28ac..dfcb8cd 100644 --- a/redfish-core/include/redfish.hpp +++ b/redfish-core/include/redfish.hpp @@ -25,6 +25,7 @@ @@ -46,8 +40,8 @@ index e94c0f3..83f2300 100644 #include "../lib/metric_report.hpp" #include "../lib/metric_report_definition.hpp" #include "../lib/network_protocol.hpp" -@@ -213,6 +214,8 @@ class RedfishService - nodes.emplace_back(std::make_unique(app)); +@@ -215,6 +216,8 @@ class RedfishService + nodes.emplace_back(std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); + nodes.emplace_back(std::make_unique(app)); @@ -56,10 +50,10 @@ index e94c0f3..83f2300 100644 std::make_unique(app)); nodes.emplace_back(std::make_unique(app)); diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp -index a3a8156..c1b7639 100644 +index 0a3af5f..54b5133 100644 --- a/redfish-core/include/utils/telemetry_utils.hpp +++ b/redfish-core/include/utils/telemetry_utils.hpp -@@ -8,6 +8,8 @@ namespace telemetry +@@ -10,6 +10,8 @@ namespace telemetry constexpr const char* service = "xyz.openbmc_project.Telemetry"; constexpr const char* reportInterface = "xyz.openbmc_project.Telemetry.Report"; @@ -70,7 +64,7 @@ index a3a8156..c1b7639 100644 constexpr const char* metricReportUri = diff --git a/redfish-core/lib/metric_definition.hpp b/redfish-core/lib/metric_definition.hpp new file mode 100644 -index 0000000..4a40af5 +index 0000000..f9c7779 --- /dev/null +++ b/redfish-core/lib/metric_definition.hpp @@ -0,0 +1,283 @@ @@ -96,7 +90,7 @@ index 0000000..4a40af5 + crow::connections::systemBus->async_method_call( + [asyncResp, + callback = std::move(cb)](const boost::system::error_code ec, -+ std::vector& chassises) { ++ std::vector& chassis) { + if (ec) + { + messages::internalError(asyncResp->res); @@ -105,18 +99,18 @@ index 0000000..4a40af5 + } + + std::vector chassisNames; -+ chassisNames.reserve(chassises.size()); -+ for (const std::string& chassis : chassises) ++ chassisNames.reserve(chassis.size()); ++ for (const auto& path : chassis) + { -+ sdbusplus::message::object_path path(chassis); -+ std::string name = path.filename(); ++ sdbusplus::message::object_path dbusPath = path; ++ std::string name = dbusPath.filename(); + if (name.empty()) + { + messages::internalError(asyncResp->res); -+ BMCWEB_LOG_ERROR << "Invalid chassis: " << chassis; ++ BMCWEB_LOG_ERROR << "Invalid chassis: " << dbusPath.str; + return; + } -+ chassisNames.push_back(name); ++ chassisNames.emplace_back(std::move(name)); + } + + callback(chassisNames); @@ -131,14 +125,15 @@ index 0000000..4a40af5 +namespace telemetry +{ + -+class DefinitionCollectionReduce ++class MetricDefinitionCollectionReduce +{ + public: -+ DefinitionCollectionReduce(const std::shared_ptr& asyncResp) : ++ MetricDefinitionCollectionReduce( ++ const std::shared_ptr& asyncResp) : + asyncResp{asyncResp} + {} + -+ ~DefinitionCollectionReduce() ++ ~MetricDefinitionCollectionReduce() + { + if (asyncResp->res.result() != boost::beast::http::status::ok) + { @@ -179,15 +174,15 @@ index 0000000..4a40af5 + boost::container::flat_set dbusTypes; +}; + -+class DefinitionReduce ++class MetricDefinitionReduce +{ + public: -+ DefinitionReduce(const std::shared_ptr& asyncResp, -+ const std::string& id) : ++ MetricDefinitionReduce(const std::shared_ptr& asyncResp, ++ const std::string& id) : + id(id), + pattern{'/' + id + '/'}, asyncResp{asyncResp} + {} -+ ~DefinitionReduce() ++ ~MetricDefinitionReduce() + { + if (asyncResp->res.result() != boost::beast::http::status::ok) + { @@ -259,11 +254,10 @@ index 0000000..4a40af5 + res.jsonValue["Members@odata.count"] = 0; + + auto asyncResp = std::make_shared(res); -+ auto collectionReduce = -+ std::make_shared(asyncResp); + utils::getChassisNames( -+ [asyncResp, -+ collectionReduce](const std::vector& chassisNames) { ++ [asyncResp](const std::vector& chassisNames) { ++ auto collectionReduce = std::make_shared< ++ telemetry::MetricDefinitionCollectionReduce>(asyncResp); + for (const std::string& chassisName : chassisNames) + { + for (const auto& [sensorNode, _] : sensors::dbus::paths) @@ -322,11 +316,11 @@ index 0000000..4a40af5 + } + + const std::string& id = params[0]; -+ auto definitionGather = -+ std::make_shared(asyncResp, id); + utils::getChassisNames( -+ [asyncResp, -+ definitionGather](const std::vector& chassisNames) { ++ [asyncResp, id](const std::vector& chassisNames) { ++ auto definitionGather = ++ std::make_shared( ++ asyncResp, id); + for (const std::string& chassisName : chassisNames) + { + for (const auto& [sensorNode, dbusPaths] : @@ -357,164 +351,6 @@ index 0000000..4a40af5 +}; + +} // namespace redfish -diff --git a/redfish-core/lib/power.hpp b/redfish-core/lib/power.hpp -index 1c7a009..99c45ef 100644 ---- a/redfish-core/lib/power.hpp -+++ b/redfish-core/lib/power.hpp -@@ -153,7 +153,7 @@ class Power : public Node - res.jsonValue["PowerControl"] = nlohmann::json::array(); - - auto sensorAsyncResp = std::make_shared( -- res, chassisName, sensors::dbus::types.at(sensors::node::power), -+ res, chassisName, sensors::dbus::paths.at(sensors::node::power), - sensors::node::power); - - getChassisData(sensorAsyncResp); -@@ -336,7 +336,7 @@ class Power : public Node - - const std::string& chassisName = params[0]; - auto asyncResp = std::make_shared( -- res, chassisName, sensors::dbus::types.at(sensors::node::power), -+ res, chassisName, sensors::dbus::paths.at(sensors::node::power), - sensors::node::power); - - std::optional> voltageCollections; -diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp -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"; - - namespace dbus - { -+ - static const boost::container::flat_map> -- types = {{node::power, -+ paths = {{node::power, - {"/xyz/openbmc_project/sensors/voltage", - "/xyz/openbmc_project/sensors/power"}}, - {node::sensors, -@@ -67,6 +68,64 @@ static const boost::container::flat_maptypes) -+ for (const char* path : sensorsAsyncResp->types) - { - for (const std::string& sensor : *allSensors) - { -- if (boost::starts_with(sensor, type)) -+ if (boost::starts_with(sensor, path)) - { - activeSensors->emplace(sensor); - } -@@ -853,18 +912,8 @@ inline void objectInterfacesToJson( - if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors) - { - sensorJson["@odata.type"] = "#Sensor.v1_0_0.Sensor"; -- if (sensorType == "power") -- { -- sensorJson["ReadingUnits"] = "Watts"; -- } -- else if (sensorType == "current") -- { -- sensorJson["ReadingUnits"] = "Amperes"; -- } -- else if (sensorType == "utilization") -- { -- sensorJson["ReadingUnits"] = "Percent"; -- } -+ sensorJson["ReadingType"] = sensors::toReadingType(sensorType); -+ sensorJson["ReadingUnits"] = sensors::toReadingUnits(sensorType); - } - else if (sensorType == "temperature") - { -@@ -2976,8 +3025,8 @@ inline void retrieveUriToDbusMap(const std::string& chassis, - const std::string& node, - SensorsAsyncResp::DataCompleteCb&& mapComplete) - { -- auto typesIt = sensors::dbus::types.find(node); -- if (typesIt == sensors::dbus::types.end()) -+ auto typesIt = sensors::dbus::paths.find(node); -+ if (typesIt == sensors::dbus::paths.end()) - { - BMCWEB_LOG_ERROR << "Wrong node provided : " << node; - mapComplete(boost::beast::http::status::bad_request, {}); -@@ -3027,7 +3076,7 @@ class SensorCollection : public Node - const std::string& chassisId = params[0]; - std::shared_ptr asyncResp = - std::make_shared( -- res, chassisId, sensors::dbus::types.at(sensors::node::sensors), -+ res, chassisId, sensors::dbus::paths.at(sensors::node::sensors), - sensors::node::sensors); - - auto getChassisCb = diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp index 61ca891..a8c8b03 100644 --- a/redfish-core/lib/telemetry_service.hpp @@ -528,28 +364,6 @@ index 61ca891..a8c8b03 100644 res.jsonValue["MetricReportDefinitions"]["@odata.id"] = "/redfish/v1/TelemetryService/MetricReportDefinitions"; res.jsonValue["MetricReports"]["@odata.id"] = -diff --git a/redfish-core/lib/thermal.hpp b/redfish-core/lib/thermal.hpp -index 8e01bee..00acdf9 100644 ---- a/redfish-core/lib/thermal.hpp -+++ b/redfish-core/lib/thermal.hpp -@@ -48,7 +48,7 @@ class Thermal : public Node - } - const std::string& chassisName = params[0]; - auto sensorAsyncResp = std::make_shared( -- res, chassisName, sensors::dbus::types.at(sensors::node::thermal), -+ res, chassisName, sensors::dbus::paths.at(sensors::node::thermal), - sensors::node::thermal); - - // TODO Need to get Chassis Redundancy information. -@@ -71,7 +71,7 @@ class Thermal : public Node - allCollections; - - auto asyncResp = std::make_shared( -- res, chassisName, sensors::dbus::types.at(sensors::node::thermal), -+ res, chassisName, sensors::dbus::paths.at(sensors::node::thermal), - sensors::node::thermal); - - if (!json_util::readJson(req, asyncResp->res, "Temperatures", -- -2.16.6 +2.17.1 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 3df9fe5ae..f2ebce6e3 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,4 +1,4 @@ -From 14a429586fd8ccb82c72611fe3310860db2e643b Mon Sep 17 00:00:00 2001 +From efcd128a3d66fce33200fd4211ba5abf13a81375 Mon Sep 17 00:00:00 2001 From: "Wludzik, Jozef" Date: Tue, 15 Dec 2020 12:30:31 +0100 Subject: [PATCH 4/4] Sync Telmetry service with EventService @@ -17,12 +17,12 @@ Tested: Change-Id: I2fc1841a6c9259a8bff30b34bddc0d4aabd41912 Signed-off-by: Wludzik, Jozef --- - redfish-core/include/event_service_manager.hpp | 157 +++++++++---------------- - redfish-core/lib/metric_report.hpp | 28 +++-- - 2 files changed, 71 insertions(+), 114 deletions(-) + .../include/event_service_manager.hpp | 156 ++++++------------ + redfish-core/lib/metric_report.hpp | 28 ++-- + 2 files changed, 69 insertions(+), 115 deletions(-) diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp -index 3db9f0c..5c5a6c1 100644 +index 148c703..27e41e3 100644 --- a/redfish-core/include/event_service_manager.hpp +++ b/redfish-core/include/event_service_manager.hpp @@ -14,6 +14,7 @@ @@ -33,7 +33,7 @@ index 3db9f0c..5c5a6c1 100644 #include "node.hpp" #include "registries.hpp" #include "registries/base_message_registry.hpp" -@@ -510,48 +511,29 @@ class Subscription +@@ -512,47 +513,32 @@ class Subscription } #endif @@ -62,8 +62,8 @@ index 3db9f0c..5c5a6c1 100644 - nlohmann::json metricValuesArray = nlohmann::json::array(); - for (const auto& it : readings) -+ nlohmann::json json; -+ if (!telemetry::fillReport(json, id, var)) ++ nlohmann::json msg; ++ if (!telemetry::fillReport(msg, id, var)) { - metricValuesArray.push_back({}); - nlohmann::json& entry = metricValuesArray.back(); @@ -74,9 +74,12 @@ index 3db9f0c..5c5a6c1 100644 - {"MetricProperty", property}, - {"MetricValue", std::to_string(value)}, - {"Timestamp", crow::utility::getDateTime(timestamp)}}; ++ BMCWEB_LOG_ERROR << "Failed to fill the MetricReport for DBus " ++ "Report with id " ++ << id; + return; } -- + - nlohmann::json msg = { - {"@odata.id", "/redfish/v1/TelemetryService/MetricReports/" + id}, - {"@odata.type", "#MetricReport.v1_3_0.MetricReport"}, @@ -86,43 +89,31 @@ index 3db9f0c..5c5a6c1 100644 - {"MetricReportDefinition", {{"@odata.id", metricReportDef}}}, - {"MetricValues", metricValuesArray}}; - -- this->sendEvent(msg.dump()); -+ this->sendEvent(json.dump()); + this->sendEvent( + msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace)); } - - void updateRetryConfig(const uint32_t retryAttempts, -@@ -1342,56 +1324,72 @@ class EventServiceManager +@@ -1348,75 +1334,6 @@ class EventServiceManager } #endif - - void getMetricReading(const std::string& service, - const std::string& objPath, const std::string& intf) -+ void unregisterMetricReportSignal() - { +- { - std::size_t found = objPath.find_last_of('/'); - if (found == std::string::npos) -+ if (matchTelemetryMonitor) - { +- { - BMCWEB_LOG_DEBUG << "Invalid objPath received"; - return; -+ BMCWEB_LOG_DEBUG << "Metrics report signal - Unregister"; -+ matchTelemetryMonitor.reset(); -+ matchTelemetryMonitor = nullptr; - } -+ } - +- } +- - std::string idStr = objPath.substr(found + 1); - if (idStr.empty()) -+ void registerMetricReportSignal() -+ { -+ if (!serviceEnabled || matchTelemetryMonitor) - { +- { - BMCWEB_LOG_DEBUG << "Invalid ID in objPath"; -+ BMCWEB_LOG_DEBUG << "Not registering metric report signal."; - return; - } - +- return; +- } +- - crow::connections::systemBus->async_method_call( - [idStr{std::move(idStr)}]( - const boost::system::error_code ec, @@ -130,120 +121,113 @@ index 3db9f0c..5c5a6c1 100644 - std::string, std::variant>& - resp) { - if (ec) -+ BMCWEB_LOG_DEBUG << "Metrics report signal - Register"; -+ std::string matchStr = "type='signal',member='PropertiesChanged'," -+ "interface='org.freedesktop.DBus.Properties'," -+ "path_namespace=/xyz/openbmc_project/Telemetry/" -+ "Reports/TelemetryService," -+ "arg0=xyz.openbmc_project.Telemetry.Report"; -+ -+ matchTelemetryMonitor = std::make_shared( -+ *crow::connections::systemBus, matchStr, -+ [this](sdbusplus::message::message& msg) { -+ if (msg.is_method_error()) - { +- { - BMCWEB_LOG_DEBUG - << "D-Bus call failed to GetAll metric readings."; -+ BMCWEB_LOG_ERROR << "TelemetryMonitor Signal error"; - return; - } - +- return; +- } +- - const int32_t* timestampPtr = - std::get_if(&resp["Timestamp"]); - if (!timestampPtr) -+ std::string intf; -+ std::vector>> -+ props; -+ std::vector invalidProp; -+ -+ msg.read(intf, props, invalidProp); -+ if (intf != "xyz.openbmc_project.Telemetry.Report") - { +- { - BMCWEB_LOG_DEBUG << "Failed to Get timestamp."; - return; - } - +- return; +- } +- - ReadingsObjType* readingsPtr = - std::get_if(&resp["Readings"]); - if (!readingsPtr) -+ const std::variant* varPtr = -+ nullptr; -+ for (const auto& [key, var] : props) -+ { -+ if (key == "Readings") -+ { -+ varPtr = &var; -+ break; -+ } -+ } -+ if (!varPtr) - { +- { - BMCWEB_LOG_DEBUG << "Failed to Get Readings property."; - return; - } - +- return; +- } +- - if (!readingsPtr->size()) -+ 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 +1399,9 @@ class EventServiceManager - std::shared_ptr entry = it.second; - if (entry->eventFormatType == metricReportFormatType) - { +- return; +- } +- +- for (const auto& it : +- EventServiceManager::getInstance().subscriptionsMap) +- { +- std::shared_ptr entry = it.second; +- if (entry->eventFormatType == metricReportFormatType) +- { - entry->filterAndSendReports( - idStr, crow::utility::getDateTime(*timestampPtr), - *readingsPtr); -+ entry->filterAndSendReports(id, *varPtr); - } - } +- } +- } - }, - service, objPath, "org.freedesktop.DBus.Properties", "GetAll", - intf); - } - -- void unregisterMetricReportSignal() -- { -- if (matchTelemetryMonitor) -- { -- BMCWEB_LOG_DEBUG << "Metrics report signal - Unregister"; -- matchTelemetryMonitor.reset(); -- matchTelemetryMonitor = nullptr; -- } -- } -- -- void registerMetricReportSignal() -- { -- if (!serviceEnabled || matchTelemetryMonitor) -- { -- BMCWEB_LOG_DEBUG << "Not registering metric report signal."; -- return; -- } -- -- BMCWEB_LOG_DEBUG << "Metrics report signal - Register"; + void unregisterMetricReportSignal() + { + if (matchTelemetryMonitor) +@@ -1436,9 +1353,11 @@ class EventServiceManager + } + + BMCWEB_LOG_DEBUG << "Metrics report signal - Register"; - std::string matchStr( - "type='signal',member='ReportUpdate', " - "interface='xyz.openbmc_project.MonitoringService.Report'"); -- -- matchTelemetryMonitor = std::make_shared( -- *crow::connections::systemBus, matchStr, -- [this](sdbusplus::message::message& msg) { -- if (msg.is_method_error()) -- { -- BMCWEB_LOG_ERROR << "TelemetryMonitor Signal error"; -- return; -- } -- ++ std::string matchStr = "type='signal',member='PropertiesChanged'," ++ "interface='org.freedesktop.DBus.Properties'," ++ "path_namespace=/xyz/openbmc_project/Telemetry/" ++ "Reports/TelemetryService," ++ "arg0=xyz.openbmc_project.Telemetry.Report"; + + matchTelemetryMonitor = std::make_shared( + *crow::connections::systemBus, matchStr, +@@ -1449,10 +1368,43 @@ class EventServiceManager + return; + } + - std::string service = msg.get_sender(); - std::string objPath = msg.get_path(); - std::string intf = msg.get_interface(); - getMetricReading(service, objPath, intf); ++ sdbusplus::message::object_path path(msg.get_path()); ++ std::string id = path.filename(); ++ if (id.empty()) ++ { ++ BMCWEB_LOG_ERROR << "Failed to get Id from path"; ++ return; ++ } ++ ++ std::string intf; ++ std::vector>> ++ props; ++ std::vector invalidProps; ++ msg.read(intf, props, invalidProps); ++ ++ auto found = ++ std::find_if(props.begin(), props.end(), [](const auto& x) { ++ return x.first == "Readings"; ++ }); ++ if (found == props.end()) ++ { ++ BMCWEB_LOG_ERROR ++ << "Failed to get Readings from Report properties"; ++ return; ++ } ++ ++ std::variant& readings = ++ found->second; ++ for (const auto& it : ++ EventServiceManager::getInstance().subscriptionsMap) ++ { ++ Subscription& entry = *it.second.get(); ++ if (entry.eventFormatType == metricReportFormatType) ++ { ++ entry.filterAndSendReports(id, readings); ++ } ++ } }); } @@ -307,5 +291,5 @@ index 9caf4a3..e79a41c 100644 telemetry::service, reportPath, "org.freedesktop.DBus.Properties", "Get", -- -2.16.6 +2.17.1 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/README index 35c6e90bc..cfb47a49b 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 @@ -3,11 +3,11 @@ Until change is integrated they will be manually merged here to enable feature i Current revisions: - Add POST and DELETE in MetricReportDefinitions - https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/32536/58 + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/32536/63 - Add support for MetricDefinition scheme - https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/33363/54 + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/33363/60 - Sync Telmetry service with EventService - https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/38798/21 + https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/38798/26 diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend index db8a7a90f..d42618b78 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend @@ -1,5 +1,5 @@ SRC_URI = "git://github.com/openbmc/bmcweb.git" -SRCREV = "2b3da45876aac57a36d3093379a992d699e7e396" +SRCREV = "8e6c099ac8d4b4b3163a26b58fa0f9abb987979a" DEPENDS += "boost-url" RDEPENDS_${PN} += "phosphor-nslcd-authority-cert-config" @@ -15,7 +15,6 @@ GROUPADD_PARAM_${PN} = "web; redfish " SRC_URI += "file://0001-Firmware-update-configuration-changes.patch \ file://0002-Use-chip-id-based-UUID-for-Service-Root.patch \ file://0004-bmcweb-handle-device-or-resource-busy-exception.patch \ - file://0005-EventService-https-client-support.patch \ file://0006-Define-Redfish-interface-Registries-Bios.patch \ file://0007-BIOS-config-Add-support-for-PATCH-operation.patch \ file://0008-Add-support-to-ResetBios-action.patch \ @@ -28,8 +27,18 @@ SRC_URI += "file://0001-Firmware-update-configuration-changes.patch \ SRC_URI += "file://0037-Add-state-sensor-messages-to-the-registry.patch \ " +# EventService: Temporary pulled to downstream. See eventservice\README for details +SRC_URI += "file://eventservice/0001-EventService-Fix-retry-handling-for-http-client.patch \ + file://eventservice/0002-EventService-https-client-support.patch \ + file://eventservice/0003-Move-EventService-init-to-later-stage.patch \ + file://eventservice/0004-Add-Server-Sent-Events-support.patch \ + file://eventservice/0005-Add-SSE-style-subscription-support-to-eventservice.patch \ + file://eventservice/0006-Add-EventService-SSE-filter-support.patch \ +" + # Temporary downstream mirror of upstream patches, see telemetry\README for details -SRC_URI += "file://telemetry/0002-Add-POST-and-DELETE-in-MetricReportDefinitions.patch \ +SRC_URI += "file://telemetry/0001-Sync_ReadingUnit_with_Redfish_Sensor_Schema.patch \ + file://telemetry/0002-Add-POST-and-DELETE-in-MetricReportDefinitions.patch \ file://telemetry/0003-Add-support-for-MetricDefinition-scheme.patch \ file://telemetry/0004-Sync-Telmetry-service-with-EventService.patch \ " @@ -39,6 +48,9 @@ SRC_URI += "file://0001-Add-ConnectedVia-property-to-virtual-media-item-temp.pat file://0003-Set-Inserted-redfish-property-for-not-inserted-resou.patch \ " +SRC_URI += "file://0038-Revert-Disable-nbd-proxy-from-the-build.patch \ +" + # Temporary fix: Move it to service file do_install_append() { install -d ${D}/var/lib/bmcweb diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/peci/peci-pcie_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/peci/peci-pcie_%.bbappend index 6744b172c..48f72ab36 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/peci/peci-pcie_%.bbappend +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/peci/peci-pcie_%.bbappend @@ -1,3 +1,3 @@ SRC_URI = "git://github.com/openbmc/peci-pcie" -SRCREV = "0b79f3e485554957a4b24d5f0cefc5bc577ad301" +SRCREV = "d570dfd4f3a7c38b029f74a8194eeb3911b5f6a5" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libmctp-intel_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libmctp-intel_git.bb index de34f59a9..ae498beb8 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libmctp-intel_git.bb +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libmctp-intel_git.bb @@ -2,7 +2,7 @@ SUMMARY = "libmctp_intel" DESCRIPTION = "Implementation of MCTP(DMTF DSP0236)" SRC_URI = "git://github.com/Intel-BMC/libmctp.git;protocol=ssh" -SRCREV = "f1532e4b19ff198d20782adf5fb9dcce36eea995" +SRCREV = "dca36d524d22d6efab3f7bfd6adda69dbebb1bf3" S = "${WORKDIR}/git" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libpldm-intel_git.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libpldm-intel_git.bb index 1b4be8136..0ed7add07 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libpldm-intel_git.bb +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/libpldm-intel_git.bb @@ -2,7 +2,7 @@ SUMMARY = "libpldm_intel" DESCRIPTION = "Provides encode/decode APIs for PLDM specifications" SRC_URI = "git://github.com/Intel-BMC/pmci.git;protocol=ssh" -SRCREV = "86d4e9d3f2214c825ed76b38865abc47d2c7a11f" +SRCREV = "07adfb357cdb679bf9bbcf2eaff7406cfb5fd52b" S = "${WORKDIR}/git/libpldm_intel" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-emulator.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-emulator.bb index 02921bae6..6a418c330 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-emulator.bb +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-emulator.bb @@ -5,7 +5,7 @@ LICENSE = "Apache-2.0" LIC_FILES_CHKSUM = "file://LICENSE;md5=bcd9ada3a943f58551867d72893cc9ab" SRC_URI = "git://github.com/Intel-BMC/pmci.git;protocol=ssh" -SRCREV = "86d4e9d3f2214c825ed76b38865abc47d2c7a11f" +SRCREV = "07adfb357cdb679bf9bbcf2eaff7406cfb5fd52b" S = "${WORKDIR}/git/mctp_emulator" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-wrapper.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-wrapper.bb index 4fdd3b0d6..3e8119c66 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-wrapper.bb +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctp-wrapper.bb @@ -5,7 +5,7 @@ LICENSE = "Apache-2.0" LIC_FILES_CHKSUM = "file://LICENSE;md5=bcd9ada3a943f58551867d72893cc9ab" SRC_URI = "git://github.com/Intel-BMC/pmci.git;protocol=ssh" -SRCREV = "86d4e9d3f2214c825ed76b38865abc47d2c7a11f" +SRCREV = "07adfb357cdb679bf9bbcf2eaff7406cfb5fd52b" S = "${WORKDIR}/git/mctp_wrapper" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpd.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpd.bb index a48a45513..c152eef70 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpd.bb +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpd.bb @@ -5,7 +5,7 @@ LICENSE = "Apache-2.0" LIC_FILES_CHKSUM = "file://${PN}/LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" SRC_URI = "git://github.com/Intel-BMC/pmci.git;protocol=ssh" -SRCREV = "86d4e9d3f2214c825ed76b38865abc47d2c7a11f" +SRCREV = "07adfb357cdb679bf9bbcf2eaff7406cfb5fd52b" S = "${WORKDIR}/git" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpwplus.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpwplus.bb index 837560d78..1fbeefced 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpwplus.bb +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/mctpwplus.bb @@ -5,7 +5,7 @@ LICENSE = "Apache-2.0" LIC_FILES_CHKSUM = "file://LICENSE;md5=615045c30a05cde5c0e924854d43c327" SRC_URI = "git://github.com/Intel-BMC/pmci.git;protocol=ssh" -SRCREV = "86d4e9d3f2214c825ed76b38865abc47d2c7a11f" +SRCREV = "07adfb357cdb679bf9bbcf2eaff7406cfb5fd52b" S = "${WORKDIR}/git/mctpwplus" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/nvmemi-daemon.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/nvmemi-daemon.bb index 510535363..d53ac712e 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/nvmemi-daemon.bb +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/nvmemi-daemon.bb @@ -4,14 +4,14 @@ DESCRIPTION = "Implementation of NVMe MI daemon" LICENSE = "Apache-2.0" LIC_FILES_CHKSUM = "file://LICENSE;md5=86d3f3a95c324c9479bd8986968f4327" -SRC_URI = "git://github.com/Intel-BMC/nvme-mi.git;protocol=ssh;nobranch=1" -SRCREV = "23fad1d6ffd8ccd16b1369b96734a9701fc2802a" +SRC_URI = "git://github.com/Intel-BMC/nvme-mi.git;protocol=ssh" +SRCREV = "63e1cf7f8b950d37fdb035745bf740ec87ede6ae" S = "${WORKDIR}/git" PV = "1.0+git${SRCPV}" inherit meson systemd SYSTEMD_SERVICE_${PN} += "xyz.openbmc_project.nvme-mi.service" -DEPENDS = "boost sdbusplus systemd phosphor-logging mctpwplus googletest" +DEPENDS = "boost sdbusplus systemd phosphor-logging mctpwplus googletest nlohmann-json" EXTRA_OEMESON = "-Dyocto_dep='enabled' -Dtests='enabled'" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pldmd.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pldmd.bb index a561bfa23..08571570b 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pldmd.bb +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pldmd.bb @@ -5,7 +5,7 @@ LICENSE = "Apache-2.0" LIC_FILES_CHKSUM = "file://LICENSE;md5=86d3f3a95c324c9479bd8986968f4327" SRC_URI += "git://github.com/Intel-BMC/pmci.git;protocol=ssh" -SRCREV = "86d4e9d3f2214c825ed76b38865abc47d2c7a11f" +SRCREV = "07adfb357cdb679bf9bbcf2eaff7406cfb5fd52b" S = "${WORKDIR}/git/pldmd" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pmci-launcher.bb b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pmci-launcher.bb index 02e4c5ad8..8b4ee807a 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pmci-launcher.bb +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/pmci/pmci-launcher.bb @@ -5,7 +5,7 @@ LICENSE = "Apache-2.0" LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" SRC_URI = "git://github.com/Intel-BMC/pmci.git;protocol=ssh" -SRCREV = "86d4e9d3f2214c825ed76b38865abc47d2c7a11f" +SRCREV = "07adfb357cdb679bf9bbcf2eaff7406cfb5fd52b" S = "${WORKDIR}/git/pmci_launcher" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend index bb1268e0a..d0370ba00 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/sensors/dbus-sensors_%.bbappend @@ -1,7 +1,7 @@ FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" PROJECT_SRC_DIR := "${THISDIR}/${PN}" -SRCREV = "d05867c0d32065d36b13bd452f7aff9dcb20ac2f" +SRCREV = "0947d7c1cb9dc5ae4bc740d18aff059cb896c309" #SRC_URI = "git://github.com/openbmc/dbus-sensors.git" SRC_URI += "\ diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/telemetry/telemetry_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/telemetry/telemetry_%.bbappend index 1c8de604c..dcbe656b1 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/telemetry/telemetry_%.bbappend +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/telemetry/telemetry_%.bbappend @@ -1,5 +1,5 @@ SRC_URI = "git://github.com/openbmc/telemetry.git" -SRCREV = "9f9ff90a39219ff3a2f1179f74fc9a6dc857e5ab" +SRCREV = "d7cebdd37fade28b0efd34bb9d641135bff758a0" EXTRA_OEMESON += " -Dmax-reports=5" EXTRA_OEMESON += " -Dmax-reading-parameters=200" diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue_%.bbappend b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue_%.bbappend index 87ad21d71..bdb411121 100644 --- a/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue_%.bbappend +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/webui/webui-vue_%.bbappend @@ -1,6 +1,6 @@ # Enable downstream autobump SRC_URI = "git://github.com/openbmc/webui-vue.git" -SRCREV = "b0fadef1f96df99ff5eb0637527f04bc793c8d6e" +SRCREV = "1915d8c4992c1a4165e8ae108e4d799b3b4ce86a" do_compile_prepend() { cp -vf ${S}/.env.intel ${S}/.env diff --git a/meta-openbmc-mods/meta-common/recipes-x86/chassis/x86-power-control_%.bbappend b/meta-openbmc-mods/meta-common/recipes-x86/chassis/x86-power-control_%.bbappend index 6681e7075..26c96db24 100755 --- a/meta-openbmc-mods/meta-common/recipes-x86/chassis/x86-power-control_%.bbappend +++ b/meta-openbmc-mods/meta-common/recipes-x86/chassis/x86-power-control_%.bbappend @@ -1,6 +1,6 @@ # Enable downstream autobump SRC_URI = "git://github.com/openbmc/x86-power-control.git;protocol=ssh" -SRCREV = "22e0bec01e283819c13310640918c89f4a74fb56" +SRCREV = "92caa4c639903ef076e2f09e985291781edd927d" FILESEXTRAPATHS_append := "${THISDIR}/${PN}:" diff --git a/meta-openbmc-mods/meta-wht/conf/bblayers.conf.sample b/meta-openbmc-mods/meta-wht/conf/bblayers.conf.sample index f76660ab9..546cd7b31 100644 --- a/meta-openbmc-mods/meta-wht/conf/bblayers.conf.sample +++ b/meta-openbmc-mods/meta-wht/conf/bblayers.conf.sample @@ -1,6 +1,6 @@ # LAYER_CONF_VERSION is increased each time build/conf/bblayers.conf # changes incompatibly -LCONF_VERSION = "13" +LCONF_VERSION = "14" BBPATH = "${TOPDIR}" BBFILES ?= "" diff --git a/meta-openbmc-mods/meta-wolfpass/conf/bblayers.conf.sample b/meta-openbmc-mods/meta-wolfpass/conf/bblayers.conf.sample index 4b9445344..925cdbd97 100644 --- a/meta-openbmc-mods/meta-wolfpass/conf/bblayers.conf.sample +++ b/meta-openbmc-mods/meta-wolfpass/conf/bblayers.conf.sample @@ -1,6 +1,6 @@ # LAYER_CONF_VERSION is increased each time build/conf/bblayers.conf # changes incompatibly -LCONF_VERSION = "13" +LCONF_VERSION = "14" BBPATH = "${TOPDIR}" BBFILES ?= "" -- cgit v1.2.3