blob: 3e3435ff498d8903d4e9530f1c8c025b32e65997 [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"}}},
AppaRao Puli0b4bdd92020-04-14 17:57:45 +053058 {"Actions",
59 {{"#EventService.SubmitTestEvent",
60 {{"target", "/redfish/v1/EventService/Actions/"
61 "EventService.SubmitTestEvent"}}}}},
AppaRao Pulie5aaf042020-03-20 01:05:52 +053062 {"@odata.id", "/redfish/v1/EventService"}};
63
64 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
AppaRao Pulib52664e2020-04-09 21:36:51 +053065 asyncResp->res.jsonValue["ServiceEnabled"] =
66 EventServiceManager::getInstance().enabled;
AppaRao Pulie5aaf042020-03-20 01:05:52 +053067 asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
AppaRao Pulib52664e2020-04-09 21:36:51 +053068 EventServiceManager::getInstance().retryAttempts;
AppaRao Pulie5aaf042020-03-20 01:05:52 +053069 asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
AppaRao Pulib52664e2020-04-09 21:36:51 +053070 EventServiceManager::getInstance().retryTimeoutInterval;
AppaRao Pulie5aaf042020-03-20 01:05:52 +053071 asyncResp->res.jsonValue["EventFormatTypes"] = supportedEvtFormatTypes;
72 asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
73 }
74
75 void doPatch(crow::Response& res, const crow::Request& req,
76 const std::vector<std::string>& params) override
77 {
78 auto asyncResp = std::make_shared<AsyncResp>(res);
79
80 std::optional<bool> serviceEnabled;
81 std::optional<uint32_t> retryAttemps;
82 std::optional<uint32_t> retryInterval;
83
84 if (!json_util::readJson(req, res, "ServiceEnabled", serviceEnabled,
85 "DeliveryRetryAttempts", retryAttemps,
86 "DeliveryRetryIntervalSeconds", retryInterval))
87 {
88 return;
89 }
90
91 if (serviceEnabled)
92 {
AppaRao Pulib52664e2020-04-09 21:36:51 +053093 EventServiceManager::getInstance().enabled = *serviceEnabled;
AppaRao Pulie5aaf042020-03-20 01:05:52 +053094 }
95
96 if (retryAttemps)
97 {
98 // Supported range [1-3]
99 if ((*retryAttemps < 1) || (*retryAttemps > 3))
100 {
101 messages::queryParameterOutOfRange(
102 asyncResp->res, std::to_string(*retryAttemps),
103 "DeliveryRetryAttempts", "[1-3]");
104 }
105 else
106 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530107 EventServiceManager::getInstance().retryAttempts =
108 *retryAttemps;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530109 }
110 }
111
112 if (retryInterval)
113 {
114 // Supported range [30 - 180]
115 if ((*retryInterval < 30) || (*retryInterval > 180))
116 {
117 messages::queryParameterOutOfRange(
118 asyncResp->res, std::to_string(*retryInterval),
119 "DeliveryRetryIntervalSeconds", "[30-180]");
120 }
121 else
122 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530123 EventServiceManager::getInstance().retryTimeoutInterval =
124 *retryInterval;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530125 }
126 }
127
AppaRao Pulib52664e2020-04-09 21:36:51 +0530128 EventServiceManager::getInstance().updateSubscriptionData();
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530129 }
130};
131
AppaRao Puli0b4bdd92020-04-14 17:57:45 +0530132class SubmitTestEvent : public Node
133{
134 public:
135 SubmitTestEvent(CrowApp& app) :
136 Node(app,
137 "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
138 {
139 entityPrivileges = {
140 {boost::beast::http::verb::get, {{"Login"}}},
141 {boost::beast::http::verb::head, {{"Login"}}},
142 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
143 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
144 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
145 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
146 }
147
148 private:
149 void doPost(crow::Response& res, const crow::Request& req,
150 const std::vector<std::string>& params) override
151 {
152 EventServiceManager::getInstance().sendTestEventLog();
153 res.result(boost::beast::http::status::no_content);
154 res.end();
155 }
156};
157
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530158class EventDestinationCollection : public Node
159{
160 public:
161 EventDestinationCollection(CrowApp& app) :
162 Node(app, "/redfish/v1/EventService/Subscriptions/")
163 {
164 entityPrivileges = {
165 {boost::beast::http::verb::get, {{"Login"}}},
166 {boost::beast::http::verb::head, {{"Login"}}},
167 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
168 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
169 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
170 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
171 }
172
173 private:
174 void doGet(crow::Response& res, const crow::Request& req,
175 const std::vector<std::string>& params) override
176 {
177 auto asyncResp = std::make_shared<AsyncResp>(res);
178
179 res.jsonValue = {
180 {"@odata.type",
181 "#EventDestinationCollection.EventDestinationCollection"},
182 {"@odata.id", "/redfish/v1/EventService/Subscriptions"},
183 {"Name", "Event Destination Collections"}};
184
185 nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530186
AppaRao Pulib52664e2020-04-09 21:36:51 +0530187 std::vector<std::string> subscripIds =
188 EventServiceManager::getInstance().getAllIDs();
189 memberArray = nlohmann::json::array();
190 asyncResp->res.jsonValue["Members@odata.count"] = subscripIds.size();
191
192 for (const std::string& id : subscripIds)
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530193 {
194 memberArray.push_back(
195 {{"@odata.id",
AppaRao Pulib52664e2020-04-09 21:36:51 +0530196 "/redfish/v1/EventService/Subscriptions/" + id}});
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530197 }
198 }
199
200 void doPost(crow::Response& res, const crow::Request& req,
201 const std::vector<std::string>& params) override
202 {
203 auto asyncResp = std::make_shared<AsyncResp>(res);
204
AppaRao Pulib52664e2020-04-09 21:36:51 +0530205 if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
206 maxNoOfSubscriptions)
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530207 {
208 messages::eventSubscriptionLimitExceeded(asyncResp->res);
209 return;
210 }
211 std::string destUrl;
212 std::string protocol;
213 std::optional<std::string> context;
214 std::optional<std::string> subscriptionType;
215 std::optional<std::string> eventFormatType;
216 std::optional<std::string> retryPolicy;
217 std::optional<std::vector<std::string>> msgIds;
218 std::optional<std::vector<std::string>> regPrefixes;
219 std::optional<std::vector<nlohmann::json>> headers;
220
221 if (!json_util::readJson(
222 req, res, "Destination", destUrl, "Context", context,
223 "Protocol", protocol, "SubscriptionType", subscriptionType,
224 "EventFormatType", eventFormatType, "HttpHeaders", headers,
225 "RegistryPrefixes", regPrefixes, "MessageIds", msgIds,
226 "DeliveryRetryPolicy", retryPolicy))
227 {
228 return;
229 }
230
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530231 // Validate the URL using regex expression
AppaRao Pulib52664e2020-04-09 21:36:51 +0530232 // Format: <protocol>://<host>:<port>/<uri>
233 // protocol: http/https
234 // host: Exclude ' ', ':', '#', '?'
235 // port: Empty or numeric value with ':' seperator.
236 // uri: Start with '/' and Exclude '#', ' '
237 // Can include query params(ex: '/event?test=1')
238 // TODO: Need to validate hostname extensively(as per rfc)
239 const std::regex urlRegex(
240 "(http|https)://([^/\\x20\\x3f\\x23\\x3a]+):?([0-9]*)(/"
241 "([^\\x20\\x23\\x3f]*\\x3f?([^\\x20\\x23\\x3f])*)?)");
242 std::cmatch match;
243 if (!std::regex_match(destUrl.c_str(), match, urlRegex))
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530244 {
245 messages::propertyValueFormatError(asyncResp->res, destUrl,
246 "Destination");
247 return;
248 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530249
250 std::string uriProto = std::string(match[1].first, match[1].second);
251 if (uriProto == "http")
252 {
253#ifndef BMCWEB_INSECURE_ENABLE_HTTP_PUSH_STYLE_EVENTING
254 messages::propertyValueFormatError(asyncResp->res, destUrl,
255 "Destination");
256 return;
257#endif
258 }
259
260 std::string host = std::string(match[2].first, match[2].second);
261 std::string port = std::string(match[3].first, match[3].second);
262 std::string path = std::string(match[4].first, match[4].second);
263 if (port.empty())
264 {
265 if (uriProto == "http")
266 {
267 port = "80";
268 }
269 else
270 {
271 port = "443";
272 }
273 }
274 if (path.empty())
275 {
276 path = "/";
277 }
278
279 std::shared_ptr<Subscription> subValue =
280 std::make_shared<Subscription>(host, port, path, uriProto);
281
282 subValue->destinationUrl = destUrl;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530283
284 if (subscriptionType)
285 {
286 if (*subscriptionType != "RedfishEvent")
287 {
288 messages::propertyValueNotInList(
289 asyncResp->res, *subscriptionType, "SubscriptionType");
290 return;
291 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530292 subValue->subscriptionType = *subscriptionType;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530293 }
294 else
295 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530296 subValue->subscriptionType = "RedfishEvent"; // Default
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530297 }
298
299 if (protocol != "Redfish")
300 {
301 messages::propertyValueNotInList(asyncResp->res, protocol,
302 "Protocol");
303 return;
304 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530305 subValue->protocol = protocol;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530306
307 if (eventFormatType)
308 {
309 if (std::find(supportedEvtFormatTypes.begin(),
310 supportedEvtFormatTypes.end(),
311 *eventFormatType) == supportedEvtFormatTypes.end())
312 {
313 messages::propertyValueNotInList(
314 asyncResp->res, *eventFormatType, "EventFormatType");
315 return;
316 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530317 subValue->eventFormatType = *eventFormatType;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530318 }
319 else
320 {
321 // If not specified, use default "Event"
AppaRao Pulib52664e2020-04-09 21:36:51 +0530322 subValue->eventFormatType.assign({"Event"});
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530323 }
324
325 if (context)
326 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530327 subValue->customText = *context;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530328 }
329
330 if (headers)
331 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530332 subValue->httpHeaders = *headers;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530333 }
334
335 if (regPrefixes)
336 {
337 for (const std::string& it : *regPrefixes)
338 {
339 if (std::find(supportedRegPrefixes.begin(),
340 supportedRegPrefixes.end(),
341 it) == supportedRegPrefixes.end())
342 {
343 messages::propertyValueNotInList(asyncResp->res, it,
344 "RegistryPrefixes");
345 return;
346 }
347 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530348 subValue->registryPrefixes = *regPrefixes;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530349 }
350
351 if (msgIds)
352 {
353 // Do we need to loop-up MessageRegistry and validate
354 // data for authenticity??? Not mandate, i believe.
AppaRao Pulib52664e2020-04-09 21:36:51 +0530355 subValue->registryMsgIds = *msgIds;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530356 }
357
358 if (retryPolicy)
359 {
360 if (std::find(supportedRetryPolicies.begin(),
361 supportedRetryPolicies.end(),
362 *retryPolicy) == supportedRetryPolicies.end())
363 {
364 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
365 "DeliveryRetryPolicy");
366 return;
367 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530368 subValue->retryPolicy = *retryPolicy;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530369 }
370 else
371 {
372 // Default "TerminateAfterRetries"
AppaRao Pulib52664e2020-04-09 21:36:51 +0530373 subValue->retryPolicy = "TerminateAfterRetries";
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530374 }
375
AppaRao Pulib52664e2020-04-09 21:36:51 +0530376 std::string id =
377 EventServiceManager::getInstance().addSubscription(subValue);
378 if (id.empty())
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530379 {
380 messages::internalError(asyncResp->res);
381 return;
382 }
383
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530384 messages::created(asyncResp->res);
385 asyncResp->res.addHeader(
386 "Location", "/redfish/v1/EventService/Subscriptions/" + id);
387 }
388};
389
390class EventDestination : public Node
391{
392 public:
393 EventDestination(CrowApp& app) :
394 Node(app, "/redfish/v1/EventService/Subscriptions/<str>/",
395 std::string())
396 {
397 entityPrivileges = {
398 {boost::beast::http::verb::get, {{"Login"}}},
399 {boost::beast::http::verb::head, {{"Login"}}},
400 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
401 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
402 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
403 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
404 }
405
406 private:
407 void doGet(crow::Response& res, const crow::Request& req,
408 const std::vector<std::string>& params) override
409 {
410 auto asyncResp = std::make_shared<AsyncResp>(res);
411 if (params.size() != 1)
412 {
413 messages::internalError(asyncResp->res);
414 return;
415 }
416
AppaRao Pulib52664e2020-04-09 21:36:51 +0530417 std::shared_ptr<Subscription> subValue =
418 EventServiceManager::getInstance().getSubscription(params[0]);
419 if (subValue == nullptr)
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530420 {
421 res.result(boost::beast::http::status::not_found);
422 res.end();
423 return;
424 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530425 const std::string& id = params[0];
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530426
427 res.jsonValue = {
428 {"@odata.type", "#EventDestination.v1_7_0.EventDestination"},
429 {"Protocol", "Redfish"}};
430 asyncResp->res.jsonValue["@odata.id"] =
431 "/redfish/v1/EventService/Subscriptions/" + id;
432 asyncResp->res.jsonValue["Id"] = id;
433 asyncResp->res.jsonValue["Name"] = "Event Destination " + id;
AppaRao Pulib52664e2020-04-09 21:36:51 +0530434 asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl;
435 asyncResp->res.jsonValue["Context"] = subValue->customText;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530436 asyncResp->res.jsonValue["SubscriptionType"] =
AppaRao Pulib52664e2020-04-09 21:36:51 +0530437 subValue->subscriptionType;
438 asyncResp->res.jsonValue["HttpHeaders"] = subValue->httpHeaders;
439 asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530440 asyncResp->res.jsonValue["RegistryPrefixes"] =
AppaRao Pulib52664e2020-04-09 21:36:51 +0530441 subValue->registryPrefixes;
442 asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds;
443 asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530444 }
445
446 void doPatch(crow::Response& res, const crow::Request& req,
447 const std::vector<std::string>& params) override
448 {
449 auto asyncResp = std::make_shared<AsyncResp>(res);
450 if (params.size() != 1)
451 {
452 messages::internalError(asyncResp->res);
453 return;
454 }
455
AppaRao Pulib52664e2020-04-09 21:36:51 +0530456 std::shared_ptr<Subscription> subValue =
457 EventServiceManager::getInstance().getSubscription(params[0]);
458 if (subValue == nullptr)
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530459 {
460 res.result(boost::beast::http::status::not_found);
461 res.end();
462 return;
463 }
464
465 std::optional<std::string> context;
466 std::optional<std::string> retryPolicy;
467 std::optional<std::vector<nlohmann::json>> headers;
468
469 if (!json_util::readJson(req, res, "Context", context,
470 "DeliveryRetryPolicy", retryPolicy,
471 "HttpHeaders", headers))
472 {
473 return;
474 }
475
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530476 if (context)
477 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530478 subValue->customText = *context;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530479 }
480
481 if (headers)
482 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530483 subValue->httpHeaders = *headers;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530484 }
485
486 if (retryPolicy)
487 {
488 if (std::find(supportedRetryPolicies.begin(),
489 supportedRetryPolicies.end(),
490 *retryPolicy) == supportedRetryPolicies.end())
491 {
492 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
493 "DeliveryRetryPolicy");
494 return;
495 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530496 subValue->retryPolicy = *retryPolicy;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530497 }
498
AppaRao Pulib52664e2020-04-09 21:36:51 +0530499 EventServiceManager::getInstance().updateSubscriptionData();
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530500 }
501
502 void doDelete(crow::Response& res, const crow::Request& req,
503 const std::vector<std::string>& params) override
504 {
505 auto asyncResp = std::make_shared<AsyncResp>(res);
506
507 if (params.size() != 1)
508 {
509 messages::internalError(asyncResp->res);
510 return;
511 }
512
AppaRao Pulib52664e2020-04-09 21:36:51 +0530513 if (!EventServiceManager::getInstance().isSubscriptionExist(params[0]))
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530514 {
515 res.result(boost::beast::http::status::not_found);
516 res.end();
517 return;
518 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530519 EventServiceManager::getInstance().deleteSubscription(params[0]);
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530520 }
521};
522
523} // namespace redfish