blob: e1919590de9b5be6f3e2397f2013ddc58517429b [file] [log] [blame]
manojkiraneda0b631ae2019-12-03 17:54:28 +05301#pragma once
2
3#include <app.h>
4
5#include <boost/algorithm/string.hpp>
6#include <boost/container/flat_map.hpp>
7#include <filesystem>
8#include <nlohmann/json.hpp>
9
10namespace crow
11{
12namespace ibm_mc_lock
13{
14
15namespace fs = std::filesystem;
16using SType = std::string;
17
18/*----------------------------------------
19|Segment flags : LockFlag | SegmentLength|
20------------------------------------------*/
21
22using SegmentFlags = std::vector<std::pair<SType, uint32_t>>;
23
24// Lockrequest = session-id | hmc-id | locktype | resourceid | segmentinfo
25using LockRequest = std::tuple<SType, SType, SType, uint64_t, SegmentFlags>;
manojkiraneda0b631ae2019-12-03 17:54:28 +053026using LockRequests = std::vector<LockRequest>;
27using Rc =
28 std::pair<bool, std::variant<uint32_t, std::pair<uint32_t, LockRequest>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053029using RcRelaseLock = std::pair<bool, std::pair<uint32_t, LockRequest>>;
manojkiraneda402b5712019-12-13 17:07:09 +053030using RcGetLockList =
31 std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053032using ListOfTransactionIds = std::vector<uint32_t>;
manojkiraneda0b631ae2019-12-03 17:54:28 +053033using RcAcquireLock = std::pair<bool, std::variant<Rc, std::pair<bool, int>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053034using RcReleaseLockApi = std::pair<bool, std::variant<bool, RcRelaseLock>>;
35using SessionFlags = std::pair<SType, SType>;
manojkiraneda402b5712019-12-13 17:07:09 +053036using ListOfSessionIds = std::vector<std::string>;
manojkiraneda0b631ae2019-12-03 17:54:28 +053037
38class Lock
39{
40 uint32_t transactionId;
41 boost::container::flat_map<uint32_t, LockRequests> lockTable;
42
43 /*
44 * This function implements the logic for validating an incomming
45 * lock request/requests.
46 *
47 * Returns : True (if Valid)
48 * Returns : False (if not a Valid lock request)
49 */
50
51 bool isValidLockRequest(const LockRequest);
52
53 /*
54 * This function implements the logic of checking if the incomming
55 * multi-lock request is not having conflicting requirements.
56 *
57 * Returns : True (if conflicting)
58 * Returns : False (if not conflicting)
59 */
60
61 bool isConflictRequest(const LockRequests);
62 /*
63 * Implements the core algorithm to find the conflicting
64 * lock requests.
65 *
66 * This functions takes two lock requests and check if both
67 * are conflicting to each other.
68 *
69 * Returns : True (if conflicting)
70 * Returns : False (if not conflicting)
71 */
72 bool isConflictRecord(const LockRequest, const LockRequest);
73
74 /*
75 * This function implements the logic of checking the conflicting
76 * locks from a incomming single/multi lock requests with the already
77 * existing lock request in the lock table.
78 *
79 */
80
81 Rc isConflictWithTable(const LockRequests);
manojkiraneda3b6dea62019-12-13 17:05:36 +053082 /*
83 * This function implements the logic of checking the ownership of the
84 * lock from the releaselock request.
85 *
86 * Returns : True (if the requesting HMC & Session owns the lock(s))
87 * Returns : False (if the request HMC or Session does not own the lock(s))
88 */
89
90 RcRelaseLock isItMyLock(const ListOfTransactionIds &, const SessionFlags &);
91
92 /*
93 * This function validates the the list of transactionID's and returns false
94 * if the transaction ID is not valid & not present in the lock table
95 */
96
97 bool validateRids(const ListOfTransactionIds &);
98
99 /*
100 * This function releases the locks that are already obtained by the
101 * requesting Management console.
102 */
103
104 void releaseLock(const ListOfTransactionIds &);
manojkiraneda0b631ae2019-12-03 17:54:28 +0530105
106 /*
107 * This function implements the algorithm for checking the respective
108 * bytes of the resource id based on the lock management algorithm.
109 */
110
111 bool checkByte(uint64_t, uint64_t, uint32_t);
112
113 /*
114 * This functions implements a counter that generates a unique 32 bit
115 * number for every successful transaction. This number will be used by
116 * the Management Console for debug.
117 */
118 uint32_t generateTransactionId();
119
120 public:
121 /*
122 * This function implements the logic for acquiring a lock on a
123 * resource if the incomming request is legitimate without any
124 * conflicting requirements & without any conflicting requirement
125 * with the exsiting locks in the lock table.
126 *
127 */
128
129 RcAcquireLock acquireLock(const LockRequests);
130
manojkiraneda3b6dea62019-12-13 17:05:36 +0530131 /*
132 * This function implements the logic for releasing the lock that are
133 * owned by a management console session.
134 *
135 * The locks can be released by two ways
136 * - Using list of transaction ID's
137 * - Using a Session ID
138 *
139 * Client can choose either of the ways by using `Type` JSON key.
140 *
141 */
142 RcReleaseLockApi releaseLock(const ListOfTransactionIds &,
143 const SessionFlags &);
144
manojkiraneda402b5712019-12-13 17:07:09 +0530145 /*
146 * This function implements the logic for getting the list of locks obtained
147 * by a particular management console.
148 */
149 RcGetLockList getLockList(const ListOfSessionIds &);
150
manojkiraneda0b631ae2019-12-03 17:54:28 +0530151 Lock()
152 {
153 transactionId = 0;
154 }
155
156} lockObject;
157
manojkiraneda402b5712019-12-13 17:07:09 +0530158RcGetLockList Lock::getLockList(const ListOfSessionIds &listSessionId)
159{
160
161 std::vector<std::pair<uint32_t, LockRequests>> lockList;
162
163 if (!lockTable.empty())
164 {
165 for (const auto &i : listSessionId)
166 {
167 auto it = lockTable.begin();
168 while (it != lockTable.end())
169 {
170 // Check if session id of this entry matches with session id
171 // given
172 if (std::get<0>(it->second[0]) == i)
173 {
174 BMCWEB_LOG_DEBUG << "Session id is found in the locktable";
175
176 // Push the whole lock record into a vector for returning
177 // the json
178 lockList.push_back(std::make_pair(it->first, it->second));
179 }
180 // Go to next entry in map
181 it++;
182 }
183 }
184 }
185 // we may have found at least one entry with the given session id
186 // return the json list of lock records pertaining to the given
187 // session id, or send an empty list if lock table is empty
188 return lockList;
189}
190
manojkiraneda3b6dea62019-12-13 17:05:36 +0530191RcReleaseLockApi Lock::releaseLock(const ListOfTransactionIds &p,
192 const SessionFlags &ids)
193{
194
195 bool status = validateRids(p);
196
197 if (!status)
198 {
199 // Validation of rids failed
200 BMCWEB_LOG_DEBUG << "Not a Valid request id";
201 return std::make_pair(false, status);
202 }
203 else
204 {
205 // Validation passed, check if all the locks are owned by the
206 // requesting HMC
207 auto status = isItMyLock(p, ids);
208 if (status.first)
209 {
210 // The current hmc owns all the locks, so we can release
211 // them
212 releaseLock(p);
213 }
214 return std::make_pair(true, status);
215 }
216 return std::make_pair(false, status);
217}
218
manojkiraneda0b631ae2019-12-03 17:54:28 +0530219RcAcquireLock Lock::acquireLock(const LockRequests lockRequestStructure)
220{
221
222 // validate the lock request
223
224 for (auto &lockRecord : lockRequestStructure)
225 {
226 bool status = isValidLockRequest(lockRecord);
227 if (!status)
228 {
229 BMCWEB_LOG_DEBUG << "Not a Valid record";
230 BMCWEB_LOG_DEBUG << "Bad json in request";
231 return std::make_pair(true, std::make_pair(status, 0));
232 }
233 }
234 // check for conflict record
235
236 const LockRequests &multiRequest = lockRequestStructure;
237 bool status = isConflictRequest(multiRequest);
238
239 if (status)
240 {
241 BMCWEB_LOG_DEBUG << "There is a conflict within itself";
242 return std::make_pair(true, std::make_pair(status, 1));
243 }
244 else
245 {
246 BMCWEB_LOG_DEBUG << "The request is not conflicting within itself";
247
248 // Need to check for conflict with the locktable entries.
249
250 auto conflict = isConflictWithTable(multiRequest);
251
252 BMCWEB_LOG_DEBUG << "Done with checking conflict with the locktable";
253 return std::make_pair(false, conflict);
254 }
255
256 return std::make_pair(true, std::make_pair(true, 1));
257}
258
manojkiraneda3b6dea62019-12-13 17:05:36 +0530259void Lock::releaseLock(const ListOfTransactionIds &refRids)
260{
261 for (const auto &id : refRids)
262 {
263 if (lockTable.erase(id))
264 {
265 BMCWEB_LOG_DEBUG << "Removing the locks with transaction ID : "
266 << id;
267 }
268
269 else
270 {
271 BMCWEB_LOG_DEBUG << "Removing the locks from the lock table "
272 "failed, tranasction ID: "
273 << id;
274 }
275 }
276}
277
278RcRelaseLock Lock::isItMyLock(const ListOfTransactionIds &refRids,
279 const SessionFlags &ids)
280{
281 for (const auto &id : refRids)
282 {
283 // Just need to compare the client id of the first lock records in the
284 // complete lock row(in the map), because the rest of the lock records
285 // would have the same client id
286
287 std::string expectedClientId = std::get<1>(lockTable[id][0]);
288 std::string expectedSessionId = std::get<0>(lockTable[id][0]);
289
290 if ((expectedClientId == ids.first) &&
291 (expectedSessionId == ids.second))
292 {
293 // It is owned by the currently request hmc
294 BMCWEB_LOG_DEBUG << "Lock is owned by the current hmc";
295 }
296 else
297 {
298 BMCWEB_LOG_DEBUG << "Lock is not owned by the current hmc";
299 return std::make_pair(false, std::make_pair(id, lockTable[id][0]));
300 }
301 }
302 return std::make_pair(true, std::make_pair(0, LockRequest()));
303}
304
305bool Lock::validateRids(const ListOfTransactionIds &refRids)
306{
307 for (const auto &id : refRids)
308 {
309 auto search = lockTable.find(id);
310
311 if (search != lockTable.end())
312 {
313 BMCWEB_LOG_DEBUG << "Valid Transaction id";
314 // continue for the next rid
315 }
316 else
317 {
318 BMCWEB_LOG_DEBUG << "Atleast 1 inValid Request id";
319 return false;
320 }
321 }
322 return true;
323}
324
manojkiraneda0b631ae2019-12-03 17:54:28 +0530325bool Lock::isValidLockRequest(const LockRequest refLockRecord)
326{
327
328 // validate the locktype
329
330 if (!((boost::equals(std::get<2>(refLockRecord), "Read") ||
331 (boost::equals(std::get<2>(refLockRecord), "Write")))))
332 {
333 BMCWEB_LOG_DEBUG << "Validation of LockType Failed";
334 BMCWEB_LOG_DEBUG << "Locktype : " << std::get<2>(refLockRecord);
335 return false;
336 }
337
338 BMCWEB_LOG_DEBUG << static_cast<int>(std::get<4>(refLockRecord).size());
339
340 // validate the number of segments
341 // Allowed No of segments are between 2 and 6
342 if ((static_cast<int>(std::get<4>(refLockRecord).size()) > 6) ||
343 (static_cast<int>(std::get<4>(refLockRecord).size()) < 2))
344 {
345 BMCWEB_LOG_DEBUG << "Validation of Number of Segements Failed";
346 BMCWEB_LOG_DEBUG << "Number of Segments provied : "
347 << sizeof(std::get<4>(refLockRecord));
348 return false;
349 }
350
351 int lockFlag = 0;
352 // validate the lockflags & segment length
353
354 for (const auto &p : std::get<4>(refLockRecord))
355 {
356
357 // validate the lock flags
358 // Allowed lockflags are locksame,lockall & dontlock
359
360 if (!((boost::equals(p.first, "LockSame") ||
361 (boost::equals(p.first, "LockAll")) ||
362 (boost::equals(p.first, "DontLock")))))
363 {
364 BMCWEB_LOG_DEBUG << "Validation of lock flags failed";
365 BMCWEB_LOG_DEBUG << p.first;
366 return false;
367 }
368
369 // validate the segment length
370 // Allowed values of segment length are between 1 and 4
371
372 if (p.second < 1 || p.second > 4)
373 {
374 BMCWEB_LOG_DEBUG << "Validation of Segment Length Failed";
375 BMCWEB_LOG_DEBUG << p.second;
376 return false;
377 }
378
379 if ((boost::equals(p.first, "LockSame") ||
380 (boost::equals(p.first, "LockAll"))))
381 {
382 ++lockFlag;
383 if (lockFlag >= 2)
384 {
385 return false;
386 }
387 }
388 }
389
manojkiraneda0b631ae2019-12-03 17:54:28 +0530390 return true;
391}
392
393Rc Lock::isConflictWithTable(const LockRequests refLockRequestStructure)
394{
395
396 uint32_t transactionId;
397
398 if (lockTable.empty())
399 {
400 transactionId = generateTransactionId();
401 BMCWEB_LOG_DEBUG << transactionId;
402 // Lock table is empty, so we are safe to add the lockrecords
403 // as there will be no conflict
404 BMCWEB_LOG_DEBUG << "Lock table is empty, so adding the lockrecords";
405 lockTable.emplace(std::pair<uint32_t, LockRequests>(
406 transactionId, refLockRequestStructure));
407
408 return std::make_pair(false, transactionId);
409 }
410
411 else
412 {
413 BMCWEB_LOG_DEBUG
414 << "Lock table is not empty, check for conflict with lock table";
415 // Lock table is not empty, compare the lockrequest entries with
416 // the entries in the lock table
417
418 for (const auto &lockRecord1 : refLockRequestStructure)
419 {
420 for (const auto &map : lockTable)
421 {
422 for (const auto &lockRecord2 : map.second)
423 {
424 bool status = isConflictRecord(lockRecord1, lockRecord2);
425 if (status)
426 {
427 return std::make_pair(
428 true, std::make_pair(map.first, lockRecord2));
429 }
430 }
431 }
432 }
433
434 // Reached here, so no conflict with the locktable, so we are safe to
435 // add the request records into the lock table
436
437 // Lock table is empty, so we are safe to add the lockrecords
438 // as there will be no conflict
439 BMCWEB_LOG_DEBUG << " Adding elements into lock table";
440 transactionId = generateTransactionId();
441 lockTable.emplace(
442 std::make_pair(transactionId, refLockRequestStructure));
443 }
444 return std::make_pair(false, transactionId);
445}
446
447bool Lock::isConflictRequest(const LockRequests refLockRequestStructure)
448{
449 // check for all the locks coming in as a part of single request
450 // return conflict if any two lock requests are conflicting
451
452 if (refLockRequestStructure.size() == 1)
453 {
454 BMCWEB_LOG_DEBUG << "Only single lock request, so there is no conflict";
455 // This means , we have only one lock request in the current
456 // request , so no conflict within the request
457 return false;
458 }
459
460 else
461 {
462 BMCWEB_LOG_DEBUG
463 << "There are multiple lock requests coming in a single request";
464
465 // There are multiple requests a part of one request
466
467 for (uint32_t i = 0; i < refLockRequestStructure.size(); i++)
468 {
469 for (uint32_t j = i + 1; j < refLockRequestStructure.size(); j++)
470 {
471 const LockRequest &p = refLockRequestStructure[i];
472 const LockRequest &q = refLockRequestStructure[j];
473 bool status = isConflictRecord(p, q);
474
475 if (status)
476 {
477 return true;
478 }
479 }
480 }
481 }
482 return false;
483}
484
485// This function converts the provided uint64_t resource id's from the two
486// lock requests subjected for comparision, and this function also compares
487// the content by bytes mentioned by a uint32_t number.
488
489// If all the elements in the lock requests which are subjected for comparison
490// are same, then the last comparision would be to check for the respective
491// bytes in the resourceid based on the segment length.
492
493bool Lock::checkByte(uint64_t resourceId1, uint64_t resourceId2,
494 uint32_t position)
495{
496 uint8_t *p = reinterpret_cast<uint8_t *>(&resourceId1);
497 uint8_t *q = reinterpret_cast<uint8_t *>(&resourceId2);
498
499 BMCWEB_LOG_DEBUG << "Comparing bytes " << std::to_string(p[position]) << ","
500 << std::to_string(q[position]);
501 if (p[position] != q[position])
502 {
503 return false;
504 }
505
506 else
507 {
508 return true;
509 }
510 return true;
511}
512
513bool Lock::isConflictRecord(const LockRequest refLockRecord1,
514 const LockRequest refLockRecord2)
515{
516 // No conflict if both are read locks
517
518 if (boost::equals(std::get<2>(refLockRecord1), "Read") &&
519 boost::equals(std::get<2>(refLockRecord2), "Read"))
520 {
521 BMCWEB_LOG_DEBUG << "Both are read locks, no conflict";
522 return false;
523 }
524
525 else
526 {
527 uint32_t i = 0;
528 for (const auto &p : std::get<4>(refLockRecord1))
529 {
530
531 // return conflict when any of them is try to lock all resources
532 // under the current resource level.
533 if (boost::equals(p.first, "LockAll") ||
534 boost::equals(std::get<4>(refLockRecord2)[i].first, "LockAll"))
535 {
536 BMCWEB_LOG_DEBUG
537 << "Either of the Comparing locks are trying to lock all "
538 "resources under the current resource level";
539 return true;
540 }
541
542 // determine if there is a lock-all-with-same-segment-size.
543 // If the current segment sizes are the same,then we should fail.
544
545 if ((boost::equals(p.first, "LockSame") ||
546 boost::equals(std::get<4>(refLockRecord2)[i].first,
547 "LockSame")) &&
548 (p.second == std::get<4>(refLockRecord2)[i].second))
549 {
550 return true;
551 }
552
553 // if segment lengths are not the same, it means two different locks
554 // So no conflict
555 if (p.second != std::get<4>(refLockRecord2)[i].second)
556 {
557 BMCWEB_LOG_DEBUG << "Segment lengths are not same";
558 BMCWEB_LOG_DEBUG << "Segment 1 length : " << p.second;
559 BMCWEB_LOG_DEBUG << "Segment 2 length : "
560 << std::get<4>(refLockRecord2)[i].second;
561 return false;
562 }
563
564 // compare segment data
565
566 for (uint32_t i = 0; i < p.second; i++)
567 {
568 // if the segment data is different , then the locks is on a
569 // different resource So no conflict between the lock records
570 if (!(checkByte(std::get<3>(refLockRecord1),
571 std::get<3>(refLockRecord2), i)))
572 {
573 return false;
574 }
575 }
576
577 ++i;
578 }
579 }
580
581 return false;
582}
583
584uint32_t Lock::generateTransactionId()
585{
586 ++transactionId;
587 return transactionId;
588}
589
590} // namespace ibm_mc_lock
591} // namespace crow