xref: /openbmc/bmcweb/features/redfish/lib/event_service.hpp (revision cb13a39253848ece442971301ade9c09d98bf08e)
1e5aaf047SAppaRao Puli /*
2e5aaf047SAppaRao Puli // Copyright (c) 2020 Intel Corporation
3e5aaf047SAppaRao Puli //
4e5aaf047SAppaRao Puli // Licensed under the Apache License, Version 2.0 (the "License");
5e5aaf047SAppaRao Puli // you may not use this file except in compliance with the License.
6e5aaf047SAppaRao Puli // You may obtain a copy of the License at
7e5aaf047SAppaRao Puli //
8e5aaf047SAppaRao Puli //      http://www.apache.org/licenses/LICENSE-2.0
9e5aaf047SAppaRao Puli //
10e5aaf047SAppaRao Puli // Unless required by applicable law or agreed to in writing, software
11e5aaf047SAppaRao Puli // distributed under the License is distributed on an "AS IS" BASIS,
12e5aaf047SAppaRao Puli // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e5aaf047SAppaRao Puli // See the License for the specific language governing permissions and
14e5aaf047SAppaRao Puli // limitations under the License.
15e5aaf047SAppaRao Puli */
16e5aaf047SAppaRao Puli #pragma once
17b52664e2SAppaRao Puli #include "event_service_manager.hpp"
18e5aaf047SAppaRao Puli 
19e5aaf047SAppaRao Puli namespace redfish
20e5aaf047SAppaRao Puli {
21e5aaf047SAppaRao Puli 
22156d6b00SAppaRao Puli static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
23156d6b00SAppaRao Puli     eventFormatType, metricReportFormatType};
24e5aaf047SAppaRao Puli static constexpr const std::array<const char*, 3> supportedRegPrefixes = {
25e5aaf047SAppaRao Puli     "Base", "OpenBMC", "Task"};
26e5aaf047SAppaRao Puli static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
27e5aaf047SAppaRao Puli     "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
28e5aaf047SAppaRao Puli 
29e56f254cSSunitha Harish #ifdef BMCWEB_ENABLE_IBM_MANAGEMENT_CONSOLE
30e56f254cSSunitha Harish static constexpr const std::array<const char*, 2> supportedResourceTypes = {
31e56f254cSSunitha Harish     "IBMConfigFile", "Task"};
32e56f254cSSunitha Harish #else
33e56f254cSSunitha Harish static constexpr const std::array<const char*, 1> supportedResourceTypes = {
34e56f254cSSunitha Harish     "Task"};
35e56f254cSSunitha Harish #endif
36e56f254cSSunitha Harish 
37e5aaf047SAppaRao Puli static constexpr const uint8_t maxNoOfSubscriptions = 20;
38e5aaf047SAppaRao Puli 
39e5aaf047SAppaRao Puli class EventService : public Node
40e5aaf047SAppaRao Puli {
41e5aaf047SAppaRao Puli   public:
4252cc112dSEd Tanous     EventService(App& app) : Node(app, "/redfish/v1/EventService/")
43e5aaf047SAppaRao Puli     {
44e5aaf047SAppaRao Puli         entityPrivileges = {
45e5aaf047SAppaRao Puli             {boost::beast::http::verb::get, {{"Login"}}},
46e5aaf047SAppaRao Puli             {boost::beast::http::verb::head, {{"Login"}}},
47e5aaf047SAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
48e5aaf047SAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
49e5aaf047SAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
50e5aaf047SAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
51e5aaf047SAppaRao Puli     }
52e5aaf047SAppaRao Puli 
53e5aaf047SAppaRao Puli   private:
54*cb13a392SEd Tanous     void doGet(crow::Response& res, const crow::Request&,
55*cb13a392SEd Tanous                const std::vector<std::string>&) override
56e5aaf047SAppaRao Puli     {
57e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
58e5aaf047SAppaRao Puli         res.jsonValue = {
59e5aaf047SAppaRao Puli             {"@odata.type", "#EventService.v1_5_0.EventService"},
60e5aaf047SAppaRao Puli             {"Id", "EventService"},
61e5aaf047SAppaRao Puli             {"Name", "Event Service"},
62e5aaf047SAppaRao Puli             {"ServerSentEventUri",
63e5aaf047SAppaRao Puli              "/redfish/v1/EventService/Subscriptions/SSE"},
64e5aaf047SAppaRao Puli             {"Subscriptions",
65e5aaf047SAppaRao Puli              {{"@odata.id", "/redfish/v1/EventService/Subscriptions"}}},
660b4bdd93SAppaRao Puli             {"Actions",
670b4bdd93SAppaRao Puli              {{"#EventService.SubmitTestEvent",
680b4bdd93SAppaRao Puli                {{"target", "/redfish/v1/EventService/Actions/"
690b4bdd93SAppaRao Puli                            "EventService.SubmitTestEvent"}}}}},
70e5aaf047SAppaRao Puli             {"@odata.id", "/redfish/v1/EventService"}};
71e5aaf047SAppaRao Puli 
727d1cc387SAppaRao Puli         const auto& [enabled, retryAttempts, retryTimeoutInterval] =
737d1cc387SAppaRao Puli             EventServiceManager::getInstance().getEventServiceConfig();
747d1cc387SAppaRao Puli 
757d1cc387SAppaRao Puli         asyncResp->res.jsonValue["Status"]["State"] =
767d1cc387SAppaRao Puli             (enabled ? "Enabled" : "Disabled");
777d1cc387SAppaRao Puli         asyncResp->res.jsonValue["ServiceEnabled"] = enabled;
787d1cc387SAppaRao Puli         asyncResp->res.jsonValue["DeliveryRetryAttempts"] = retryAttempts;
79e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
807d1cc387SAppaRao Puli             retryTimeoutInterval;
81e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["EventFormatTypes"] = supportedEvtFormatTypes;
82e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
83e56f254cSSunitha Harish         asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes;
8407941a88SAyushi Smriti 
8507941a88SAyushi Smriti         nlohmann::json supportedSSEFilters = {
8607941a88SAyushi Smriti             {"EventFormatType", true},        {"MessageId", true},
8707941a88SAyushi Smriti             {"MetricReportDefinition", true}, {"RegistryPrefix", true},
8807941a88SAyushi Smriti             {"OriginResource", false},        {"ResourceType", false}};
8907941a88SAyushi Smriti 
9007941a88SAyushi Smriti         asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
9107941a88SAyushi Smriti             supportedSSEFilters;
92e5aaf047SAppaRao Puli     }
93e5aaf047SAppaRao Puli 
94e5aaf047SAppaRao Puli     void doPatch(crow::Response& res, const crow::Request& req,
95*cb13a392SEd Tanous                  const std::vector<std::string>&) override
96e5aaf047SAppaRao Puli     {
97e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
98e5aaf047SAppaRao Puli 
99e5aaf047SAppaRao Puli         std::optional<bool> serviceEnabled;
100e5aaf047SAppaRao Puli         std::optional<uint32_t> retryAttemps;
101e5aaf047SAppaRao Puli         std::optional<uint32_t> retryInterval;
102e5aaf047SAppaRao Puli 
103e5aaf047SAppaRao Puli         if (!json_util::readJson(req, res, "ServiceEnabled", serviceEnabled,
104e5aaf047SAppaRao Puli                                  "DeliveryRetryAttempts", retryAttemps,
105e5aaf047SAppaRao Puli                                  "DeliveryRetryIntervalSeconds", retryInterval))
106e5aaf047SAppaRao Puli         {
107e5aaf047SAppaRao Puli             return;
108e5aaf047SAppaRao Puli         }
109e5aaf047SAppaRao Puli 
1107d1cc387SAppaRao Puli         auto [enabled, retryCount, retryTimeoutInterval] =
1117d1cc387SAppaRao Puli             EventServiceManager::getInstance().getEventServiceConfig();
1127d1cc387SAppaRao Puli 
113e5aaf047SAppaRao Puli         if (serviceEnabled)
114e5aaf047SAppaRao Puli         {
1157d1cc387SAppaRao Puli             enabled = *serviceEnabled;
116e5aaf047SAppaRao Puli         }
117e5aaf047SAppaRao Puli 
118e5aaf047SAppaRao Puli         if (retryAttemps)
119e5aaf047SAppaRao Puli         {
120e5aaf047SAppaRao Puli             // Supported range [1-3]
121e5aaf047SAppaRao Puli             if ((*retryAttemps < 1) || (*retryAttemps > 3))
122e5aaf047SAppaRao Puli             {
123e5aaf047SAppaRao Puli                 messages::queryParameterOutOfRange(
124e5aaf047SAppaRao Puli                     asyncResp->res, std::to_string(*retryAttemps),
125e5aaf047SAppaRao Puli                     "DeliveryRetryAttempts", "[1-3]");
126e5aaf047SAppaRao Puli             }
127e5aaf047SAppaRao Puli             else
128e5aaf047SAppaRao Puli             {
1297d1cc387SAppaRao Puli                 retryCount = *retryAttemps;
130e5aaf047SAppaRao Puli             }
131e5aaf047SAppaRao Puli         }
132e5aaf047SAppaRao Puli 
133e5aaf047SAppaRao Puli         if (retryInterval)
134e5aaf047SAppaRao Puli         {
135e5aaf047SAppaRao Puli             // Supported range [30 - 180]
136e5aaf047SAppaRao Puli             if ((*retryInterval < 30) || (*retryInterval > 180))
137e5aaf047SAppaRao Puli             {
138e5aaf047SAppaRao Puli                 messages::queryParameterOutOfRange(
139e5aaf047SAppaRao Puli                     asyncResp->res, std::to_string(*retryInterval),
140e5aaf047SAppaRao Puli                     "DeliveryRetryIntervalSeconds", "[30-180]");
141e5aaf047SAppaRao Puli             }
142e5aaf047SAppaRao Puli             else
143e5aaf047SAppaRao Puli             {
1447d1cc387SAppaRao Puli                 retryTimeoutInterval = *retryInterval;
145e5aaf047SAppaRao Puli             }
146e5aaf047SAppaRao Puli         }
147e5aaf047SAppaRao Puli 
1487d1cc387SAppaRao Puli         EventServiceManager::getInstance().setEventServiceConfig(
1497d1cc387SAppaRao Puli             std::make_tuple(enabled, retryCount, retryTimeoutInterval));
150e5aaf047SAppaRao Puli     }
151e5aaf047SAppaRao Puli };
152e5aaf047SAppaRao Puli 
1530b4bdd93SAppaRao Puli class SubmitTestEvent : public Node
1540b4bdd93SAppaRao Puli {
1550b4bdd93SAppaRao Puli   public:
15652cc112dSEd Tanous     SubmitTestEvent(App& app) :
1570b4bdd93SAppaRao Puli         Node(app,
1580b4bdd93SAppaRao Puli              "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
1590b4bdd93SAppaRao Puli     {
1600b4bdd93SAppaRao Puli         entityPrivileges = {
1610b4bdd93SAppaRao Puli             {boost::beast::http::verb::get, {{"Login"}}},
1620b4bdd93SAppaRao Puli             {boost::beast::http::verb::head, {{"Login"}}},
1630b4bdd93SAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1640b4bdd93SAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1650b4bdd93SAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1660b4bdd93SAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1670b4bdd93SAppaRao Puli     }
1680b4bdd93SAppaRao Puli 
1690b4bdd93SAppaRao Puli   private:
170*cb13a392SEd Tanous     void doPost(crow::Response& res, const crow::Request&,
171*cb13a392SEd Tanous                 const std::vector<std::string>&) override
1720b4bdd93SAppaRao Puli     {
1730b4bdd93SAppaRao Puli         EventServiceManager::getInstance().sendTestEventLog();
1740b4bdd93SAppaRao Puli         res.result(boost::beast::http::status::no_content);
1750b4bdd93SAppaRao Puli         res.end();
1760b4bdd93SAppaRao Puli     }
1770b4bdd93SAppaRao Puli };
1780b4bdd93SAppaRao Puli 
179e5aaf047SAppaRao Puli class EventDestinationCollection : public Node
180e5aaf047SAppaRao Puli {
181e5aaf047SAppaRao Puli   public:
18252cc112dSEd Tanous     EventDestinationCollection(App& app) :
183e5aaf047SAppaRao Puli         Node(app, "/redfish/v1/EventService/Subscriptions/")
184e5aaf047SAppaRao Puli     {
185e5aaf047SAppaRao Puli         entityPrivileges = {
186e5aaf047SAppaRao Puli             {boost::beast::http::verb::get, {{"Login"}}},
187e5aaf047SAppaRao Puli             {boost::beast::http::verb::head, {{"Login"}}},
188e5aaf047SAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
189e5aaf047SAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
190e5aaf047SAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
191e5aaf047SAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
192e5aaf047SAppaRao Puli     }
193e5aaf047SAppaRao Puli 
194e5aaf047SAppaRao Puli   private:
195*cb13a392SEd Tanous     void doGet(crow::Response& res, const crow::Request&,
196*cb13a392SEd Tanous                const std::vector<std::string>&) override
197e5aaf047SAppaRao Puli     {
198e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
199e5aaf047SAppaRao Puli 
200e5aaf047SAppaRao Puli         res.jsonValue = {
201e5aaf047SAppaRao Puli             {"@odata.type",
202e5aaf047SAppaRao Puli              "#EventDestinationCollection.EventDestinationCollection"},
203e5aaf047SAppaRao Puli             {"@odata.id", "/redfish/v1/EventService/Subscriptions"},
204e5aaf047SAppaRao Puli             {"Name", "Event Destination Collections"}};
205e5aaf047SAppaRao Puli 
206e5aaf047SAppaRao Puli         nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
207e5aaf047SAppaRao Puli 
208b52664e2SAppaRao Puli         std::vector<std::string> subscripIds =
209b52664e2SAppaRao Puli             EventServiceManager::getInstance().getAllIDs();
210b52664e2SAppaRao Puli         memberArray = nlohmann::json::array();
211b52664e2SAppaRao Puli         asyncResp->res.jsonValue["Members@odata.count"] = subscripIds.size();
212b52664e2SAppaRao Puli 
213b52664e2SAppaRao Puli         for (const std::string& id : subscripIds)
214e5aaf047SAppaRao Puli         {
215e5aaf047SAppaRao Puli             memberArray.push_back(
216e5aaf047SAppaRao Puli                 {{"@odata.id",
217b52664e2SAppaRao Puli                   "/redfish/v1/EventService/Subscriptions/" + id}});
218e5aaf047SAppaRao Puli         }
219e5aaf047SAppaRao Puli     }
220e5aaf047SAppaRao Puli 
221e5aaf047SAppaRao Puli     void doPost(crow::Response& res, const crow::Request& req,
222*cb13a392SEd Tanous                 const std::vector<std::string>&) override
223e5aaf047SAppaRao Puli     {
224e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
225e5aaf047SAppaRao Puli 
226b52664e2SAppaRao Puli         if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
227b52664e2SAppaRao Puli             maxNoOfSubscriptions)
228e5aaf047SAppaRao Puli         {
229e5aaf047SAppaRao Puli             messages::eventSubscriptionLimitExceeded(asyncResp->res);
230e5aaf047SAppaRao Puli             return;
231e5aaf047SAppaRao Puli         }
232e5aaf047SAppaRao Puli         std::string destUrl;
233e5aaf047SAppaRao Puli         std::string protocol;
234e5aaf047SAppaRao Puli         std::optional<std::string> context;
235e5aaf047SAppaRao Puli         std::optional<std::string> subscriptionType;
23623a21a1cSEd Tanous         std::optional<std::string> eventFormatType2;
237e5aaf047SAppaRao Puli         std::optional<std::string> retryPolicy;
238e5aaf047SAppaRao Puli         std::optional<std::vector<std::string>> msgIds;
239e5aaf047SAppaRao Puli         std::optional<std::vector<std::string>> regPrefixes;
240e56f254cSSunitha Harish         std::optional<std::vector<std::string>> resTypes;
241e5aaf047SAppaRao Puli         std::optional<std::vector<nlohmann::json>> headers;
242144b6318SAppaRao Puli         std::optional<std::vector<nlohmann::json>> mrdJsonArray;
243e5aaf047SAppaRao Puli 
244e5aaf047SAppaRao Puli         if (!json_util::readJson(
245e5aaf047SAppaRao Puli                 req, res, "Destination", destUrl, "Context", context,
246e5aaf047SAppaRao Puli                 "Protocol", protocol, "SubscriptionType", subscriptionType,
24723a21a1cSEd Tanous                 "EventFormatType", eventFormatType2, "HttpHeaders", headers,
248e5aaf047SAppaRao Puli                 "RegistryPrefixes", regPrefixes, "MessageIds", msgIds,
249156d6b00SAppaRao Puli                 "DeliveryRetryPolicy", retryPolicy, "MetricReportDefinitions",
250144b6318SAppaRao Puli                 mrdJsonArray, "ResourceTypes", resTypes))
251e5aaf047SAppaRao Puli         {
252e5aaf047SAppaRao Puli             return;
253e5aaf047SAppaRao Puli         }
254e5aaf047SAppaRao Puli 
255e5aaf047SAppaRao Puli         // Validate the URL using regex expression
256b52664e2SAppaRao Puli         // Format: <protocol>://<host>:<port>/<uri>
257b52664e2SAppaRao Puli         // protocol: http/https
258b52664e2SAppaRao Puli         // host: Exclude ' ', ':', '#', '?'
2594e0453b1SGunnar Mills         // port: Empty or numeric value with ':' separator.
260b52664e2SAppaRao Puli         // uri: Start with '/' and Exclude '#', ' '
261b52664e2SAppaRao Puli         //      Can include query params(ex: '/event?test=1')
262b52664e2SAppaRao Puli         // TODO: Need to validate hostname extensively(as per rfc)
263b52664e2SAppaRao Puli         const std::regex urlRegex(
264b52664e2SAppaRao Puli             "(http|https)://([^/\\x20\\x3f\\x23\\x3a]+):?([0-9]*)(/"
265b52664e2SAppaRao Puli             "([^\\x20\\x23\\x3f]*\\x3f?([^\\x20\\x23\\x3f])*)?)");
266b52664e2SAppaRao Puli         std::cmatch match;
267b52664e2SAppaRao Puli         if (!std::regex_match(destUrl.c_str(), match, urlRegex))
268e5aaf047SAppaRao Puli         {
269e5aaf047SAppaRao Puli             messages::propertyValueFormatError(asyncResp->res, destUrl,
270e5aaf047SAppaRao Puli                                                "Destination");
271e5aaf047SAppaRao Puli             return;
272e5aaf047SAppaRao Puli         }
273b52664e2SAppaRao Puli 
274b52664e2SAppaRao Puli         std::string uriProto = std::string(match[1].first, match[1].second);
275b52664e2SAppaRao Puli         if (uriProto == "http")
276b52664e2SAppaRao Puli         {
277b52664e2SAppaRao Puli #ifndef BMCWEB_INSECURE_ENABLE_HTTP_PUSH_STYLE_EVENTING
278b52664e2SAppaRao Puli             messages::propertyValueFormatError(asyncResp->res, destUrl,
279b52664e2SAppaRao Puli                                                "Destination");
280b52664e2SAppaRao Puli             return;
281b52664e2SAppaRao Puli #endif
282b52664e2SAppaRao Puli         }
283b52664e2SAppaRao Puli 
284b52664e2SAppaRao Puli         std::string host = std::string(match[2].first, match[2].second);
285b52664e2SAppaRao Puli         std::string port = std::string(match[3].first, match[3].second);
286b52664e2SAppaRao Puli         std::string path = std::string(match[4].first, match[4].second);
287b52664e2SAppaRao Puli         if (port.empty())
288b52664e2SAppaRao Puli         {
289b52664e2SAppaRao Puli             if (uriProto == "http")
290b52664e2SAppaRao Puli             {
291b52664e2SAppaRao Puli                 port = "80";
292b52664e2SAppaRao Puli             }
293b52664e2SAppaRao Puli             else
294b52664e2SAppaRao Puli             {
295b52664e2SAppaRao Puli                 port = "443";
296b52664e2SAppaRao Puli             }
297b52664e2SAppaRao Puli         }
298b52664e2SAppaRao Puli         if (path.empty())
299b52664e2SAppaRao Puli         {
300b52664e2SAppaRao Puli             path = "/";
301b52664e2SAppaRao Puli         }
302b52664e2SAppaRao Puli 
303b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
304b52664e2SAppaRao Puli             std::make_shared<Subscription>(host, port, path, uriProto);
305b52664e2SAppaRao Puli 
306b52664e2SAppaRao Puli         subValue->destinationUrl = destUrl;
307e5aaf047SAppaRao Puli 
308e5aaf047SAppaRao Puli         if (subscriptionType)
309e5aaf047SAppaRao Puli         {
310e5aaf047SAppaRao Puli             if (*subscriptionType != "RedfishEvent")
311e5aaf047SAppaRao Puli             {
312e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(
313e5aaf047SAppaRao Puli                     asyncResp->res, *subscriptionType, "SubscriptionType");
314e5aaf047SAppaRao Puli                 return;
315e5aaf047SAppaRao Puli             }
316b52664e2SAppaRao Puli             subValue->subscriptionType = *subscriptionType;
317e5aaf047SAppaRao Puli         }
318e5aaf047SAppaRao Puli         else
319e5aaf047SAppaRao Puli         {
320b52664e2SAppaRao Puli             subValue->subscriptionType = "RedfishEvent"; // Default
321e5aaf047SAppaRao Puli         }
322e5aaf047SAppaRao Puli 
323e5aaf047SAppaRao Puli         if (protocol != "Redfish")
324e5aaf047SAppaRao Puli         {
325e5aaf047SAppaRao Puli             messages::propertyValueNotInList(asyncResp->res, protocol,
326e5aaf047SAppaRao Puli                                              "Protocol");
327e5aaf047SAppaRao Puli             return;
328e5aaf047SAppaRao Puli         }
329b52664e2SAppaRao Puli         subValue->protocol = protocol;
330e5aaf047SAppaRao Puli 
33123a21a1cSEd Tanous         if (eventFormatType2)
332e5aaf047SAppaRao Puli         {
333e5aaf047SAppaRao Puli             if (std::find(supportedEvtFormatTypes.begin(),
334e5aaf047SAppaRao Puli                           supportedEvtFormatTypes.end(),
33523a21a1cSEd Tanous                           *eventFormatType2) == supportedEvtFormatTypes.end())
336e5aaf047SAppaRao Puli             {
337e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(
33823a21a1cSEd Tanous                     asyncResp->res, *eventFormatType2, "EventFormatType");
339e5aaf047SAppaRao Puli                 return;
340e5aaf047SAppaRao Puli             }
34123a21a1cSEd Tanous             subValue->eventFormatType = *eventFormatType2;
342e5aaf047SAppaRao Puli         }
343e5aaf047SAppaRao Puli         else
344e5aaf047SAppaRao Puli         {
345e5aaf047SAppaRao Puli             // If not specified, use default "Event"
34623a21a1cSEd Tanous             subValue->eventFormatType = "Event";
347e5aaf047SAppaRao Puli         }
348e5aaf047SAppaRao Puli 
349e5aaf047SAppaRao Puli         if (context)
350e5aaf047SAppaRao Puli         {
351b52664e2SAppaRao Puli             subValue->customText = *context;
352e5aaf047SAppaRao Puli         }
353e5aaf047SAppaRao Puli 
354e5aaf047SAppaRao Puli         if (headers)
355e5aaf047SAppaRao Puli         {
356b52664e2SAppaRao Puli             subValue->httpHeaders = *headers;
357e5aaf047SAppaRao Puli         }
358e5aaf047SAppaRao Puli 
359e5aaf047SAppaRao Puli         if (regPrefixes)
360e5aaf047SAppaRao Puli         {
361e5aaf047SAppaRao Puli             for (const std::string& it : *regPrefixes)
362e5aaf047SAppaRao Puli             {
363e5aaf047SAppaRao Puli                 if (std::find(supportedRegPrefixes.begin(),
364e5aaf047SAppaRao Puli                               supportedRegPrefixes.end(),
365e5aaf047SAppaRao Puli                               it) == supportedRegPrefixes.end())
366e5aaf047SAppaRao Puli                 {
367e5aaf047SAppaRao Puli                     messages::propertyValueNotInList(asyncResp->res, it,
368e5aaf047SAppaRao Puli                                                      "RegistryPrefixes");
369e5aaf047SAppaRao Puli                     return;
370e5aaf047SAppaRao Puli                 }
371e5aaf047SAppaRao Puli             }
372b52664e2SAppaRao Puli             subValue->registryPrefixes = *regPrefixes;
373e5aaf047SAppaRao Puli         }
374e5aaf047SAppaRao Puli 
375e56f254cSSunitha Harish         if (resTypes)
376e56f254cSSunitha Harish         {
377e56f254cSSunitha Harish             for (const std::string& it : *resTypes)
378e56f254cSSunitha Harish             {
379e56f254cSSunitha Harish                 if (std::find(supportedResourceTypes.begin(),
380e56f254cSSunitha Harish                               supportedResourceTypes.end(),
381e56f254cSSunitha Harish                               it) == supportedResourceTypes.end())
382e56f254cSSunitha Harish                 {
383e56f254cSSunitha Harish                     messages::propertyValueNotInList(asyncResp->res, it,
384e56f254cSSunitha Harish                                                      "ResourceTypes");
385e56f254cSSunitha Harish                     return;
386e56f254cSSunitha Harish                 }
387e56f254cSSunitha Harish             }
388e56f254cSSunitha Harish             subValue->resourceTypes = *resTypes;
389e56f254cSSunitha Harish         }
390e56f254cSSunitha Harish 
391e5aaf047SAppaRao Puli         if (msgIds)
392e5aaf047SAppaRao Puli         {
393e5aaf047SAppaRao Puli             // Do we need to loop-up MessageRegistry and validate
394e5aaf047SAppaRao Puli             // data for authenticity??? Not mandate, i believe.
395b52664e2SAppaRao Puli             subValue->registryMsgIds = *msgIds;
396e5aaf047SAppaRao Puli         }
397e5aaf047SAppaRao Puli 
398e5aaf047SAppaRao Puli         if (retryPolicy)
399e5aaf047SAppaRao Puli         {
400e5aaf047SAppaRao Puli             if (std::find(supportedRetryPolicies.begin(),
401e5aaf047SAppaRao Puli                           supportedRetryPolicies.end(),
402e5aaf047SAppaRao Puli                           *retryPolicy) == supportedRetryPolicies.end())
403e5aaf047SAppaRao Puli             {
404e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
405e5aaf047SAppaRao Puli                                                  "DeliveryRetryPolicy");
406e5aaf047SAppaRao Puli                 return;
407e5aaf047SAppaRao Puli             }
408b52664e2SAppaRao Puli             subValue->retryPolicy = *retryPolicy;
409e5aaf047SAppaRao Puli         }
410e5aaf047SAppaRao Puli         else
411e5aaf047SAppaRao Puli         {
412e5aaf047SAppaRao Puli             // Default "TerminateAfterRetries"
413b52664e2SAppaRao Puli             subValue->retryPolicy = "TerminateAfterRetries";
414e5aaf047SAppaRao Puli         }
415e5aaf047SAppaRao Puli 
416144b6318SAppaRao Puli         if (mrdJsonArray)
417156d6b00SAppaRao Puli         {
418144b6318SAppaRao Puli             for (nlohmann::json& mrdObj : *mrdJsonArray)
419144b6318SAppaRao Puli             {
420144b6318SAppaRao Puli                 std::string mrdUri;
421144b6318SAppaRao Puli                 if (json_util::getValueFromJsonObject(mrdObj, "@odata.id",
422144b6318SAppaRao Puli                                                       mrdUri))
423144b6318SAppaRao Puli                 {
424144b6318SAppaRao Puli                     subValue->metricReportDefinitions.emplace_back(mrdUri);
425144b6318SAppaRao Puli                 }
426144b6318SAppaRao Puli                 else
427144b6318SAppaRao Puli                 {
428144b6318SAppaRao Puli                     messages::propertyValueFormatError(
429144b6318SAppaRao Puli                         asyncResp->res, mrdObj.dump(),
430144b6318SAppaRao Puli                         "MetricReportDefinitions");
431144b6318SAppaRao Puli                     return;
432144b6318SAppaRao Puli                 }
433144b6318SAppaRao Puli             }
434156d6b00SAppaRao Puli         }
435156d6b00SAppaRao Puli 
436b52664e2SAppaRao Puli         std::string id =
437b52664e2SAppaRao Puli             EventServiceManager::getInstance().addSubscription(subValue);
438b52664e2SAppaRao Puli         if (id.empty())
439e5aaf047SAppaRao Puli         {
440e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
441e5aaf047SAppaRao Puli             return;
442e5aaf047SAppaRao Puli         }
443e5aaf047SAppaRao Puli 
444e5aaf047SAppaRao Puli         messages::created(asyncResp->res);
445e5aaf047SAppaRao Puli         asyncResp->res.addHeader(
446e5aaf047SAppaRao Puli             "Location", "/redfish/v1/EventService/Subscriptions/" + id);
447e5aaf047SAppaRao Puli     }
448e5aaf047SAppaRao Puli };
449e5aaf047SAppaRao Puli 
4504bbf237fSAppaRao Puli class EventServiceSSE : public Node
4514bbf237fSAppaRao Puli {
4524bbf237fSAppaRao Puli   public:
45352cc112dSEd Tanous     EventServiceSSE(App& app) :
4544bbf237fSAppaRao Puli         Node(app, "/redfish/v1/EventService/Subscriptions/SSE/")
4554bbf237fSAppaRao Puli     {
4564bbf237fSAppaRao Puli         entityPrivileges = {
4574bbf237fSAppaRao Puli             {boost::beast::http::verb::get, {{"ConfigureManager"}}},
4584bbf237fSAppaRao Puli             {boost::beast::http::verb::head, {{"ConfigureManager"}}},
4594bbf237fSAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
4604bbf237fSAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
4614bbf237fSAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
4624bbf237fSAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
4634bbf237fSAppaRao Puli     }
4644bbf237fSAppaRao Puli 
4654bbf237fSAppaRao Puli   private:
4664bbf237fSAppaRao Puli     void doGet(crow::Response& res, const crow::Request& req,
467*cb13a392SEd Tanous                const std::vector<std::string>&) override
4684bbf237fSAppaRao Puli     {
4694bbf237fSAppaRao Puli         if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
4704bbf237fSAppaRao Puli             maxNoOfSubscriptions)
4714bbf237fSAppaRao Puli         {
4724bbf237fSAppaRao Puli             messages::eventSubscriptionLimitExceeded(res);
4734bbf237fSAppaRao Puli             res.end();
4744bbf237fSAppaRao Puli             return;
4754bbf237fSAppaRao Puli         }
4764bbf237fSAppaRao Puli 
4774bbf237fSAppaRao Puli         std::shared_ptr<crow::Request::Adaptor> sseConn =
4784bbf237fSAppaRao Puli             std::make_shared<crow::Request::Adaptor>(std::move(req.socket()));
4794bbf237fSAppaRao Puli         std::shared_ptr<Subscription> subValue =
4804bbf237fSAppaRao Puli             std::make_shared<Subscription>(sseConn);
4814bbf237fSAppaRao Puli 
4824bbf237fSAppaRao Puli         // GET on this URI means, Its SSE subscriptionType.
4834bbf237fSAppaRao Puli         subValue->subscriptionType = "SSE";
48407941a88SAyushi Smriti 
48507941a88SAyushi Smriti         // Default values
4864bbf237fSAppaRao Puli         subValue->protocol = "Redfish";
48707941a88SAyushi Smriti         subValue->retryPolicy = "TerminateAfterRetries";
4884bbf237fSAppaRao Puli 
4895a7e877eSJames Feist         boost::urls::url_view::params_type::iterator it =
4905a7e877eSJames Feist             req.urlParams.find("$filter");
4915a7e877eSJames Feist         if (it == req.urlParams.end())
4924bbf237fSAppaRao Puli         {
4934bbf237fSAppaRao Puli             subValue->eventFormatType = "Event";
4944bbf237fSAppaRao Puli         }
4955a7e877eSJames Feist 
4964bbf237fSAppaRao Puli         else
4974bbf237fSAppaRao Puli         {
4985a7e877eSJames Feist             std::string filters = it->value();
49907941a88SAyushi Smriti             // Reading from query params.
50007941a88SAyushi Smriti             bool status = readSSEQueryParams(
50107941a88SAyushi Smriti                 filters, subValue->eventFormatType, subValue->registryMsgIds,
50207941a88SAyushi Smriti                 subValue->registryPrefixes, subValue->metricReportDefinitions);
50307941a88SAyushi Smriti 
50407941a88SAyushi Smriti             if (!status)
50507941a88SAyushi Smriti             {
50607941a88SAyushi Smriti                 messages::invalidObject(res, filters);
50707941a88SAyushi Smriti                 return;
50807941a88SAyushi Smriti             }
50907941a88SAyushi Smriti 
51007941a88SAyushi Smriti             if (!subValue->eventFormatType.empty())
51107941a88SAyushi Smriti             {
51207941a88SAyushi Smriti                 if (std::find(supportedEvtFormatTypes.begin(),
51307941a88SAyushi Smriti                               supportedEvtFormatTypes.end(),
51407941a88SAyushi Smriti                               subValue->eventFormatType) ==
51507941a88SAyushi Smriti                     supportedEvtFormatTypes.end())
51607941a88SAyushi Smriti                 {
51707941a88SAyushi Smriti                     messages::propertyValueNotInList(
51807941a88SAyushi Smriti                         res, subValue->eventFormatType, "EventFormatType");
51907941a88SAyushi Smriti                     return;
52007941a88SAyushi Smriti                 }
52107941a88SAyushi Smriti             }
52207941a88SAyushi Smriti             else
52307941a88SAyushi Smriti             {
52407941a88SAyushi Smriti                 // If nothing specified, using default "Event"
52523a21a1cSEd Tanous                 subValue->eventFormatType = "Event";
52607941a88SAyushi Smriti             }
52707941a88SAyushi Smriti 
52807941a88SAyushi Smriti             if (!subValue->registryPrefixes.empty())
52907941a88SAyushi Smriti             {
53007941a88SAyushi Smriti                 for (const std::string& it : subValue->registryPrefixes)
53107941a88SAyushi Smriti                 {
53207941a88SAyushi Smriti                     if (std::find(supportedRegPrefixes.begin(),
53307941a88SAyushi Smriti                                   supportedRegPrefixes.end(),
53407941a88SAyushi Smriti                                   it) == supportedRegPrefixes.end())
53507941a88SAyushi Smriti                     {
53607941a88SAyushi Smriti                         messages::propertyValueNotInList(res, it,
53707941a88SAyushi Smriti                                                          "RegistryPrefixes");
53807941a88SAyushi Smriti                         return;
53907941a88SAyushi Smriti                     }
54007941a88SAyushi Smriti                 }
54107941a88SAyushi Smriti             }
5424bbf237fSAppaRao Puli         }
5434bbf237fSAppaRao Puli 
5444bbf237fSAppaRao Puli         std::string id =
5454bbf237fSAppaRao Puli             EventServiceManager::getInstance().addSubscription(subValue, false);
5464bbf237fSAppaRao Puli         if (id.empty())
5474bbf237fSAppaRao Puli         {
5484bbf237fSAppaRao Puli             messages::internalError(res);
5494bbf237fSAppaRao Puli             res.end();
5504bbf237fSAppaRao Puli             return;
5514bbf237fSAppaRao Puli         }
5524bbf237fSAppaRao Puli     }
5534bbf237fSAppaRao Puli };
5544bbf237fSAppaRao Puli 
555e5aaf047SAppaRao Puli class EventDestination : public Node
556e5aaf047SAppaRao Puli {
557e5aaf047SAppaRao Puli   public:
55852cc112dSEd Tanous     EventDestination(App& app) :
559e5aaf047SAppaRao Puli         Node(app, "/redfish/v1/EventService/Subscriptions/<str>/",
560e5aaf047SAppaRao Puli              std::string())
561e5aaf047SAppaRao Puli     {
562e5aaf047SAppaRao Puli         entityPrivileges = {
563e5aaf047SAppaRao Puli             {boost::beast::http::verb::get, {{"Login"}}},
564e5aaf047SAppaRao Puli             {boost::beast::http::verb::head, {{"Login"}}},
565e5aaf047SAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
566e5aaf047SAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
567e5aaf047SAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
568e5aaf047SAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
569e5aaf047SAppaRao Puli     }
570e5aaf047SAppaRao Puli 
571e5aaf047SAppaRao Puli   private:
572*cb13a392SEd Tanous     void doGet(crow::Response& res, const crow::Request&,
573e5aaf047SAppaRao Puli                const std::vector<std::string>& params) override
574e5aaf047SAppaRao Puli     {
575e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
576e5aaf047SAppaRao Puli         if (params.size() != 1)
577e5aaf047SAppaRao Puli         {
578e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
579e5aaf047SAppaRao Puli             return;
580e5aaf047SAppaRao Puli         }
581e5aaf047SAppaRao Puli 
582b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
583b52664e2SAppaRao Puli             EventServiceManager::getInstance().getSubscription(params[0]);
584b52664e2SAppaRao Puli         if (subValue == nullptr)
585e5aaf047SAppaRao Puli         {
586e5aaf047SAppaRao Puli             res.result(boost::beast::http::status::not_found);
587e5aaf047SAppaRao Puli             res.end();
588e5aaf047SAppaRao Puli             return;
589e5aaf047SAppaRao Puli         }
590b52664e2SAppaRao Puli         const std::string& id = params[0];
591e5aaf047SAppaRao Puli 
592e5aaf047SAppaRao Puli         res.jsonValue = {
593e5aaf047SAppaRao Puli             {"@odata.type", "#EventDestination.v1_7_0.EventDestination"},
594e5aaf047SAppaRao Puli             {"Protocol", "Redfish"}};
595e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["@odata.id"] =
596e5aaf047SAppaRao Puli             "/redfish/v1/EventService/Subscriptions/" + id;
597e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["Id"] = id;
598e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["Name"] = "Event Destination " + id;
599b52664e2SAppaRao Puli         asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl;
600b52664e2SAppaRao Puli         asyncResp->res.jsonValue["Context"] = subValue->customText;
601e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["SubscriptionType"] =
602b52664e2SAppaRao Puli             subValue->subscriptionType;
603b52664e2SAppaRao Puli         asyncResp->res.jsonValue["HttpHeaders"] = subValue->httpHeaders;
604b52664e2SAppaRao Puli         asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType;
605e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["RegistryPrefixes"] =
606b52664e2SAppaRao Puli             subValue->registryPrefixes;
607e56f254cSSunitha Harish         asyncResp->res.jsonValue["ResourceTypes"] = subValue->resourceTypes;
608e56f254cSSunitha Harish 
609b52664e2SAppaRao Puli         asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds;
610b52664e2SAppaRao Puli         asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy;
611144b6318SAppaRao Puli 
612144b6318SAppaRao Puli         std::vector<nlohmann::json> mrdJsonArray;
613144b6318SAppaRao Puli         for (const auto& mdrUri : subValue->metricReportDefinitions)
614144b6318SAppaRao Puli         {
615144b6318SAppaRao Puli             mrdJsonArray.push_back({{"@odata.id", mdrUri}});
616144b6318SAppaRao Puli         }
617144b6318SAppaRao Puli         asyncResp->res.jsonValue["MetricReportDefinitions"] = mrdJsonArray;
618e5aaf047SAppaRao Puli     }
619e5aaf047SAppaRao Puli 
620e5aaf047SAppaRao Puli     void doPatch(crow::Response& res, const crow::Request& req,
621e5aaf047SAppaRao Puli                  const std::vector<std::string>& params) override
622e5aaf047SAppaRao Puli     {
623e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
624e5aaf047SAppaRao Puli         if (params.size() != 1)
625e5aaf047SAppaRao Puli         {
626e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
627e5aaf047SAppaRao Puli             return;
628e5aaf047SAppaRao Puli         }
629e5aaf047SAppaRao Puli 
630b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
631b52664e2SAppaRao Puli             EventServiceManager::getInstance().getSubscription(params[0]);
632b52664e2SAppaRao Puli         if (subValue == nullptr)
633e5aaf047SAppaRao Puli         {
634e5aaf047SAppaRao Puli             res.result(boost::beast::http::status::not_found);
635e5aaf047SAppaRao Puli             res.end();
636e5aaf047SAppaRao Puli             return;
637e5aaf047SAppaRao Puli         }
638e5aaf047SAppaRao Puli 
639e5aaf047SAppaRao Puli         std::optional<std::string> context;
640e5aaf047SAppaRao Puli         std::optional<std::string> retryPolicy;
641e5aaf047SAppaRao Puli         std::optional<std::vector<nlohmann::json>> headers;
642e5aaf047SAppaRao Puli 
643e5aaf047SAppaRao Puli         if (!json_util::readJson(req, res, "Context", context,
644e5aaf047SAppaRao Puli                                  "DeliveryRetryPolicy", retryPolicy,
645e5aaf047SAppaRao Puli                                  "HttpHeaders", headers))
646e5aaf047SAppaRao Puli         {
647e5aaf047SAppaRao Puli             return;
648e5aaf047SAppaRao Puli         }
649e5aaf047SAppaRao Puli 
650e5aaf047SAppaRao Puli         if (context)
651e5aaf047SAppaRao Puli         {
652b52664e2SAppaRao Puli             subValue->customText = *context;
653e5aaf047SAppaRao Puli         }
654e5aaf047SAppaRao Puli 
655e5aaf047SAppaRao Puli         if (headers)
656e5aaf047SAppaRao Puli         {
657b52664e2SAppaRao Puli             subValue->httpHeaders = *headers;
658e5aaf047SAppaRao Puli         }
659e5aaf047SAppaRao Puli 
660e5aaf047SAppaRao Puli         if (retryPolicy)
661e5aaf047SAppaRao Puli         {
662e5aaf047SAppaRao Puli             if (std::find(supportedRetryPolicies.begin(),
663e5aaf047SAppaRao Puli                           supportedRetryPolicies.end(),
664e5aaf047SAppaRao Puli                           *retryPolicy) == supportedRetryPolicies.end())
665e5aaf047SAppaRao Puli             {
666e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
667e5aaf047SAppaRao Puli                                                  "DeliveryRetryPolicy");
668e5aaf047SAppaRao Puli                 return;
669e5aaf047SAppaRao Puli             }
670b52664e2SAppaRao Puli             subValue->retryPolicy = *retryPolicy;
671fe44eb0bSAyushi Smriti             subValue->updateRetryPolicy();
672e5aaf047SAppaRao Puli         }
673e5aaf047SAppaRao Puli 
674b52664e2SAppaRao Puli         EventServiceManager::getInstance().updateSubscriptionData();
675e5aaf047SAppaRao Puli     }
676e5aaf047SAppaRao Puli 
677*cb13a392SEd Tanous     void doDelete(crow::Response& res, const crow::Request&,
678e5aaf047SAppaRao Puli                   const std::vector<std::string>& params) override
679e5aaf047SAppaRao Puli     {
680e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
681e5aaf047SAppaRao Puli 
682e5aaf047SAppaRao Puli         if (params.size() != 1)
683e5aaf047SAppaRao Puli         {
684e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
685e5aaf047SAppaRao Puli             return;
686e5aaf047SAppaRao Puli         }
687e5aaf047SAppaRao Puli 
688b52664e2SAppaRao Puli         if (!EventServiceManager::getInstance().isSubscriptionExist(params[0]))
689e5aaf047SAppaRao Puli         {
690e5aaf047SAppaRao Puli             res.result(boost::beast::http::status::not_found);
691e5aaf047SAppaRao Puli             res.end();
692e5aaf047SAppaRao Puli             return;
693e5aaf047SAppaRao Puli         }
694b52664e2SAppaRao Puli         EventServiceManager::getInstance().deleteSubscription(params[0]);
695e5aaf047SAppaRao Puli     }
696e5aaf047SAppaRao Puli };
697e5aaf047SAppaRao Puli 
698e5aaf047SAppaRao Puli } // namespace redfish
699