blob: dcd110f8dc692fc416c0bc30b1f7954a196eaa6e [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>>;
manojkiraneda0b631ae2019-12-03 17:54:28 +053030using RcGetLocklist = std::pair<
31 bool,
32 std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053033using ListOfTransactionIds = std::vector<uint32_t>;
manojkiraneda0b631ae2019-12-03 17:54:28 +053034using RcAcquireLock = std::pair<bool, std::variant<Rc, std::pair<bool, int>>>;
manojkiraneda3b6dea62019-12-13 17:05:36 +053035using RcReleaseLockApi = std::pair<bool, std::variant<bool, RcRelaseLock>>;
36using SessionFlags = std::pair<SType, SType>;
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
manojkiraneda0b631ae2019-12-03 17:54:28 +0530145 Lock()
146 {
147 transactionId = 0;
148 }
149
150} lockObject;
151
manojkiraneda3b6dea62019-12-13 17:05:36 +0530152RcReleaseLockApi Lock::releaseLock(const ListOfTransactionIds &p,
153 const SessionFlags &ids)
154{
155
156 bool status = validateRids(p);
157
158 if (!status)
159 {
160 // Validation of rids failed
161 BMCWEB_LOG_DEBUG << "Not a Valid request id";
162 return std::make_pair(false, status);
163 }
164 else
165 {
166 // Validation passed, check if all the locks are owned by the
167 // requesting HMC
168 auto status = isItMyLock(p, ids);
169 if (status.first)
170 {
171 // The current hmc owns all the locks, so we can release
172 // them
173 releaseLock(p);
174 }
175 return std::make_pair(true, status);
176 }
177 return std::make_pair(false, status);
178}
179
manojkiraneda0b631ae2019-12-03 17:54:28 +0530180RcAcquireLock Lock::acquireLock(const LockRequests lockRequestStructure)
181{
182
183 // validate the lock request
184
185 for (auto &lockRecord : lockRequestStructure)
186 {
187 bool status = isValidLockRequest(lockRecord);
188 if (!status)
189 {
190 BMCWEB_LOG_DEBUG << "Not a Valid record";
191 BMCWEB_LOG_DEBUG << "Bad json in request";
192 return std::make_pair(true, std::make_pair(status, 0));
193 }
194 }
195 // check for conflict record
196
197 const LockRequests &multiRequest = lockRequestStructure;
198 bool status = isConflictRequest(multiRequest);
199
200 if (status)
201 {
202 BMCWEB_LOG_DEBUG << "There is a conflict within itself";
203 return std::make_pair(true, std::make_pair(status, 1));
204 }
205 else
206 {
207 BMCWEB_LOG_DEBUG << "The request is not conflicting within itself";
208
209 // Need to check for conflict with the locktable entries.
210
211 auto conflict = isConflictWithTable(multiRequest);
212
213 BMCWEB_LOG_DEBUG << "Done with checking conflict with the locktable";
214 return std::make_pair(false, conflict);
215 }
216
217 return std::make_pair(true, std::make_pair(true, 1));
218}
219
manojkiraneda3b6dea62019-12-13 17:05:36 +0530220void Lock::releaseLock(const ListOfTransactionIds &refRids)
221{
222 for (const auto &id : refRids)
223 {
224 if (lockTable.erase(id))
225 {
226 BMCWEB_LOG_DEBUG << "Removing the locks with transaction ID : "
227 << id;
228 }
229
230 else
231 {
232 BMCWEB_LOG_DEBUG << "Removing the locks from the lock table "
233 "failed, tranasction ID: "
234 << id;
235 }
236 }
237}
238
239RcRelaseLock Lock::isItMyLock(const ListOfTransactionIds &refRids,
240 const SessionFlags &ids)
241{
242 for (const auto &id : refRids)
243 {
244 // Just need to compare the client id of the first lock records in the
245 // complete lock row(in the map), because the rest of the lock records
246 // would have the same client id
247
248 std::string expectedClientId = std::get<1>(lockTable[id][0]);
249 std::string expectedSessionId = std::get<0>(lockTable[id][0]);
250
251 if ((expectedClientId == ids.first) &&
252 (expectedSessionId == ids.second))
253 {
254 // It is owned by the currently request hmc
255 BMCWEB_LOG_DEBUG << "Lock is owned by the current hmc";
256 }
257 else
258 {
259 BMCWEB_LOG_DEBUG << "Lock is not owned by the current hmc";
260 return std::make_pair(false, std::make_pair(id, lockTable[id][0]));
261 }
262 }
263 return std::make_pair(true, std::make_pair(0, LockRequest()));
264}
265
266bool Lock::validateRids(const ListOfTransactionIds &refRids)
267{
268 for (const auto &id : refRids)
269 {
270 auto search = lockTable.find(id);
271
272 if (search != lockTable.end())
273 {
274 BMCWEB_LOG_DEBUG << "Valid Transaction id";
275 // continue for the next rid
276 }
277 else
278 {
279 BMCWEB_LOG_DEBUG << "Atleast 1 inValid Request id";
280 return false;
281 }
282 }
283 return true;
284}
285
manojkiraneda0b631ae2019-12-03 17:54:28 +0530286bool Lock::isValidLockRequest(const LockRequest refLockRecord)
287{
288
289 // validate the locktype
290
291 if (!((boost::equals(std::get<2>(refLockRecord), "Read") ||
292 (boost::equals(std::get<2>(refLockRecord), "Write")))))
293 {
294 BMCWEB_LOG_DEBUG << "Validation of LockType Failed";
295 BMCWEB_LOG_DEBUG << "Locktype : " << std::get<2>(refLockRecord);
296 return false;
297 }
298
299 BMCWEB_LOG_DEBUG << static_cast<int>(std::get<4>(refLockRecord).size());
300
301 // validate the number of segments
302 // Allowed No of segments are between 2 and 6
303 if ((static_cast<int>(std::get<4>(refLockRecord).size()) > 6) ||
304 (static_cast<int>(std::get<4>(refLockRecord).size()) < 2))
305 {
306 BMCWEB_LOG_DEBUG << "Validation of Number of Segements Failed";
307 BMCWEB_LOG_DEBUG << "Number of Segments provied : "
308 << sizeof(std::get<4>(refLockRecord));
309 return false;
310 }
311
312 int lockFlag = 0;
313 // validate the lockflags & segment length
314
315 for (const auto &p : std::get<4>(refLockRecord))
316 {
317
318 // validate the lock flags
319 // Allowed lockflags are locksame,lockall & dontlock
320
321 if (!((boost::equals(p.first, "LockSame") ||
322 (boost::equals(p.first, "LockAll")) ||
323 (boost::equals(p.first, "DontLock")))))
324 {
325 BMCWEB_LOG_DEBUG << "Validation of lock flags failed";
326 BMCWEB_LOG_DEBUG << p.first;
327 return false;
328 }
329
330 // validate the segment length
331 // Allowed values of segment length are between 1 and 4
332
333 if (p.second < 1 || p.second > 4)
334 {
335 BMCWEB_LOG_DEBUG << "Validation of Segment Length Failed";
336 BMCWEB_LOG_DEBUG << p.second;
337 return false;
338 }
339
340 if ((boost::equals(p.first, "LockSame") ||
341 (boost::equals(p.first, "LockAll"))))
342 {
343 ++lockFlag;
344 if (lockFlag >= 2)
345 {
346 return false;
347 }
348 }
349 }
350
manojkiraneda0b631ae2019-12-03 17:54:28 +0530351 return true;
352}
353
354Rc Lock::isConflictWithTable(const LockRequests refLockRequestStructure)
355{
356
357 uint32_t transactionId;
358
359 if (lockTable.empty())
360 {
361 transactionId = generateTransactionId();
362 BMCWEB_LOG_DEBUG << transactionId;
363 // Lock table is empty, so we are safe to add the lockrecords
364 // as there will be no conflict
365 BMCWEB_LOG_DEBUG << "Lock table is empty, so adding the lockrecords";
366 lockTable.emplace(std::pair<uint32_t, LockRequests>(
367 transactionId, refLockRequestStructure));
368
369 return std::make_pair(false, transactionId);
370 }
371
372 else
373 {
374 BMCWEB_LOG_DEBUG
375 << "Lock table is not empty, check for conflict with lock table";
376 // Lock table is not empty, compare the lockrequest entries with
377 // the entries in the lock table
378
379 for (const auto &lockRecord1 : refLockRequestStructure)
380 {
381 for (const auto &map : lockTable)
382 {
383 for (const auto &lockRecord2 : map.second)
384 {
385 bool status = isConflictRecord(lockRecord1, lockRecord2);
386 if (status)
387 {
388 return std::make_pair(
389 true, std::make_pair(map.first, lockRecord2));
390 }
391 }
392 }
393 }
394
395 // Reached here, so no conflict with the locktable, so we are safe to
396 // add the request records into the lock table
397
398 // Lock table is empty, so we are safe to add the lockrecords
399 // as there will be no conflict
400 BMCWEB_LOG_DEBUG << " Adding elements into lock table";
401 transactionId = generateTransactionId();
402 lockTable.emplace(
403 std::make_pair(transactionId, refLockRequestStructure));
404 }
405 return std::make_pair(false, transactionId);
406}
407
408bool Lock::isConflictRequest(const LockRequests refLockRequestStructure)
409{
410 // check for all the locks coming in as a part of single request
411 // return conflict if any two lock requests are conflicting
412
413 if (refLockRequestStructure.size() == 1)
414 {
415 BMCWEB_LOG_DEBUG << "Only single lock request, so there is no conflict";
416 // This means , we have only one lock request in the current
417 // request , so no conflict within the request
418 return false;
419 }
420
421 else
422 {
423 BMCWEB_LOG_DEBUG
424 << "There are multiple lock requests coming in a single request";
425
426 // There are multiple requests a part of one request
427
428 for (uint32_t i = 0; i < refLockRequestStructure.size(); i++)
429 {
430 for (uint32_t j = i + 1; j < refLockRequestStructure.size(); j++)
431 {
432 const LockRequest &p = refLockRequestStructure[i];
433 const LockRequest &q = refLockRequestStructure[j];
434 bool status = isConflictRecord(p, q);
435
436 if (status)
437 {
438 return true;
439 }
440 }
441 }
442 }
443 return false;
444}
445
446// This function converts the provided uint64_t resource id's from the two
447// lock requests subjected for comparision, and this function also compares
448// the content by bytes mentioned by a uint32_t number.
449
450// If all the elements in the lock requests which are subjected for comparison
451// are same, then the last comparision would be to check for the respective
452// bytes in the resourceid based on the segment length.
453
454bool Lock::checkByte(uint64_t resourceId1, uint64_t resourceId2,
455 uint32_t position)
456{
457 uint8_t *p = reinterpret_cast<uint8_t *>(&resourceId1);
458 uint8_t *q = reinterpret_cast<uint8_t *>(&resourceId2);
459
460 BMCWEB_LOG_DEBUG << "Comparing bytes " << std::to_string(p[position]) << ","
461 << std::to_string(q[position]);
462 if (p[position] != q[position])
463 {
464 return false;
465 }
466
467 else
468 {
469 return true;
470 }
471 return true;
472}
473
474bool Lock::isConflictRecord(const LockRequest refLockRecord1,
475 const LockRequest refLockRecord2)
476{
477 // No conflict if both are read locks
478
479 if (boost::equals(std::get<2>(refLockRecord1), "Read") &&
480 boost::equals(std::get<2>(refLockRecord2), "Read"))
481 {
482 BMCWEB_LOG_DEBUG << "Both are read locks, no conflict";
483 return false;
484 }
485
486 else
487 {
488 uint32_t i = 0;
489 for (const auto &p : std::get<4>(refLockRecord1))
490 {
491
492 // return conflict when any of them is try to lock all resources
493 // under the current resource level.
494 if (boost::equals(p.first, "LockAll") ||
495 boost::equals(std::get<4>(refLockRecord2)[i].first, "LockAll"))
496 {
497 BMCWEB_LOG_DEBUG
498 << "Either of the Comparing locks are trying to lock all "
499 "resources under the current resource level";
500 return true;
501 }
502
503 // determine if there is a lock-all-with-same-segment-size.
504 // If the current segment sizes are the same,then we should fail.
505
506 if ((boost::equals(p.first, "LockSame") ||
507 boost::equals(std::get<4>(refLockRecord2)[i].first,
508 "LockSame")) &&
509 (p.second == std::get<4>(refLockRecord2)[i].second))
510 {
511 return true;
512 }
513
514 // if segment lengths are not the same, it means two different locks
515 // So no conflict
516 if (p.second != std::get<4>(refLockRecord2)[i].second)
517 {
518 BMCWEB_LOG_DEBUG << "Segment lengths are not same";
519 BMCWEB_LOG_DEBUG << "Segment 1 length : " << p.second;
520 BMCWEB_LOG_DEBUG << "Segment 2 length : "
521 << std::get<4>(refLockRecord2)[i].second;
522 return false;
523 }
524
525 // compare segment data
526
527 for (uint32_t i = 0; i < p.second; i++)
528 {
529 // if the segment data is different , then the locks is on a
530 // different resource So no conflict between the lock records
531 if (!(checkByte(std::get<3>(refLockRecord1),
532 std::get<3>(refLockRecord2), i)))
533 {
534 return false;
535 }
536 }
537
538 ++i;
539 }
540 }
541
542 return false;
543}
544
545uint32_t Lock::generateTransactionId()
546{
547 ++transactionId;
548 return transactionId;
549}
550
551} // namespace ibm_mc_lock
552} // namespace crow