xref: /openbmc/bmcweb/include/ibm/management_console_rest.hpp (revision 96330b9965034bacf1f676c8f049f273db82cc14)
1 #pragma once
2 #include <app.h>
3 #include <tinyxml2.h>
4 
5 #include <async_resp.hpp>
6 #include <boost/algorithm/string.hpp>
7 #include <boost/container/flat_set.hpp>
8 #include <error_messages.hpp>
9 #include <event_service_manager.hpp>
10 #include <ibm/locks.hpp>
11 #include <nlohmann/json.hpp>
12 #include <resource_messages.hpp>
13 #include <sdbusplus/message/types.hpp>
14 #include <utils/json_utils.hpp>
15 
16 #include <filesystem>
17 #include <fstream>
18 #include <regex>
19 
20 // Allow save area file size to 500KB
21 #define MAX_SAVE_AREA_FILESIZE 500000
22 
23 using SType = std::string;
24 using SegmentFlags = std::vector<std::pair<std::string, uint32_t>>;
25 using LockRequest = std::tuple<SType, SType, SType, uint64_t, SegmentFlags>;
26 using LockRequests = std::vector<LockRequest>;
27 using Rc = std::pair<bool, std::variant<uint32_t, LockRequest>>;
28 using RcGetLockList =
29     std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>;
30 using ListOfSessionIds = std::vector<std::string>;
31 namespace crow
32 {
33 namespace ibm_mc
34 {
35 using namespace redfish;
36 constexpr const char* methodNotAllowedMsg = "Method Not Allowed";
37 constexpr const char* resourceNotFoundMsg = "Resource Not Found";
38 constexpr const char* contentNotAcceptableMsg = "Content Not Acceptable";
39 constexpr const char* internalServerError = "Internal Server Error";
40 
41 bool createSaveAreaPath(crow::Response& res)
42 {
43     // The path /var/lib/obmc will be created by initrdscripts
44     // Create the directories for the save-area files, when we get
45     // first file upload request
46     std::error_code ec;
47     if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt", ec))
48     {
49         std::filesystem::create_directory("/var/lib/obmc/bmc-console-mgmt", ec);
50     }
51     if (ec)
52     {
53         res.result(boost::beast::http::status::internal_server_error);
54         res.jsonValue["Description"] = internalServerError;
55         BMCWEB_LOG_DEBUG
56             << "handleIbmPost: Failed to prepare save-area directory. ec : "
57             << ec;
58         return false;
59     }
60 
61     if (!std::filesystem::is_directory(
62             "/var/lib/obmc/bmc-console-mgmt/save-area", ec))
63     {
64         std::filesystem::create_directory(
65             "/var/lib/obmc/bmc-console-mgmt/save-area", ec);
66     }
67     if (ec)
68     {
69         res.result(boost::beast::http::status::internal_server_error);
70         res.jsonValue["Description"] = internalServerError;
71         BMCWEB_LOG_DEBUG
72             << "handleIbmPost: Failed to prepare save-area directory. ec : "
73             << ec;
74         return false;
75     }
76     return true;
77 }
78 void handleFilePut(const crow::Request& req, crow::Response& res,
79                    const std::string& fileID)
80 {
81     // Check the content-type of the request
82     std::string_view contentType = req.getHeaderValue("content-type");
83     if (boost::starts_with(contentType, "multipart/form-data"))
84     {
85         BMCWEB_LOG_DEBUG
86             << "This is multipart/form-data. Invalid content for PUT";
87 
88         res.result(boost::beast::http::status::not_acceptable);
89         res.jsonValue["Description"] = contentNotAcceptableMsg;
90         return;
91     }
92     else
93     {
94         BMCWEB_LOG_DEBUG << "Not a multipart/form-data. Continue..";
95     }
96 
97     BMCWEB_LOG_DEBUG
98         << "handleIbmPut: Request to create/update the save-area file";
99     if (!createSaveAreaPath(res))
100     {
101         res.result(boost::beast::http::status::not_found);
102         res.jsonValue["Description"] = resourceNotFoundMsg;
103         return;
104     }
105     // Create the file
106     std::ofstream file;
107     std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area");
108     loc /= fileID;
109 
110     std::string data = std::move(req.body);
111     BMCWEB_LOG_DEBUG << "data capaticty : " << data.capacity();
112     if (data.capacity() > MAX_SAVE_AREA_FILESIZE)
113     {
114         res.result(boost::beast::http::status::bad_request);
115         res.jsonValue["Description"] =
116             "File size exceeds maximum allowed size[500KB]";
117         return;
118     }
119     BMCWEB_LOG_DEBUG << "Creating file " << loc;
120     file.open(loc, std::ofstream::out);
121     if (file.fail())
122     {
123         BMCWEB_LOG_DEBUG << "Error while opening the file for writing";
124         res.result(boost::beast::http::status::internal_server_error);
125         res.jsonValue["Description"] = "Error while creating the file";
126         return;
127     }
128     else
129     {
130         file << data;
131         BMCWEB_LOG_DEBUG << "save-area file is created";
132         res.jsonValue["Description"] = "File Created";
133         // Push an event
134         std::string origin = "/ibm/v1/Host/ConfigFiles/" + fileID;
135         redfish::EventServiceManager::getInstance().sendEvent(
136             redfish::messages::ResourceCreated(), origin, "IBMConfigFile");
137     }
138 }
139 
140 void handleConfigFileList(crow::Response& res)
141 {
142     std::vector<std::string> pathObjList;
143     std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area");
144     if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc))
145     {
146         for (const auto& file : std::filesystem::directory_iterator(loc))
147         {
148             std::filesystem::path pathObj(file.path());
149             pathObjList.push_back("/ibm/v1/Host/ConfigFiles/" +
150                                   pathObj.filename().string());
151         }
152     }
153     res.jsonValue["@odata.type"] = "#IBMConfigFile.v1_0_0.IBMConfigFile";
154     res.jsonValue["@odata.id"] = "/ibm/v1/Host/ConfigFiles/";
155     res.jsonValue["Id"] = "ConfigFiles";
156     res.jsonValue["Name"] = "ConfigFiles";
157 
158     res.jsonValue["Members"] = std::move(pathObjList);
159     res.jsonValue["Actions"]["#IBMConfigFiles.DeleteAll"] = {
160         {"target",
161          "/ibm/v1/Host/ConfigFiles/Actions/IBMConfigFiles.DeleteAll"}};
162     res.end();
163 }
164 
165 void deleteConfigFiles(crow::Response& res)
166 {
167     std::vector<std::string> pathObjList;
168     std::error_code ec;
169     std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area");
170     if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc))
171     {
172         std::filesystem::remove_all(loc, ec);
173         if (ec)
174         {
175             res.result(boost::beast::http::status::internal_server_error);
176             res.jsonValue["Description"] = internalServerError;
177             BMCWEB_LOG_DEBUG << "deleteConfigFiles: Failed to delete the "
178                                 "config files directory. ec : "
179                              << ec;
180         }
181     }
182     res.end();
183 }
184 
185 void getLockServiceData(crow::Response& res)
186 {
187     res.jsonValue["@odata.type"] = "#LockService.v1_0_0.LockService";
188     res.jsonValue["@odata.id"] = "/ibm/v1/HMC/LockService/";
189     res.jsonValue["Id"] = "LockService";
190     res.jsonValue["Name"] = "LockService";
191 
192     res.jsonValue["Actions"]["#LockService.AcquireLock"] = {
193         {"target", "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock"}};
194     res.jsonValue["Actions"]["#LockService.ReleaseLock"] = {
195         {"target", "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock"}};
196     res.jsonValue["Actions"]["#LockService.GetLockList"] = {
197         {"target", "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList"}};
198     res.end();
199 }
200 
201 void handleFileGet(crow::Response& res, const std::string& fileID)
202 {
203     BMCWEB_LOG_DEBUG << "HandleGet on SaveArea files on path: " << fileID;
204     std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area/" +
205                               fileID);
206     if (!std::filesystem::exists(loc))
207     {
208         BMCWEB_LOG_ERROR << loc << "Not found";
209         res.result(boost::beast::http::status::not_found);
210         res.jsonValue["Description"] = resourceNotFoundMsg;
211         return;
212     }
213 
214     std::ifstream readfile(loc.string());
215     if (!readfile)
216     {
217         BMCWEB_LOG_ERROR << loc.string() << "Not found";
218         res.result(boost::beast::http::status::not_found);
219         res.jsonValue["Description"] = resourceNotFoundMsg;
220         return;
221     }
222 
223     std::string contentDispositionParam =
224         "attachment; filename=\"" + fileID + "\"";
225     res.addHeader("Content-Disposition", contentDispositionParam);
226     std::string fileData;
227     fileData = {std::istreambuf_iterator<char>(readfile),
228                 std::istreambuf_iterator<char>()};
229     res.jsonValue["Data"] = fileData;
230     return;
231 }
232 
233 void handleFileDelete(crow::Response& res, const std::string& fileID)
234 {
235     std::string filePath("/var/lib/obmc/bmc-console-mgmt/save-area/" + fileID);
236     BMCWEB_LOG_DEBUG << "Removing the file : " << filePath << "\n";
237 
238     std::ifstream file_open(filePath.c_str());
239     if (static_cast<bool>(file_open))
240         if (remove(filePath.c_str()) == 0)
241         {
242             BMCWEB_LOG_DEBUG << "File removed!\n";
243             res.jsonValue["Description"] = "File Deleted";
244         }
245         else
246         {
247             BMCWEB_LOG_ERROR << "File not removed!\n";
248             res.result(boost::beast::http::status::internal_server_error);
249             res.jsonValue["Description"] = internalServerError;
250         }
251     else
252     {
253         BMCWEB_LOG_ERROR << "File not found!\n";
254         res.result(boost::beast::http::status::not_found);
255         res.jsonValue["Description"] = resourceNotFoundMsg;
256     }
257     return;
258 }
259 
260 inline void handleFileUrl(const crow::Request& req, crow::Response& res,
261                           const std::string& fileID)
262 {
263     if (req.method() == boost::beast::http::verb::put)
264     {
265         handleFilePut(req, res, fileID);
266         res.end();
267         return;
268     }
269     if (req.method() == boost::beast::http::verb::get)
270     {
271         handleFileGet(res, fileID);
272         res.end();
273         return;
274     }
275     if (req.method() == boost::beast::http::verb::delete_)
276     {
277         handleFileDelete(res, fileID);
278         res.end();
279         return;
280     }
281 }
282 
283 void handleAcquireLockAPI(const crow::Request& req, crow::Response& res,
284                           std::vector<nlohmann::json> body)
285 {
286     LockRequests lockRequestStructure;
287     for (auto& element : body)
288     {
289         std::string lockType;
290         uint64_t resourceId;
291 
292         SegmentFlags segInfo;
293         std::vector<nlohmann::json> segmentFlags;
294 
295         if (!redfish::json_util::readJson(element, res, "LockType", lockType,
296                                           "ResourceID", resourceId,
297                                           "SegmentFlags", segmentFlags))
298         {
299             BMCWEB_LOG_DEBUG << "Not a Valid JSON";
300             res.result(boost::beast::http::status::bad_request);
301             res.end();
302             return;
303         }
304         BMCWEB_LOG_DEBUG << lockType;
305         BMCWEB_LOG_DEBUG << resourceId;
306 
307         BMCWEB_LOG_DEBUG << "Segment Flags are present";
308 
309         for (auto& e : segmentFlags)
310         {
311             std::string lockFlags;
312             uint32_t segmentLength;
313 
314             if (!redfish::json_util::readJson(e, res, "LockFlag", lockFlags,
315                                               "SegmentLength", segmentLength))
316             {
317                 res.result(boost::beast::http::status::bad_request);
318                 res.end();
319                 return;
320             }
321 
322             BMCWEB_LOG_DEBUG << "Lockflag : " << lockFlags;
323             BMCWEB_LOG_DEBUG << "SegmentLength : " << segmentLength;
324 
325             segInfo.push_back(std::make_pair(lockFlags, segmentLength));
326         }
327         lockRequestStructure.push_back(
328             make_tuple(req.session->uniqueId, req.session->clientId, lockType,
329                        resourceId, segInfo));
330     }
331 
332     // print lock request into journal
333 
334     for (uint32_t i = 0; i < lockRequestStructure.size(); i++)
335     {
336         BMCWEB_LOG_DEBUG << std::get<0>(lockRequestStructure[i]);
337         BMCWEB_LOG_DEBUG << std::get<1>(lockRequestStructure[i]);
338         BMCWEB_LOG_DEBUG << std::get<2>(lockRequestStructure[i]);
339         BMCWEB_LOG_DEBUG << std::get<3>(lockRequestStructure[i]);
340 
341         for (const auto& p : std::get<4>(lockRequestStructure[i]))
342         {
343             BMCWEB_LOG_DEBUG << p.first << ", " << p.second;
344         }
345     }
346 
347     const LockRequests& t = lockRequestStructure;
348 
349     auto varAcquireLock = crow::ibm_mc_lock::Lock::getInstance().acquireLock(t);
350 
351     if (varAcquireLock.first)
352     {
353         // Either validity failure of there is a conflict with itself
354 
355         auto validityStatus =
356             std::get<std::pair<bool, int>>(varAcquireLock.second);
357 
358         if ((!validityStatus.first) && (validityStatus.second == 0))
359         {
360             BMCWEB_LOG_DEBUG << "Not a Valid record";
361             BMCWEB_LOG_DEBUG << "Bad json in request";
362             res.result(boost::beast::http::status::bad_request);
363             res.end();
364             return;
365         }
366         if (validityStatus.first && (validityStatus.second == 1))
367         {
368             BMCWEB_LOG_DEBUG << "There is a conflict within itself";
369             res.result(boost::beast::http::status::bad_request);
370             res.end();
371             return;
372         }
373     }
374     else
375     {
376         auto conflictStatus =
377             std::get<crow::ibm_mc_lock::Rc>(varAcquireLock.second);
378         if (!conflictStatus.first)
379         {
380             BMCWEB_LOG_DEBUG << "There is no conflict with the locktable";
381             res.result(boost::beast::http::status::ok);
382 
383             auto var = std::get<uint32_t>(conflictStatus.second);
384             nlohmann::json returnJson;
385             returnJson["id"] = var;
386             res.jsonValue["TransactionID"] = var;
387             res.end();
388             return;
389         }
390         else
391         {
392             BMCWEB_LOG_DEBUG << "There is a conflict with the lock table";
393             res.result(boost::beast::http::status::conflict);
394             auto var = std::get<std::pair<uint32_t, LockRequest>>(
395                 conflictStatus.second);
396             nlohmann::json returnJson, segments;
397             nlohmann::json myarray = nlohmann::json::array();
398             returnJson["TransactionID"] = var.first;
399             returnJson["SessionID"] = std::get<0>(var.second);
400             returnJson["HMCID"] = std::get<1>(var.second);
401             returnJson["LockType"] = std::get<2>(var.second);
402             returnJson["ResourceID"] = std::get<3>(var.second);
403 
404             for (uint32_t i = 0; i < std::get<4>(var.second).size(); i++)
405             {
406                 segments["LockFlag"] = std::get<4>(var.second)[i].first;
407                 segments["SegmentLength"] = std::get<4>(var.second)[i].second;
408                 myarray.push_back(segments);
409             }
410 
411             returnJson["SegmentFlags"] = myarray;
412 
413             res.jsonValue["Record"] = returnJson;
414             res.end();
415             return;
416         }
417     }
418 }
419 void handleRelaseAllAPI(const crow::Request& req, crow::Response& res)
420 {
421     crow::ibm_mc_lock::Lock::getInstance().releaseLock(req.session->uniqueId);
422     res.result(boost::beast::http::status::ok);
423     res.end();
424     return;
425 }
426 
427 void handleReleaseLockAPI(const crow::Request& req, crow::Response& res,
428                           const std::vector<uint32_t>& listTransactionIds)
429 {
430     BMCWEB_LOG_DEBUG << listTransactionIds.size();
431     BMCWEB_LOG_DEBUG << "Data is present";
432     for (uint32_t i = 0; i < listTransactionIds.size(); i++)
433     {
434         BMCWEB_LOG_DEBUG << listTransactionIds[i];
435     }
436 
437     // validate the request ids
438 
439     auto varReleaselock = crow::ibm_mc_lock::Lock::getInstance().releaseLock(
440         listTransactionIds,
441         std::make_pair(req.session->clientId, req.session->uniqueId));
442 
443     if (!varReleaselock.first)
444     {
445         // validation Failed
446         res.result(boost::beast::http::status::bad_request);
447         res.end();
448         return;
449     }
450     else
451     {
452         auto statusRelease =
453             std::get<crow::ibm_mc_lock::RcRelaseLock>(varReleaselock.second);
454         if (statusRelease.first)
455         {
456             // The current hmc owns all the locks, so we already released
457             // them
458             res.result(boost::beast::http::status::ok);
459             res.end();
460             return;
461         }
462 
463         else
464         {
465             // valid rid, but the current hmc does not own all the locks
466             BMCWEB_LOG_DEBUG << "Current HMC does not own all the locks";
467             res.result(boost::beast::http::status::unauthorized);
468 
469             auto var = statusRelease.second;
470             nlohmann::json returnJson, segments;
471             nlohmann::json myArray = nlohmann::json::array();
472             returnJson["TransactionID"] = var.first;
473             returnJson["SessionID"] = std::get<0>(var.second);
474             returnJson["HMCID"] = std::get<1>(var.second);
475             returnJson["LockType"] = std::get<2>(var.second);
476             returnJson["ResourceID"] = std::get<3>(var.second);
477 
478             for (uint32_t i = 0; i < std::get<4>(var.second).size(); i++)
479             {
480                 segments["LockFlag"] = std::get<4>(var.second)[i].first;
481                 segments["SegmentLength"] = std::get<4>(var.second)[i].second;
482                 myArray.push_back(segments);
483             }
484 
485             returnJson["SegmentFlags"] = myArray;
486             res.jsonValue["Record"] = returnJson;
487             res.end();
488             return;
489         }
490     }
491 }
492 
493 void handleGetLockListAPI(const crow::Request& req, crow::Response& res,
494                           const ListOfSessionIds& listSessionIds)
495 {
496     BMCWEB_LOG_DEBUG << listSessionIds.size();
497 
498     auto status =
499         crow::ibm_mc_lock::Lock::getInstance().getLockList(listSessionIds);
500     auto var = std::get<std::vector<std::pair<uint32_t, LockRequests>>>(status);
501 
502     nlohmann::json lockRecords = nlohmann::json::array();
503 
504     for (const auto& transactionId : var)
505     {
506         for (const auto& lockRecord : transactionId.second)
507         {
508             nlohmann::json returnJson;
509 
510             returnJson["TransactionID"] = transactionId.first;
511             returnJson["SessionID"] = std::get<0>(lockRecord);
512             returnJson["HMCID"] = std::get<1>(lockRecord);
513             returnJson["LockType"] = std::get<2>(lockRecord);
514             returnJson["ResourceID"] = std::get<3>(lockRecord);
515 
516             nlohmann::json segments;
517             nlohmann::json segmentInfoArray = nlohmann::json::array();
518 
519             for (const auto& segment : std::get<4>(lockRecord))
520             {
521                 segments["LockFlag"] = segment.first;
522                 segments["SegmentLength"] = segment.second;
523                 segmentInfoArray.push_back(segments);
524             }
525 
526             returnJson["SegmentFlags"] = segmentInfoArray;
527             lockRecords.push_back(returnJson);
528         }
529     }
530     res.result(boost::beast::http::status::ok);
531     res.jsonValue["Records"] = lockRecords;
532     res.end();
533 }
534 
535 template <typename... Middlewares>
536 void requestRoutes(Crow<Middlewares...>& app)
537 {
538 
539     // allowed only for admin
540     BMCWEB_ROUTE(app, "/ibm/v1/")
541         .requires({"ConfigureComponents", "ConfigureManager"})
542         .methods(boost::beast::http::verb::get)(
543             [](const crow::Request& req, crow::Response& res) {
544                 res.jsonValue["@odata.type"] =
545                     "#ibmServiceRoot.v1_0_0.ibmServiceRoot";
546                 res.jsonValue["@odata.id"] = "/ibm/v1/";
547                 res.jsonValue["Id"] = "IBM Rest RootService";
548                 res.jsonValue["Name"] = "IBM Service Root";
549                 res.jsonValue["ConfigFiles"] = {
550                     {"@odata.id", "/ibm/v1/Host/ConfigFiles"}};
551                 res.jsonValue["LockService"] = {
552                     {"@odata.id", "/ibm/v1/HMC/LockService"}};
553                 res.end();
554             });
555 
556     BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles")
557         .requires({"ConfigureComponents", "ConfigureManager"})
558         .methods(boost::beast::http::verb::get)(
559             [](const crow::Request& req, crow::Response& res) {
560                 handleConfigFileList(res);
561             });
562 
563     BMCWEB_ROUTE(app,
564                  "/ibm/v1/Host/ConfigFiles/Actions/IBMConfigFiles.DeleteAll")
565         .requires({"ConfigureComponents", "ConfigureManager"})
566         .methods(boost::beast::http::verb::post)(
567             [](const crow::Request& req, crow::Response& res) {
568                 deleteConfigFiles(res);
569             });
570 
571     BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles/<path>")
572         .requires({"ConfigureComponents", "ConfigureManager"})
573         .methods(boost::beast::http::verb::put, boost::beast::http::verb::get,
574                  boost::beast::http::verb::delete_)(
575             [](const crow::Request& req, crow::Response& res,
576                const std::string& path) { handleFileUrl(req, res, path); });
577 
578     BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService")
579         .requires({"ConfigureComponents", "ConfigureManager"})
580         .methods(boost::beast::http::verb::get)(
581             [](const crow::Request& req, crow::Response& res) {
582                 getLockServiceData(res);
583             });
584 
585     BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock")
586         .requires({"ConfigureComponents", "ConfigureManager"})
587         .methods(boost::beast::http::verb::post)(
588             [](const crow::Request& req, crow::Response& res) {
589                 std::vector<nlohmann::json> body;
590                 if (!redfish::json_util::readJson(req, res, "Request", body))
591                 {
592                     BMCWEB_LOG_DEBUG << "Not a Valid JSON";
593                     res.result(boost::beast::http::status::bad_request);
594                     res.end();
595                     return;
596                 }
597                 handleAcquireLockAPI(req, res, body);
598             });
599     BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock")
600         .requires({"ConfigureComponents", "ConfigureManager"})
601         .methods(boost::beast::http::verb::post)([](const crow::Request& req,
602                                                     crow::Response& res) {
603             std::string type;
604             std::vector<uint32_t> listTransactionIds;
605 
606             if (!redfish::json_util::readJson(req, res, "Type", type,
607                                               "TransactionIDs",
608                                               listTransactionIds))
609             {
610                 res.result(boost::beast::http::status::bad_request);
611                 res.end();
612                 return;
613             }
614             if (type == "Transaction")
615             {
616                 handleReleaseLockAPI(req, res, listTransactionIds);
617             }
618             else if (type == "Session")
619             {
620                 handleRelaseAllAPI(req, res);
621             }
622             else
623             {
624                 BMCWEB_LOG_DEBUG << " Value of Type : " << type
625                                  << "is Not a Valid key";
626                 redfish::messages::propertyValueNotInList(res, type, "Type");
627             }
628         });
629     BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList")
630         .requires({"ConfigureComponents", "ConfigureManager"})
631         .methods(boost::beast::http::verb::post)(
632             [](const crow::Request& req, crow::Response& res) {
633                 ListOfSessionIds listSessionIds;
634 
635                 if (!redfish::json_util::readJson(req, res, "SessionIDs",
636                                                   listSessionIds))
637                 {
638                     res.result(boost::beast::http::status::bad_request);
639                     res.end();
640                     return;
641                 }
642                 handleGetLockListAPI(req, res, listSessionIds);
643             });
644 }
645 
646 } // namespace ibm_mc
647 } // namespace crow
648