blob: 43517ddc63cb21c3bf21a125c37bd4ea96896422 [file] [log] [blame]
AppaRao Pulie5aaf042020-03-20 01:05:52 +05301/*
2// Copyright (c) 2020 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
AppaRao Pulib52664e2020-04-09 21:36:51 +053017#include "event_service_manager.hpp"
AppaRao Pulie5aaf042020-03-20 01:05:52 +053018
19namespace redfish
20{
21
22static constexpr const std::array<const char*, 1> supportedEvtFormatTypes = {
23 "Event"};
24static constexpr const std::array<const char*, 3> supportedRegPrefixes = {
25 "Base", "OpenBMC", "Task"};
26static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
27 "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
28
29static constexpr const uint8_t maxNoOfSubscriptions = 20;
30
AppaRao Pulie5aaf042020-03-20 01:05:52 +053031class EventService : public Node
32{
33 public:
34 EventService(CrowApp& app) : Node(app, "/redfish/v1/EventService/")
35 {
AppaRao Pulie5aaf042020-03-20 01:05:52 +053036 entityPrivileges = {
37 {boost::beast::http::verb::get, {{"Login"}}},
38 {boost::beast::http::verb::head, {{"Login"}}},
39 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
40 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
41 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
42 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
43 }
44
45 private:
46 void doGet(crow::Response& res, const crow::Request& req,
47 const std::vector<std::string>& params) override
48 {
49 auto asyncResp = std::make_shared<AsyncResp>(res);
50 res.jsonValue = {
51 {"@odata.type", "#EventService.v1_5_0.EventService"},
52 {"Id", "EventService"},
53 {"Name", "Event Service"},
54 {"ServerSentEventUri",
55 "/redfish/v1/EventService/Subscriptions/SSE"},
56 {"Subscriptions",
57 {{"@odata.id", "/redfish/v1/EventService/Subscriptions"}}},
58 {"@odata.id", "/redfish/v1/EventService"}};
59
60 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
AppaRao Pulib52664e2020-04-09 21:36:51 +053061 asyncResp->res.jsonValue["ServiceEnabled"] =
62 EventServiceManager::getInstance().enabled;
AppaRao Pulie5aaf042020-03-20 01:05:52 +053063 asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
AppaRao Pulib52664e2020-04-09 21:36:51 +053064 EventServiceManager::getInstance().retryAttempts;
AppaRao Pulie5aaf042020-03-20 01:05:52 +053065 asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
AppaRao Pulib52664e2020-04-09 21:36:51 +053066 EventServiceManager::getInstance().retryTimeoutInterval;
AppaRao Pulie5aaf042020-03-20 01:05:52 +053067 asyncResp->res.jsonValue["EventFormatTypes"] = supportedEvtFormatTypes;
68 asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
69 }
70
71 void doPatch(crow::Response& res, const crow::Request& req,
72 const std::vector<std::string>& params) override
73 {
74 auto asyncResp = std::make_shared<AsyncResp>(res);
75
76 std::optional<bool> serviceEnabled;
77 std::optional<uint32_t> retryAttemps;
78 std::optional<uint32_t> retryInterval;
79
80 if (!json_util::readJson(req, res, "ServiceEnabled", serviceEnabled,
81 "DeliveryRetryAttempts", retryAttemps,
82 "DeliveryRetryIntervalSeconds", retryInterval))
83 {
84 return;
85 }
86
87 if (serviceEnabled)
88 {
AppaRao Pulib52664e2020-04-09 21:36:51 +053089 EventServiceManager::getInstance().enabled = *serviceEnabled;
AppaRao Pulie5aaf042020-03-20 01:05:52 +053090 }
91
92 if (retryAttemps)
93 {
94 // Supported range [1-3]
95 if ((*retryAttemps < 1) || (*retryAttemps > 3))
96 {
97 messages::queryParameterOutOfRange(
98 asyncResp->res, std::to_string(*retryAttemps),
99 "DeliveryRetryAttempts", "[1-3]");
100 }
101 else
102 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530103 EventServiceManager::getInstance().retryAttempts =
104 *retryAttemps;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530105 }
106 }
107
108 if (retryInterval)
109 {
110 // Supported range [30 - 180]
111 if ((*retryInterval < 30) || (*retryInterval > 180))
112 {
113 messages::queryParameterOutOfRange(
114 asyncResp->res, std::to_string(*retryInterval),
115 "DeliveryRetryIntervalSeconds", "[30-180]");
116 }
117 else
118 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530119 EventServiceManager::getInstance().retryTimeoutInterval =
120 *retryInterval;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530121 }
122 }
123
AppaRao Pulib52664e2020-04-09 21:36:51 +0530124 EventServiceManager::getInstance().updateSubscriptionData();
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530125 }
126};
127
128class EventDestinationCollection : public Node
129{
130 public:
131 EventDestinationCollection(CrowApp& app) :
132 Node(app, "/redfish/v1/EventService/Subscriptions/")
133 {
134 entityPrivileges = {
135 {boost::beast::http::verb::get, {{"Login"}}},
136 {boost::beast::http::verb::head, {{"Login"}}},
137 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
138 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
139 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
140 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
141 }
142
143 private:
144 void doGet(crow::Response& res, const crow::Request& req,
145 const std::vector<std::string>& params) override
146 {
147 auto asyncResp = std::make_shared<AsyncResp>(res);
148
149 res.jsonValue = {
150 {"@odata.type",
151 "#EventDestinationCollection.EventDestinationCollection"},
152 {"@odata.id", "/redfish/v1/EventService/Subscriptions"},
153 {"Name", "Event Destination Collections"}};
154
155 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530156
AppaRao Pulib52664e2020-04-09 21:36:51 +0530157 std::vector<std::string> subscripIds =
158 EventServiceManager::getInstance().getAllIDs();
159 memberArray = nlohmann::json::array();
160 asyncResp->res.jsonValue["Members@odata.count"] = subscripIds.size();
161
162 for (const std::string& id : subscripIds)
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530163 {
164 memberArray.push_back(
165 {{"@odata.id",
AppaRao Pulib52664e2020-04-09 21:36:51 +0530166 "/redfish/v1/EventService/Subscriptions/" + id}});
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530167 }
168 }
169
170 void doPost(crow::Response& res, const crow::Request& req,
171 const std::vector<std::string>& params) override
172 {
173 auto asyncResp = std::make_shared<AsyncResp>(res);
174
AppaRao Pulib52664e2020-04-09 21:36:51 +0530175 if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
176 maxNoOfSubscriptions)
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530177 {
178 messages::eventSubscriptionLimitExceeded(asyncResp->res);
179 return;
180 }
181 std::string destUrl;
182 std::string protocol;
183 std::optional<std::string> context;
184 std::optional<std::string> subscriptionType;
185 std::optional<std::string> eventFormatType;
186 std::optional<std::string> retryPolicy;
187 std::optional<std::vector<std::string>> msgIds;
188 std::optional<std::vector<std::string>> regPrefixes;
189 std::optional<std::vector<nlohmann::json>> headers;
190
191 if (!json_util::readJson(
192 req, res, "Destination", destUrl, "Context", context,
193 "Protocol", protocol, "SubscriptionType", subscriptionType,
194 "EventFormatType", eventFormatType, "HttpHeaders", headers,
195 "RegistryPrefixes", regPrefixes, "MessageIds", msgIds,
196 "DeliveryRetryPolicy", retryPolicy))
197 {
198 return;
199 }
200
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530201 // Validate the URL using regex expression
AppaRao Pulib52664e2020-04-09 21:36:51 +0530202 // Format: <protocol>://<host>:<port>/<uri>
203 // protocol: http/https
204 // host: Exclude ' ', ':', '#', '?'
205 // port: Empty or numeric value with ':' seperator.
206 // uri: Start with '/' and Exclude '#', ' '
207 // Can include query params(ex: '/event?test=1')
208 // TODO: Need to validate hostname extensively(as per rfc)
209 const std::regex urlRegex(
210 "(http|https)://([^/\\x20\\x3f\\x23\\x3a]+):?([0-9]*)(/"
211 "([^\\x20\\x23\\x3f]*\\x3f?([^\\x20\\x23\\x3f])*)?)");
212 std::cmatch match;
213 if (!std::regex_match(destUrl.c_str(), match, urlRegex))
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530214 {
215 messages::propertyValueFormatError(asyncResp->res, destUrl,
216 "Destination");
217 return;
218 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530219
220 std::string uriProto = std::string(match[1].first, match[1].second);
221 if (uriProto == "http")
222 {
223#ifndef BMCWEB_INSECURE_ENABLE_HTTP_PUSH_STYLE_EVENTING
224 messages::propertyValueFormatError(asyncResp->res, destUrl,
225 "Destination");
226 return;
227#endif
228 }
229
230 std::string host = std::string(match[2].first, match[2].second);
231 std::string port = std::string(match[3].first, match[3].second);
232 std::string path = std::string(match[4].first, match[4].second);
233 if (port.empty())
234 {
235 if (uriProto == "http")
236 {
237 port = "80";
238 }
239 else
240 {
241 port = "443";
242 }
243 }
244 if (path.empty())
245 {
246 path = "/";
247 }
248
249 std::shared_ptr<Subscription> subValue =
250 std::make_shared<Subscription>(host, port, path, uriProto);
251
252 subValue->destinationUrl = destUrl;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530253
254 if (subscriptionType)
255 {
256 if (*subscriptionType != "RedfishEvent")
257 {
258 messages::propertyValueNotInList(
259 asyncResp->res, *subscriptionType, "SubscriptionType");
260 return;
261 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530262 subValue->subscriptionType = *subscriptionType;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530263 }
264 else
265 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530266 subValue->subscriptionType = "RedfishEvent"; // Default
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530267 }
268
269 if (protocol != "Redfish")
270 {
271 messages::propertyValueNotInList(asyncResp->res, protocol,
272 "Protocol");
273 return;
274 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530275 subValue->protocol = protocol;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530276
277 if (eventFormatType)
278 {
279 if (std::find(supportedEvtFormatTypes.begin(),
280 supportedEvtFormatTypes.end(),
281 *eventFormatType) == supportedEvtFormatTypes.end())
282 {
283 messages::propertyValueNotInList(
284 asyncResp->res, *eventFormatType, "EventFormatType");
285 return;
286 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530287 subValue->eventFormatType = *eventFormatType;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530288 }
289 else
290 {
291 // If not specified, use default "Event"
AppaRao Pulib52664e2020-04-09 21:36:51 +0530292 subValue->eventFormatType.assign({"Event"});
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530293 }
294
295 if (context)
296 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530297 subValue->customText = *context;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530298 }
299
300 if (headers)
301 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530302 subValue->httpHeaders = *headers;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530303 }
304
305 if (regPrefixes)
306 {
307 for (const std::string& it : *regPrefixes)
308 {
309 if (std::find(supportedRegPrefixes.begin(),
310 supportedRegPrefixes.end(),
311 it) == supportedRegPrefixes.end())
312 {
313 messages::propertyValueNotInList(asyncResp->res, it,
314 "RegistryPrefixes");
315 return;
316 }
317 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530318 subValue->registryPrefixes = *regPrefixes;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530319 }
320
321 if (msgIds)
322 {
323 // Do we need to loop-up MessageRegistry and validate
324 // data for authenticity??? Not mandate, i believe.
AppaRao Pulib52664e2020-04-09 21:36:51 +0530325 subValue->registryMsgIds = *msgIds;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530326 }
327
328 if (retryPolicy)
329 {
330 if (std::find(supportedRetryPolicies.begin(),
331 supportedRetryPolicies.end(),
332 *retryPolicy) == supportedRetryPolicies.end())
333 {
334 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
335 "DeliveryRetryPolicy");
336 return;
337 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530338 subValue->retryPolicy = *retryPolicy;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530339 }
340 else
341 {
342 // Default "TerminateAfterRetries"
AppaRao Pulib52664e2020-04-09 21:36:51 +0530343 subValue->retryPolicy = "TerminateAfterRetries";
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530344 }
345
AppaRao Pulib52664e2020-04-09 21:36:51 +0530346 std::string id =
347 EventServiceManager::getInstance().addSubscription(subValue);
348 if (id.empty())
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530349 {
350 messages::internalError(asyncResp->res);
351 return;
352 }
353
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530354 messages::created(asyncResp->res);
355 asyncResp->res.addHeader(
356 "Location", "/redfish/v1/EventService/Subscriptions/" + id);
357 }
358};
359
360class EventDestination : public Node
361{
362 public:
363 EventDestination(CrowApp& app) :
364 Node(app, "/redfish/v1/EventService/Subscriptions/<str>/",
365 std::string())
366 {
367 entityPrivileges = {
368 {boost::beast::http::verb::get, {{"Login"}}},
369 {boost::beast::http::verb::head, {{"Login"}}},
370 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
371 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
372 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
373 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
374 }
375
376 private:
377 void doGet(crow::Response& res, const crow::Request& req,
378 const std::vector<std::string>& params) override
379 {
380 auto asyncResp = std::make_shared<AsyncResp>(res);
381 if (params.size() != 1)
382 {
383 messages::internalError(asyncResp->res);
384 return;
385 }
386
AppaRao Pulib52664e2020-04-09 21:36:51 +0530387 std::shared_ptr<Subscription> subValue =
388 EventServiceManager::getInstance().getSubscription(params[0]);
389 if (subValue == nullptr)
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530390 {
391 res.result(boost::beast::http::status::not_found);
392 res.end();
393 return;
394 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530395 const std::string& id = params[0];
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530396
397 res.jsonValue = {
398 {"@odata.type", "#EventDestination.v1_7_0.EventDestination"},
399 {"Protocol", "Redfish"}};
400 asyncResp->res.jsonValue["@odata.id"] =
401 "/redfish/v1/EventService/Subscriptions/" + id;
402 asyncResp->res.jsonValue["Id"] = id;
403 asyncResp->res.jsonValue["Name"] = "Event Destination " + id;
AppaRao Pulib52664e2020-04-09 21:36:51 +0530404 asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl;
405 asyncResp->res.jsonValue["Context"] = subValue->customText;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530406 asyncResp->res.jsonValue["SubscriptionType"] =
AppaRao Pulib52664e2020-04-09 21:36:51 +0530407 subValue->subscriptionType;
408 asyncResp->res.jsonValue["HttpHeaders"] = subValue->httpHeaders;
409 asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530410 asyncResp->res.jsonValue["RegistryPrefixes"] =
AppaRao Pulib52664e2020-04-09 21:36:51 +0530411 subValue->registryPrefixes;
412 asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds;
413 asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530414 }
415
416 void doPatch(crow::Response& res, const crow::Request& req,
417 const std::vector<std::string>& params) override
418 {
419 auto asyncResp = std::make_shared<AsyncResp>(res);
420 if (params.size() != 1)
421 {
422 messages::internalError(asyncResp->res);
423 return;
424 }
425
AppaRao Pulib52664e2020-04-09 21:36:51 +0530426 std::shared_ptr<Subscription> subValue =
427 EventServiceManager::getInstance().getSubscription(params[0]);
428 if (subValue == nullptr)
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530429 {
430 res.result(boost::beast::http::status::not_found);
431 res.end();
432 return;
433 }
434
435 std::optional<std::string> context;
436 std::optional<std::string> retryPolicy;
437 std::optional<std::vector<nlohmann::json>> headers;
438
439 if (!json_util::readJson(req, res, "Context", context,
440 "DeliveryRetryPolicy", retryPolicy,
441 "HttpHeaders", headers))
442 {
443 return;
444 }
445
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530446 if (context)
447 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530448 subValue->customText = *context;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530449 }
450
451 if (headers)
452 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530453 subValue->httpHeaders = *headers;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530454 }
455
456 if (retryPolicy)
457 {
458 if (std::find(supportedRetryPolicies.begin(),
459 supportedRetryPolicies.end(),
460 *retryPolicy) == supportedRetryPolicies.end())
461 {
462 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
463 "DeliveryRetryPolicy");
464 return;
465 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530466 subValue->retryPolicy = *retryPolicy;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530467 }
468
AppaRao Pulib52664e2020-04-09 21:36:51 +0530469 EventServiceManager::getInstance().updateSubscriptionData();
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530470 }
471
472 void doDelete(crow::Response& res, const crow::Request& req,
473 const std::vector<std::string>& params) override
474 {
475 auto asyncResp = std::make_shared<AsyncResp>(res);
476
477 if (params.size() != 1)
478 {
479 messages::internalError(asyncResp->res);
480 return;
481 }
482
AppaRao Pulib52664e2020-04-09 21:36:51 +0530483 if (!EventServiceManager::getInstance().isSubscriptionExist(params[0]))
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530484 {
485 res.result(boost::beast::http::status::not_found);
486 res.end();
487 return;
488 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530489 EventServiceManager::getInstance().deleteSubscription(params[0]);
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530490 }
491};
492
493} // namespace redfish