blob: 63302d60729bc32b0aa56c6dc3587881b6906646 [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
AppaRao Puli156d6b02020-04-25 06:04:05 +053022static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
23 eventFormatType, metricReportFormatType};
AppaRao Pulie5aaf042020-03-20 01:05:52 +053024static 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;
AppaRao Puli156d6b02020-04-25 06:04:05 +0530220 std::optional<std::vector<nlohmann::json>> metricReportDefinitions;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530221
222 if (!json_util::readJson(
223 req, res, "Destination", destUrl, "Context", context,
224 "Protocol", protocol, "SubscriptionType", subscriptionType,
225 "EventFormatType", eventFormatType, "HttpHeaders", headers,
226 "RegistryPrefixes", regPrefixes, "MessageIds", msgIds,
AppaRao Puli156d6b02020-04-25 06:04:05 +0530227 "DeliveryRetryPolicy", retryPolicy, "MetricReportDefinitions",
228 metricReportDefinitions))
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530229 {
230 return;
231 }
232
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530233 // Validate the URL using regex expression
AppaRao Pulib52664e2020-04-09 21:36:51 +0530234 // Format: <protocol>://<host>:<port>/<uri>
235 // protocol: http/https
236 // host: Exclude ' ', ':', '#', '?'
237 // port: Empty or numeric value with ':' seperator.
238 // uri: Start with '/' and Exclude '#', ' '
239 // Can include query params(ex: '/event?test=1')
240 // TODO: Need to validate hostname extensively(as per rfc)
241 const std::regex urlRegex(
242 "(http|https)://([^/\\x20\\x3f\\x23\\x3a]+):?([0-9]*)(/"
243 "([^\\x20\\x23\\x3f]*\\x3f?([^\\x20\\x23\\x3f])*)?)");
244 std::cmatch match;
245 if (!std::regex_match(destUrl.c_str(), match, urlRegex))
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530246 {
247 messages::propertyValueFormatError(asyncResp->res, destUrl,
248 "Destination");
249 return;
250 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530251
252 std::string uriProto = std::string(match[1].first, match[1].second);
253 if (uriProto == "http")
254 {
255#ifndef BMCWEB_INSECURE_ENABLE_HTTP_PUSH_STYLE_EVENTING
256 messages::propertyValueFormatError(asyncResp->res, destUrl,
257 "Destination");
258 return;
259#endif
260 }
261
262 std::string host = std::string(match[2].first, match[2].second);
263 std::string port = std::string(match[3].first, match[3].second);
264 std::string path = std::string(match[4].first, match[4].second);
265 if (port.empty())
266 {
267 if (uriProto == "http")
268 {
269 port = "80";
270 }
271 else
272 {
273 port = "443";
274 }
275 }
276 if (path.empty())
277 {
278 path = "/";
279 }
280
281 std::shared_ptr<Subscription> subValue =
282 std::make_shared<Subscription>(host, port, path, uriProto);
283
284 subValue->destinationUrl = destUrl;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530285
286 if (subscriptionType)
287 {
288 if (*subscriptionType != "RedfishEvent")
289 {
290 messages::propertyValueNotInList(
291 asyncResp->res, *subscriptionType, "SubscriptionType");
292 return;
293 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530294 subValue->subscriptionType = *subscriptionType;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530295 }
296 else
297 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530298 subValue->subscriptionType = "RedfishEvent"; // Default
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530299 }
300
301 if (protocol != "Redfish")
302 {
303 messages::propertyValueNotInList(asyncResp->res, protocol,
304 "Protocol");
305 return;
306 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530307 subValue->protocol = protocol;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530308
309 if (eventFormatType)
310 {
311 if (std::find(supportedEvtFormatTypes.begin(),
312 supportedEvtFormatTypes.end(),
313 *eventFormatType) == supportedEvtFormatTypes.end())
314 {
315 messages::propertyValueNotInList(
316 asyncResp->res, *eventFormatType, "EventFormatType");
317 return;
318 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530319 subValue->eventFormatType = *eventFormatType;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530320 }
321 else
322 {
323 // If not specified, use default "Event"
AppaRao Pulib52664e2020-04-09 21:36:51 +0530324 subValue->eventFormatType.assign({"Event"});
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530325 }
326
327 if (context)
328 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530329 subValue->customText = *context;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530330 }
331
332 if (headers)
333 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530334 subValue->httpHeaders = *headers;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530335 }
336
337 if (regPrefixes)
338 {
339 for (const std::string& it : *regPrefixes)
340 {
341 if (std::find(supportedRegPrefixes.begin(),
342 supportedRegPrefixes.end(),
343 it) == supportedRegPrefixes.end())
344 {
345 messages::propertyValueNotInList(asyncResp->res, it,
346 "RegistryPrefixes");
347 return;
348 }
349 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530350 subValue->registryPrefixes = *regPrefixes;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530351 }
352
353 if (msgIds)
354 {
355 // Do we need to loop-up MessageRegistry and validate
356 // data for authenticity??? Not mandate, i believe.
AppaRao Pulib52664e2020-04-09 21:36:51 +0530357 subValue->registryMsgIds = *msgIds;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530358 }
359
360 if (retryPolicy)
361 {
362 if (std::find(supportedRetryPolicies.begin(),
363 supportedRetryPolicies.end(),
364 *retryPolicy) == supportedRetryPolicies.end())
365 {
366 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
367 "DeliveryRetryPolicy");
368 return;
369 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530370 subValue->retryPolicy = *retryPolicy;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530371 }
372 else
373 {
374 // Default "TerminateAfterRetries"
AppaRao Pulib52664e2020-04-09 21:36:51 +0530375 subValue->retryPolicy = "TerminateAfterRetries";
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530376 }
377
AppaRao Puli156d6b02020-04-25 06:04:05 +0530378 if (metricReportDefinitions)
379 {
380 subValue->metricReportDefinitions = *metricReportDefinitions;
381 }
382
AppaRao Pulib52664e2020-04-09 21:36:51 +0530383 std::string id =
384 EventServiceManager::getInstance().addSubscription(subValue);
385 if (id.empty())
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530386 {
387 messages::internalError(asyncResp->res);
388 return;
389 }
390
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530391 messages::created(asyncResp->res);
392 asyncResp->res.addHeader(
393 "Location", "/redfish/v1/EventService/Subscriptions/" + id);
394 }
395};
396
397class EventDestination : public Node
398{
399 public:
400 EventDestination(CrowApp& app) :
401 Node(app, "/redfish/v1/EventService/Subscriptions/<str>/",
402 std::string())
403 {
404 entityPrivileges = {
405 {boost::beast::http::verb::get, {{"Login"}}},
406 {boost::beast::http::verb::head, {{"Login"}}},
407 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
408 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
409 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
410 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
411 }
412
413 private:
414 void doGet(crow::Response& res, const crow::Request& req,
415 const std::vector<std::string>& params) override
416 {
417 auto asyncResp = std::make_shared<AsyncResp>(res);
418 if (params.size() != 1)
419 {
420 messages::internalError(asyncResp->res);
421 return;
422 }
423
AppaRao Pulib52664e2020-04-09 21:36:51 +0530424 std::shared_ptr<Subscription> subValue =
425 EventServiceManager::getInstance().getSubscription(params[0]);
426 if (subValue == nullptr)
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530427 {
428 res.result(boost::beast::http::status::not_found);
429 res.end();
430 return;
431 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530432 const std::string& id = params[0];
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530433
434 res.jsonValue = {
435 {"@odata.type", "#EventDestination.v1_7_0.EventDestination"},
436 {"Protocol", "Redfish"}};
437 asyncResp->res.jsonValue["@odata.id"] =
438 "/redfish/v1/EventService/Subscriptions/" + id;
439 asyncResp->res.jsonValue["Id"] = id;
440 asyncResp->res.jsonValue["Name"] = "Event Destination " + id;
AppaRao Pulib52664e2020-04-09 21:36:51 +0530441 asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl;
442 asyncResp->res.jsonValue["Context"] = subValue->customText;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530443 asyncResp->res.jsonValue["SubscriptionType"] =
AppaRao Pulib52664e2020-04-09 21:36:51 +0530444 subValue->subscriptionType;
445 asyncResp->res.jsonValue["HttpHeaders"] = subValue->httpHeaders;
446 asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530447 asyncResp->res.jsonValue["RegistryPrefixes"] =
AppaRao Pulib52664e2020-04-09 21:36:51 +0530448 subValue->registryPrefixes;
449 asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds;
450 asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy;
AppaRao Puli156d6b02020-04-25 06:04:05 +0530451 asyncResp->res.jsonValue["MetricReportDefinitions"] =
452 subValue->metricReportDefinitions;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530453 }
454
455 void doPatch(crow::Response& res, const crow::Request& req,
456 const std::vector<std::string>& params) override
457 {
458 auto asyncResp = std::make_shared<AsyncResp>(res);
459 if (params.size() != 1)
460 {
461 messages::internalError(asyncResp->res);
462 return;
463 }
464
AppaRao Pulib52664e2020-04-09 21:36:51 +0530465 std::shared_ptr<Subscription> subValue =
466 EventServiceManager::getInstance().getSubscription(params[0]);
467 if (subValue == nullptr)
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530468 {
469 res.result(boost::beast::http::status::not_found);
470 res.end();
471 return;
472 }
473
474 std::optional<std::string> context;
475 std::optional<std::string> retryPolicy;
476 std::optional<std::vector<nlohmann::json>> headers;
477
478 if (!json_util::readJson(req, res, "Context", context,
479 "DeliveryRetryPolicy", retryPolicy,
480 "HttpHeaders", headers))
481 {
482 return;
483 }
484
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530485 if (context)
486 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530487 subValue->customText = *context;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530488 }
489
490 if (headers)
491 {
AppaRao Pulib52664e2020-04-09 21:36:51 +0530492 subValue->httpHeaders = *headers;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530493 }
494
495 if (retryPolicy)
496 {
497 if (std::find(supportedRetryPolicies.begin(),
498 supportedRetryPolicies.end(),
499 *retryPolicy) == supportedRetryPolicies.end())
500 {
501 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
502 "DeliveryRetryPolicy");
503 return;
504 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530505 subValue->retryPolicy = *retryPolicy;
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530506 }
507
AppaRao Pulib52664e2020-04-09 21:36:51 +0530508 EventServiceManager::getInstance().updateSubscriptionData();
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530509 }
510
511 void doDelete(crow::Response& res, const crow::Request& req,
512 const std::vector<std::string>& params) override
513 {
514 auto asyncResp = std::make_shared<AsyncResp>(res);
515
516 if (params.size() != 1)
517 {
518 messages::internalError(asyncResp->res);
519 return;
520 }
521
AppaRao Pulib52664e2020-04-09 21:36:51 +0530522 if (!EventServiceManager::getInstance().isSubscriptionExist(params[0]))
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530523 {
524 res.result(boost::beast::http::status::not_found);
525 res.end();
526 return;
527 }
AppaRao Pulib52664e2020-04-09 21:36:51 +0530528 EventServiceManager::getInstance().deleteSubscription(params[0]);
AppaRao Pulie5aaf042020-03-20 01:05:52 +0530529 }
530};
531
532} // namespace redfish