Refactor: break up event_service_manager.hpp
'class Subscription' can be extracted into a separate file.
No changes have been made to the code.
Tested:
- Using Redfish Event Listener, test subscriptions and eventing.
- Redfish Service Validator passes
Change-Id: Id0076ef617e36cbb85629a386a4511a4fdb5e4da
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/redfish-core/src/subscription.cpp b/redfish-core/src/subscription.cpp
new file mode 100644
index 0000000..f74435d
--- /dev/null
+++ b/redfish-core/src/subscription.cpp
@@ -0,0 +1,286 @@
+/*
+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.
+*/
+#include "subscription.hpp"
+
+#include "event_logs_object_type.hpp"
+#include "event_matches_filter.hpp"
+#include "event_service_manager.hpp"
+#include "event_service_store.hpp"
+#include "filter_expr_executor.hpp"
+#include "generated/enums/log_entry.hpp"
+#include "http_client.hpp"
+#include "http_response.hpp"
+#include "logging.hpp"
+#include "metric_report.hpp"
+#include "server_sent_event.hpp"
+#include "ssl_key_handler.hpp"
+#include "utils/time_utils.hpp"
+
+#include <boost/asio/io_context.hpp>
+#include <boost/beast/http/verb.hpp>
+#include <boost/system/errc.hpp>
+#include <boost/url/format.hpp>
+#include <boost/url/url_view_base.hpp>
+#include <nlohmann/json.hpp>
+
+#include <algorithm>
+#include <cstdint>
+#include <cstdlib>
+#include <ctime>
+#include <format>
+#include <functional>
+#include <memory>
+#include <span>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace redfish
+{
+
+Subscription::Subscription(
+ std::shared_ptr<persistent_data::UserSubscription> userSubIn,
+ const boost::urls::url_view_base& url, boost::asio::io_context& ioc) :
+ userSub{std::move(userSubIn)},
+ policy(std::make_shared<crow::ConnectionPolicy>())
+{
+ userSub->destinationUrl = url;
+ client.emplace(ioc, policy);
+ // Subscription constructor
+ policy->invalidResp = retryRespHandler;
+}
+
+Subscription::Subscription(crow::sse_socket::Connection& connIn) :
+ userSub{std::make_shared<persistent_data::UserSubscription>()},
+ sseConn(&connIn)
+{}
+
+// callback for subscription sendData
+void Subscription::resHandler(const std::shared_ptr<Subscription>& /*unused*/,
+ const crow::Response& res)
+{
+ BMCWEB_LOG_DEBUG("Response handled with return code: {}", res.resultInt());
+
+ if (!client)
+ {
+ BMCWEB_LOG_ERROR(
+ "Http client wasn't filled but http client callback was called.");
+ return;
+ }
+
+ if (userSub->retryPolicy != "TerminateAfterRetries")
+ {
+ return;
+ }
+ if (client->isTerminated())
+ {
+ if (deleter)
+ {
+ BMCWEB_LOG_INFO("Subscription {} is deleted after MaxRetryAttempts",
+ userSub->id);
+ deleter();
+ }
+ }
+}
+
+bool Subscription::sendEventToSubscriber(std::string&& msg)
+{
+ persistent_data::EventServiceConfig eventServiceConfig =
+ persistent_data::EventServiceStore::getInstance()
+ .getEventServiceConfig();
+ if (!eventServiceConfig.enabled)
+ {
+ return false;
+ }
+
+ if (client)
+ {
+ client->sendDataWithCallback(
+ std::move(msg), userSub->destinationUrl,
+ static_cast<ensuressl::VerifyCertificate>(
+ userSub->verifyCertificate),
+ userSub->httpHeaders, boost::beast::http::verb::post,
+ std::bind_front(&Subscription::resHandler, this,
+ shared_from_this()));
+ return true;
+ }
+
+ if (sseConn != nullptr)
+ {
+ eventSeqNum++;
+ sseConn->sendSseEvent(std::to_string(eventSeqNum), msg);
+ }
+ return true;
+}
+
+bool Subscription::sendTestEventLog()
+{
+ nlohmann::json::array_t logEntryArray;
+ nlohmann::json& logEntryJson = logEntryArray.emplace_back();
+
+ logEntryJson["EventId"] = "TestID";
+ logEntryJson["Severity"] = log_entry::EventSeverity::OK;
+ logEntryJson["Message"] = "Generated test event";
+ logEntryJson["MessageId"] = "OpenBMC.0.2.TestEventLog";
+ // MemberId is 0 : since we are sending one event record.
+ logEntryJson["MemberId"] = "0";
+ logEntryJson["MessageArgs"] = nlohmann::json::array();
+ logEntryJson["EventTimestamp"] =
+ redfish::time_utils::getDateTimeOffsetNow().first;
+ logEntryJson["Context"] = userSub->customText;
+
+ nlohmann::json msg;
+ msg["@odata.type"] = "#Event.v1_4_0.Event";
+ msg["Id"] = std::to_string(eventSeqNum);
+ msg["Name"] = "Event Log";
+ msg["Events"] = logEntryArray;
+
+ std::string strMsg =
+ msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
+ return sendEventToSubscriber(std::move(strMsg));
+}
+
+void Subscription::filterAndSendEventLogs(
+ const std::vector<EventLogObjectsType>& eventRecords)
+{
+ nlohmann::json::array_t logEntryArray;
+ for (const EventLogObjectsType& logEntry : eventRecords)
+ {
+ std::vector<std::string_view> messageArgsView(
+ logEntry.messageArgs.begin(), logEntry.messageArgs.end());
+
+ nlohmann::json::object_t bmcLogEntry;
+ if (event_log::formatEventLogEntry(
+ logEntry.id, logEntry.messageId, messageArgsView,
+ logEntry.timestamp, userSub->customText, bmcLogEntry) != 0)
+ {
+ BMCWEB_LOG_DEBUG("Read eventLog entry failed");
+ continue;
+ }
+
+ if (!eventMatchesFilter(*userSub, bmcLogEntry, ""))
+ {
+ BMCWEB_LOG_DEBUG("Event {} did not match the filter",
+ nlohmann::json(bmcLogEntry).dump());
+ continue;
+ }
+
+ if (filter)
+ {
+ if (!memberMatches(bmcLogEntry, *filter))
+ {
+ BMCWEB_LOG_DEBUG("Filter didn't match");
+ continue;
+ }
+ }
+
+ logEntryArray.emplace_back(std::move(bmcLogEntry));
+ }
+
+ if (logEntryArray.empty())
+ {
+ BMCWEB_LOG_DEBUG("No log entries available to be transferred.");
+ return;
+ }
+
+ nlohmann::json msg;
+ msg["@odata.type"] = "#Event.v1_4_0.Event";
+ msg["Id"] = std::to_string(eventSeqNum);
+ msg["Name"] = "Event Log";
+ msg["Events"] = std::move(logEntryArray);
+ std::string strMsg =
+ msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
+ sendEventToSubscriber(std::move(strMsg));
+ eventSeqNum++;
+}
+
+void Subscription::filterAndSendReports(const std::string& reportId,
+ const telemetry::TimestampReadings& var)
+{
+ boost::urls::url mrdUri = boost::urls::format(
+ "/redfish/v1/TelemetryService/MetricReportDefinitions/{}", reportId);
+
+ // Empty list means no filter. Send everything.
+ if (!userSub->metricReportDefinitions.empty())
+ {
+ if (std::ranges::find(userSub->metricReportDefinitions,
+ mrdUri.buffer()) ==
+ userSub->metricReportDefinitions.end())
+ {
+ return;
+ }
+ }
+
+ nlohmann::json msg;
+ if (!telemetry::fillReport(msg, reportId, var))
+ {
+ BMCWEB_LOG_ERROR("Failed to fill the MetricReport for DBus "
+ "Report with id {}",
+ reportId);
+ return;
+ }
+
+ // Context is set by user during Event subscription and it must be
+ // set for MetricReport response.
+ if (!userSub->customText.empty())
+ {
+ msg["Context"] = userSub->customText;
+ }
+
+ std::string strMsg =
+ msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
+ sendEventToSubscriber(std::move(strMsg));
+}
+
+void Subscription::updateRetryConfig(uint32_t retryAttempts,
+ uint32_t retryTimeoutInterval)
+{
+ if (policy == nullptr)
+ {
+ BMCWEB_LOG_DEBUG("Retry policy was nullptr, ignoring set");
+ return;
+ }
+ policy->maxRetryAttempts = retryAttempts;
+ policy->retryIntervalSecs = std::chrono::seconds(retryTimeoutInterval);
+}
+
+uint64_t Subscription::getEventSeqNum() const
+{
+ return eventSeqNum;
+}
+
+bool Subscription::matchSseId(const crow::sse_socket::Connection& thisConn)
+{
+ return &thisConn == sseConn;
+}
+
+// Check used to indicate what response codes are valid as part of our retry
+// policy. 2XX is considered acceptable
+boost::system::error_code Subscription::retryRespHandler(unsigned int respCode)
+{
+ BMCWEB_LOG_DEBUG("Checking response code validity for SubscriptionEvent");
+ if ((respCode < 200) || (respCode >= 300))
+ {
+ return boost::system::errc::make_error_code(
+ boost::system::errc::result_out_of_range);
+ }
+
+ // Return 0 if the response code is valid
+ return boost::system::errc::make_error_code(boost::system::errc::success);
+}
+
+} // namespace redfish