blob: a4bf31ea72bd74e50d6c3a83a34622078c72c2a4 [file] [log] [blame]
Ratan Gupta453fed02019-12-14 09:39:47 +05301#pragma once
2#include <app.h>
3#include <tinyxml2.h>
4
5#include <async_resp.hpp>
Sunitha Harish97b0e432019-11-21 04:59:29 -06006#include <boost/algorithm/string.hpp>
7#include <boost/container/flat_set.hpp>
8#include <filesystem>
9#include <fstream>
manojkiraneda0b631ae2019-12-03 17:54:28 +053010#include <ibm/locks.hpp>
11#include <nlohmann/json.hpp>
Sunitha Harish97b0e432019-11-21 04:59:29 -060012#include <regex>
13#include <sdbusplus/message/types.hpp>
manojkiraneda0b631ae2019-12-03 17:54:28 +053014#include <utils/json_utils.hpp>
Sunitha Harish97b0e432019-11-21 04:59:29 -060015
16#define MAX_SAVE_AREA_FILESIZE 200000
manojkiraneda0b631ae2019-12-03 17:54:28 +053017using SType = std::string;
18using SegmentFlags = std::vector<std::pair<std::string, uint32_t>>;
19using LockRequest = std::tuple<SType, SType, SType, uint64_t, SegmentFlags>;
20using LockRequests = std::vector<LockRequest>;
21using Rc = std::pair<bool, std::variant<uint32_t, LockRequest>>;
22using RcGetLockList = std::pair<
23 bool,
24 std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>>;
Ratan Gupta453fed02019-12-14 09:39:47 +053025
26namespace crow
27{
28namespace ibm_mc
29{
Sunitha Harish97b0e432019-11-21 04:59:29 -060030constexpr const char *methodNotAllowedMsg = "Method Not Allowed";
31constexpr const char *resourceNotFoundMsg = "Resource Not Found";
32constexpr const char *contentNotAcceptableMsg = "Content Not Acceptable";
33constexpr const char *internalServerError = "Internal Server Error";
34
35bool createSaveAreaPath(crow::Response &res)
36{
37 // The path /var/lib/obmc will be created by initrdscripts
38 // Create the directories for the save-area files, when we get
39 // first file upload request
40 std::error_code ec;
41 if (!std::filesystem::is_directory("/var/lib/obmc/bmc-console-mgmt", ec))
42 {
43 std::filesystem::create_directory("/var/lib/obmc/bmc-console-mgmt", ec);
44 }
45 if (ec)
46 {
47 res.result(boost::beast::http::status::internal_server_error);
48 res.jsonValue["Description"] = internalServerError;
49 BMCWEB_LOG_DEBUG
50 << "handleIbmPost: Failed to prepare save-area directory. ec : "
51 << ec;
52 return false;
53 }
54
55 if (!std::filesystem::is_directory(
56 "/var/lib/obmc/bmc-console-mgmt/save-area", ec))
57 {
58 std::filesystem::create_directory(
59 "/var/lib/obmc/bmc-console-mgmt/save-area", ec);
60 }
61 if (ec)
62 {
63 res.result(boost::beast::http::status::internal_server_error);
64 res.jsonValue["Description"] = internalServerError;
65 BMCWEB_LOG_DEBUG
66 << "handleIbmPost: Failed to prepare save-area directory. ec : "
67 << ec;
68 return false;
69 }
70 return true;
71}
72void handleFilePut(const crow::Request &req, crow::Response &res,
asmithakarun1c7b07c2019-09-09 03:42:59 -050073 const std::string &fileID)
Sunitha Harish97b0e432019-11-21 04:59:29 -060074{
75 // Check the content-type of the request
76 std::string_view contentType = req.getHeaderValue("content-type");
77 if (boost::starts_with(contentType, "multipart/form-data"))
78 {
79 BMCWEB_LOG_DEBUG
80 << "This is multipart/form-data. Invalid content for PUT";
81
82 res.result(boost::beast::http::status::not_acceptable);
83 res.jsonValue["Description"] = contentNotAcceptableMsg;
84 return;
85 }
86 else
87 {
88 BMCWEB_LOG_DEBUG << "Not a multipart/form-data. Continue..";
89 }
asmithakarun1c7b07c2019-09-09 03:42:59 -050090
91 BMCWEB_LOG_DEBUG
92 << "handleIbmPut: Request to create/update the save-area file";
93 if (!createSaveAreaPath(res))
Sunitha Harish97b0e432019-11-21 04:59:29 -060094 {
asmithakarun1c7b07c2019-09-09 03:42:59 -050095 res.result(boost::beast::http::status::not_found);
96 res.jsonValue["Description"] = resourceNotFoundMsg;
97 return;
98 }
99 // Create the file
100 std::ofstream file;
101 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area");
102 loc /= fileID;
Sunitha Harish97b0e432019-11-21 04:59:29 -0600103
asmithakarun1c7b07c2019-09-09 03:42:59 -0500104 std::string data = std::move(req.body);
105 BMCWEB_LOG_DEBUG << "data capaticty : " << data.capacity();
106 if (data.capacity() > MAX_SAVE_AREA_FILESIZE)
107 {
108 res.result(boost::beast::http::status::bad_request);
109 res.jsonValue["Description"] =
110 "File size exceeds 200KB. Maximum allowed size is 200KB";
111 return;
112 }
asmithakarun1c7b07c2019-09-09 03:42:59 -0500113 BMCWEB_LOG_DEBUG << "Creating file " << loc;
114 file.open(loc, std::ofstream::out);
115 if (file.fail())
116 {
117 BMCWEB_LOG_DEBUG << "Error while opening the file for writing";
118 res.result(boost::beast::http::status::internal_server_error);
119 res.jsonValue["Description"] = "Error while creating the file";
120 return;
Sunitha Harish97b0e432019-11-21 04:59:29 -0600121 }
122 else
123 {
asmithakarun1c7b07c2019-09-09 03:42:59 -0500124 file << data;
125 BMCWEB_LOG_DEBUG << "save-area file is created";
126 res.jsonValue["Description"] = "File Created";
127 }
Ratan Guptad3630cb2019-12-14 11:21:35 +0530128}
asmithakarun1c7b07c2019-09-09 03:42:59 -0500129
Ratan Guptad3630cb2019-12-14 11:21:35 +0530130void handleConfigFileList(crow::Response &res)
131{
132 std::vector<std::string> pathObjList;
133 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area");
134 if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc))
135 {
136 for (const auto &file : std::filesystem::directory_iterator(loc))
137 {
138 std::filesystem::path pathObj(file.path());
139 pathObjList.push_back("/ibm/v1/Host/ConfigFiles/" +
140 pathObj.filename().string());
141 }
142 }
143 res.jsonValue["@odata.type"] = "#FileCollection.v1_0_0.FileCollection";
144 res.jsonValue["@odata.id"] = "/ibm/v1/Host/ConfigFiles/";
145 res.jsonValue["Id"] = "ConfigFiles";
146 res.jsonValue["Name"] = "ConfigFiles";
147
148 res.jsonValue["Members"] = std::move(pathObjList);
149 res.jsonValue["Actions"]["#FileCollection.DeleteAll"] = {
150 {"target",
151 "/ibm/v1/Host/ConfigFiles/Actions/FileCollection.DeleteAll"}};
152 res.end();
153}
154
155void deleteConfigFiles(crow::Response &res)
156{
157 std::vector<std::string> pathObjList;
158 std::error_code ec;
159 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area");
160 if (std::filesystem::exists(loc) && std::filesystem::is_directory(loc))
161 {
162 std::filesystem::remove_all(loc, ec);
163 if (ec)
164 {
165 res.result(boost::beast::http::status::internal_server_error);
166 res.jsonValue["Description"] = internalServerError;
167 BMCWEB_LOG_DEBUG << "deleteConfigFiles: Failed to delete the "
168 "config files directory. ec : "
169 << ec;
170 }
171 }
172 res.end();
asmithakarun1c7b07c2019-09-09 03:42:59 -0500173}
174
Ratan Gupta734a1c32019-12-14 11:53:48 +0530175void getLockServiceData(crow::Response &res)
176{
177 res.jsonValue["@odata.type"] = "#LockService.v1_0_0.LockService";
178 res.jsonValue["@odata.id"] = "/ibm/v1/HMC/LockService/";
179 res.jsonValue["Id"] = "LockService";
180 res.jsonValue["Name"] = "LockService";
181
182 res.jsonValue["Actions"]["#LockService.AcquireLock"] = {
183 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock"}};
184 res.jsonValue["Actions"]["#LockService.ReleaseLock"] = {
185 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock"}};
186 res.jsonValue["Actions"]["#LockService.GetLockList"] = {
187 {"target", "/ibm/v1/HMC/LockService/Actions/LockService.GetLockList"}};
188 res.end();
189}
190
asmithakarun1c7b07c2019-09-09 03:42:59 -0500191void handleFileGet(crow::Response &res, const std::string &fileID)
192{
193 BMCWEB_LOG_DEBUG << "HandleGet on SaveArea files on path: " << fileID;
194 std::filesystem::path loc("/var/lib/obmc/bmc-console-mgmt/save-area/" +
195 fileID);
196 if (!std::filesystem::exists(loc))
197 {
198 BMCWEB_LOG_ERROR << loc << "Not found";
199 res.result(boost::beast::http::status::not_found);
200 res.jsonValue["Description"] = resourceNotFoundMsg;
201 return;
202 }
203
204 std::ifstream readfile(loc.string());
205 if (!readfile)
206 {
207 BMCWEB_LOG_ERROR << loc.string() << "Not found";
208 res.result(boost::beast::http::status::not_found);
209 res.jsonValue["Description"] = resourceNotFoundMsg;
210 return;
211 }
212
213 std::string contentDispositionParam =
214 "attachment; filename=\"" + fileID + "\"";
215 res.addHeader("Content-Disposition", contentDispositionParam);
216 std::string fileData;
217 fileData = {std::istreambuf_iterator<char>(readfile),
218 std::istreambuf_iterator<char>()};
219 res.jsonValue["Data"] = fileData;
220 return;
221}
222
223void handleFileDelete(crow::Response &res, const std::string &fileID)
224{
225 std::string filePath("/var/lib/obmc/bmc-console-mgmt/save-area/" + fileID);
226 BMCWEB_LOG_DEBUG << "Removing the file : " << filePath << "\n";
227
228 std::ifstream file_open(filePath.c_str());
229 if (static_cast<bool>(file_open))
230 if (remove(filePath.c_str()) == 0)
231 {
232 BMCWEB_LOG_DEBUG << "File removed!\n";
233 res.jsonValue["Description"] = "File Deleted";
234 }
235 else
236 {
237 BMCWEB_LOG_ERROR << "File not removed!\n";
238 res.result(boost::beast::http::status::internal_server_error);
239 res.jsonValue["Description"] = internalServerError;
240 }
241 else
242 {
243 BMCWEB_LOG_ERROR << "File not found!\n";
Sunitha Harish97b0e432019-11-21 04:59:29 -0600244 res.result(boost::beast::http::status::not_found);
245 res.jsonValue["Description"] = resourceNotFoundMsg;
246 }
247 return;
248}
249
250inline void handleFileUrl(const crow::Request &req, crow::Response &res,
asmithakarun1c7b07c2019-09-09 03:42:59 -0500251 const std::string &fileID)
Sunitha Harish97b0e432019-11-21 04:59:29 -0600252{
Sunitha Harish97b0e432019-11-21 04:59:29 -0600253 if (req.method() == "PUT"_method)
254 {
asmithakarun1c7b07c2019-09-09 03:42:59 -0500255 handleFilePut(req, res, fileID);
Sunitha Harish97b0e432019-11-21 04:59:29 -0600256 res.end();
257 return;
258 }
asmithakarun1c7b07c2019-09-09 03:42:59 -0500259 if (req.method() == "GET"_method)
260 {
261 handleFileGet(res, fileID);
262 res.end();
263 return;
264 }
265 if (req.method() == "DELETE"_method)
266 {
267 handleFileDelete(res, fileID);
268 res.end();
269 return;
270 }
Sunitha Harish97b0e432019-11-21 04:59:29 -0600271}
Ratan Gupta453fed02019-12-14 09:39:47 +0530272
manojkiraneda0b631ae2019-12-03 17:54:28 +0530273void handleAcquireLockAPI(const crow::Request &req, crow::Response &res,
274 std::vector<nlohmann::json> body)
275{
276 LockRequests lockRequestStructure;
277 for (auto &element : body)
278 {
279 std::string lockType;
280 uint64_t resourceId;
281
282 SegmentFlags segInfo;
283 std::vector<nlohmann::json> segmentFlags;
284
285 if (!redfish::json_util::readJson(element, res, "LockType", lockType,
286 "ResourceID", resourceId,
287 "SegmentFlags", segmentFlags))
288 {
289 BMCWEB_LOG_DEBUG << "Not a Valid JSON";
290 res.result(boost::beast::http::status::bad_request);
291 res.end();
292 return;
293 }
294 BMCWEB_LOG_DEBUG << lockType;
295 BMCWEB_LOG_DEBUG << resourceId;
296
297 BMCWEB_LOG_DEBUG << "Segment Flags are present";
298
299 for (auto &e : segmentFlags)
300 {
301 std::string lockFlags;
302 uint32_t segmentLength;
303
304 if (!redfish::json_util::readJson(e, res, "LockFlag", lockFlags,
305 "SegmentLength", segmentLength))
306 {
307 res.result(boost::beast::http::status::bad_request);
308 res.end();
309 return;
310 }
311
312 BMCWEB_LOG_DEBUG << "Lockflag : " << lockFlags;
313 BMCWEB_LOG_DEBUG << "SegmentLength : " << segmentLength;
314
315 segInfo.push_back(std::make_pair(lockFlags, segmentLength));
316 }
317 lockRequestStructure.push_back(make_tuple(
318 req.session->uniqueId, "hmc-id", lockType, resourceId, segInfo));
319 }
320
321 // print lock request into journal
322
323 for (uint32_t i = 0; i < lockRequestStructure.size(); i++)
324 {
325 BMCWEB_LOG_DEBUG << std::get<0>(lockRequestStructure[i]);
326 BMCWEB_LOG_DEBUG << std::get<1>(lockRequestStructure[i]);
327 BMCWEB_LOG_DEBUG << std::get<2>(lockRequestStructure[i]);
328 BMCWEB_LOG_DEBUG << std::get<3>(lockRequestStructure[i]);
329
330 for (const auto &p : std::get<4>(lockRequestStructure[i]))
331 {
332 BMCWEB_LOG_DEBUG << p.first << ", " << p.second;
333 }
334 }
335
336 const LockRequests &t = lockRequestStructure;
337
338 auto varAcquireLock = crow::ibm_mc_lock::lockObject.acquireLock(t);
339
340 if (varAcquireLock.first)
341 {
342 // Either validity failure of there is a conflict with itself
343
344 auto validityStatus =
345 std::get<std::pair<bool, int>>(varAcquireLock.second);
346
347 if ((!validityStatus.first) && (validityStatus.second == 0))
348 {
349 BMCWEB_LOG_DEBUG << "Not a Valid record";
350 BMCWEB_LOG_DEBUG << "Bad json in request";
351 res.result(boost::beast::http::status::bad_request);
352 res.end();
353 return;
354 }
355 if (validityStatus.first && (validityStatus.second == 1))
356 {
357 BMCWEB_LOG_DEBUG << "There is a conflict within itself";
358 res.result(boost::beast::http::status::bad_request);
359 res.end();
360 return;
361 }
362 }
363 else
364 {
365 auto conflictStatus =
366 std::get<crow::ibm_mc_lock::Rc>(varAcquireLock.second);
367 if (!conflictStatus.first)
368 {
369 BMCWEB_LOG_DEBUG << "There is no conflict with the locktable";
370 res.result(boost::beast::http::status::ok);
371
372 auto var = std::get<uint32_t>(conflictStatus.second);
373 nlohmann::json returnJson;
374 returnJson["id"] = var;
375 res.jsonValue["TransactionID"] = var;
376 res.end();
377 return;
378 }
379 else
380 {
381 BMCWEB_LOG_DEBUG << "There is a conflict with the lock table";
382 res.result(boost::beast::http::status::conflict);
383 auto var = std::get<std::pair<uint32_t, LockRequest>>(
384 conflictStatus.second);
385 nlohmann::json returnJson, segments;
386 nlohmann::json myarray = nlohmann::json::array();
387 returnJson["TransactionID"] = var.first;
388 returnJson["SessionID"] = std::get<0>(var.second);
389 returnJson["HMCID"] = std::get<1>(var.second);
390 returnJson["LockType"] = std::get<2>(var.second);
391 returnJson["ResourceID"] = std::get<3>(var.second);
392
393 for (uint32_t i = 0; i < std::get<4>(var.second).size(); i++)
394 {
395 segments["LockFlag"] = std::get<4>(var.second)[i].first;
396 segments["SegmentLength"] = std::get<4>(var.second)[i].second;
397 myarray.push_back(segments);
398 }
399
400 returnJson["SegmentFlags"] = myarray;
401
402 res.jsonValue["Record"] = returnJson;
403 res.end();
404 return;
405 }
406 }
407}
408
manojkiraneda3b6dea62019-12-13 17:05:36 +0530409void handleReleaseLockAPI(const crow::Request &req, crow::Response &res,
410 const std::vector<uint32_t> &listTransactionIds)
411{
412 BMCWEB_LOG_DEBUG << listTransactionIds.size();
413 BMCWEB_LOG_DEBUG << "Data is present";
414 for (uint32_t i = 0; i < listTransactionIds.size(); i++)
415 {
416 BMCWEB_LOG_DEBUG << listTransactionIds[i];
417 }
418
419 std::string clientId = "hmc-id";
420 std::string sessionId = req.session->uniqueId;
421
422 // validate the request ids
423
424 auto varReleaselock = crow::ibm_mc_lock::lockObject.releaseLock(
425 listTransactionIds, std::make_pair(clientId, sessionId));
426
427 if (!varReleaselock.first)
428 {
429 // validation Failed
430 res.result(boost::beast::http::status::bad_request);
431 res.end();
432 return;
433 }
434 else
435 {
436 auto statusRelease =
437 std::get<crow::ibm_mc_lock::RcRelaseLock>(varReleaselock.second);
438 if (statusRelease.first)
439 {
440 // The current hmc owns all the locks, so we already released
441 // them
442 res.result(boost::beast::http::status::ok);
443 res.end();
444 return;
445 }
446
447 else
448 {
449 // valid rid, but the current hmc does not own all the locks
450 BMCWEB_LOG_DEBUG << "Current HMC does not own all the locks";
451 res.result(boost::beast::http::status::unauthorized);
452
453 auto var = statusRelease.second;
454 nlohmann::json returnJson, segments;
455 nlohmann::json myArray = nlohmann::json::array();
456 returnJson["TransactionID"] = var.first;
457 returnJson["SessionID"] = std::get<0>(var.second);
458 returnJson["HMCID"] = std::get<1>(var.second);
459 returnJson["LockType"] = std::get<2>(var.second);
460 returnJson["ResourceID"] = std::get<3>(var.second);
461
462 for (uint32_t i = 0; i < std::get<4>(var.second).size(); i++)
463 {
464 segments["LockFlag"] = std::get<4>(var.second)[i].first;
465 segments["SegmentLength"] = std::get<4>(var.second)[i].second;
466 myArray.push_back(segments);
467 }
468
469 returnJson["SegmentFlags"] = myArray;
470 res.jsonValue["Record"] = returnJson;
471 res.end();
472 return;
473 }
474 }
475}
476
Ratan Gupta453fed02019-12-14 09:39:47 +0530477template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
478{
479
480 // allowed only for admin
481 BMCWEB_ROUTE(app, "/ibm/v1/")
482 .requires({"ConfigureComponents", "ConfigureManager"})
483 .methods("GET"_method)(
484 [](const crow::Request &req, crow::Response &res) {
485 res.jsonValue["@odata.type"] =
486 "#ibmServiceRoot.v1_0_0.ibmServiceRoot";
487 res.jsonValue["@odata.id"] = "/ibm/v1/";
488 res.jsonValue["Id"] = "IBM Rest RootService";
489 res.jsonValue["Name"] = "IBM Service Root";
490 res.jsonValue["ConfigFiles"] = {
491 {"@odata.id", "/ibm/v1/Host/ConfigFiles"}};
492 res.jsonValue["LockService"] = {
493 {"@odata.id", "/ibm/v1/HMC/LockService"}};
494 res.end();
495 });
Sunitha Harish97b0e432019-11-21 04:59:29 -0600496
Ratan Guptad3630cb2019-12-14 11:21:35 +0530497 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles")
498 .requires({"ConfigureComponents", "ConfigureManager"})
499 .methods("GET"_method)(
500 [](const crow::Request &req, crow::Response &res) {
501 handleConfigFileList(res);
502 });
503
504 BMCWEB_ROUTE(app,
505 "/ibm/v1/Host/ConfigFiles/Actions/FileCollection.DeleteAll")
506 .requires({"ConfigureComponents", "ConfigureManager"})
507 .methods("POST"_method)(
508 [](const crow::Request &req, crow::Response &res) {
509 deleteConfigFiles(res);
510 });
511
asmithakarun1c7b07c2019-09-09 03:42:59 -0500512 BMCWEB_ROUTE(app, "/ibm/v1/Host/ConfigFiles/<path>")
Sunitha Harish97b0e432019-11-21 04:59:29 -0600513 .requires({"ConfigureComponents", "ConfigureManager"})
asmithakarun1c7b07c2019-09-09 03:42:59 -0500514 .methods("PUT"_method, "GET"_method, "DELETE"_method)(
515 [](const crow::Request &req, crow::Response &res,
516 const std::string &path) { handleFileUrl(req, res, path); });
Ratan Gupta734a1c32019-12-14 11:53:48 +0530517
518 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService")
519 .requires({"ConfigureComponents", "ConfigureManager"})
520 .methods("GET"_method)(
521 [](const crow::Request &req, crow::Response &res) {
522 getLockServiceData(res);
523 });
manojkiraneda0b631ae2019-12-03 17:54:28 +0530524
525 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock")
526 .requires({"ConfigureComponents", "ConfigureManager"})
527 .methods("POST"_method)(
528 [](const crow::Request &req, crow::Response &res) {
529 std::vector<nlohmann::json> body;
530
531 if (!redfish::json_util::readJson(req, res, "Request", body))
532 {
533 BMCWEB_LOG_DEBUG << "Not a Valid JSON";
534 res.result(boost::beast::http::status::bad_request);
535 res.end();
536 return;
537 }
538 handleAcquireLockAPI(req, res, body);
539 });
manojkiraneda3b6dea62019-12-13 17:05:36 +0530540
541 BMCWEB_ROUTE(app, "/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock")
542 .requires({"ConfigureComponents", "ConfigureManager"})
543 .methods("POST"_method)(
544 [](const crow::Request &req, crow::Response &res) {
545 std::vector<uint32_t> listTransactionIds;
546
547 if (!redfish::json_util::readJson(req, res, "TransactionIDs",
548 listTransactionIds))
549 {
550 res.result(boost::beast::http::status::bad_request);
551 res.end();
552 return;
553 }
554
555 handleReleaseLockAPI(req, res, listTransactionIds);
556 });
Ratan Gupta453fed02019-12-14 09:39:47 +0530557}
558
559} // namespace ibm_mc
560} // namespace crow