In progress trxns criteria fix 05/65305/7
authorBalaji, Ramya (rb111y) <rb111y@att.com>
Fri, 7 Sep 2018 20:00:46 +0000 (16:00 -0400)
committerTakamune Cho <tc012c@att.com>
Tue, 11 Sep 2018 15:10:09 +0000 (15:10 +0000)
Changed the criteria for inprogress
transactions to the last X hours.
This will eliminate stale transactions
causing an incorrect scope
overlap error.

Issue-ID: APPC-1194
Change-Id: I2f3147503f4202e8e8931b2615e626e3b9af8261
Signed-off-by: Balaji, Ramya (rb111y) <rb111y@att.com>
appc-dispatcher/appc-dispatcher-common/transaction-recorder/src/main/java/org/onap/appc/transactionrecorder/TransactionRecorder.java
appc-dispatcher/appc-dispatcher-common/transaction-recorder/src/main/java/org/onap/appc/transactionrecorder/impl/TransactionRecorderImpl.java
appc-dispatcher/appc-dispatcher-common/transaction-recorder/src/test/java/org/onap/appc/transactionrecorder/impl/TransactionRecorderImplTest.java
appc-dispatcher/appc-request-handler/appc-request-handler-core/src/main/java/org/onap/appc/requesthandler/impl/RequestValidatorImpl.java
appc-dispatcher/appc-request-handler/appc-request-handler-core/src/test/java/org/onap/appc/requesthandler/impl/RequestValidatorImplTest.java

index 299115d..17e0e25 100644 (file)
@@ -9,15 +9,15 @@
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- * 
+ *
  * ============LICENSE_END=========================================================
  */
 
@@ -62,10 +62,11 @@ public interface TransactionRecorder {
      * Fetch list of Transactions which are in non-terminal state i.e. ACCEPTED or RECEIVED for particular TargetId.
      * @param record Transactions object from which TargetId and StartTime is extracted to fetch list of in progress
      *               requests which APPC received before the current request.
+     * @param interval Number of hours - Time window to consider requests as being in progress
      * @return List of Transactions in non terminal state.
      * @throws APPCException
      */
-    List<TransactionRecord> getInProgressRequests(TransactionRecord record) throws APPCException;
+    List<TransactionRecord> getInProgressRequests(TransactionRecord record, int interval) throws APPCException;
 
     /**
      * Checks whether the incoming request is duplicate.
index 7c1581f..98ea1b5 100644 (file)
@@ -9,15 +9,15 @@
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- * 
+ *
  * ============LICENSE_END=========================================================
  */
 
@@ -45,6 +45,7 @@ import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.time.temporal.ChronoUnit;
 
 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.TRANSACTION_ATTRIBUTES.*;
 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.*;
@@ -165,19 +166,24 @@ public class TransactionRecorderImpl implements TransactionRecorder {
     }
 
     @Override
-    public List<TransactionRecord> getInProgressRequests(TransactionRecord record) throws APPCException {
+    public List<TransactionRecord> getInProgressRequests(TransactionRecord record, int interval) throws APPCException {
 
-        final String IN_PROGRESS_REQUESTS_QUERY = "SELECT * FROM " +
+        String IN_PROGRESS_REQUESTS_QUERY = "SELECT * FROM " +
             TransactionConstants.TRANSACTIONS + WHERE +
             TARGET_ID + " = ? AND " +
             STATE.getColumnName() + " IN (?,?) AND " +
             START_TIME.getColumnName() + " < ?";
 
         ArrayList<String> inProgressQueryParams = new ArrayList<>();
+        Instant window = record.getStartTime().minus(interval, ChronoUnit.HOURS);
         inProgressQueryParams.add(record.getTargetId());
         inProgressQueryParams.add(RequestStatus.RECEIVED.name());
         inProgressQueryParams.add(RequestStatus.ACCEPTED.name());
         inProgressQueryParams.add(dateToStringConverterMillis(record.getStartTime()));
+        if (interval > 0) {
+            IN_PROGRESS_REQUESTS_QUERY += " AND " + START_TIME.getColumnName() + " > ? ";
+            inProgressQueryParams.add(dateToStringConverterMillis(window));
+        }
 
         try (CachedRowSet rowSet = dbLibService.getData(IN_PROGRESS_REQUESTS_QUERY, inProgressQueryParams, SCHEMA)) {
             List<TransactionRecord> inProgressRecords = new ArrayList<>();
index fd1790d..1418e89 100644 (file)
@@ -9,15 +9,15 @@
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- * 
+ *
  * ============LICENSE_END=========================================================
  */
 
@@ -45,8 +45,10 @@ import java.sql.*;
 import java.time.Instant;
 import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -152,7 +154,21 @@ public class TransactionRecorderImplTest {
         input.setStartTime(Instant.now());
         Mockito.when(dbLibService.getData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
                 inMemoryExecutionWithResultSet(invocation.getArguments()));
-        Assert.assertEquals(1, transactionRecorderImpl.getInProgressRequests(input).size());
+        Assert.assertEquals(1, transactionRecorderImpl.getInProgressRequests(input,0).size());
+
+    }
+
+    @Test
+    public void testGetInProgressRequestsWithinTimeInterval() throws SQLException, APPCException {
+        TransactionRecord record1 = prepareTransactionsInput();
+        record1.setStartTime(Instant.now().minus(4,ChronoUnit.HOURS));
+        insertRecord(record1);
+        TransactionRecord input = prepareTransactionsInput();
+        input.setStartTime(Instant.now());
+        Mockito.when(dbLibService.getData(anyString(), anyObject(), anyString())).thenAnswer(invocation ->
+                inMemoryExecutionWithResultSet(invocation.getArguments()));
+        List<TransactionRecord> aList= transactionRecorderImpl.getInProgressRequests(input,12);
+        Assert.assertEquals(1, transactionRecorderImpl.getInProgressRequests(input,12).size());
 
     }
 
index 53f15a0..e97f7c0 100644 (file)
@@ -9,15 +9,15 @@
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- * 
+ *
  * ============LICENSE_END=========================================================
  */
 
@@ -93,21 +93,26 @@ public class RequestValidatorImpl extends AbstractRequestValidatorImpl {
     private RequestValidationPolicy requestValidationPolicy;
     private RestClientInvoker client;
     private String path;
+    private int transactionWindowInterval=0;
 
     static final String SCOPE_OVERLAP_ENDPOINT = "appc.LCM.scopeOverlap.endpoint";
     static final String ODL_USER = "appc.LCM.provider.user";
     static final String ODL_PASS = "appc.LCM.provider.pass";
-    
+    static final String TRANSACTION_WINDOW_HOURS = "appc.inProgressTransactionWindow.hours";
+
     public void initialize() throws APPCException {
         logger.info("Initializing RequestValidatorImpl.");
         String endpoint = null;
         String user = null;
         String pass =null;
+        String transactionWindow = null;
+
         Properties properties = configuration.getProperties();
         if (properties != null) {
             endpoint = properties.getProperty(SCOPE_OVERLAP_ENDPOINT);
             user = properties.getProperty(ODL_USER);
             pass = properties.getProperty(ODL_PASS);
+            transactionWindow = properties.getProperty(TRANSACTION_WINDOW_HOURS);
         }
         if (endpoint == null) {
             String message = "End point is not defined for scope over lap service in appc.properties.";
@@ -122,12 +127,24 @@ public class RequestValidatorImpl extends AbstractRequestValidatorImpl {
             return;
         }
 
+        if (StringUtils.isNotBlank(transactionWindow)) {
+            logger.info("RequestValidatorImpl::TransactionWindow defined !!!");
+            try {
+                transactionWindowInterval = Integer.parseInt(transactionWindow);
+            }
+            catch (NumberFormatException e) {
+                String message = "RequestValidatorImpl:::Error parsing transaction window interval!";
+                logger.error(message, e);
+                throw new APPCException(message);
+            }
+        }
+
         try {
             URL url = new URL(endpoint);
             client = new RestClientInvoker(url);
             client.setAuthentication(user, pass);
-            path = url.getPath();    
-            
+            path = url.getPath();
+
         } catch (MalformedURLException e) {
             String message = "Invalid endpoint " + endpoint;
             logger.error(message, e);
@@ -199,10 +216,22 @@ public class RequestValidatorImpl extends AbstractRequestValidatorImpl {
             return;
         }
 
+        List<TransactionRecord> inProgressTransactionsAll = transactionRecorder
+                .getInProgressRequests(runtimeContext.getTransactionRecord(),0);
         List<TransactionRecord> inProgressTransactions = transactionRecorder
-                .getInProgressRequests(runtimeContext.getTransactionRecord());
+                .getInProgressRequests(runtimeContext.getTransactionRecord(),transactionWindowInterval);
+
+        long inProgressTransactionsAllCount = inProgressTransactionsAll.size();
+        long inProgressTransactionsRelevant = inProgressTransactions.size();
         logger.debug("In progress requests " + inProgressTransactions.toString());
 
+        if ( inProgressTransactions.isEmpty()){ //No need to check for scope overlap
+            return;
+        }
+
+        logInProgressTransactions(inProgressTransactions,inProgressTransactionsAllCount,
+            inProgressTransactionsRelevant );
+
         Long exclusiveRequestCount = inProgressTransactions.stream()
                 .filter(record -> record.getMode().equals(Flags.Mode.EXCLUSIVE.name())).count();
         if (exclusiveRequestCount > 0) {
@@ -319,7 +348,7 @@ public class RequestValidatorImpl extends AbstractRequestValidatorImpl {
         ObjectMapper objectMapper = new ObjectMapper();
         ScopeOverlapModel scopeOverlapModel = getScopeOverlapModel(requestContext, inProgressTransactions);
         // Added for change in interface for action level
-        
+
         JsonNode jsonObject = objectMapper.valueToTree(scopeOverlapModel);
 
         return jsonObject;
@@ -375,7 +404,7 @@ public class RequestValidatorImpl extends AbstractRequestValidatorImpl {
         Request request = new Request();
 
         Date date = new Date();
-        request.setRequestID("RequestId-ScopeOverlap " + date.toString()); 
+        request.setRequestID("RequestId-ScopeOverlap " + date.toString());
         request.setAction("isScopeOverlap");
         ObjectMapper objectMapper = new ObjectMapper();
         JsonNode json = objectMapper.valueToTree(requestData);
@@ -383,7 +412,7 @@ public class RequestValidatorImpl extends AbstractRequestValidatorImpl {
         Input input = new Input();
         input.setRequest(request);
         scopeOverlapModel.setInput(input);
-        
+
         return scopeOverlapModel;
     }
 
@@ -497,4 +526,28 @@ public class RequestValidatorImpl extends AbstractRequestValidatorImpl {
         }
         return workflowRequest;
     }
+
+    public String logInProgressTransactions(List<TransactionRecord> inProgressTransactions,
+            long inProgressTransactionsAllCount, long inProgressTransactionsRelevant) {
+            if (inProgressTransactionsAllCount > inProgressTransactionsRelevant) {
+                logger.info("Found Stale Transactions! Ignoring Stale Transactions for target, only considering "
+                    + "transactions within the last " + transactionWindowInterval + " hours as transactions in-progress");
+            }
+            String logMsg="";
+            for (TransactionRecord tr: inProgressTransactions) {
+                logMsg = ("In Progress transaction for Target ID - "+ tr.getTargetId()
+                        + " in state " + tr.getRequestState()
+                        + " with Start time " + tr.getStartTime().toString()
+                        + " for more than configurable time period " + transactionWindowInterval
+                        + " hours [transaction details - Request ID - " + tr.getTransactionId()
+                        + ", Service Instance Id -" + tr.getServiceInstanceId()
+                        + ", Vserver_id - " + tr.getVserverId()
+                        + ", VNFC_name - "+ tr.getVnfcName()
+                        + ", VF module Id - " + tr.getVfModuleId()
+                        + " Start time " + tr.getStartTime().toString()
+                        + "]" );
+            }
+            return logMsg;
+
+    }
 }
index e1c10da..8e3d77c 100644 (file)
@@ -25,6 +25,7 @@
 
 package org.onap.appc.requesthandler.impl;
 
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Matchers.anyString;
@@ -37,10 +38,13 @@ import com.att.eelf.configuration.EELFManager;
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.net.MalformedURLException;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.http.HttpResponse;
 import org.apache.http.ProtocolVersion;
 import org.apache.http.entity.BasicHttpEntity;
@@ -60,6 +64,7 @@ import org.onap.appc.domainmodel.lcm.CommonHeader;
 import org.onap.appc.domainmodel.lcm.Flags;
 import org.onap.appc.domainmodel.lcm.Flags.Mode;
 import org.onap.appc.domainmodel.lcm.RequestContext;
+import org.onap.appc.domainmodel.lcm.RequestStatus;
 import org.onap.appc.domainmodel.lcm.ResponseContext;
 import org.onap.appc.domainmodel.lcm.RuntimeContext;
 import org.onap.appc.domainmodel.lcm.TransactionRecord;
@@ -184,7 +189,7 @@ public class RequestValidatorImplTest implements LocalRequestHanlderTestHelper {
     }
 
 
-    @Test(expected = RequestValidationException.class)
+    @Test (expected = RequestValidationException.class)
     public void testValidateRequest() throws Exception {
         RuntimeContext runtimeContext = createRequestValidatorInput();
         logger = Mockito.spy(EELFManager.getInstance().getLogger(LCMStateManager.class));
@@ -193,9 +198,15 @@ public class RequestValidatorImplTest implements LocalRequestHanlderTestHelper {
         lcmStateManager.enableLCMOperations();
         transactionRecorder = Mockito.mock(TransactionRecorder.class);
         Mockito.when(transactionRecorder.isTransactionDuplicate(anyObject())).thenReturn(false);
+        List<TransactionRecord> transactionRecordList = new ArrayList<TransactionRecord>(1);
         TransactionRecord transactionRecord = new TransactionRecord();
         transactionRecord.setMode(Mode.EXCLUSIVE);
+        transactionRecord.setStartTime(Instant.now().minus(5, ChronoUnit.HOURS));
+        transactionRecord.setRequestState(RequestStatus.ACCEPTED);
         runtimeContext.setTransactionRecord(transactionRecord);
+        transactionRecordList.add(transactionRecord);
+        Mockito.when(transactionRecorder.getInProgressRequests(Mockito.any(TransactionRecord.class),Mockito.any(int.class)))
+        .thenReturn(transactionRecordList);
         impl.setTransactionRecorder(transactionRecorder);
         WorkflowExistsOutput workflowExistsOutput = new WorkflowExistsOutput(true, true);
         WorkFlowManager workflowManager = Mockito.mock(WorkFlowManagerImpl.class);
@@ -244,8 +255,10 @@ public class RequestValidatorImplTest implements LocalRequestHanlderTestHelper {
         List<TransactionRecord> transactionRecordList = new ArrayList<TransactionRecord>(1);
         TransactionRecord inProgressTransaction = new TransactionRecord();
         inProgressTransaction.setMode(Mode.EXCLUSIVE);
+        inProgressTransaction.setStartTime(Instant.now().minus(5, ChronoUnit.HOURS));
+        inProgressTransaction.setRequestState(RequestStatus.ACCEPTED);
         transactionRecordList.add(inProgressTransaction);
-        Mockito.when(transactionRecorder.getInProgressRequests(Mockito.any(TransactionRecord.class)))
+        Mockito.when(transactionRecorder.getInProgressRequests(Mockito.any(TransactionRecord.class),Mockito.any(int.class)))
             .thenReturn(transactionRecordList);
         runtimeContext.setTransactionRecord(inProgressTransaction);
         impl.setTransactionRecorder(transactionRecorder);
@@ -277,18 +290,21 @@ public class RequestValidatorImplTest implements LocalRequestHanlderTestHelper {
         RuntimeContext runtimeContext = createRequestValidatorInput();
         lcmStateManager.enableLCMOperations();
         transactionRecorder = Mockito.mock(TransactionRecorder.class);
-        Mockito.when(transactionRecorder.isTransactionDuplicate(anyObject())).thenReturn(false);
+
         List<TransactionRecord> transactionRecordList = new ArrayList<TransactionRecord>(1);
         TransactionRecord inProgressTransaction = new TransactionRecord();
         inProgressTransaction.setMode(Mode.NORMAL);
         inProgressTransaction.setOperation(VNFOperation.ActionStatus);
+        inProgressTransaction.setRequestState(RequestStatus.ACCEPTED);
+        inProgressTransaction.setStartTime(Instant.now().minus(48, ChronoUnit.HOURS));
         transactionRecordList.add(inProgressTransaction);
-        runtimeContext.setTransactionRecord(inProgressTransaction);
-        Mockito.when(transactionRecorder.getInProgressRequests(Mockito.any(TransactionRecord.class)))
-            .thenReturn(transactionRecordList);
+        Mockito.when(transactionRecorder.getInProgressRequests(Mockito.any(TransactionRecord.class),Mockito.any(int.class)))
+        .thenReturn(transactionRecordList);
+        Mockito.when(transactionRecorder.isTransactionDuplicate(anyObject())).thenReturn(false);
         impl.setTransactionRecorder(transactionRecorder);
-        WorkflowExistsOutput workflowExistsOutput = new WorkflowExistsOutput(true, true);
+        runtimeContext.setTransactionRecord(inProgressTransaction);
         WorkFlowManager workflowManager = Mockito.mock(WorkFlowManagerImpl.class);
+        WorkflowExistsOutput workflowExistsOutput = Mockito.spy(new WorkflowExistsOutput(true, true));
         Mockito.when(workflowManager.workflowExists(Mockito.any(WorkflowRequest.class)))
             .thenReturn(workflowExistsOutput);
         impl.setWorkflowManager(workflowManager);
@@ -384,6 +400,19 @@ public class RequestValidatorImplTest implements LocalRequestHanlderTestHelper {
         impl.validateRequest(runtimeContext);
     }
 
+    @Test
+    public void testLogInProgressTransactions() {
+        ArrayList<TransactionRecord> trArray = new ArrayList();
+        TransactionRecord tr = new TransactionRecord();
+        tr.setRequestState(RequestStatus.ACCEPTED);
+        tr.setStartTime(Instant.now().minus(48, ChronoUnit.HOURS));
+        tr.setTargetId("Vnf001");
+        trArray.add(tr);
+        String loggedMessage = impl.logInProgressTransactions(trArray, 1, 1);
+        String partMessage = "In Progress transaction for Target ID - Vnf001 in state ACCEPTED";
+        assertTrue(StringUtils.contains(loggedMessage, partMessage));
+    }
+
     private RuntimeContext createRequestValidatorInput() {
         return createRequestHandlerRuntimeContext("VSCP", "{\"request-id\":\"request-id\"}");
     }