xref: /openbmc/bmcweb/include/ibm/management_console_rest.hpp (revision 10693fa500d4406c362ef0703f25d5caea226728)
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 << "Writing to the file: " << loc;
120 
121     bool fileExists = false;
122     if (std::filesystem::exists(loc))
123     {
124         fileExists = true;
125     }
126     file.open(loc, std::ofstream::out);
127     if (file.fail())
128     {
129         BMCWEB_LOG_DEBUG << "Error while opening the file for writing";
130         res.result(boost::beast::http::status::internal_server_error);
131         res.jsonValue["Description"] = "Error while creating the file";
132         return;
133     }
134     else
135     {
136         file << data;
137         std::string origin = "/ibm/v1/Host/ConfigFiles/" + fileID;
138         // Push an event
139         if (fileExists)
140         {
141             BMCWEB_LOG_DEBUG << "config file is updated";
142             res.jsonValue["Description"] = "File Updated";
143 
144             redfish::EventServiceManager::getInstance().sendEvent(
145                 redfish::messages::ResourceChanged(), origin, "IBMConfigFile");
146         }
147         else
148         {
149             BMCWEB_LOG_DEBUG << "config file is created";
150             res.jsonValue["Description"] = "File Created";
151 
152             redfish::EventServiceManager::getInstance().sendEvent(
153                 redfish::messages::ResourceCreated(), origin, "IBMConfigFile");
154         }
155     }
156 }
157 
158 void handleConfigFileList(crow::Response& res)
159 {
160     std::vector<std::string> pathObjList;
161     std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area");
162     if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc))
163     {
164         for (const auto& file : std::filesystem::directory_iterator(loc))
165         {
166             std::filesystem::path pathObj(file.path());
167             pathObjList.push_back("/ibm/v1/Host/ConfigFiles/" +
168                                   pathObj.filename().string());
169         }
170     }
171     res.jsonValue["@odata.type"] = "#IBMConfigFile.v1_0_0.IBMConfigFile";
172     res.jsonValue["@odata.id"] = "/ibm/v1/Host/ConfigFiles/";
173     res.jsonValue["Id"] = "ConfigFiles";
174     res.jsonValue["Name"] = "ConfigFiles";
175 
176     res.jsonValue["Members"] = std::move(pathObjList);
177     res.jsonValue["Actions"]["#IBMConfigFiles.DeleteAll"] = {
178         {"target",
179          "/ibm/v1/Host/ConfigFiles/Actions/IBMConfigFiles.DeleteAll"}};
180     res.end();
181 }
182 
183 void deleteConfigFiles(crow::Response& res)
184 {
185     std::vector<std::string> pathObjList;
186     std::error_code ec;
187     std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area");
188     if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc))
189     {
190         std::filesystem::remove_all(loc, ec);
191         if (ec)
192         {
193             res.result(boost::beast::http::status::internal_server_error);
194             res.jsonValue["Description"] = internalServerError;
195             BMCWEB_LOG_DEBUG << "deleteConfigFiles: Failed to delete the "
196                                 "config files directory. ec : "
197                              << ec;
198         }
199     }
200     res.end();
201 }
202 
203 void getLockServiceData(crow::Response& res)
204 {
205     res.jsonValue["@odata.type"] = "#LockService.v1_0_0.LockService";
206     res.jsonValue["@odata.id"] = "/ibm/v1/HMC/LockService/";
207     res.jsonValue["Id"] = "LockService";
208     res.jsonValue["Name"] = "LockService";
209 
210     res.jsonValue["Actions"]["#LockService.AcquireLock"] = {
211         {"target", "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock"}};
212     res.jsonValue["Actions"]["#LockService.ReleaseLock"] = {
213         {"target", "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock"}};
214     res.jsonValue["Actions"]["#LockService.GetLockList"] = {
215         {"target", "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList"}};
216     res.end();
217 }
218 
219 void handleFileGet(crow::Response& res, const std::string& fileID)
220 {
221     BMCWEB_LOG_DEBUG << "HandleGet on SaveArea files on path: " << fileID;
222     std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area/" +
223                               fileID);
224     if (!std::filesystem::exists(loc))
225     {
226         BMCWEB_LOG_ERROR << loc << "Not found";
227         res.result(boost::beast::http::status::not_found);
228         res.jsonValue["Description"] = resourceNotFoundMsg;
229         return;
230     }
231 
232     std::ifstream readfile(loc.string());
233     if (!readfile)
234     {
235         BMCWEB_LOG_ERROR << loc.string() << "Not found";
236         res.result(boost::beast::http::status::not_found);
237         res.jsonValue["Description"] = resourceNotFoundMsg;
238         return;
239     }
240 
241     std::string contentDispositionParam =
242         "attachment; filename=\"" + fileID + "\"";
243     res.addHeader("Content-Disposition", contentDispositionParam);
244     std::string fileData;
245     fileData = {std::istreambuf_iterator<char>(readfile),
246                 std::istreambuf_iterator<char>()};
247     res.jsonValue["Data"] = fileData;
248     return;
249 }
250 
251 void handleFileDelete(crow::Response& res, const std::string& fileID)
252 {
253     std::string filePath("/var/lib/obmc/bmc-console-mgmt/save-area/" + fileID);
254     BMCWEB_LOG_DEBUG << "Removing the file : " << filePath << "\n";
255 
256     std::ifstream file_open(filePath.c_str());
257     if (static_cast<bool>(file_open))
258         if (remove(filePath.c_str()) == 0)
259         {
260             BMCWEB_LOG_DEBUG << "File removed!\n";
261             res.jsonValue["Description"] = "File Deleted";
262         }
263         else
264         {
265             BMCWEB_LOG_ERROR << "File not removed!\n";
266             res.result(boost::beast::http::status::internal_server_error);
267             res.jsonValue["Description"] = internalServerError;
268         }
269     else
270     {
271         BMCWEB_LOG_ERROR << "File not found!\n";
272         res.result(boost::beast::http::status::not_found);
273         res.jsonValue["Description"] = resourceNotFoundMsg;
274     }
275     return;
276 }
277 
278 inline void handleFileUrl(const crow::Request& req, crow::Response& res,
279                           const std::string& fileID)
280 {
281     if (req.method() == boost::beast::http::verb::put)
282     {
283         handleFilePut(req, res, fileID);
284         res.end();
285         return;
286     }
287     if (req.method() == boost::beast::http::verb::get)
288     {
289         handleFileGet(res, fileID);
290         res.end();
291         return;
292     }
293     if (req.method() == boost::beast::http::verb::delete_)
294     {
295         handleFileDelete(res, fileID);
296         res.end();
297         return;
298     }
299 }
300 
301 void handleAcquireLockAPI(const crow::Request& req, crow::Response& res,
302                           std::vector<nlohmann::json> body)
303 {
304     LockRequests lockRequestStructure;
305     for (auto& element : body)
306     {
307         std::string lockType;
308         uint64_t resourceId;
309 
310         SegmentFlags segInfo;
311         std::vector<nlohmann::json> segmentFlags;
312 
313         if (!redfish::json_util::readJson(element, res, "LockType", lockType,
314                                           "ResourceID", resourceId,
315                                           "SegmentFlags", segmentFlags))
316         {
317             BMCWEB_LOG_DEBUG << "Not a Valid JSON";
318             res.result(boost::beast::http::status::bad_request);
319             res.end();
320             return;
321         }
322         BMCWEB_LOG_DEBUG << lockType;
323         BMCWEB_LOG_DEBUG << resourceId;
324 
325         BMCWEB_LOG_DEBUG << "Segment Flags are present";
326 
327         for (auto& e : segmentFlags)
328         {
329             std::string lockFlags;
330             uint32_t segmentLength;
331 
332             if (!redfish::json_util::readJson(e, res, "LockFlag", lockFlags,
333                                               "SegmentLength", segmentLength))
334             {
335                 res.result(boost::beast::http::status::bad_request);
336                 res.end();
337                 return;
338             }
339 
340             BMCWEB_LOG_DEBUG << "Lockflag : " << lockFlags;
341             BMCWEB_LOG_DEBUG << "SegmentLength : " << segmentLength;
342 
343             segInfo.push_back(std::make_pair(lockFlags, segmentLength));
344         }
345         lockRequestStructure.push_back(
346             make_tuple(req.session->uniqueId, req.session->clientId, lockType,
347                        resourceId, segInfo));
348     }
349 
350     // print lock request into journal
351 
352     for (uint32_t i = 0; i < lockRequestStructure.size(); i++)
353     {
354         BMCWEB_LOG_DEBUG << std::get<0>(lockRequestStructure[i]);
355         BMCWEB_LOG_DEBUG << std::get<1>(lockRequestStructure[i]);
356         BMCWEB_LOG_DEBUG << std::get<2>(lockRequestStructure[i]);
357         BMCWEB_LOG_DEBUG << std::get<3>(lockRequestStructure[i]);
358 
359         for (const auto& p : std::get<4>(lockRequestStructure[i]))
360         {
361             BMCWEB_LOG_DEBUG << p.first << ", " << p.second;
362         }
363     }
364 
365     const LockRequests& t = lockRequestStructure;
366 
367     auto varAcquireLock = crow::ibm_mc_lock::Lock::getInstance().acquireLock(t);
368 
369     if (varAcquireLock.first)
370     {
371         // Either validity failure of there is a conflict with itself
372 
373         auto validityStatus =
374             std::get<std::pair<bool, int>>(varAcquireLock.second);
375 
376         if ((!validityStatus.first) && (validityStatus.second == 0))
377         {
378             BMCWEB_LOG_DEBUG << "Not a Valid record";
379             BMCWEB_LOG_DEBUG << "Bad json in request";
380             res.result(boost::beast::http::status::bad_request);
381             res.end();
382             return;
383         }
384         if (validityStatus.first && (validityStatus.second == 1))
385         {
386             BMCWEB_LOG_DEBUG << "There is a conflict within itself";
387             res.result(boost::beast::http::status::bad_request);
388             res.end();
389             return;
390         }
391     }
392     else
393     {
394         auto conflictStatus =
395             std::get<crow::ibm_mc_lock::Rc>(varAcquireLock.second);
396         if (!conflictStatus.first)
397         {
398             BMCWEB_LOG_DEBUG << "There is no conflict with the locktable";
399             res.result(boost::beast::http::status::ok);
400 
401             auto var = std::get<uint32_t>(conflictStatus.second);
402             nlohmann::json returnJson;
403             returnJson["id"] = var;
404             res.jsonValue["TransactionID"] = var;
405             res.end();
406             return;
407         }
408         else
409         {
410             BMCWEB_LOG_DEBUG << "There is a conflict with the lock table";
411             res.result(boost::beast::http::status::conflict);
412             auto var = std::get<std::pair<uint32_t, LockRequest>>(
413                 conflictStatus.second);
414             nlohmann::json returnJson, segments;
415             nlohmann::json myarray = nlohmann::json::array();
416             returnJson["TransactionID"] = var.first;
417             returnJson["SessionID"] = std::get<0>(var.second);
418             returnJson["HMCID"] = std::get<1>(var.second);
419             returnJson["LockType"] = std::get<2>(var.second);
420             returnJson["ResourceID"] = std::get<3>(var.second);
421 
422             for (uint32_t i = 0; i < std::get<4>(var.second).size(); i++)
423             {
424                 segments["LockFlag"] = std::get<4>(var.second)[i].first;
425                 segments["SegmentLength"] = std::get<4>(var.second)[i].second;
426                 myarray.push_back(segments);
427             }
428 
429             returnJson["SegmentFlags"] = myarray;
430 
431             res.jsonValue["Record"] = returnJson;
432             res.end();
433             return;
434         }
435     }
436 }
437 void handleRelaseAllAPI(const crow::Request& req, crow::Response& res)
438 {
439     crow::ibm_mc_lock::Lock::getInstance().releaseLock(req.session->uniqueId);
440     res.result(boost::beast::http::status::ok);
441     res.end();
442     return;
443 }
444 
445 void handleReleaseLockAPI(const crow::Request& req, crow::Response& res,
446                           const std::vector<uint32_t>& listTransactionIds)
447 {
448     BMCWEB_LOG_DEBUG << listTransactionIds.size();
449     BMCWEB_LOG_DEBUG << "Data is present";
450     for (uint32_t i = 0; i < listTransactionIds.size(); i++)
451     {
452         BMCWEB_LOG_DEBUG << listTransactionIds[i];
453     }
454 
455     // validate the request ids
456 
457     auto varReleaselock = crow::ibm_mc_lock::Lock::getInstance().releaseLock(
458         listTransactionIds,
459         std::make_pair(req.session->clientId, req.session->uniqueId));
460 
461     if (!varReleaselock.first)
462     {
463         // validation Failed
464         res.result(boost::beast::http::status::bad_request);
465         res.end();
466         return;
467     }
468     else
469     {
470         auto statusRelease =
471             std::get<crow::ibm_mc_lock::RcRelaseLock>(varReleaselock.second);
472         if (statusRelease.first)
473         {
474             // The current hmc owns all the locks, so we already released
475             // them
476             res.result(boost::beast::http::status::ok);
477             res.end();
478             return;
479         }
480 
481         else
482         {
483             // valid rid, but the current hmc does not own all the locks
484             BMCWEB_LOG_DEBUG << "Current HMC does not own all the locks";
485             res.result(boost::beast::http::status::unauthorized);
486 
487             auto var = statusRelease.second;
488             nlohmann::json returnJson, segments;
489             nlohmann::json myArray = nlohmann::json::array();
490             returnJson["TransactionID"] = var.first;
491             returnJson["SessionID"] = std::get<0>(var.second);
492             returnJson["HMCID"] = std::get<1>(var.second);
493             returnJson["LockType"] = std::get<2>(var.second);
494             returnJson["ResourceID"] = std::get<3>(var.second);
495 
496             for (uint32_t i = 0; i < std::get<4>(var.second).size(); i++)
497             {
498                 segments["LockFlag"] = std::get<4>(var.second)[i].first;
499                 segments["SegmentLength"] = std::get<4>(var.second)[i].second;
500                 myArray.push_back(segments);
501             }
502 
503             returnJson["SegmentFlags"] = myArray;
504             res.jsonValue["Record"] = returnJson;
505             res.end();
506             return;
507         }
508     }
509 }
510 
511 void handleGetLockListAPI(const crow::Request& req, crow::Response& res,
512                           const ListOfSessionIds& listSessionIds)
513 {
514     BMCWEB_LOG_DEBUG << listSessionIds.size();
515 
516     auto status =
517         crow::ibm_mc_lock::Lock::getInstance().getLockList(listSessionIds);
518     auto var = std::get<std::vector<std::pair<uint32_t, LockRequests>>>(status);
519 
520     nlohmann::json lockRecords = nlohmann::json::array();
521 
522     for (const auto& transactionId : var)
523     {
524         for (const auto& lockRecord : transactionId.second)
525         {
526             nlohmann::json returnJson;
527 
528             returnJson["TransactionID"] = transactionId.first;
529             returnJson["SessionID"] = std::get<0>(lockRecord);
530             returnJson["HMCID"] = std::get<1>(lockRecord);
531             returnJson["LockType"] = std::get<2>(lockRecord);
532             returnJson["ResourceID"] = std::get<3>(lockRecord);
533 
534             nlohmann::json segments;
535             nlohmann::json segmentInfoArray = nlohmann::json::array();
536 
537             for (const auto& segment : std::get<4>(lockRecord))
538             {
539                 segments["LockFlag"] = segment.first;
540                 segments["SegmentLength"] = segment.second;
541                 segmentInfoArray.push_back(segments);
542             }
543 
544             returnJson["SegmentFlags"] = segmentInfoArray;
545             lockRecords.push_back(returnJson);
546         }
547     }
548     res.result(boost::beast::http::status::ok);
549     res.jsonValue["Records"] = lockRecords;
550     res.end();
551 }
552 
553 template <typename... Middlewares>
554 void requestRoutes(Crow<Middlewares...>& app)
555 {
556 
557     // allowed only for admin
558     BMCWEB_ROUTE(app, "/ibm/v1/")
559         .requires({"ConfigureComponents", "ConfigureManager"})
560         .methods(boost::beast::http::verb::get)(
561             [](const crow::Request& req, crow::Response& res) {
562                 res.jsonValue["@odata.type"] =
563                     "#ibmServiceRoot.v1_0_0.ibmServiceRoot";
564                 res.jsonValue["@odata.id"] = "/ibm/v1/";
565                 res.jsonValue["Id"] = "IBM Rest RootService";
566                 res.jsonValue["Name"] = "IBM Service Root";
567                 res.jsonValue["ConfigFiles"] = {
568                     {"@odata.id", "/ibm/v1/Host/ConfigFiles"}};
569                 res.jsonValue["LockService"] = {
570                     {"@odata.id", "/ibm/v1/HMC/LockService"}};
571                 res.end();
572             });
573 
574     BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles")
575         .requires({"ConfigureComponents", "ConfigureManager"})
576         .methods(boost::beast::http::verb::get)(
577             [](const crow::Request& req, crow::Response& res) {
578                 handleConfigFileList(res);
579             });
580 
581     BMCWEB_ROUTE(app,
582                  "/ibm/v1/Host/ConfigFiles/Actions/IBMConfigFiles.DeleteAll")
583         .requires({"ConfigureComponents", "ConfigureManager"})
584         .methods(boost::beast::http::verb::post)(
585             [](const crow::Request& req, crow::Response& res) {
586                 deleteConfigFiles(res);
587             });
588 
589     BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles/<path>")
590         .requires({"ConfigureComponents", "ConfigureManager"})
591         .methods(boost::beast::http::verb::put, boost::beast::http::verb::get,
592                  boost::beast::http::verb::delete_)(
593             [](const crow::Request& req, crow::Response& res,
594                const std::string& path) { handleFileUrl(req, res, path); });
595 
596     BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService")
597         .requires({"ConfigureComponents", "ConfigureManager"})
598         .methods(boost::beast::http::verb::get)(
599             [](const crow::Request& req, crow::Response& res) {
600                 getLockServiceData(res);
601             });
602 
603     BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock")
604         .requires({"ConfigureComponents", "ConfigureManager"})
605         .methods(boost::beast::http::verb::post)(
606             [](const crow::Request& req, crow::Response& res) {
607                 std::vector<nlohmann::json> body;
608                 if (!redfish::json_util::readJson(req, res, "Request", body))
609                 {
610                     BMCWEB_LOG_DEBUG << "Not a Valid JSON";
611                     res.result(boost::beast::http::status::bad_request);
612                     res.end();
613                     return;
614                 }
615                 handleAcquireLockAPI(req, res, body);
616             });
617     BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock")
618         .requires({"ConfigureComponents", "ConfigureManager"})
619         .methods(boost::beast::http::verb::post)([](const crow::Request& req,
620                                                     crow::Response& res) {
621             std::string type;
622             std::vector<uint32_t> listTransactionIds;
623 
624             if (!redfish::json_util::readJson(req, res, "Type", type,
625                                               "TransactionIDs",
626                                               listTransactionIds))
627             {
628                 res.result(boost::beast::http::status::bad_request);
629                 res.end();
630                 return;
631             }
632             if (type == "Transaction")
633             {
634                 handleReleaseLockAPI(req, res, listTransactionIds);
635             }
636             else if (type == "Session")
637             {
638                 handleRelaseAllAPI(req, res);
639             }
640             else
641             {
642                 BMCWEB_LOG_DEBUG << " Value of Type : " << type
643                                  << "is Not a Valid key";
644                 redfish::messages::propertyValueNotInList(res, type, "Type");
645             }
646         });
647     BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList")
648         .requires({"ConfigureComponents", "ConfigureManager"})
649         .methods(boost::beast::http::verb::post)(
650             [](const crow::Request& req, crow::Response& res) {
651                 ListOfSessionIds listSessionIds;
652 
653                 if (!redfish::json_util::readJson(req, res, "SessionIDs",
654                                                   listSessionIds))
655                 {
656                     res.result(boost::beast::http::status::bad_request);
657                     res.end();
658                     return;
659                 }
660                 handleGetLockListAPI(req, res, listSessionIds);
661             });
662 }
663 
664 } // namespace ibm_mc
665 } // namespace crow
666