Implement Release Lock in Lock Service

- This commit implements the Release Lock rest API, so that
  any external client(having admin-privelege) can release the
  locks owned by it.

Tested By:

1.curl -k -H "X-Auth-Token:$bmc_tokens" -XPOST -H "Content-type: application/json" -d '{
 "Request" :[
             {
                 "LockType":"Read",
                 "SegmentFlags":
                                 [
                                   {"LockFlag":"LockSame","SegmentLength":3},
                                   {"LockFlag":"DontLock","SegmentLength":4}
                                 ],
                 "ResourceID": 256
              }
         ]
 }' https://<ip>/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock
{
  "TransactionID": 1
}

2.curl -k -H "X-Auth-Token:$bmc_tokens" -XPOST -H "Content-type: application/json" -d '{
  "Request" :[
             {
                 "LockType":"Read",
                 "SegmentFlags":
                                 [
                                   {"LockFlag":"LockSame","SegmentLength":3},
                                   {"LockFlag":"DontLock","SegmentLength":4}
                                 ],
                 "ResourceID": 256
              }
         ]
 }' https://<ip>/ibm/v1/HMC/LockService/Actions/LockService.AcquireLock
{
  "TransactionID": 2
}

3. Try releasing the lock owned by it

curl -k -H "X-Auth-Token:$bmc_tokens" -XPOST -H "Content-type: application/json" -d
'{"TransactionIDs": [1]}' https://<ip>/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock

4. Try releasing the lock, which is not owned by the same session

curl -k -H "X-Auth-Token:$bmc_tokens" -XPOST -H "Content-type: application/json" -d
'{"TransactionIDs": [2]}' https://127.0.0.1:2443/ibm/v1/HMC/LockService/Actions/LockService.ReleaseLock

{
  "Record": {
    "HMCID": "hmc-id",
    "LockType": "Read",
    "ResourceID": 256,
    "SegmentFlags": [
      {
        "LockFlag": "LockSame",
        "SegmentLength": 3
      },
      {
        "LockFlag": "DontLock",
        "SegmentLength": 4
      }
    ],
    "SessionID": "qM4D0VfZt3",
    "TransactionID": 2
  }
}

Signed-off-by: manojkiraneda <manojkiran.eda@gmail.com>
Change-Id: I5d75d44ce805358b25dc293db4dc0f44f4317c06
diff --git a/include/ibm/locks.hpp b/include/ibm/locks.hpp
index 60c55bf..dcd110f 100644
--- a/include/ibm/locks.hpp
+++ b/include/ibm/locks.hpp
@@ -23,16 +23,17 @@
 
 // Lockrequest = session-id | hmc-id | locktype | resourceid | segmentinfo
 using LockRequest = std::tuple<SType, SType, SType, uint64_t, SegmentFlags>;
-
 using LockRequests = std::vector<LockRequest>;
 using Rc =
     std::pair<bool, std::variant<uint32_t, std::pair<uint32_t, LockRequest>>>;
-using RcRelaseLock = std::pair<bool, LockRequest>;
+using RcRelaseLock = std::pair<bool, std::pair<uint32_t, LockRequest>>;
 using RcGetLocklist = std::pair<
     bool,
     std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>>;
-
+using ListOfTransactionIds = std::vector<uint32_t>;
 using RcAcquireLock = std::pair<bool, std::variant<Rc, std::pair<bool, int>>>;
+using RcReleaseLockApi = std::pair<bool, std::variant<bool, RcRelaseLock>>;
+using SessionFlags = std::pair<SType, SType>;
 
 class Lock
 {
@@ -78,6 +79,29 @@
      */
 
     Rc isConflictWithTable(const LockRequests);
+    /*
+     * This function implements the logic of checking the ownership of the
+     * lock from the releaselock request.
+     *
+     * Returns : True (if the requesting HMC & Session owns the lock(s))
+     * Returns : False (if the request HMC or Session does not own the lock(s))
+     */
+
+    RcRelaseLock isItMyLock(const ListOfTransactionIds &, const SessionFlags &);
+
+    /*
+     * This function validates the the list of transactionID's and returns false
+     * if the transaction ID is not valid & not present in the lock table
+     */
+
+    bool validateRids(const ListOfTransactionIds &);
+
+    /*
+     * This function releases the locks that are already obtained by the
+     * requesting Management console.
+     */
+
+    void releaseLock(const ListOfTransactionIds &);
 
     /*
      * This function implements the algorithm for checking the respective
@@ -104,7 +128,20 @@
 
     RcAcquireLock acquireLock(const LockRequests);
 
-  public:
+    /*
+     * This function implements the logic for releasing the lock that are
+     * owned by a management console session.
+     *
+     * The locks can be released by two ways
+     *  - Using list of transaction ID's
+     *  - Using a Session ID
+     *
+     * Client can choose either of the ways by using `Type` JSON key.
+     *
+     */
+    RcReleaseLockApi releaseLock(const ListOfTransactionIds &,
+                                 const SessionFlags &);
+
     Lock()
     {
         transactionId = 0;
@@ -112,6 +149,34 @@
 
 } lockObject;
 
+RcReleaseLockApi Lock::releaseLock(const ListOfTransactionIds &p,
+                                   const SessionFlags &ids)
+{
+
+    bool status = validateRids(p);
+
+    if (!status)
+    {
+        // Validation of rids failed
+        BMCWEB_LOG_DEBUG << "Not a Valid request id";
+        return std::make_pair(false, status);
+    }
+    else
+    {
+        // Validation passed, check if all the locks are owned by the
+        // requesting HMC
+        auto status = isItMyLock(p, ids);
+        if (status.first)
+        {
+            // The current hmc owns all the locks, so we can release
+            // them
+            releaseLock(p);
+        }
+        return std::make_pair(true, status);
+    }
+    return std::make_pair(false, status);
+}
+
 RcAcquireLock Lock::acquireLock(const LockRequests lockRequestStructure)
 {
 
@@ -152,6 +217,72 @@
     return std::make_pair(true, std::make_pair(true, 1));
 }
 
+void Lock::releaseLock(const ListOfTransactionIds &refRids)
+{
+    for (const auto &id : refRids)
+    {
+        if (lockTable.erase(id))
+        {
+            BMCWEB_LOG_DEBUG << "Removing the locks with transaction ID : "
+                             << id;
+        }
+
+        else
+        {
+            BMCWEB_LOG_DEBUG << "Removing the locks from the lock table "
+                                "failed, tranasction ID: "
+                             << id;
+        }
+    }
+}
+
+RcRelaseLock Lock::isItMyLock(const ListOfTransactionIds &refRids,
+                              const SessionFlags &ids)
+{
+    for (const auto &id : refRids)
+    {
+        // Just need to compare the client id of the first lock records in the
+        // complete lock row(in the map), because the rest of the lock records
+        // would have the same client id
+
+        std::string expectedClientId = std::get<1>(lockTable[id][0]);
+        std::string expectedSessionId = std::get<0>(lockTable[id][0]);
+
+        if ((expectedClientId == ids.first) &&
+            (expectedSessionId == ids.second))
+        {
+            // It is owned by the currently request hmc
+            BMCWEB_LOG_DEBUG << "Lock is owned  by the current hmc";
+        }
+        else
+        {
+            BMCWEB_LOG_DEBUG << "Lock is not owned by the current hmc";
+            return std::make_pair(false, std::make_pair(id, lockTable[id][0]));
+        }
+    }
+    return std::make_pair(true, std::make_pair(0, LockRequest()));
+}
+
+bool Lock::validateRids(const ListOfTransactionIds &refRids)
+{
+    for (const auto &id : refRids)
+    {
+        auto search = lockTable.find(id);
+
+        if (search != lockTable.end())
+        {
+            BMCWEB_LOG_DEBUG << "Valid Transaction id";
+            //  continue for the next rid
+        }
+        else
+        {
+            BMCWEB_LOG_DEBUG << "Atleast 1 inValid Request id";
+            return false;
+        }
+    }
+    return true;
+}
+
 bool Lock::isValidLockRequest(const LockRequest refLockRecord)
 {
 
@@ -217,7 +348,6 @@
         }
     }
 
-    // validate the segment length
     return true;
 }