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.Optional;
35 import java.util.concurrent.*;
37 import static com.nokia.cbam.lcm.v32.model.OperationType.*;
38 import static junit.framework.TestCase.*;
39 import static org.mockito.Mockito.*;
40 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCM_API_VERSION;
41 import static org.springframework.test.util.ReflectionTestUtils.setField;
43 public class TestLifecycleChangeNotificationManager extends TestBase {
45 public static final String OPERATION_EXECUTION_ID = "myOperationExecutionId";
48 private LifecycleChangeNotificationManager lifecycleChangeNotificationManager;
49 private VnfLifecycleChangeNotification recievedLcn = new VnfLifecycleChangeNotification();
50 private List<OperationExecution> operationExecutions = new ArrayList<>();
51 private OperationExecution instantiationOperation = new OperationExecution();
52 private OperationExecution scaleOperation = new OperationExecution();
53 private OperationExecution healOperation = new OperationExecution();
54 private OperationExecution terminationOperation = new OperationExecution();
56 private ArgumentCaptor<OperationExecution> currentOperationExecution = ArgumentCaptor.forClass(OperationExecution.class);
57 private ArgumentCaptor<Optional> affectedConnectionPoints = ArgumentCaptor.forClass(Optional.class);
59 private List<VnfInfo> vnfs = new ArrayList<>();
60 private VnfInfo vnf = new VnfInfo();
63 public void initMocks() throws Exception {
64 setField(LifecycleChangeNotificationManager.class, "logger", logger);
65 instantiationOperation.setId("instantiationOperationExecutionId");
66 instantiationOperation.setStartTime(OffsetDateTime.now());
67 instantiationOperation.setOperationType(OperationType.INSTANTIATE);
68 scaleOperation.setId("scaleOperationExecutionId");
69 scaleOperation.setStartTime(OffsetDateTime.now().plusDays(1));
70 scaleOperation.setOperationType(OperationType.SCALE);
71 terminationOperation.setId("terminationExecutionId");
72 terminationOperation.setStartTime(OffsetDateTime.now().plusDays(1));
73 terminationOperation.setOperationType(OperationType.TERMINATE);
74 healOperation.setId("healOperaitonExecutionId");
75 healOperation.setOperationType(OperationType.HEAL);
76 recievedLcn.setLifecycleOperationOccurrenceId("instantiationOperationExecutionId");
77 healOperation.setStartTime(OffsetDateTime.now().plusDays(1));
78 recievedLcn.setVnfInstanceId(VNF_ID);
79 when(vnfApi.vnfsVnfInstanceIdOperationExecutionsGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(operationExecutions);
80 prepOperation(instantiationOperation);
81 prepOperation(scaleOperation);
82 prepOperation(healOperation);
83 prepOperation(terminationOperation);
84 doNothing().when(notificationSender).processNotification(eq(recievedLcn), currentOperationExecution.capture(), affectedConnectionPoints.capture(), eq(VIM_ID));
85 InstantiateVnfRequest instantiateVnfRequest = new InstantiateVnfRequest();
86 VimInfo vimInfo = new VimInfo();
87 vimInfo.setId(VIM_ID);
88 instantiateVnfRequest.getVims().add(vimInfo);
89 when(operationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(instantiationOperation.getId(), NOKIA_LCM_API_VERSION)).thenReturn(new Gson().toJsonTree(instantiateVnfRequest));
90 when(vnfApi.vnfsGet(NOKIA_LCM_API_VERSION)).thenReturn(vnfs);
93 VnfProperty prop = new VnfProperty();
94 prop.setName(LifecycleManager.EXTERNAL_VNFM_ID);
95 prop.setValue(VNFM_ID);
96 vnf.setExtensions(new ArrayList<>());
97 vnf.getExtensions().add(prop);
98 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(vnf);
101 private void prepOperation(OperationExecution operationExecution) throws ApiException {
102 addEmptyModifiedConnectionPoints(operationExecution);
103 JsonElement root = new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + JOB_ID + "\"}}");
104 operationExecution.setOperationParams(root);
105 switch (operationExecution.getOperationType()) {
107 root.getAsJsonObject().addProperty("terminationType", "GRACEFULL");
109 when(operationExecutionApi.operationExecutionsOperationExecutionIdGet(operationExecution.getId(), NOKIA_LCM_API_VERSION)).thenReturn(operationExecution);
110 operationExecutions.add(operationExecution);
113 private void addEmptyModifiedConnectionPoints(OperationExecution operationExecution) {
114 OperationResult operationResult = new OperationResult();
115 operationResult.operationResult = new ReportedAffectedConnectionPoints();
116 JsonElement additionalData = new Gson().toJsonTree(operationResult);
117 operationExecution.setAdditionalData(additionalData);
121 * The first instantiation before the current operation is selected
124 public void testLastInstantiationSelection() {
125 List<OperationExecution> operations = new ArrayList<>();
127 OperationExecution operation = buildOperation(OffsetDateTime.now(), TERMINATE);
128 OperationExecution operationScale = buildOperation(OffsetDateTime.now().minusDays(1), SCALE);
129 OperationExecution operationClosestInstantiate = buildOperation(OffsetDateTime.now().minusDays(2), INSTANTIATE);
130 OperationExecution operationFurthers = buildOperation(OffsetDateTime.now().minusDays(3), INSTANTIATE);
132 operations.add(operation);
133 operations.add(operationScale);
134 operations.add(operationClosestInstantiate);
135 operations.add(operationFurthers);
136 assertEquals(operationClosestInstantiate, LifecycleChangeNotificationManager.findLastInstantiationBefore(operations, operation));
140 * The instantiation operation itself is valid as the last instantiation operation
143 public void testInstantiationSufficesTheLastInstantiation() {
144 DateTime baseTime = DateTime.now();
145 List<OperationExecution> operations = new ArrayList<>();
147 OperationExecution operation = buildOperation(OffsetDateTime.now(), INSTANTIATE);
148 OperationExecution operationScale = buildOperation(OffsetDateTime.now().minusDays(1), SCALE);
149 OperationExecution operationFurthers = buildOperation(OffsetDateTime.now().minusDays(2), INSTANTIATE);
151 operations.add(operation);
152 operations.add(operationScale);
153 operations.add(operationFurthers);
154 assertEquals(operation, LifecycleChangeNotificationManager.findLastInstantiationBefore(operations, operation));
158 * If no instantiation operation is found for before the selected operation
161 public void testNoInstantiation() {
162 DateTime baseTime = DateTime.now();
163 List<OperationExecution> operations = new ArrayList<>();
165 OperationExecution operation = buildOperation(OffsetDateTime.now(), TERMINATE);
166 OperationExecution operationScale = buildOperation(OffsetDateTime.now().minusDays(1), SCALE);
168 operations.add(operation);
169 operations.add(operationScale);
171 LifecycleChangeNotificationManager.findLastInstantiationBefore(operations, operation);
173 } catch (NoSuchElementException e) {
179 * the operations are ordered from newest (first) to oldest (last)
182 public void testOperationOrdering() {
183 List<OperationExecution> operationExecutions = new ArrayList<>();
184 OperationExecution before = buildOperation(OffsetDateTime.now(), OperationType.INSTANTIATE);
185 operationExecutions.add(before);
186 OperationExecution after = buildOperation(OffsetDateTime.now().plusDays(1), OperationType.SCALE);
187 operationExecutions.add(after);
188 List<OperationExecution> sorted1 = LifecycleChangeNotificationManager.NEWEST_OPERATIONS_FIRST.sortedCopy(operationExecutions);
189 assertEquals(after, sorted1.get(0));
190 assertEquals(before, sorted1.get(1));
194 * if VNF listing fails the processing of the notifications is aborted
197 public void testUnableToListVnfs() throws Exception {
198 ApiException expectedException = new ApiException();
199 when(vnfApi.vnfsGet(NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
202 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
204 } catch (Exception e) {
205 assertEquals(expectedException, e.getCause());
206 verify(logger).error("Unable to list VNFs / query VNF", expectedException);
211 * if VNF query fails the processing of the notifications is aborted
214 public void testUnableToQueryVnf() throws Exception {
215 ApiException expectedException = new ApiException();
216 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
219 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
221 } catch (Exception e) {
222 assertEquals(expectedException, e.getCause());
223 verify(logger).error("Unable to list VNFs / query VNF", expectedException);
228 * if the VNF is not managed by this VNFM the LCN is dropped
231 public void testNonManagedVnf() throws Exception {
232 vnf.getExtensions().clear();
234 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
236 Mockito.verifyZeroInteractions(operationExecutionApi);
237 verify(logger).warn("The VNF with " + VNF_ID + " identifier is not a managed VNF");
241 * LCN is not logged in case of non info log level
244 public void testNoLogging() throws Exception {
245 vnf.getExtensions().clear();
246 when(logger.isInfoEnabled()).thenReturn(false);
248 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
250 verify(logger, never()).info(eq("Received LCN: {}"), anyString());
254 * if the VNF is not managed by this VNFM the LCN is dropped
257 public void testManagedByOtherVnf() throws Exception {
258 vnf.getExtensions().get(0).setValue("unknownVnfmId");
260 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
262 Mockito.verifyZeroInteractions(operationExecutionApi);
263 verify(logger).warn("The VNF with " + VNF_ID + " identifier is not a managed by the VNFM with id unknownVnfmId");
267 * if the VNF disappeared before processing the LCN
270 public void testDisappearedVnf() throws Exception {
273 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
275 Mockito.verifyZeroInteractions(operationExecutionApi);
276 verify(logger).warn("The VNF with " + VNF_ID + " identifier disappeared before being able to process the LCN");
280 * if the operation parameters of the last instantiation is non querieable error is propagated
283 public void testUnableToQueryOperationParams() throws Exception {
284 recievedLcn.setOperation(OperationType.TERMINATE);
285 recievedLcn.setStatus(OperationStatus.FINISHED);
286 RuntimeException expectedException = new RuntimeException();
287 when(operationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(instantiationOperation.getId(), NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
290 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
292 } catch (Exception e) {
294 Mockito.verifyZeroInteractions(nsLcmApi);
295 assertEquals(expectedException, e.getCause());
296 verify(logger).error("Unable to detect last instantiation operation", e.getCause());
301 * if unable to send LCN to VF-C the error is propagated
304 public void testUnableToQueryCurrentOperation() throws Exception {
305 recievedLcn.setOperation(OperationType.TERMINATE);
306 recievedLcn.setStatus(OperationStatus.FINISHED);
307 ApiException expectedException = new ApiException();
308 when(vnfApi.vnfsVnfInstanceIdOperationExecutionsGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
311 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
313 } catch (Exception e) {
315 assertEquals(expectedException, e.getCause());
316 verify(logger).error("Unable to retrieve the current VNF myVnfId", e.getCause());
321 * test that waitForTerminationToBeProcessed outwaits the successfull processing of the termination notification
324 public void testWaitForTermination() throws Exception {
326 //add an non processed notification
327 VnfLifecycleChangeNotification nonProcessedEvent = new VnfLifecycleChangeNotification();
328 nonProcessedEvent.setStatus(OperationStatus.FINISHED);
329 nonProcessedEvent.setOperation(OperationType.TERMINATE);
330 OperationExecution secondTerminationOperationExecution = new OperationExecution();
331 secondTerminationOperationExecution.setOperationType(OperationType.TERMINATE);
332 secondTerminationOperationExecution.setId("secondId");
333 secondTerminationOperationExecution.setOperationParams(buildTerminationParams());
334 nonProcessedEvent.setLifecycleOperationOccurrenceId(secondTerminationOperationExecution.getId());
335 lifecycleChangeNotificationManager.handleLcn(nonProcessedEvent);
336 //add second termination
337 recievedLcn.setOperation(OperationType.TERMINATE);
338 recievedLcn.setStatus(OperationStatus.FINISHED);
339 recievedLcn.setLifecycleOperationOccurrenceId(terminationOperation.getId());
340 ExecutorService executorService = Executors.newCachedThreadPool();
341 Future<Boolean> waitExitedWithSuccess = executorService.submit(new Callable<Boolean>() {
343 public Boolean call() throws Exception {
345 lifecycleChangeNotificationManager.waitForTerminationToBeProcessed(terminationOperation.getId());
347 } catch (Exception e) {
353 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
355 assertTrue(waitExitedWithSuccess.get());
359 * the processing of the start notification does not trigger the deletion of the VNF
362 public void testStartLcnForTerminate() throws Exception {
363 recievedLcn.setOperation(OperationType.TERMINATE);
364 recievedLcn.setStatus(OperationStatus.STARTED);
365 recievedLcn.setLifecycleOperationOccurrenceId(terminationOperation.getId());
366 ExecutorService executorService = Executors.newCachedThreadPool();
367 Future<Boolean> waitExitedWithSuccess = executorService.submit(() -> {
369 lifecycleChangeNotificationManager.waitForTerminationToBeProcessed(terminationOperation.getId());
371 } catch (Exception e) {
375 //processing the start notification
376 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
379 waitExitedWithSuccess.get(10, TimeUnit.MILLISECONDS);
381 } catch (Exception e) {
383 recievedLcn.setStatus(OperationStatus.FINISHED);
385 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
387 assertTrue(waitExitedWithSuccess.get());
391 * Forceful termination results in an empty affected connection points
394 public void testMissingPreResultForForcefullTermination() {
396 recievedLcn.setOperation(OperationType.INSTANTIATE);
397 recievedLcn.setStatus(OperationStatus.FINISHED);
398 recievedLcn.setLifecycleOperationOccurrenceId(terminationOperation.getId());
399 JsonObject additionalData = new JsonObject();
400 additionalData.add("operationResult", new JsonObject());
401 ((JsonObject) terminationOperation.getOperationParams()).addProperty("terminationType", "FORCEFUL");
402 terminationOperation.setAdditionalData(additionalData);
403 terminationOperation.setStatus(OperationStatus.FINISHED);
404 terminationOperation.setOperationType(OperationType.TERMINATE);
406 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
407 assertFalse(affectedConnectionPoints.getValue().isPresent());
408 verify(logger).warn("Unable to send information related to affected connection points during forceful termination");
412 * Failures in affected connection point processing are tolerated for failed operation
413 * (because the POST script was not able to run)
416 public void testFailedOperations() throws Exception {
418 recievedLcn.setOperation(OperationType.INSTANTIATE);
419 recievedLcn.setStatus(OperationStatus.FAILED);
420 recievedLcn.setLifecycleOperationOccurrenceId(instantiationOperation.getId());
421 instantiationOperation.setAdditionalData(null);
422 instantiationOperation.setStatus(OperationStatus.FAILED);
424 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
426 assertFalse(affectedConnectionPoints.getValue().isPresent());
427 verify(logger).warn("The operation failed and the affected connection points were not reported");
431 * Failures in affected connection point processing are tolerated for failed operation
432 * (because the POST script was not able to run)
435 public void testMissingOperationResult() throws Exception {
437 recievedLcn.setOperation(OperationType.INSTANTIATE);
438 recievedLcn.setStatus(OperationStatus.FAILED);
439 recievedLcn.setLifecycleOperationOccurrenceId(instantiationOperation.getId());
440 instantiationOperation.setStatus(OperationStatus.FAILED);
441 JsonObject additionalData = (JsonObject) instantiationOperation.getAdditionalData();
442 additionalData.remove("operationResult");
444 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
446 assertFalse(affectedConnectionPoints.getValue().isPresent());
447 verify(logger).warn("The operation failed and the affected connection points were not reported");
451 * test end notification scenario for failed scale-out
452 * - LCN is sent to VF-C, but the
455 public void testMissingPreResultForFailedOperation() {
457 recievedLcn.setOperation(OperationType.SCALE);
458 recievedLcn.setStatus(OperationStatus.FAILED);
459 recievedLcn.setLifecycleOperationOccurrenceId(scaleOperation.getId());
460 ScaleVnfRequest request = new ScaleVnfRequest();
461 request.setAdditionalParams(new JsonParser().parse("{ \"type\" : \"IN\", \"jobId\" : \"" + JOB_ID + "\" }"));
462 request.setType(ScaleDirection.OUT);
463 scaleOperation.setOperationParams(request);
464 scaleOperation.setAdditionalData(null);
465 scaleOperation.setStatus(OperationStatus.FAILED);
466 scaleOperation.setOperationType(OperationType.SCALE);
468 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
469 assertFalse(affectedConnectionPoints.getValue().isPresent());
470 verify(logger).warn("The operation failed and the affected connection points were not reported");
474 * if the cbam_post is missing error handling should be applied
477 public void testMissingPostResultForFailedOperation() {
479 recievedLcn.setOperation(OperationType.SCALE);
480 recievedLcn.setStatus(OperationStatus.FAILED);
481 recievedLcn.setLifecycleOperationOccurrenceId(scaleOperation.getId());
482 ScaleVnfRequest request = new ScaleVnfRequest();
483 request.setAdditionalParams(new JsonParser().parse("{ \"type\" : \"IN\", \"jobId\" : \"" + JOB_ID + "\" }"));
484 request.setType(ScaleDirection.OUT);
485 scaleOperation.setOperationParams(request);
486 scaleOperation.setStatus(OperationStatus.FAILED);
487 ((JsonObject) scaleOperation.getAdditionalData()).get("operationResult").getAsJsonObject().remove("cbam_post");
488 scaleOperation.setOperationType(OperationType.SCALE);
490 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
491 assertFalse(affectedConnectionPoints.getValue().isPresent());
492 verify(logger).warn("The operation failed and the affected connection points were not reported");
496 * if invalid type is specified for cbam_post error handling should be applied
499 public void testInvalidPost() {
501 recievedLcn.setOperation(OperationType.SCALE);
502 recievedLcn.setStatus(OperationStatus.FAILED);
503 recievedLcn.setLifecycleOperationOccurrenceId(scaleOperation.getId());
504 ScaleVnfRequest request = new ScaleVnfRequest();
505 request.setAdditionalParams(new JsonParser().parse("{ \"type\" : \"IN\", \"jobId\" : \"" + JOB_ID + "\" }"));
506 request.setType(ScaleDirection.OUT);
507 scaleOperation.setOperationParams(request);
508 scaleOperation.setStatus(OperationStatus.FAILED);
509 JsonObject operationResult = ((JsonObject) scaleOperation.getAdditionalData()).get("operationResult").getAsJsonObject();
510 operationResult.remove("cbam_post");
511 operationResult.addProperty("cbam_post", "");
512 scaleOperation.setOperationType(OperationType.SCALE);
514 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
515 assertFalse(affectedConnectionPoints.getValue().isPresent());
516 verify(logger).warn("The operation failed and the affected connection points were not reported");
521 * test end notification success scenario for scale-out
522 * - LCN is sent to VF-C
525 public void testMissingPreResult() {
527 recievedLcn.setOperation(OperationType.SCALE);
528 recievedLcn.setStatus(OperationStatus.FINISHED);
529 recievedLcn.setLifecycleOperationOccurrenceId(scaleOperation.getId());
530 ScaleVnfRequest request = new ScaleVnfRequest();
531 request.setAdditionalParams(new JsonParser().parse("{ \"type\" : \"IN\", \"jobId\" : \"" + JOB_ID + "\" }"));
532 request.setType(ScaleDirection.OUT);
533 scaleOperation.setOperationParams(request);
534 JsonObject additionalData = new JsonObject();
535 additionalData.add("operationResult", new JsonObject());
536 scaleOperation.setAdditionalData(additionalData);
537 scaleOperation.setStatus(OperationStatus.FINISHED);
538 scaleOperation.setOperationType(OperationType.SCALE);
539 JsonElement root = new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + JOB_ID + "\"}}");
540 JsonObject operationParams = new JsonObject();
543 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
545 } catch (Exception e) {
546 assertEquals("All operations must return the { \"operationResult\" : { \"cbam_pre\" : [<fillMeOut>], \"cbam_post\" : [<fillMeOut>] } } structure", e.getMessage());
550 private JsonObject buildTerminationParams() {
551 JsonObject root = new JsonObject();
552 root.add("terminationType", new JsonPrimitive("GRACEFULL"));
556 private OperationExecution buildOperation(OffsetDateTime baseTime, OperationType operationType) {
557 OperationExecution operation = new OperationExecution();
558 operation.setStartTime(baseTime);
559 operation.setOperationType(operationType);
563 class OperationResult {
564 ReportedAffectedConnectionPoints operationResult;