xref: /openbmc/bmcweb/features/redfish/include/event_service_manager.hpp (revision 6136e852e4eadf9d3e83b58be17f393a96ea2f64)
140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
340e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2020 Intel Corporation
4b52664e2SAppaRao Puli #pragma once
5d7857201SEd Tanous #include "bmcweb_config.h"
6d7857201SEd Tanous 
7b26ff34dSEd Tanous #include "dbus_log_watcher.hpp"
83ccb3adbSEd Tanous #include "error_messages.hpp"
9d7857201SEd Tanous #include "event_logs_object_type.hpp"
10d3a48a14SEd Tanous #include "event_matches_filter.hpp"
113ccb3adbSEd Tanous #include "event_service_store.hpp"
122185ddeaSEd Tanous #include "filesystem_log_watcher.hpp"
139838eb20SEd Tanous #include "io_context_singleton.hpp"
14d7857201SEd Tanous #include "logging.hpp"
15c0353249SWludzik, Jozef #include "metric_report.hpp"
162c6ffdb0SEd Tanous #include "ossl_random.hpp"
173ccb3adbSEd Tanous #include "persistent_data.hpp"
18d7857201SEd Tanous #include "server_sent_event.hpp"
1902c1e29fSAlexander Hansen #include "subscription.hpp"
205b90429aSEd Tanous #include "utils/time_utils.hpp"
217f4eb588SAppaRao Puli 
22f80a87f2SEd Tanous #include <boost/circular_buffer.hpp>
23d7857201SEd Tanous #include <boost/circular_buffer/base.hpp>
24b52664e2SAppaRao Puli #include <boost/container/flat_map.hpp>
25d7857201SEd Tanous #include <boost/system/result.hpp>
26d7857201SEd Tanous #include <boost/url/parse.hpp>
274a7fbefdSEd Tanous #include <boost/url/url_view_base.hpp>
281214b7e7SGunnar Mills 
295e44e3d8SAppaRao Puli #include <algorithm>
30d7857201SEd Tanous #include <cstdint>
31b52664e2SAppaRao Puli #include <cstdlib>
32b52664e2SAppaRao Puli #include <ctime>
33d7857201SEd Tanous #include <filesystem>
34a14c9113SEd Tanous #include <format>
351bf712bcSAyushi Smriti #include <fstream>
36b52664e2SAppaRao Puli #include <memory>
37d7857201SEd Tanous #include <optional>
38d7857201SEd Tanous #include <random>
39a14c9113SEd Tanous #include <string>
4056ba386dSMyung Bae #include <string_view>
41d7857201SEd Tanous #include <system_error>
425fe4ef35SMyung Bae #include <utility>
43d7857201SEd Tanous #include <vector>
44b52664e2SAppaRao Puli 
45b52664e2SAppaRao Puli namespace redfish
46b52664e2SAppaRao Puli {
47156d6b00SAppaRao Puli 
48156d6b00SAppaRao Puli static constexpr const char* eventFormatType = "Event";
49156d6b00SAppaRao Puli static constexpr const char* metricReportFormatType = "MetricReport";
50156d6b00SAppaRao Puli 
511bf712bcSAyushi Smriti static constexpr const char* eventServiceFile =
521bf712bcSAyushi Smriti     "/var/lib/bmcweb/eventservice_config.json";
531bf712bcSAyushi Smriti 
54b52664e2SAppaRao Puli class EventServiceManager
55b52664e2SAppaRao Puli {
56b52664e2SAppaRao Puli   private:
57d3a9e084SEd Tanous     bool serviceEnabled = false;
58d3a9e084SEd Tanous     uint32_t retryAttempts = 0;
59d3a9e084SEd Tanous     uint32_t retryTimeoutInterval = 0;
607d1cc387SAppaRao Puli 
619f616dd1SEd Tanous     size_t noOfEventLogSubscribers{0};
629f616dd1SEd Tanous     size_t noOfMetricReportSubscribers{0};
636c58a03eSAlexander Hansen     std::optional<DbusEventLogMonitor> dbusEventLogMonitor;
642ac69850SEd Tanous     std::optional<DbusTelemetryMonitor> matchTelemetryMonitor;
657b669723SEd Tanous     std::optional<FilesystemLogWatcher> filesystemLogMonitor;
66b52664e2SAppaRao Puli     boost::container::flat_map<std::string, std::shared_ptr<Subscription>>
67b52664e2SAppaRao Puli         subscriptionsMap;
68b52664e2SAppaRao Puli 
699f616dd1SEd Tanous     uint64_t eventId{1};
7096330b99SSunitha Harish 
71f80a87f2SEd Tanous     struct Event
72f80a87f2SEd Tanous     {
734a19a7b5SEd Tanous         uint64_t id;
744a19a7b5SEd Tanous         nlohmann::json::object_t message;
75f80a87f2SEd Tanous     };
76f80a87f2SEd Tanous 
77f80a87f2SEd Tanous     constexpr static size_t maxMessages = 200;
78f80a87f2SEd Tanous     boost::circular_buffer<Event> messages{maxMessages};
79f80a87f2SEd Tanous 
80b52664e2SAppaRao Puli   public:
819f616dd1SEd Tanous     EventServiceManager(const EventServiceManager&) = delete;
829f616dd1SEd Tanous     EventServiceManager& operator=(const EventServiceManager&) = delete;
839f616dd1SEd Tanous     EventServiceManager(EventServiceManager&&) = delete;
849f616dd1SEd Tanous     EventServiceManager& operator=(EventServiceManager&&) = delete;
85ecd6a3a2SEd Tanous     ~EventServiceManager() = default;
869f616dd1SEd Tanous 
879838eb20SEd Tanous     explicit EventServiceManager()
88b52664e2SAppaRao Puli     {
89f8ca6d79SEd Tanous         // Load config from persist store.
90f8ca6d79SEd Tanous         initConfig();
91f8ca6d79SEd Tanous     }
92f8ca6d79SEd Tanous 
939838eb20SEd Tanous     static EventServiceManager& getInstance()
94f8ca6d79SEd Tanous     {
959838eb20SEd Tanous         static EventServiceManager handler;
96b52664e2SAppaRao Puli         return handler;
97b52664e2SAppaRao Puli     }
98b52664e2SAppaRao Puli 
991bf712bcSAyushi Smriti     void initConfig()
1001bf712bcSAyushi Smriti     {
10128afb49cSJunLin Chen         loadOldBehavior();
1021bf712bcSAyushi Smriti 
10328afb49cSJunLin Chen         persistent_data::EventServiceConfig eventServiceConfig =
10428afb49cSJunLin Chen             persistent_data::EventServiceStore::getInstance()
10528afb49cSJunLin Chen                 .getEventServiceConfig();
1061bf712bcSAyushi Smriti 
10728afb49cSJunLin Chen         serviceEnabled = eventServiceConfig.enabled;
10828afb49cSJunLin Chen         retryAttempts = eventServiceConfig.retryAttempts;
10928afb49cSJunLin Chen         retryTimeoutInterval = eventServiceConfig.retryTimeoutInterval;
1101bf712bcSAyushi Smriti 
11128afb49cSJunLin Chen         for (const auto& it : persistent_data::EventServiceStore::getInstance()
11228afb49cSJunLin Chen                                   .subscriptionsConfigMap)
1131bf712bcSAyushi Smriti         {
1145fe4ef35SMyung Bae             std::shared_ptr<persistent_data::UserSubscription> newSub =
1155fe4ef35SMyung Bae                 it.second;
1164bbf237fSAppaRao Puli 
1176fd29553SEd Tanous             boost::system::result<boost::urls::url> url =
1185fe4ef35SMyung Bae                 boost::urls::parse_absolute_uri(newSub->destinationUrl);
1191bf712bcSAyushi Smriti 
120a716aa74SEd Tanous             if (!url)
1211bf712bcSAyushi Smriti             {
12262598e31SEd Tanous                 BMCWEB_LOG_ERROR(
12362598e31SEd Tanous                     "Failed to validate and split destination url");
1241bf712bcSAyushi Smriti                 continue;
1251bf712bcSAyushi Smriti             }
1261bf712bcSAyushi Smriti             std::shared_ptr<Subscription> subValue =
1279838eb20SEd Tanous                 std::make_shared<Subscription>(newSub, *url, getIoContext());
1285fe4ef35SMyung Bae             std::string id = subValue->userSub->id;
129a0969c70SMyung Bae             subValue->deleter = [id]() {
130a0969c70SMyung Bae                 EventServiceManager::getInstance().deleteSubscription(id);
131a0969c70SMyung Bae             };
1321bf712bcSAyushi Smriti 
133a0969c70SMyung Bae             subscriptionsMap.emplace(id, subValue);
13428afb49cSJunLin Chen 
13528afb49cSJunLin Chen             updateNoOfSubscribersCount();
13628afb49cSJunLin Chen 
13728afb49cSJunLin Chen             // Update retry configuration.
13828afb49cSJunLin Chen             subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
139fb546105SMyung Bae 
140fb546105SMyung Bae             // schedule a heartbeat if sendHeartbeat was set to true
141fb546105SMyung Bae             if (subValue->userSub->sendHeartbeat)
142fb546105SMyung Bae             {
143fb546105SMyung Bae                 subValue->scheduleNextHeartbeatEvent();
144fb546105SMyung Bae             }
1451bf712bcSAyushi Smriti         }
1461bf712bcSAyushi Smriti     }
1471bf712bcSAyushi Smriti 
14856d2396dSEd Tanous     static void loadOldBehavior()
149b52664e2SAppaRao Puli     {
15028afb49cSJunLin Chen         std::ifstream eventConfigFile(eventServiceFile);
15128afb49cSJunLin Chen         if (!eventConfigFile.good())
1521bf712bcSAyushi Smriti         {
15362598e31SEd Tanous             BMCWEB_LOG_DEBUG("Old eventService config not exist");
15428afb49cSJunLin Chen             return;
15528afb49cSJunLin Chen         }
15628afb49cSJunLin Chen         auto jsonData = nlohmann::json::parse(eventConfigFile, nullptr, false);
15728afb49cSJunLin Chen         if (jsonData.is_discarded())
1584bbf237fSAppaRao Puli         {
15962598e31SEd Tanous             BMCWEB_LOG_ERROR("Old eventService config parse error.");
16028afb49cSJunLin Chen             return;
16128afb49cSJunLin Chen         }
16228afb49cSJunLin Chen 
1630bdda665SEd Tanous         const nlohmann::json::object_t* obj =
1640bdda665SEd Tanous             jsonData.get_ptr<const nlohmann::json::object_t*>();
1651c588de9SAbiola Asojo         if (obj == nullptr)
1661c588de9SAbiola Asojo         {
1671c588de9SAbiola Asojo             return;
1681c588de9SAbiola Asojo         }
1690bdda665SEd Tanous         for (const auto& item : *obj)
17028afb49cSJunLin Chen         {
1710bdda665SEd Tanous             if (item.first == "Configuration")
17228afb49cSJunLin Chen             {
17328afb49cSJunLin Chen                 persistent_data::EventServiceStore::getInstance()
17428afb49cSJunLin Chen                     .getEventServiceConfig()
1750bdda665SEd Tanous                     .fromJson(item.second);
17628afb49cSJunLin Chen             }
1770bdda665SEd Tanous             else if (item.first == "Subscriptions")
17828afb49cSJunLin Chen             {
1790bdda665SEd Tanous                 for (const auto& elem : item.second)
18028afb49cSJunLin Chen                 {
1814b712a29SEd Tanous                     std::optional<persistent_data::UserSubscription>
18228afb49cSJunLin Chen                         newSubscription =
18328afb49cSJunLin Chen                             persistent_data::UserSubscription::fromJson(elem,
18428afb49cSJunLin Chen                                                                         true);
1854b712a29SEd Tanous                     if (!newSubscription)
18628afb49cSJunLin Chen                     {
18762598e31SEd Tanous                         BMCWEB_LOG_ERROR("Problem reading subscription "
18862598e31SEd Tanous                                          "from old persistent store");
1894bbf237fSAppaRao Puli                         continue;
1904bbf237fSAppaRao Puli                     }
1914b712a29SEd Tanous                     persistent_data::UserSubscription& newSub =
1924b712a29SEd Tanous                         *newSubscription;
1931bf712bcSAyushi Smriti 
19428afb49cSJunLin Chen                     std::uniform_int_distribution<uint32_t> dist(0);
19528afb49cSJunLin Chen                     bmcweb::OpenSSLGenerator gen;
1961bf712bcSAyushi Smriti 
19728afb49cSJunLin Chen                     std::string id;
1981bf712bcSAyushi Smriti 
19928afb49cSJunLin Chen                     int retry = 3;
200e662eae8SEd Tanous                     while (retry != 0)
2011bf712bcSAyushi Smriti                     {
20228afb49cSJunLin Chen                         id = std::to_string(dist(gen));
20328afb49cSJunLin Chen                         if (gen.error())
2047d1cc387SAppaRao Puli                         {
20528afb49cSJunLin Chen                             retry = 0;
20628afb49cSJunLin Chen                             break;
20728afb49cSJunLin Chen                         }
2084b712a29SEd Tanous                         newSub.id = id;
20928afb49cSJunLin Chen                         auto inserted =
21028afb49cSJunLin Chen                             persistent_data::EventServiceStore::getInstance()
2115fe4ef35SMyung Bae                                 .subscriptionsConfigMap.insert(std::pair(
2125fe4ef35SMyung Bae                                     id, std::make_shared<
2135fe4ef35SMyung Bae                                             persistent_data::UserSubscription>(
2145fe4ef35SMyung Bae                                             newSub)));
21528afb49cSJunLin Chen                         if (inserted.second)
21628afb49cSJunLin Chen                         {
21728afb49cSJunLin Chen                             break;
21828afb49cSJunLin Chen                         }
21928afb49cSJunLin Chen                         --retry;
2207d1cc387SAppaRao Puli                     }
2217d1cc387SAppaRao Puli 
22228afb49cSJunLin Chen                     if (retry <= 0)
22328afb49cSJunLin Chen                     {
22462598e31SEd Tanous                         BMCWEB_LOG_ERROR(
22562598e31SEd Tanous                             "Failed to generate random number from old "
22662598e31SEd Tanous                             "persistent store");
22728afb49cSJunLin Chen                         continue;
22828afb49cSJunLin Chen                     }
22928afb49cSJunLin Chen                 }
23028afb49cSJunLin Chen             }
23128afb49cSJunLin Chen 
23228afb49cSJunLin Chen             persistent_data::getConfig().writeData();
2334c521c3cSEd Tanous             std::error_code ec;
2344c521c3cSEd Tanous             std::filesystem::remove(eventServiceFile, ec);
2354c521c3cSEd Tanous             if (ec)
2364c521c3cSEd Tanous             {
2374c521c3cSEd Tanous                 BMCWEB_LOG_DEBUG(
2384c521c3cSEd Tanous                     "Failed to remove old event service file.  Ignoring");
2394c521c3cSEd Tanous             }
2404c521c3cSEd Tanous             else
2414c521c3cSEd Tanous             {
24262598e31SEd Tanous                 BMCWEB_LOG_DEBUG("Remove old eventservice config");
24328afb49cSJunLin Chen             }
24428afb49cSJunLin Chen         }
2454c521c3cSEd Tanous     }
24628afb49cSJunLin Chen 
2479eb808c1SEd Tanous     void updateSubscriptionData() const
24828afb49cSJunLin Chen     {
24928afb49cSJunLin Chen         persistent_data::EventServiceStore::getInstance()
25028afb49cSJunLin Chen             .eventServiceConfig.enabled = serviceEnabled;
25128afb49cSJunLin Chen         persistent_data::EventServiceStore::getInstance()
25228afb49cSJunLin Chen             .eventServiceConfig.retryAttempts = retryAttempts;
25328afb49cSJunLin Chen         persistent_data::EventServiceStore::getInstance()
25428afb49cSJunLin Chen             .eventServiceConfig.retryTimeoutInterval = retryTimeoutInterval;
25528afb49cSJunLin Chen 
25628afb49cSJunLin Chen         persistent_data::getConfig().writeData();
25728afb49cSJunLin Chen     }
25828afb49cSJunLin Chen 
25928afb49cSJunLin Chen     void setEventServiceConfig(const persistent_data::EventServiceConfig& cfg)
2607d1cc387SAppaRao Puli     {
2617d1cc387SAppaRao Puli         bool updateConfig = false;
262fe44eb0bSAyushi Smriti         bool updateRetryCfg = false;
2637d1cc387SAppaRao Puli 
2642ac69850SEd Tanous         if (serviceEnabled)
2657d1cc387SAppaRao Puli         {
2667b669723SEd Tanous             if (noOfEventLogSubscribers > 0U)
2677b669723SEd Tanous             {
2686c58a03eSAlexander Hansen                 if constexpr (BMCWEB_REDFISH_DBUS_LOG)
2696c58a03eSAlexander Hansen                 {
2706c58a03eSAlexander Hansen                     if (!dbusEventLogMonitor)
2716c58a03eSAlexander Hansen                     {
2726c58a03eSAlexander Hansen                         if constexpr (
2736c58a03eSAlexander Hansen                             BMCWEB_EXPERIMENTAL_REDFISH_DBUS_LOG_SUBSCRIPTION)
2746c58a03eSAlexander Hansen                         {
2756c58a03eSAlexander Hansen                             dbusEventLogMonitor.emplace();
2766c58a03eSAlexander Hansen                         }
2776c58a03eSAlexander Hansen                     }
2786c58a03eSAlexander Hansen                 }
2796c58a03eSAlexander Hansen                 else
2807b669723SEd Tanous                 {
2817b669723SEd Tanous                     if (!filesystemLogMonitor)
2827b669723SEd Tanous                     {
2839838eb20SEd Tanous                         filesystemLogMonitor.emplace(getIoContext());
2847b669723SEd Tanous                     }
2857b669723SEd Tanous                 }
2867b669723SEd Tanous             }
2877b669723SEd Tanous             else
2887b669723SEd Tanous             {
2896c58a03eSAlexander Hansen                 dbusEventLogMonitor.reset();
2907b669723SEd Tanous                 filesystemLogMonitor.reset();
2917b669723SEd Tanous             }
2927b669723SEd Tanous 
2932ac69850SEd Tanous             if (noOfMetricReportSubscribers > 0U)
2947d1cc387SAppaRao Puli             {
2952ac69850SEd Tanous                 if (!matchTelemetryMonitor)
2962ac69850SEd Tanous                 {
2972ac69850SEd Tanous                     matchTelemetryMonitor.emplace();
2982ac69850SEd Tanous                 }
2997d1cc387SAppaRao Puli             }
3007d1cc387SAppaRao Puli             else
3017d1cc387SAppaRao Puli             {
3022ac69850SEd Tanous                 matchTelemetryMonitor.reset();
3037d1cc387SAppaRao Puli             }
3042ac69850SEd Tanous         }
3052ac69850SEd Tanous         else
3062ac69850SEd Tanous         {
3072ac69850SEd Tanous             matchTelemetryMonitor.reset();
3086c58a03eSAlexander Hansen             dbusEventLogMonitor.reset();
3097b669723SEd Tanous             filesystemLogMonitor.reset();
3102ac69850SEd Tanous         }
3112ac69850SEd Tanous 
3122ac69850SEd Tanous         if (serviceEnabled != cfg.enabled)
3132ac69850SEd Tanous         {
3142ac69850SEd Tanous             serviceEnabled = cfg.enabled;
3157d1cc387SAppaRao Puli             updateConfig = true;
3167d1cc387SAppaRao Puli         }
3177d1cc387SAppaRao Puli 
31828afb49cSJunLin Chen         if (retryAttempts != cfg.retryAttempts)
3197d1cc387SAppaRao Puli         {
32028afb49cSJunLin Chen             retryAttempts = cfg.retryAttempts;
3217d1cc387SAppaRao Puli             updateConfig = true;
322fe44eb0bSAyushi Smriti             updateRetryCfg = true;
3237d1cc387SAppaRao Puli         }
3247d1cc387SAppaRao Puli 
32528afb49cSJunLin Chen         if (retryTimeoutInterval != cfg.retryTimeoutInterval)
3267d1cc387SAppaRao Puli         {
32728afb49cSJunLin Chen             retryTimeoutInterval = cfg.retryTimeoutInterval;
3287d1cc387SAppaRao Puli             updateConfig = true;
329fe44eb0bSAyushi Smriti             updateRetryCfg = true;
3307d1cc387SAppaRao Puli         }
3317d1cc387SAppaRao Puli 
3327d1cc387SAppaRao Puli         if (updateConfig)
3337d1cc387SAppaRao Puli         {
3347d1cc387SAppaRao Puli             updateSubscriptionData();
3357d1cc387SAppaRao Puli         }
336fe44eb0bSAyushi Smriti 
337fe44eb0bSAyushi Smriti         if (updateRetryCfg)
338fe44eb0bSAyushi Smriti         {
339fe44eb0bSAyushi Smriti             // Update the changed retry config to all subscriptions
340fe44eb0bSAyushi Smriti             for (const auto& it :
341fe44eb0bSAyushi Smriti                  EventServiceManager::getInstance().subscriptionsMap)
342fe44eb0bSAyushi Smriti             {
3435e44e3d8SAppaRao Puli                 Subscription& entry = *it.second;
3445e44e3d8SAppaRao Puli                 entry.updateRetryConfig(retryAttempts, retryTimeoutInterval);
345fe44eb0bSAyushi Smriti             }
346fe44eb0bSAyushi Smriti         }
3477d1cc387SAppaRao Puli     }
3487d1cc387SAppaRao Puli 
3497d1cc387SAppaRao Puli     void updateNoOfSubscribersCount()
3507d1cc387SAppaRao Puli     {
3517d1cc387SAppaRao Puli         size_t eventLogSubCount = 0;
3527d1cc387SAppaRao Puli         size_t metricReportSubCount = 0;
3537d1cc387SAppaRao Puli         for (const auto& it : subscriptionsMap)
3547d1cc387SAppaRao Puli         {
3557d1cc387SAppaRao Puli             std::shared_ptr<Subscription> entry = it.second;
3565fe4ef35SMyung Bae             if (entry->userSub->eventFormatType == eventFormatType)
3577d1cc387SAppaRao Puli             {
3587d1cc387SAppaRao Puli                 eventLogSubCount++;
3597d1cc387SAppaRao Puli             }
3605fe4ef35SMyung Bae             else if (entry->userSub->eventFormatType == metricReportFormatType)
3617d1cc387SAppaRao Puli             {
3627d1cc387SAppaRao Puli                 metricReportSubCount++;
3637d1cc387SAppaRao Puli             }
3647d1cc387SAppaRao Puli         }
3657d1cc387SAppaRao Puli         noOfEventLogSubscribers = eventLogSubCount;
3666c58a03eSAlexander Hansen         if (eventLogSubCount > 0U)
3677d1cc387SAppaRao Puli         {
3686c58a03eSAlexander Hansen             if constexpr (BMCWEB_REDFISH_DBUS_LOG)
3696c58a03eSAlexander Hansen             {
3706c58a03eSAlexander Hansen                 if (!dbusEventLogMonitor &&
3716c58a03eSAlexander Hansen                     BMCWEB_EXPERIMENTAL_REDFISH_DBUS_LOG_SUBSCRIPTION)
3726c58a03eSAlexander Hansen                 {
3736c58a03eSAlexander Hansen                     dbusEventLogMonitor.emplace();
3746c58a03eSAlexander Hansen                 }
3756c58a03eSAlexander Hansen             }
3766c58a03eSAlexander Hansen             else
3776c58a03eSAlexander Hansen             {
3786c58a03eSAlexander Hansen                 if (!filesystemLogMonitor)
3796c58a03eSAlexander Hansen                 {
3809838eb20SEd Tanous                     filesystemLogMonitor.emplace(getIoContext());
3816c58a03eSAlexander Hansen                 }
3826c58a03eSAlexander Hansen             }
3836c58a03eSAlexander Hansen         }
3846c58a03eSAlexander Hansen         else
3856c58a03eSAlexander Hansen         {
3866c58a03eSAlexander Hansen             dbusEventLogMonitor.reset();
3876c58a03eSAlexander Hansen             filesystemLogMonitor.reset();
3886c58a03eSAlexander Hansen         }
3896c58a03eSAlexander Hansen 
3907d1cc387SAppaRao Puli         noOfMetricReportSubscribers = metricReportSubCount;
3916c58a03eSAlexander Hansen         if (metricReportSubCount > 0U)
3927d1cc387SAppaRao Puli         {
3932ac69850SEd Tanous             if (!matchTelemetryMonitor)
3942ac69850SEd Tanous             {
3952ac69850SEd Tanous                 matchTelemetryMonitor.emplace();
3962ac69850SEd Tanous             }
3977d1cc387SAppaRao Puli         }
3987d1cc387SAppaRao Puli         else
3997d1cc387SAppaRao Puli         {
4002ac69850SEd Tanous             matchTelemetryMonitor.reset();
4017d1cc387SAppaRao Puli         }
4027d1cc387SAppaRao Puli     }
4037d1cc387SAppaRao Puli 
404b52664e2SAppaRao Puli     std::shared_ptr<Subscription> getSubscription(const std::string& id)
405b52664e2SAppaRao Puli     {
406b52664e2SAppaRao Puli         auto obj = subscriptionsMap.find(id);
407b52664e2SAppaRao Puli         if (obj == subscriptionsMap.end())
408b52664e2SAppaRao Puli         {
40962598e31SEd Tanous             BMCWEB_LOG_ERROR("No subscription exist with ID:{}", id);
410b52664e2SAppaRao Puli             return nullptr;
411b52664e2SAppaRao Puli         }
412b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue = obj->second;
413b52664e2SAppaRao Puli         return subValue;
414b52664e2SAppaRao Puli     }
415b52664e2SAppaRao Puli 
416504af5a0SPatrick Williams     std::string addSubscriptionInternal(
417504af5a0SPatrick Williams         const std::shared_ptr<Subscription>& subValue)
418b52664e2SAppaRao Puli     {
419fc76b8acSEd Tanous         std::uniform_int_distribution<uint32_t> dist(0);
420fc76b8acSEd Tanous         bmcweb::OpenSSLGenerator gen;
421fc76b8acSEd Tanous 
422b52664e2SAppaRao Puli         std::string id;
423b52664e2SAppaRao Puli 
424b52664e2SAppaRao Puli         int retry = 3;
425e662eae8SEd Tanous         while (retry != 0)
426b52664e2SAppaRao Puli         {
427fc76b8acSEd Tanous             id = std::to_string(dist(gen));
428fc76b8acSEd Tanous             if (gen.error())
429fc76b8acSEd Tanous             {
430fc76b8acSEd Tanous                 retry = 0;
431fc76b8acSEd Tanous                 break;
432fc76b8acSEd Tanous             }
433b52664e2SAppaRao Puli             auto inserted = subscriptionsMap.insert(std::pair(id, subValue));
434b52664e2SAppaRao Puli             if (inserted.second)
435b52664e2SAppaRao Puli             {
436b52664e2SAppaRao Puli                 break;
437b52664e2SAppaRao Puli             }
438b52664e2SAppaRao Puli             --retry;
43923a21a1cSEd Tanous         }
440b52664e2SAppaRao Puli 
441b52664e2SAppaRao Puli         if (retry <= 0)
442b52664e2SAppaRao Puli         {
44362598e31SEd Tanous             BMCWEB_LOG_ERROR("Failed to generate random number");
444abb93cddSEd Tanous             return "";
445b52664e2SAppaRao Puli         }
446b52664e2SAppaRao Puli 
44756ba386dSMyung Bae         // Set Subscription ID for back trace
4485fe4ef35SMyung Bae         subValue->userSub->id = id;
449a14c9113SEd Tanous 
45028afb49cSJunLin Chen         persistent_data::EventServiceStore::getInstance()
4515fe4ef35SMyung Bae             .subscriptionsConfigMap.emplace(id, subValue->userSub);
45228afb49cSJunLin Chen 
4537d1cc387SAppaRao Puli         updateNoOfSubscribersCount();
4541bf712bcSAyushi Smriti 
455fe44eb0bSAyushi Smriti         // Update retry configuration.
456fe44eb0bSAyushi Smriti         subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
457fe44eb0bSAyushi Smriti 
458f80a87f2SEd Tanous         return id;
459f80a87f2SEd Tanous     }
460f80a87f2SEd Tanous 
461504af5a0SPatrick Williams     std::string addSSESubscription(
462504af5a0SPatrick Williams         const std::shared_ptr<Subscription>& subValue,
463f80a87f2SEd Tanous         std::string_view lastEventId)
464f80a87f2SEd Tanous     {
465f80a87f2SEd Tanous         std::string id = addSubscriptionInternal(subValue);
466f80a87f2SEd Tanous 
467f80a87f2SEd Tanous         if (!lastEventId.empty())
468f80a87f2SEd Tanous         {
469f80a87f2SEd Tanous             BMCWEB_LOG_INFO("Attempting to find message for last id {}",
470f80a87f2SEd Tanous                             lastEventId);
471f80a87f2SEd Tanous             boost::circular_buffer<Event>::iterator lastEvent =
4724a19a7b5SEd Tanous                 std::ranges::find_if(
4734a19a7b5SEd Tanous                     messages, [&lastEventId](const Event& event) {
4744a19a7b5SEd Tanous                         return std::to_string(event.id) == lastEventId;
475f80a87f2SEd Tanous                     });
476f80a87f2SEd Tanous             // Can't find a matching ID
477f80a87f2SEd Tanous             if (lastEvent == messages.end())
478f80a87f2SEd Tanous             {
479f80a87f2SEd Tanous                 nlohmann::json msg = messages::eventBufferExceeded();
4804a19a7b5SEd Tanous 
4814a19a7b5SEd Tanous                 std::string strMsg = msg.dump(
4824a19a7b5SEd Tanous                     2, ' ', true, nlohmann::json::error_handler_t::replace);
4834a19a7b5SEd Tanous                 eventId++;
4844a19a7b5SEd Tanous                 subValue->sendEventToSubscriber(eventId, std::move(strMsg));
485f80a87f2SEd Tanous             }
486f80a87f2SEd Tanous             else
487f80a87f2SEd Tanous             {
488f80a87f2SEd Tanous                 // Skip the last event the user already has
489f80a87f2SEd Tanous                 lastEvent++;
490f80a87f2SEd Tanous 
491f80a87f2SEd Tanous                 for (boost::circular_buffer<Event>::const_iterator event =
492f80a87f2SEd Tanous                          lastEvent;
4934a19a7b5SEd Tanous                      event != messages.end(); event++)
494f80a87f2SEd Tanous                 {
4954a19a7b5SEd Tanous                     std::string strMsg =
4964a19a7b5SEd Tanous                         nlohmann::json(event->message)
4974a19a7b5SEd Tanous                             .dump(2, ' ', true,
4984a19a7b5SEd Tanous                                   nlohmann::json::error_handler_t::replace);
4994a19a7b5SEd Tanous 
5004a19a7b5SEd Tanous                     subValue->sendEventToSubscriber(event->id,
5014a19a7b5SEd Tanous                                                     std::move(strMsg));
5024a19a7b5SEd Tanous                 }
503f80a87f2SEd Tanous             }
504f80a87f2SEd Tanous         }
505f80a87f2SEd Tanous         return id;
506f80a87f2SEd Tanous     }
507f80a87f2SEd Tanous 
508504af5a0SPatrick Williams     std::string addPushSubscription(
509504af5a0SPatrick Williams         const std::shared_ptr<Subscription>& subValue)
510f80a87f2SEd Tanous     {
511f80a87f2SEd Tanous         std::string id = addSubscriptionInternal(subValue);
512a0969c70SMyung Bae         subValue->deleter = [id]() {
513a0969c70SMyung Bae             EventServiceManager::getInstance().deleteSubscription(id);
514a0969c70SMyung Bae         };
515f80a87f2SEd Tanous         updateSubscriptionData();
516b52664e2SAppaRao Puli         return id;
517b52664e2SAppaRao Puli     }
518b52664e2SAppaRao Puli 
519b52664e2SAppaRao Puli     bool isSubscriptionExist(const std::string& id)
520b52664e2SAppaRao Puli     {
521b52664e2SAppaRao Puli         auto obj = subscriptionsMap.find(id);
52255f79e6fSEd Tanous         return obj != subscriptionsMap.end();
523b52664e2SAppaRao Puli     }
524b52664e2SAppaRao Puli 
5254b712a29SEd Tanous     bool deleteSubscription(const std::string& id)
526b52664e2SAppaRao Puli     {
527b52664e2SAppaRao Puli         auto obj = subscriptionsMap.find(id);
5284b712a29SEd Tanous         if (obj == subscriptionsMap.end())
529b52664e2SAppaRao Puli         {
5304b712a29SEd Tanous             BMCWEB_LOG_WARNING("Could not find subscription with id {}", id);
5314b712a29SEd Tanous             return false;
5324b712a29SEd Tanous         }
533b52664e2SAppaRao Puli         subscriptionsMap.erase(obj);
5344b712a29SEd Tanous         auto& event = persistent_data::EventServiceStore::getInstance();
5354b712a29SEd Tanous         auto persistentObj = event.subscriptionsConfigMap.find(id);
5364b712a29SEd Tanous         if (persistentObj == event.subscriptionsConfigMap.end())
5374b712a29SEd Tanous         {
538*6136e852SMyung Bae             BMCWEB_LOG_ERROR("Subscription {} wasn't in persistent data", id);
5394b712a29SEd Tanous             return true;
5404b712a29SEd Tanous         }
54128afb49cSJunLin Chen         persistent_data::EventServiceStore::getInstance()
5424b712a29SEd Tanous             .subscriptionsConfigMap.erase(persistentObj);
5437d1cc387SAppaRao Puli         updateNoOfSubscribersCount();
544b52664e2SAppaRao Puli         updateSubscriptionData();
5454b712a29SEd Tanous 
5464b712a29SEd Tanous         return true;
547b52664e2SAppaRao Puli     }
548b52664e2SAppaRao Puli 
5495e44e3d8SAppaRao Puli     void deleteSseSubscription(const crow::sse_socket::Connection& thisConn)
5505e44e3d8SAppaRao Puli     {
551bdbfae2aSEd Tanous         for (auto it = subscriptionsMap.begin(); it != subscriptionsMap.end();)
5525e44e3d8SAppaRao Puli         {
553bdbfae2aSEd Tanous             std::shared_ptr<Subscription> entry = it->second;
5545e44e3d8SAppaRao Puli             bool entryIsThisConn = entry->matchSseId(thisConn);
5555e44e3d8SAppaRao Puli             if (entryIsThisConn)
5565e44e3d8SAppaRao Puli             {
5575e44e3d8SAppaRao Puli                 persistent_data::EventServiceStore::getInstance()
5585fe4ef35SMyung Bae                     .subscriptionsConfigMap.erase(entry->userSub->id);
559bdbfae2aSEd Tanous                 it = subscriptionsMap.erase(it);
5605e44e3d8SAppaRao Puli                 return;
5615e44e3d8SAppaRao Puli             }
562bdbfae2aSEd Tanous             it++;
5635e44e3d8SAppaRao Puli         }
5645e44e3d8SAppaRao Puli     }
5655e44e3d8SAppaRao Puli 
5665e44e3d8SAppaRao Puli     size_t getNumberOfSubscriptions() const
567b52664e2SAppaRao Puli     {
568b52664e2SAppaRao Puli         return subscriptionsMap.size();
569b52664e2SAppaRao Puli     }
570b52664e2SAppaRao Puli 
5715e44e3d8SAppaRao Puli     size_t getNumberOfSSESubscriptions() const
5725e44e3d8SAppaRao Puli     {
5733544d2a7SEd Tanous         auto size = std::ranges::count_if(
5743544d2a7SEd Tanous             subscriptionsMap,
5755e44e3d8SAppaRao Puli             [](const std::pair<std::string, std::shared_ptr<Subscription>>&
5765e44e3d8SAppaRao Puli                    entry) {
5775fe4ef35SMyung Bae                 return (entry.second->userSub->subscriptionType ==
5784b712a29SEd Tanous                         subscriptionTypeSSE);
5795e44e3d8SAppaRao Puli             });
5805e44e3d8SAppaRao Puli         return static_cast<size_t>(size);
5815e44e3d8SAppaRao Puli     }
5825e44e3d8SAppaRao Puli 
583b52664e2SAppaRao Puli     std::vector<std::string> getAllIDs()
584b52664e2SAppaRao Puli     {
585b52664e2SAppaRao Puli         std::vector<std::string> idList;
586b52664e2SAppaRao Puli         for (const auto& it : subscriptionsMap)
587b52664e2SAppaRao Puli         {
588b52664e2SAppaRao Puli             idList.emplace_back(it.first);
589b52664e2SAppaRao Puli         }
590b52664e2SAppaRao Puli         return idList;
591b52664e2SAppaRao Puli     }
592b52664e2SAppaRao Puli 
59381ee0e74SChandramohan Harkude     bool sendTestEventLog(TestEvent& testEvent)
5940b4bdd93SAppaRao Puli     {
5954a19a7b5SEd Tanous         eventId++;
5964a19a7b5SEd Tanous         nlohmann::json::array_t logEntryArray;
5974a19a7b5SEd Tanous         nlohmann::json& logEntryJson = logEntryArray.emplace_back();
5984a19a7b5SEd Tanous 
5994a19a7b5SEd Tanous         if (testEvent.eventGroupId)
6004a19a7b5SEd Tanous         {
6014a19a7b5SEd Tanous             logEntryJson["EventGroupId"] = *testEvent.eventGroupId;
6024a19a7b5SEd Tanous         }
6034a19a7b5SEd Tanous 
6044a19a7b5SEd Tanous         if (testEvent.eventTimestamp)
6054a19a7b5SEd Tanous         {
6064a19a7b5SEd Tanous             logEntryJson["EventTimestamp"] = *testEvent.eventTimestamp;
6074a19a7b5SEd Tanous         }
6084a19a7b5SEd Tanous 
6094a19a7b5SEd Tanous         if (testEvent.originOfCondition)
6104a19a7b5SEd Tanous         {
6114a19a7b5SEd Tanous             logEntryJson["OriginOfCondition"]["@odata.id"] =
6124a19a7b5SEd Tanous                 *testEvent.originOfCondition;
6134a19a7b5SEd Tanous         }
6144a19a7b5SEd Tanous         if (testEvent.severity)
6154a19a7b5SEd Tanous         {
6164a19a7b5SEd Tanous             logEntryJson["Severity"] = *testEvent.severity;
6174a19a7b5SEd Tanous         }
6184a19a7b5SEd Tanous 
6194a19a7b5SEd Tanous         if (testEvent.message)
6204a19a7b5SEd Tanous         {
6214a19a7b5SEd Tanous             logEntryJson["Message"] = *testEvent.message;
6224a19a7b5SEd Tanous         }
6234a19a7b5SEd Tanous 
6244a19a7b5SEd Tanous         if (testEvent.resolution)
6254a19a7b5SEd Tanous         {
6264a19a7b5SEd Tanous             logEntryJson["Resolution"] = *testEvent.resolution;
6274a19a7b5SEd Tanous         }
6284a19a7b5SEd Tanous 
6294a19a7b5SEd Tanous         if (testEvent.messageId)
6304a19a7b5SEd Tanous         {
6314a19a7b5SEd Tanous             logEntryJson["MessageId"] = *testEvent.messageId;
6324a19a7b5SEd Tanous         }
6334a19a7b5SEd Tanous 
6344a19a7b5SEd Tanous         if (testEvent.messageArgs)
6354a19a7b5SEd Tanous         {
6364a19a7b5SEd Tanous             logEntryJson["MessageArgs"] = *testEvent.messageArgs;
6374a19a7b5SEd Tanous         }
6384a19a7b5SEd Tanous         // MemberId is 0 : since we are sending one event record.
6394a19a7b5SEd Tanous         logEntryJson["MemberId"] = "0";
6404a19a7b5SEd Tanous 
6414a19a7b5SEd Tanous         nlohmann::json msg;
6424a19a7b5SEd Tanous         msg["@odata.type"] = "#Event.v1_4_0.Event";
6434a19a7b5SEd Tanous         msg["Id"] = std::to_string(eventId);
6444a19a7b5SEd Tanous         msg["Name"] = "Event Log";
6454a19a7b5SEd Tanous         msg["Events"] = logEntryArray;
6464a19a7b5SEd Tanous 
6474a19a7b5SEd Tanous         std::string strMsg =
6484a19a7b5SEd Tanous             msg.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
6494a19a7b5SEd Tanous 
6504a19a7b5SEd Tanous         messages.push_back(Event(eventId, msg));
6515e44e3d8SAppaRao Puli         for (const auto& it : subscriptionsMap)
6520b4bdd93SAppaRao Puli         {
6530b4bdd93SAppaRao Puli             std::shared_ptr<Subscription> entry = it.second;
6544a19a7b5SEd Tanous             if (!entry->sendEventToSubscriber(eventId, std::string(strMsg)))
6556ba8c82eSsunharis_in             {
6566ba8c82eSsunharis_in                 return false;
6570b4bdd93SAppaRao Puli             }
6580b4bdd93SAppaRao Puli         }
6596ba8c82eSsunharis_in         return true;
6606ba8c82eSsunharis_in     }
661e9a14131SAppaRao Puli 
662504af5a0SPatrick Williams     static void sendEventsToSubs(
663504af5a0SPatrick Williams         const std::vector<EventLogObjectsType>& eventRecords)
6643433b03aSEd Tanous     {
6654a19a7b5SEd Tanous         EventServiceManager& mgr = EventServiceManager::getInstance();
6664a19a7b5SEd Tanous         mgr.eventId++;
6674a19a7b5SEd Tanous         for (const auto& it : mgr.subscriptionsMap)
6683433b03aSEd Tanous         {
6693433b03aSEd Tanous             Subscription& entry = *it.second;
6704a19a7b5SEd Tanous             entry.filterAndSendEventLogs(mgr.eventId, eventRecords);
6713433b03aSEd Tanous         }
6723433b03aSEd Tanous     }
6733433b03aSEd Tanous 
674b26ff34dSEd Tanous     static void sendTelemetryReportToSubs(
675b26ff34dSEd Tanous         const std::string& reportId, const telemetry::TimestampReadings& var)
676b26ff34dSEd Tanous     {
6774a19a7b5SEd Tanous         EventServiceManager& mgr = EventServiceManager::getInstance();
6784a19a7b5SEd Tanous         mgr.eventId++;
6794a19a7b5SEd Tanous 
6804a19a7b5SEd Tanous         for (const auto& it : mgr.subscriptionsMap)
681b26ff34dSEd Tanous         {
682b26ff34dSEd Tanous             Subscription& entry = *it.second;
6834a19a7b5SEd Tanous             entry.filterAndSendReports(mgr.eventId, reportId, var);
684b26ff34dSEd Tanous         }
685b26ff34dSEd Tanous     }
686b26ff34dSEd Tanous 
687f80a87f2SEd Tanous     void sendEvent(nlohmann::json::object_t eventMessage,
688f80a87f2SEd Tanous                    std::string_view origin, std::string_view resourceType)
68996330b99SSunitha Harish     {
6904a19a7b5SEd Tanous         eventId++;
691613dabeaSEd Tanous         eventMessage["EventId"] = eventId;
692f80a87f2SEd Tanous 
693613dabeaSEd Tanous         eventMessage["EventTimestamp"] =
694613dabeaSEd Tanous             redfish::time_utils::getDateTimeOffsetNow().first;
6954a19a7b5SEd Tanous 
6964a19a7b5SEd Tanous         if (!origin.empty())
6974a19a7b5SEd Tanous         {
698613dabeaSEd Tanous             eventMessage["OriginOfCondition"] = origin;
6994a19a7b5SEd Tanous         }
700613dabeaSEd Tanous 
701f80a87f2SEd Tanous         // MemberId is 0 : since we are sending one event record.
702788b091bSIgor Kanyuka         eventMessage["MemberId"] = "0";
70396330b99SSunitha Harish 
7044a19a7b5SEd Tanous         messages.push_back(Event(eventId, eventMessage));
705f80a87f2SEd Tanous 
706f80a87f2SEd Tanous         for (auto& it : subscriptionsMap)
70796330b99SSunitha Harish         {
708f80a87f2SEd Tanous             std::shared_ptr<Subscription>& entry = it.second;
7095fe4ef35SMyung Bae             if (!eventMatchesFilter(*entry->userSub, eventMessage,
7105fe4ef35SMyung Bae                                     resourceType))
71196330b99SSunitha Harish             {
712f80a87f2SEd Tanous                 BMCWEB_LOG_DEBUG("Filter didn't match");
713f80a87f2SEd Tanous                 continue;
71496330b99SSunitha Harish             }
715f80a87f2SEd Tanous 
716f80a87f2SEd Tanous             nlohmann::json::array_t eventRecord;
717f80a87f2SEd Tanous             eventRecord.emplace_back(eventMessage);
718f80a87f2SEd Tanous 
719613dabeaSEd Tanous             nlohmann::json msgJson;
720613dabeaSEd Tanous 
721613dabeaSEd Tanous             msgJson["@odata.type"] = "#Event.v1_4_0.Event";
722613dabeaSEd Tanous             msgJson["Name"] = "Event Log";
723613dabeaSEd Tanous             msgJson["Id"] = eventId;
724f80a87f2SEd Tanous             msgJson["Events"] = std::move(eventRecord);
725f52c03c1SCarson Labrado 
726f52c03c1SCarson Labrado             std::string strMsg = msgJson.dump(
727f52c03c1SCarson Labrado                 2, ' ', true, nlohmann::json::error_handler_t::replace);
7284a19a7b5SEd Tanous             entry->sendEventToSubscriber(eventId, std::move(strMsg));
72996330b99SSunitha Harish         }
73096330b99SSunitha Harish     }
73123a21a1cSEd Tanous };
732b52664e2SAppaRao Puli 
733b52664e2SAppaRao Puli } // namespace redfish
734