2  * Copyright 2016-2017, Nokia Corporation
 
   4  * Licensed under the Apache License, Version 2.0 (the "License");
 
   5  * you may not use this file except in compliance with the License.
 
   6  * You may obtain a copy of the License at
 
   8  *     http://www.apache.org/licenses/LICENSE-2.0
 
  10  * Unless required by applicable law or agreed to in writing, software
 
  11  * distributed under the License is distributed on an "AS IS" BASIS,
 
  12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  13  * See the License for the specific language governing permissions and
 
  14  * limitations under the License.
 
  16 package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification;
 
  18 import com.google.gson.*;
 
  19 import com.nokia.cbam.lcm.v32.ApiException;
 
  20 import com.nokia.cbam.lcm.v32.model.*;
 
  21 import org.joda.time.DateTime;
 
  22 import org.junit.Before;
 
  23 import org.junit.Test;
 
  24 import org.mockito.ArgumentCaptor;
 
  25 import org.mockito.InjectMocks;
 
  26 import org.mockito.Mockito;
 
  27 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.LifecycleManager;
 
  28 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.TestBase;
 
  29 import org.threeten.bp.OffsetDateTime;
 
  31 import java.util.ArrayList;
 
  32 import java.util.List;
 
  33 import java.util.NoSuchElementException;
 
  34 import java.util.concurrent.Callable;
 
  35 import java.util.concurrent.ExecutorService;
 
  36 import java.util.concurrent.Executors;
 
  37 import java.util.concurrent.Future;
 
  39 import static com.nokia.cbam.lcm.v32.model.OperationType.*;
 
  40 import static junit.framework.TestCase.*;
 
  41 import static org.mockito.Mockito.*;
 
  42 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCM_API_VERSION;
 
  43 import static org.springframework.test.util.ReflectionTestUtils.setField;
 
  45 public class TestLifecycleChangeNotificationManager extends TestBase {
 
  47     public static final String OPERATION_EXECUTION_ID = "myOperationExecutionId";
 
  50     private LifecycleChangeNotificationManager lifecycleChangeNotificationManager;
 
  51     private VnfLifecycleChangeNotification recievedLcn = new VnfLifecycleChangeNotification();
 
  52     private List<OperationExecution> operationExecutions = new ArrayList<>();
 
  53     private OperationExecution instantiationOperation = new OperationExecution();
 
  54     private OperationExecution scaleOperation = new OperationExecution();
 
  55     private OperationExecution healOperation = new OperationExecution();
 
  56     private OperationExecution terminationOperation = new OperationExecution();
 
  58     private ArgumentCaptor<OperationExecution> currentOperationExecution = ArgumentCaptor.forClass(OperationExecution.class);
 
  59     private ArgumentCaptor<ReportedAffectedConnectionPoints> affectedConnectionPoints = ArgumentCaptor.forClass(ReportedAffectedConnectionPoints.class);
 
  61     private List<VnfInfo> vnfs = new ArrayList<>();
 
  62     private VnfInfo vnf = new VnfInfo();
 
  65     public void initMocks() throws Exception {
 
  66         setField(LifecycleChangeNotificationManager.class, "logger", logger);
 
  67         instantiationOperation.setId("instantiationOperationExecutionId");
 
  68         instantiationOperation.setStartTime(OffsetDateTime.now());
 
  69         instantiationOperation.setOperationType(OperationType.INSTANTIATE);
 
  70         scaleOperation.setId("scaleOperationExecutionId");
 
  71         scaleOperation.setStartTime(OffsetDateTime.now().plusDays(1));
 
  72         scaleOperation.setOperationType(OperationType.SCALE);
 
  73         terminationOperation.setId("terminationExecutionId");
 
  74         terminationOperation.setStartTime(OffsetDateTime.now().plusDays(1));
 
  75         terminationOperation.setOperationType(OperationType.TERMINATE);
 
  76         healOperation.setId("healOperaitonExecutionId");
 
  77         healOperation.setOperationType(OperationType.HEAL);
 
  78         recievedLcn.setLifecycleOperationOccurrenceId("instantiationOperationExecutionId");
 
  79         healOperation.setStartTime(OffsetDateTime.now().plusDays(1));
 
  80         recievedLcn.setVnfInstanceId(VNF_ID);
 
  81         when(vnfApi.vnfsVnfInstanceIdOperationExecutionsGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(operationExecutions);
 
  82         prepOperation(instantiationOperation);
 
  83         prepOperation(scaleOperation);
 
  84         prepOperation(healOperation);
 
  85         prepOperation(terminationOperation);
 
  86         doNothing().when(notificationSender).processNotification(eq(recievedLcn), currentOperationExecution.capture(), affectedConnectionPoints.capture(), eq(VIM_ID));
 
  87         InstantiateVnfRequest instantiateVnfRequest = new InstantiateVnfRequest();
 
  88         VimInfo vimInfo = new VimInfo();
 
  89         vimInfo.setId(VIM_ID);
 
  90         instantiateVnfRequest.getVims().add(vimInfo);
 
  91         when(operationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(instantiationOperation.getId(), NOKIA_LCM_API_VERSION)).thenReturn(new Gson().toJsonTree(instantiateVnfRequest));
 
  92         when(vnfApi.vnfsGet(NOKIA_LCM_API_VERSION)).thenReturn(vnfs);
 
  95         VnfProperty prop = new VnfProperty();
 
  96         prop.setName(LifecycleManager.EXTERNAL_VNFM_ID);
 
  97         prop.setValue(VNFM_ID);
 
  98         vnf.setExtensions(new ArrayList<>());
 
  99         vnf.getExtensions().add(prop);
 
 100         when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(vnf);
 
 103     private void prepOperation(OperationExecution operationExecution) throws ApiException {
 
 104         addEmptyModifiedConnectionPoints(operationExecution);
 
 105         JsonElement root = new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + JOB_ID + "\"}}");
 
 106         operationExecution.setOperationParams(root);
 
 107         switch (operationExecution.getOperationType()) {
 
 109                 root.getAsJsonObject().addProperty("terminationType", "GRACEFULL");
 
 111         when(operationExecutionApi.operationExecutionsOperationExecutionIdGet(operationExecution.getId(), NOKIA_LCM_API_VERSION)).thenReturn(operationExecution);
 
 112         operationExecutions.add(operationExecution);
 
 115     private void addEmptyModifiedConnectionPoints(OperationExecution operationExecution) {
 
 116         OperationResult operationResult = new OperationResult();
 
 117         operationResult.operationResult = new ReportedAffectedConnectionPoints();
 
 118         JsonElement additionalData = new Gson().toJsonTree(operationResult);
 
 119         operationExecution.setAdditionalData(additionalData);
 
 123      * The first instantiation before the current operation is selected
 
 126     public void testLastInstantiationSelection() {
 
 127         List<OperationExecution> operations = new ArrayList<>();
 
 129         OperationExecution operation = buildOperation(OffsetDateTime.now(), TERMINATE);
 
 130         OperationExecution operationScale = buildOperation(OffsetDateTime.now().minusDays(1), SCALE);
 
 131         OperationExecution operationClosestInstantiate = buildOperation(OffsetDateTime.now().minusDays(2), INSTANTIATE);
 
 132         OperationExecution operationFurthers = buildOperation(OffsetDateTime.now().minusDays(3), INSTANTIATE);
 
 134         operations.add(operation);
 
 135         operations.add(operationScale);
 
 136         operations.add(operationClosestInstantiate);
 
 137         operations.add(operationFurthers);
 
 138         assertEquals(operationClosestInstantiate, LifecycleChangeNotificationManager.findLastInstantiationBefore(operations, operation));
 
 142      * The instantiation operation itself is valid as the last instantiation operation
 
 145     public void testInstantiationSufficesTheLastInstantiation() {
 
 146         DateTime baseTime = DateTime.now();
 
 147         List<OperationExecution> operations = new ArrayList<>();
 
 149         OperationExecution operation = buildOperation(OffsetDateTime.now(), INSTANTIATE);
 
 150         OperationExecution operationScale = buildOperation(OffsetDateTime.now().minusDays(1), SCALE);
 
 151         OperationExecution operationFurthers = buildOperation(OffsetDateTime.now().minusDays(2), INSTANTIATE);
 
 153         operations.add(operation);
 
 154         operations.add(operationScale);
 
 155         operations.add(operationFurthers);
 
 156         assertEquals(operation, LifecycleChangeNotificationManager.findLastInstantiationBefore(operations, operation));
 
 160      * If no instantiation operation is found for before the selected operation
 
 163     public void testNoInstantiation() {
 
 164         DateTime baseTime = DateTime.now();
 
 165         List<OperationExecution> operations = new ArrayList<>();
 
 167         OperationExecution operation = buildOperation(OffsetDateTime.now(), TERMINATE);
 
 168         OperationExecution operationScale = buildOperation(OffsetDateTime.now().minusDays(1), SCALE);
 
 170         operations.add(operation);
 
 171         operations.add(operationScale);
 
 173             LifecycleChangeNotificationManager.findLastInstantiationBefore(operations, operation);
 
 175         } catch (NoSuchElementException e) {
 
 181      * the operations are ordered from newest (first) to oldest (last)
 
 184     public void testOperationOrdering() {
 
 185         List<OperationExecution> operationExecutions = new ArrayList<>();
 
 186         OperationExecution before = buildOperation(OffsetDateTime.now(), OperationType.INSTANTIATE);
 
 187         operationExecutions.add(before);
 
 188         OperationExecution after = buildOperation(OffsetDateTime.now().plusDays(1), OperationType.SCALE);
 
 189         operationExecutions.add(after);
 
 190         List<OperationExecution> sorted1 = LifecycleChangeNotificationManager.NEWEST_OPERATIONS_FIRST.sortedCopy(operationExecutions);
 
 191         assertEquals(after, sorted1.get(0));
 
 192         assertEquals(before, sorted1.get(1));
 
 196      * if VNF listing fails the processing of the notifications is aborted
 
 199     public void testUnableToListVnfs() throws Exception {
 
 200         ApiException expectedException = new ApiException();
 
 201         when(vnfApi.vnfsGet(NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
 
 204             lifecycleChangeNotificationManager.handleLcn(recievedLcn);
 
 206         } catch (Exception e) {
 
 207             assertEquals(expectedException, e.getCause());
 
 208             verify(logger).error("Unable to list VNFs / query VNF", expectedException);
 
 213      * if VNF query fails the processing of the notifications is aborted
 
 216     public void testUnableToQueryVnf() throws Exception {
 
 217         ApiException expectedException = new ApiException();
 
 218         when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
 
 221             lifecycleChangeNotificationManager.handleLcn(recievedLcn);
 
 223         } catch (Exception e) {
 
 224             assertEquals(expectedException, e.getCause());
 
 225             verify(logger).error("Unable to list VNFs / query VNF", expectedException);
 
 230      * if the VNF is not managed by this VNFM the LCN is dropped
 
 233     public void testNonManagedVnf() throws Exception {
 
 234         vnf.getExtensions().clear();
 
 236         lifecycleChangeNotificationManager.handleLcn(recievedLcn);
 
 238         Mockito.verifyZeroInteractions(operationExecutionApi);
 
 239         verify(logger).warn("The VNF with " + VNF_ID + " identifier is not a managed VNF");
 
 243      * if the VNF is not managed by this VNFM the LCN is dropped
 
 246     public void testManagedByOtherVnf() throws Exception {
 
 247         vnf.getExtensions().get(0).setValue("unknownVnfmId");
 
 249         lifecycleChangeNotificationManager.handleLcn(recievedLcn);
 
 251         Mockito.verifyZeroInteractions(operationExecutionApi);
 
 252         verify(logger).warn("The VNF with " + VNF_ID + " identifier is not a managed by the VNFM with id unknownVnfmId");
 
 256      * if the VNF disappeared before processing the LCN
 
 259     public void testDisappearedVnf() throws Exception {
 
 262         lifecycleChangeNotificationManager.handleLcn(recievedLcn);
 
 264         Mockito.verifyZeroInteractions(operationExecutionApi);
 
 265         verify(logger).warn("The VNF with " + VNF_ID + " identifier disappeared before being able to process the LCN");
 
 269      * if the operation parameters of the last instantiation is non querieable error is propagated
 
 272     public void testUnableToQueryOperationParams() throws Exception {
 
 273         recievedLcn.setOperation(OperationType.TERMINATE);
 
 274         recievedLcn.setStatus(OperationStatus.FINISHED);
 
 275         RuntimeException expectedException = new RuntimeException();
 
 276         when(operationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(instantiationOperation.getId(), NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
 
 279             lifecycleChangeNotificationManager.handleLcn(recievedLcn);
 
 281         } catch (Exception e) {
 
 283             Mockito.verifyZeroInteractions(nsLcmApi);
 
 284             assertEquals(expectedException, e.getCause());
 
 285             verify(logger).error("Unable to detect last instantiation operation", e.getCause());
 
 290      * if unable to send LCN to VF-C the error is propagated
 
 293     public void testUnableToQueryCurrentOperation() throws Exception {
 
 294         recievedLcn.setOperation(OperationType.TERMINATE);
 
 295         recievedLcn.setStatus(OperationStatus.FINISHED);
 
 296         ApiException expectedException = new ApiException();
 
 297         when(vnfApi.vnfsVnfInstanceIdOperationExecutionsGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
 
 300             lifecycleChangeNotificationManager.handleLcn(recievedLcn);
 
 302         } catch (Exception e) {
 
 304             assertEquals(expectedException, e.getCause());
 
 305             verify(logger).error("Unable to retrieve the current VNF myVnfId", e.getCause());
 
 310      * test that waitForTerminationToBeProcessed outwaits the successfull processing of the termination notification
 
 313     public void testWaitForTermination() throws Exception {
 
 315         //add an non processed notification
 
 316         VnfLifecycleChangeNotification nonProcessedEvent = new VnfLifecycleChangeNotification();
 
 317         nonProcessedEvent.setStatus(OperationStatus.FINISHED);
 
 318         nonProcessedEvent.setOperation(OperationType.TERMINATE);
 
 319         OperationExecution secondTerminationOperationExecution = new OperationExecution();
 
 320         secondTerminationOperationExecution.setOperationType(OperationType.TERMINATE);
 
 321         secondTerminationOperationExecution.setId("secondId");
 
 322         secondTerminationOperationExecution.setOperationParams(buildTerminationParams());
 
 323         nonProcessedEvent.setLifecycleOperationOccurrenceId(secondTerminationOperationExecution.getId());
 
 324         lifecycleChangeNotificationManager.handleLcn(nonProcessedEvent);
 
 325         //add second termination
 
 326         recievedLcn.setOperation(OperationType.TERMINATE);
 
 327         recievedLcn.setStatus(OperationStatus.FINISHED);
 
 328         recievedLcn.setLifecycleOperationOccurrenceId(terminationOperation.getId());
 
 329         ExecutorService executorService = Executors.newCachedThreadPool();
 
 330         Future<Boolean> waitExitedWithSuccess = executorService.submit(new Callable<Boolean>() {
 
 332             public Boolean call() throws Exception {
 
 334                     lifecycleChangeNotificationManager.waitForTerminationToBeProcessed(terminationOperation.getId());
 
 336                 } catch (Exception e) {
 
 342         lifecycleChangeNotificationManager.handleLcn(recievedLcn);
 
 344         assertTrue(waitExitedWithSuccess.get());
 
 348      * Forceful termination results in an empty affected connection points
 
 351     public void testMissingPreResultForForcefullTermination() {
 
 353         recievedLcn.setOperation(OperationType.INSTANTIATE);
 
 354         recievedLcn.setStatus(OperationStatus.FINISHED);
 
 355         recievedLcn.setLifecycleOperationOccurrenceId(terminationOperation.getId());
 
 356         JsonObject additionalData = new JsonObject();
 
 357         additionalData.add("operationResult", new JsonObject());
 
 358         ((JsonObject) terminationOperation.getOperationParams()).addProperty("terminationType", "FORCEFUL");
 
 359         terminationOperation.setAdditionalData(additionalData);
 
 360         terminationOperation.setStatus(OperationStatus.FINISHED);
 
 361         terminationOperation.setOperationType(OperationType.TERMINATE);
 
 363         lifecycleChangeNotificationManager.handleLcn(recievedLcn);
 
 364         assertNull(affectedConnectionPoints.getValue());
 
 365         verify(logger).warn("Unable to send information related to affected connection points during forceful termination");
 
 369      * test end notification scenario for failed scale-out
 
 370      * - LCN is sent to VF-C, but the
 
 373     public void testMissingPreResultForFailedOperation() {
 
 375         recievedLcn.setOperation(OperationType.SCALE);
 
 376         recievedLcn.setStatus(OperationStatus.FAILED);
 
 377         recievedLcn.setLifecycleOperationOccurrenceId(scaleOperation.getId());
 
 378         ScaleVnfRequest request = new ScaleVnfRequest();
 
 379         request.setAdditionalParams(new JsonParser().parse("{ \"type\" : \"IN\", \"jobId\" : \"" + JOB_ID + "\" }"));
 
 380         request.setType(ScaleDirection.OUT);
 
 381         scaleOperation.setOperationParams(request);
 
 382         scaleOperation.setAdditionalData(null);
 
 383         scaleOperation.setStatus(OperationStatus.FAILED);
 
 384         scaleOperation.setOperationType(OperationType.SCALE);
 
 386         lifecycleChangeNotificationManager.handleLcn(recievedLcn);
 
 387         assertEquals(0, affectedConnectionPoints.getValue().getPost().size());
 
 388         assertEquals(0, affectedConnectionPoints.getValue().getPre().size());
 
 389         verify(logger).warn("The operation failed and the affected connection points were not reported");
 
 393      * test end notification success scenario for scale-out
 
 394      * - LCN is sent to VF-C
 
 397     public void testMissingPreResult() {
 
 399         recievedLcn.setOperation(OperationType.SCALE);
 
 400         recievedLcn.setStatus(OperationStatus.FINISHED);
 
 401         recievedLcn.setLifecycleOperationOccurrenceId(scaleOperation.getId());
 
 402         ScaleVnfRequest request = new ScaleVnfRequest();
 
 403         request.setAdditionalParams(new JsonParser().parse("{ \"type\" : \"IN\", \"jobId\" : \"" + JOB_ID + "\" }"));
 
 404         request.setType(ScaleDirection.OUT);
 
 405         scaleOperation.setOperationParams(request);
 
 406         JsonObject additionalData = new JsonObject();
 
 407         additionalData.add("operationResult", new JsonObject());
 
 408         scaleOperation.setAdditionalData(additionalData);
 
 409         scaleOperation.setStatus(OperationStatus.FINISHED);
 
 410         scaleOperation.setOperationType(OperationType.SCALE);
 
 411         JsonElement root = new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + JOB_ID + "\"}}");
 
 412         JsonObject operationParams = new JsonObject();
 
 415             lifecycleChangeNotificationManager.handleLcn(recievedLcn);
 
 417         } catch (Exception e) {
 
 418             assertEquals("All operations must return the { \"operationResult\" : { \"cbam_pre\" : [<fillMeOut>], \"cbam_post\" : [<fillMeOut>] } } structure", e.getMessage());
 
 423      * missing connection points are tolerated in case of failed operations
 
 426     public void testMissingConnectionPoints() {
 
 428         recievedLcn.setOperation(OperationType.INSTANTIATE);
 
 429         recievedLcn.setStatus(OperationStatus.FAILED);
 
 430         recievedLcn.setLifecycleOperationOccurrenceId(instantiationOperation.getId());
 
 431         instantiationOperation.setAdditionalData(null);
 
 432         instantiationOperation.setStatus(OperationStatus.FAILED);
 
 434         lifecycleChangeNotificationManager.handleLcn(recievedLcn);
 
 435         assertEquals(0, affectedConnectionPoints.getValue().getPost().size());
 
 436         assertEquals(0, affectedConnectionPoints.getValue().getPre().size());
 
 437         verify(logger).warn("The operation failed and the affected connection points were not reported");
 
 440     private JsonObject buildTerminationParams() {
 
 441         JsonObject root = new JsonObject();
 
 442         root.add("terminationType", new JsonPrimitive("GRACEFULL"));
 
 446     private OperationExecution buildOperation(OffsetDateTime baseTime, OperationType operationType) {
 
 447         OperationExecution operation = new OperationExecution();
 
 448         operation.setStartTime(baseTime);
 
 449         operation.setOperationType(operationType);
 
 453     class OperationResult {
 
 454         ReportedAffectedConnectionPoints operationResult;