blob: 60c55bffa4fffadc72b7d86803a8500602806fdf [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>;
26
27using LockRequests = std::vector<LockRequest>;
28using Rc =
29 std::pair<bool, std::variant<uint32_t, std::pair<uint32_t, LockRequest>>>;
30using RcRelaseLock = std::pair<bool, LockRequest>;
31using RcGetLocklist = std::pair<
32 bool,
33 std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>>;
34
35using RcAcquireLock = std::pair<bool, std::variant<Rc, std::pair<bool, int>>>;
36
37class Lock
38{
39 uint32_t transactionId;
40 boost::container::flat_map<uint32_t, LockRequests> lockTable;
41
42 /*
43 * This function implements the logic for validating an incomming
44 * lock request/requests.
45 *
46 * Returns : True (if Valid)
47 * Returns : False (if not a Valid lock request)
48 */
49
50 bool isValidLockRequest(const LockRequest);
51
52 /*
53 * This function implements the logic of checking if the incomming
54 * multi-lock request is not having conflicting requirements.
55 *
56 * Returns : True (if conflicting)
57 * Returns : False (if not conflicting)
58 */
59
60 bool isConflictRequest(const LockRequests);
61 /*
62 * Implements the core algorithm to find the conflicting
63 * lock requests.
64 *
65 * This functions takes two lock requests and check if both
66 * are conflicting to each other.
67 *
68 * Returns : True (if conflicting)
69 * Returns : False (if not conflicting)
70 */
71 bool isConflictRecord(const LockRequest, const LockRequest);
72
73 /*
74 * This function implements the logic of checking the conflicting
75 * locks from a incomming single/multi lock requests with the already
76 * existing lock request in the lock table.
77 *
78 */
79
80 Rc isConflictWithTable(const LockRequests);
81
82 /*
83 * This function implements the algorithm for checking the respective
84 * bytes of the resource id based on the lock management algorithm.
85 */
86
87 bool checkByte(uint64_t, uint64_t, uint32_t);
88
89 /*
90 * This functions implements a counter that generates a unique 32 bit
91 * number for every successful transaction. This number will be used by
92 * the Management Console for debug.
93 */
94 uint32_t generateTransactionId();
95
96 public:
97 /*
98 * This function implements the logic for acquiring a lock on a
99 * resource if the incomming request is legitimate without any
100 * conflicting requirements & without any conflicting requirement
101 * with the exsiting locks in the lock table.
102 *
103 */
104
105 RcAcquireLock acquireLock(const LockRequests);
106
107 public:
108 Lock()
109 {
110 transactionId = 0;
111 }
112
113} lockObject;
114
115RcAcquireLock Lock::acquireLock(const LockRequests lockRequestStructure)
116{
117
118 // validate the lock request
119
120 for (auto &lockRecord : lockRequestStructure)
121 {
122 bool status = isValidLockRequest(lockRecord);
123 if (!status)
124 {
125 BMCWEB_LOG_DEBUG << "Not a Valid record";
126 BMCWEB_LOG_DEBUG << "Bad json in request";
127 return std::make_pair(true, std::make_pair(status, 0));
128 }
129 }
130 // check for conflict record
131
132 const LockRequests &multiRequest = lockRequestStructure;
133 bool status = isConflictRequest(multiRequest);
134
135 if (status)
136 {
137 BMCWEB_LOG_DEBUG << "There is a conflict within itself";
138 return std::make_pair(true, std::make_pair(status, 1));
139 }
140 else
141 {
142 BMCWEB_LOG_DEBUG << "The request is not conflicting within itself";
143
144 // Need to check for conflict with the locktable entries.
145
146 auto conflict = isConflictWithTable(multiRequest);
147
148 BMCWEB_LOG_DEBUG << "Done with checking conflict with the locktable";
149 return std::make_pair(false, conflict);
150 }
151
152 return std::make_pair(true, std::make_pair(true, 1));
153}
154
155bool Lock::isValidLockRequest(const LockRequest refLockRecord)
156{
157
158 // validate the locktype
159
160 if (!((boost::equals(std::get<2>(refLockRecord), "Read") ||
161 (boost::equals(std::get<2>(refLockRecord), "Write")))))
162 {
163 BMCWEB_LOG_DEBUG << "Validation of LockType Failed";
164 BMCWEB_LOG_DEBUG << "Locktype : " << std::get<2>(refLockRecord);
165 return false;
166 }
167
168 BMCWEB_LOG_DEBUG << static_cast<int>(std::get<4>(refLockRecord).size());
169
170 // validate the number of segments
171 // Allowed No of segments are between 2 and 6
172 if ((static_cast<int>(std::get<4>(refLockRecord).size()) > 6) ||
173 (static_cast<int>(std::get<4>(refLockRecord).size()) < 2))
174 {
175 BMCWEB_LOG_DEBUG << "Validation of Number of Segements Failed";
176 BMCWEB_LOG_DEBUG << "Number of Segments provied : "
177 << sizeof(std::get<4>(refLockRecord));
178 return false;
179 }
180
181 int lockFlag = 0;
182 // validate the lockflags & segment length
183
184 for (const auto &p : std::get<4>(refLockRecord))
185 {
186
187 // validate the lock flags
188 // Allowed lockflags are locksame,lockall & dontlock
189
190 if (!((boost::equals(p.first, "LockSame") ||
191 (boost::equals(p.first, "LockAll")) ||
192 (boost::equals(p.first, "DontLock")))))
193 {
194 BMCWEB_LOG_DEBUG << "Validation of lock flags failed";
195 BMCWEB_LOG_DEBUG << p.first;
196 return false;
197 }
198
199 // validate the segment length
200 // Allowed values of segment length are between 1 and 4
201
202 if (p.second < 1 || p.second > 4)
203 {
204 BMCWEB_LOG_DEBUG << "Validation of Segment Length Failed";
205 BMCWEB_LOG_DEBUG << p.second;
206 return false;
207 }
208
209 if ((boost::equals(p.first, "LockSame") ||
210 (boost::equals(p.first, "LockAll"))))
211 {
212 ++lockFlag;
213 if (lockFlag >= 2)
214 {
215 return false;
216 }
217 }
218 }
219
220 // validate the segment length
221 return true;
222}
223
224Rc Lock::isConflictWithTable(const LockRequests refLockRequestStructure)
225{
226
227 uint32_t transactionId;
228
229 if (lockTable.empty())
230 {
231 transactionId = generateTransactionId();
232 BMCWEB_LOG_DEBUG << transactionId;
233 // Lock table is empty, so we are safe to add the lockrecords
234 // as there will be no conflict
235 BMCWEB_LOG_DEBUG << "Lock table is empty, so adding the lockrecords";
236 lockTable.emplace(std::pair<uint32_t, LockRequests>(
237 transactionId, refLockRequestStructure));
238
239 return std::make_pair(false, transactionId);
240 }
241
242 else
243 {
244 BMCWEB_LOG_DEBUG
245 << "Lock table is not empty, check for conflict with lock table";
246 // Lock table is not empty, compare the lockrequest entries with
247 // the entries in the lock table
248
249 for (const auto &lockRecord1 : refLockRequestStructure)
250 {
251 for (const auto &map : lockTable)
252 {
253 for (const auto &lockRecord2 : map.second)
254 {
255 bool status = isConflictRecord(lockRecord1, lockRecord2);
256 if (status)
257 {
258 return std::make_pair(
259 true, std::make_pair(map.first, lockRecord2));
260 }
261 }
262 }
263 }
264
265 // Reached here, so no conflict with the locktable, so we are safe to
266 // add the request records into the lock table
267
268 // Lock table is empty, so we are safe to add the lockrecords
269 // as there will be no conflict
270 BMCWEB_LOG_DEBUG << " Adding elements into lock table";
271 transactionId = generateTransactionId();
272 lockTable.emplace(
273 std::make_pair(transactionId, refLockRequestStructure));
274 }
275 return std::make_pair(false, transactionId);
276}
277
278bool Lock::isConflictRequest(const LockRequests refLockRequestStructure)
279{
280 // check for all the locks coming in as a part of single request
281 // return conflict if any two lock requests are conflicting
282
283 if (refLockRequestStructure.size() == 1)
284 {
285 BMCWEB_LOG_DEBUG << "Only single lock request, so there is no conflict";
286 // This means , we have only one lock request in the current
287 // request , so no conflict within the request
288 return false;
289 }
290
291 else
292 {
293 BMCWEB_LOG_DEBUG
294 << "There are multiple lock requests coming in a single request";
295
296 // There are multiple requests a part of one request
297
298 for (uint32_t i = 0; i < refLockRequestStructure.size(); i++)
299 {
300 for (uint32_t j = i + 1; j < refLockRequestStructure.size(); j++)
301 {
302 const LockRequest &p = refLockRequestStructure[i];
303 const LockRequest &q = refLockRequestStructure[j];
304 bool status = isConflictRecord(p, q);
305
306 if (status)
307 {
308 return true;
309 }
310 }
311 }
312 }
313 return false;
314}
315
316// This function converts the provided uint64_t resource id's from the two
317// lock requests subjected for comparision, and this function also compares
318// the content by bytes mentioned by a uint32_t number.
319
320// If all the elements in the lock requests which are subjected for comparison
321// are same, then the last comparision would be to check for the respective
322// bytes in the resourceid based on the segment length.
323
324bool Lock::checkByte(uint64_t resourceId1, uint64_t resourceId2,
325 uint32_t position)
326{
327 uint8_t *p = reinterpret_cast<uint8_t *>(&resourceId1);
328 uint8_t *q = reinterpret_cast<uint8_t *>(&resourceId2);
329
330 BMCWEB_LOG_DEBUG << "Comparing bytes " << std::to_string(p[position]) << ","
331 << std::to_string(q[position]);
332 if (p[position] != q[position])
333 {
334 return false;
335 }
336
337 else
338 {
339 return true;
340 }
341 return true;
342}
343
344bool Lock::isConflictRecord(const LockRequest refLockRecord1,
345 const LockRequest refLockRecord2)
346{
347 // No conflict if both are read locks
348
349 if (boost::equals(std::get<2>(refLockRecord1), "Read") &&
350 boost::equals(std::get<2>(refLockRecord2), "Read"))
351 {
352 BMCWEB_LOG_DEBUG << "Both are read locks, no conflict";
353 return false;
354 }
355
356 else
357 {
358 uint32_t i = 0;
359 for (const auto &p : std::get<4>(refLockRecord1))
360 {
361
362 // return conflict when any of them is try to lock all resources
363 // under the current resource level.
364 if (boost::equals(p.first, "LockAll") ||
365 boost::equals(std::get<4>(refLockRecord2)[i].first, "LockAll"))
366 {
367 BMCWEB_LOG_DEBUG
368 << "Either of the Comparing locks are trying to lock all "
369 "resources under the current resource level";
370 return true;
371 }
372
373 // determine if there is a lock-all-with-same-segment-size.
374 // If the current segment sizes are the same,then we should fail.
375
376 if ((boost::equals(p.first, "LockSame") ||
377 boost::equals(std::get<4>(refLockRecord2)[i].first,
378 "LockSame")) &&
379 (p.second == std::get<4>(refLockRecord2)[i].second))
380 {
381 return true;
382 }
383
384 // if segment lengths are not the same, it means two different locks
385 // So no conflict
386 if (p.second != std::get<4>(refLockRecord2)[i].second)
387 {
388 BMCWEB_LOG_DEBUG << "Segment lengths are not same";
389 BMCWEB_LOG_DEBUG << "Segment 1 length : " << p.second;
390 BMCWEB_LOG_DEBUG << "Segment 2 length : "
391 << std::get<4>(refLockRecord2)[i].second;
392 return false;
393 }
394
395 // compare segment data
396
397 for (uint32_t i = 0; i < p.second; i++)
398 {
399 // if the segment data is different , then the locks is on a
400 // different resource So no conflict between the lock records
401 if (!(checkByte(std::get<3>(refLockRecord1),
402 std::get<3>(refLockRecord2), i)))
403 {
404 return false;
405 }
406 }
407
408 ++i;
409 }
410 }
411
412 return false;
413}
414
415uint32_t Lock::generateTransactionId()
416{
417 ++transactionId;
418 return transactionId;
419}
420
421} // namespace ibm_mc_lock
422} // namespace crow