Skip to content

Commit 5e44e3d

Browse files
apuli1edtanous
authored andcommitted
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 uris from browser. 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. Change-Id: I7f4b7a34974080739c4ba968ed570489af0474de Signed-off-by: AppaRao Puli <[email protected]> Signed-off-by: P Dheeraj Srujan Kumar <[email protected]> Signed-off-by: Ed Tanous <[email protected]>
1 parent c9374ff commit 5e44e3d

File tree

6 files changed

+154
-37
lines changed

6 files changed

+154
-37
lines changed

http/http_client.hpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,7 @@ class ConnectionPool : public std::enable_shared_from_this<ConnectionPool>
710710
}
711711
}
712712

713-
void sendData(std::string& data, const std::string& destUri,
713+
void sendData(std::string&& data, const std::string& destUri,
714714
const boost::beast::http::fields& httpHeader,
715715
const boost::beast::http::verb verb,
716716
const std::function<void(Response&)>& resHandler)
@@ -866,19 +866,19 @@ class HttpClient
866866

867867
// Send a request to destIP:destPort where additional processing of the
868868
// result is not required
869-
void sendData(std::string& data, const std::string& destIP,
869+
void sendData(std::string&& data, const std::string& destIP,
870870
uint16_t destPort, const std::string& destUri, bool useSSL,
871871
const boost::beast::http::fields& httpHeader,
872872
const boost::beast::http::verb verb)
873873
{
874874
const std::function<void(Response&)> cb = genericResHandler;
875-
sendDataWithCallback(data, destIP, destPort, destUri, useSSL,
875+
sendDataWithCallback(std::move(data), destIP, destPort, destUri, useSSL,
876876
httpHeader, verb, cb);
877877
}
878878

879879
// Send request to destIP:destPort and use the provided callback to
880880
// handle the response
881-
void sendDataWithCallback(std::string& data, const std::string& destIP,
881+
void sendDataWithCallback(std::string&& data, const std::string& destIP,
882882
uint16_t destPort, const std::string& destUri,
883883
bool useSSL,
884884
const boost::beast::http::fields& httpHeader,
@@ -897,7 +897,7 @@ class HttpClient
897897
}
898898
// Send the data using either the existing connection pool or the newly
899899
// created connection pool
900-
pool.first->second->sendData(data, destUri, httpHeader, verb,
900+
pool.first->second->sendData(std::move(data), destUri, httpHeader, verb,
901901
resHandler);
902902
}
903903
};

redfish-core/include/event_service_manager.hpp

+83-22
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "registries.hpp"
2525
#include "registries/base_message_registry.hpp"
2626
#include "registries/openbmc_message_registry.hpp"
27+
#include "registries/privilege_registry.hpp"
2728
#include "registries/task_event_message_registry.hpp"
2829
#include "server_sent_events.hpp"
2930
#include "str_utility.hpp"
@@ -38,6 +39,7 @@
3839
#include <boost/url/format.hpp>
3940
#include <sdbusplus/bus/match.hpp>
4041

42+
#include <algorithm>
4143
#include <cstdlib>
4244
#include <ctime>
4345
#include <fstream>
@@ -53,9 +55,13 @@ using ReadingsObjType =
5355
static constexpr const char* eventFormatType = "Event";
5456
static constexpr const char* metricReportFormatType = "MetricReport";
5557

58+
static constexpr const char* subscriptionTypeSSE = "SSE";
5659
static constexpr const char* eventServiceFile =
5760
"/var/lib/bmcweb/eventservice_config.json";
5861

62+
static constexpr const uint8_t maxNoOfSubscriptions = 20;
63+
static constexpr const uint8_t maxNoOfSSESubscriptions = 10;
64+
5965
namespace registries
6066
{
6167
inline std::span<const MessageEntry>
@@ -382,15 +388,20 @@ class Subscription : public persistent_data::UserSubscription
382388
boost::asio::io_context& ioc) :
383389
host(inHost),
384390
port(inPort), policy(std::make_shared<crow::ConnectionPolicy>()),
385-
client(ioc, policy), path(inPath), uriProto(inUriProto)
391+
path(inPath), uriProto(inUriProto)
386392
{
393+
client.emplace(ioc, policy);
387394
// Subscription constructor
388395
policy->invalidResp = retryRespHandler;
389396
}
390397

398+
explicit Subscription(crow::sse_socket::Connection& connIn) :
399+
sseConn(&connIn)
400+
{}
401+
391402
~Subscription() = default;
392403

393-
bool sendEvent(std::string& msg)
404+
bool sendEvent(std::string&& msg)
394405
{
395406
persistent_data::EventServiceConfig eventServiceConfig =
396407
persistent_data::EventServiceStore::getInstance()
@@ -402,13 +413,17 @@ class Subscription : public persistent_data::UserSubscription
402413

403414
bool useSSL = (uriProto == "https");
404415
// A connection pool will be created if one does not already exist
405-
client.sendData(msg, host, port, path, useSSL, httpHeaders,
406-
boost::beast::http::verb::post);
407-
eventSeqNum++;
416+
if (client)
417+
{
418+
client->sendData(std::move(msg), host, port, path, useSSL,
419+
httpHeaders, boost::beast::http::verb::post);
420+
return true;
421+
}
408422

409423
if (sseConn != nullptr)
410424
{
411-
sseConn->sendData(eventSeqNum, msg);
425+
eventSeqNum++;
426+
sseConn->sendEvent(std::to_string(eventSeqNum), msg);
412427
}
413428
return true;
414429
}
@@ -437,7 +452,7 @@ class Subscription : public persistent_data::UserSubscription
437452

438453
std::string strMsg = msg.dump(2, ' ', true,
439454
nlohmann::json::error_handler_t::replace);
440-
return this->sendEvent(strMsg);
455+
return sendEvent(std::move(strMsg));
441456
}
442457

443458
#ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
@@ -503,10 +518,10 @@ class Subscription : public persistent_data::UserSubscription
503518
msg["Id"] = std::to_string(eventSeqNum);
504519
msg["Name"] = "Event Log";
505520
msg["Events"] = logEntryArray;
506-
507521
std::string strMsg = msg.dump(2, ' ', true,
508522
nlohmann::json::error_handler_t::replace);
509-
this->sendEvent(strMsg);
523+
sendEvent(std::move(strMsg));
524+
eventSeqNum++;
510525
}
511526
#endif
512527

@@ -546,7 +561,7 @@ class Subscription : public persistent_data::UserSubscription
546561

547562
std::string strMsg = msg.dump(2, ' ', true,
548563
nlohmann::json::error_handler_t::replace);
549-
this->sendEvent(strMsg);
564+
sendEvent(std::move(strMsg));
550565
}
551566

552567
void updateRetryConfig(uint32_t retryAttempts,
@@ -561,15 +576,32 @@ class Subscription : public persistent_data::UserSubscription
561576
return eventSeqNum;
562577
}
563578

579+
void setSubscriptionId(const std::string& id2)
580+
{
581+
BMCWEB_LOG_DEBUG << "Subscription ID: " << id2;
582+
subId = id2;
583+
}
584+
585+
std::string getSubscriptionId()
586+
{
587+
return subId;
588+
}
589+
590+
bool matchSseId(const crow::sse_socket::Connection& thisConn)
591+
{
592+
return &thisConn == sseConn;
593+
}
594+
564595
private:
596+
std::string subId;
565597
uint64_t eventSeqNum = 1;
566598
std::string host;
567599
uint16_t port = 0;
568600
std::shared_ptr<crow::ConnectionPolicy> policy;
569-
crow::HttpClient client;
601+
crow::sse_socket::Connection* sseConn = nullptr;
602+
std::optional<crow::HttpClient> client;
570603
std::string path;
571604
std::string uriProto;
572-
std::shared_ptr<crow::ServerSentEvents> sseConn = nullptr;
573605

574606
// Check used to indicate what response codes are valid as part of our retry
575607
// policy. 2XX is considered acceptable
@@ -828,8 +860,8 @@ class EventServiceManager
828860
for (const auto& it :
829861
EventServiceManager::getInstance().subscriptionsMap)
830862
{
831-
std::shared_ptr<Subscription> entry = it.second;
832-
entry->updateRetryConfig(retryAttempts, retryTimeoutInterval);
863+
Subscription& entry = *it.second;
864+
entry.updateRetryConfig(retryAttempts, retryTimeoutInterval);
833865
}
834866
}
835867
}
@@ -942,6 +974,8 @@ class EventServiceManager
942974
// Update retry configuration.
943975
subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
944976

977+
// Set Subscription ID for back trace
978+
subValue->setSubscriptionId(id);
945979
return id;
946980
}
947981

@@ -966,11 +1000,38 @@ class EventServiceManager
9661000
}
9671001
}
9681002

969-
size_t getNumberOfSubscriptions()
1003+
void deleteSseSubscription(const crow::sse_socket::Connection& thisConn)
1004+
{
1005+
for (const auto& it : subscriptionsMap)
1006+
{
1007+
std::shared_ptr<Subscription> entry = it.second;
1008+
bool entryIsThisConn = entry->matchSseId(thisConn);
1009+
if (entryIsThisConn)
1010+
{
1011+
persistent_data::EventServiceStore::getInstance()
1012+
.subscriptionsConfigMap.erase(
1013+
it.second->getSubscriptionId());
1014+
return;
1015+
}
1016+
}
1017+
}
1018+
1019+
size_t getNumberOfSubscriptions() const
9701020
{
9711021
return subscriptionsMap.size();
9721022
}
9731023

1024+
size_t getNumberOfSSESubscriptions() const
1025+
{
1026+
auto size = std::count_if(
1027+
subscriptionsMap.begin(), subscriptionsMap.end(),
1028+
[](const std::pair<std::string, std::shared_ptr<Subscription>>&
1029+
entry) {
1030+
return (entry.second->subscriptionType == subscriptionTypeSSE);
1031+
});
1032+
return static_cast<size_t>(size);
1033+
}
1034+
9741035
std::vector<std::string> getAllIDs()
9751036
{
9761037
std::vector<std::string> idList;
@@ -981,7 +1042,7 @@ class EventServiceManager
9811042
return idList;
9821043
}
9831044

984-
bool isDestinationExist(const std::string& destUrl)
1045+
bool isDestinationExist(const std::string& destUrl) const
9851046
{
9861047
for (const auto& it : subscriptionsMap)
9871048
{
@@ -997,7 +1058,7 @@ class EventServiceManager
9971058

9981059
bool sendTestEventLog()
9991060
{
1000-
for (const auto& it : this->subscriptionsMap)
1061+
for (const auto& it : subscriptionsMap)
10011062
{
10021063
std::shared_ptr<Subscription> entry = it.second;
10031064
if (!entry->sendTestEventLog())
@@ -1027,7 +1088,7 @@ class EventServiceManager
10271088

10281089
eventRecord.emplace_back(std::move(eventMessage));
10291090

1030-
for (const auto& it : this->subscriptionsMap)
1091+
for (const auto& it : subscriptionsMap)
10311092
{
10321093
std::shared_ptr<Subscription> entry = it.second;
10331094
bool isSubscribed = false;
@@ -1062,7 +1123,7 @@ class EventServiceManager
10621123

10631124
std::string strMsg = msgJson.dump(
10641125
2, ' ', true, nlohmann::json::error_handler_t::replace);
1065-
entry->sendEvent(strMsg);
1126+
entry->sendEvent(std::move(strMsg));
10661127
eventId++; // increament the eventId
10671128
}
10681129
else
@@ -1073,7 +1134,7 @@ class EventServiceManager
10731134
}
10741135
void sendBroadcastMsg(const std::string& broadcastMsg)
10751136
{
1076-
for (const auto& it : this->subscriptionsMap)
1137+
for (const auto& it : subscriptionsMap)
10771138
{
10781139
std::shared_ptr<Subscription> entry = it.second;
10791140
nlohmann::json msgJson;
@@ -1085,7 +1146,7 @@ class EventServiceManager
10851146

10861147
std::string strMsg = msgJson.dump(
10871148
2, ' ', true, nlohmann::json::error_handler_t::replace);
1088-
entry->sendEvent(strMsg);
1149+
entry->sendEvent(std::move(strMsg));
10891150
}
10901151
}
10911152

@@ -1188,7 +1249,7 @@ class EventServiceManager
11881249
return;
11891250
}
11901251

1191-
for (const auto& it : this->subscriptionsMap)
1252+
for (const auto& it : subscriptionsMap)
11921253
{
11931254
std::shared_ptr<Subscription> entry = it.second;
11941255
if (entry->eventFormatType == "Event")

redfish-core/include/redfish.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "environment_metrics.hpp"
2525
#include "ethernet.hpp"
2626
#include "event_service.hpp"
27+
#include "eventservice_sse.hpp"
2728
#include "fabric_adapters.hpp"
2829
#include "hypervisor_system.hpp"
2930
#include "log_services.hpp"
@@ -222,6 +223,7 @@ class RedfishService
222223
requestRoutesTaskCollection(app);
223224
requestRoutesTask(app);
224225
requestRoutesEventService(app);
226+
requestRoutesEventServiceSse(app);
225227
requestRoutesEventDestinationCollection(app);
226228
requestRoutesEventDestination(app);
227229
requestRoutesFabricAdapters(app);

redfish-core/include/redfish_aggregator.hpp

+8-8
Original file line numberDiff line numberDiff line change
@@ -702,10 +702,10 @@ class RedfishAggregator
702702
std::bind_front(processResponse, prefix, asyncResp);
703703

704704
std::string data = thisReq.req.body();
705-
client.sendDataWithCallback(data, std::string(sat->second.host()),
706-
sat->second.port_number(), targetURI,
707-
false /*useSSL*/, thisReq.fields(),
708-
thisReq.method(), cb);
705+
client.sendDataWithCallback(
706+
std::move(data), std::string(sat->second.host()),
707+
sat->second.port_number(), targetURI, false /*useSSL*/,
708+
thisReq.fields(), thisReq.method(), cb);
709709
}
710710

711711
// Forward a request for a collection URI to each known satellite BMC
@@ -721,10 +721,10 @@ class RedfishAggregator
721721

722722
std::string targetURI(thisReq.target());
723723
std::string data = thisReq.req.body();
724-
client.sendDataWithCallback(data, std::string(sat.second.host()),
725-
sat.second.port_number(), targetURI,
726-
false /*useSSL*/, thisReq.fields(),
727-
thisReq.method(), cb);
724+
client.sendDataWithCallback(
725+
std::move(data), std::string(sat.second.host()),
726+
sat.second.port_number(), targetURI, false /*useSSL*/,
727+
thisReq.fields(), thisReq.method(), cb);
728728
}
729729
}
730730

redfish-core/lib/event_service.hpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ static constexpr const std::array<const char*, 1> supportedResourceTypes = {
4343
"Task"};
4444
#endif
4545

46-
static constexpr const uint8_t maxNoOfSubscriptions = 20;
47-
4846
inline void requestRoutesEventService(App& app)
4947
{
5048
BMCWEB_ROUTE(app, "/redfish/v1/EventService/")
@@ -62,6 +60,9 @@ inline void requestRoutesEventService(App& app)
6260
"#EventService.v1_5_0.EventService";
6361
asyncResp->res.jsonValue["Id"] = "EventService";
6462
asyncResp->res.jsonValue["Name"] = "Event Service";
63+
asyncResp->res.jsonValue["ServerSentEventUri"] =
64+
"/redfish/v1/EventService/SSE";
65+
6566
asyncResp->res.jsonValue["Subscriptions"]["@odata.id"] =
6667
"/redfish/v1/EventService/Subscriptions";
6768
asyncResp->res

0 commit comments

Comments
 (0)