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.model.*;
20 import org.junit.Before;
21 import org.junit.Test;
22 import org.mockito.ArgumentCaptor;
23 import org.mockito.InjectMocks;
24 import org.mockito.Mockito;
25 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.LifecycleManager;
26 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.TestBase;
27 import org.threeten.bp.OffsetDateTime;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.NoSuchElementException;
32 import java.util.Optional;
33 import java.util.concurrent.*;
35 import static com.nokia.cbam.lcm.v32.model.OperationType.*;
36 import static junit.framework.TestCase.*;
37 import static org.mockito.Mockito.*;
38 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCM_API_VERSION;
39 import static org.springframework.test.util.ReflectionTestUtils.setField;
41 public class TestLifecycleChangeNotificationManager extends TestBase {
44 private LifecycleChangeNotificationManager lifecycleChangeNotificationManager;
45 private VnfLifecycleChangeNotification recievedLcn = new VnfLifecycleChangeNotification();
46 private List<OperationExecution> operationExecutions = new ArrayList<>();
47 private OperationExecution instantiationOperation = new OperationExecution();
48 private OperationExecution scaleOperation = new OperationExecution();
49 private OperationExecution healOperation = new OperationExecution();
50 private OperationExecution terminationOperation = new OperationExecution();
52 private ArgumentCaptor<OperationExecution> currentOperationExecution = ArgumentCaptor.forClass(OperationExecution.class);
53 private ArgumentCaptor<Optional> affectedConnectionPoints = ArgumentCaptor.forClass(Optional.class);
55 private List<VnfInfo> vnfs = new ArrayList<>();
56 private VnfInfo vnf = new VnfInfo();
59 public void initMocks() throws Exception {
60 setField(LifecycleChangeNotificationManager.class, "logger", logger);
61 instantiationOperation.setId("instantiationOperationExecutionId");
62 instantiationOperation.setStartTime(OffsetDateTime.now());
63 instantiationOperation.setOperationType(OperationType.INSTANTIATE);
64 scaleOperation.setId("scaleOperationExecutionId");
65 scaleOperation.setStartTime(OffsetDateTime.now().plusDays(1));
66 scaleOperation.setOperationType(OperationType.SCALE);
67 terminationOperation.setId("terminationExecutionId");
68 terminationOperation.setStartTime(OffsetDateTime.now().plusDays(1));
69 terminationOperation.setOperationType(OperationType.TERMINATE);
70 healOperation.setId("healOperaitonExecutionId");
71 healOperation.setOperationType(OperationType.HEAL);
72 recievedLcn.setLifecycleOperationOccurrenceId("instantiationOperationExecutionId");
73 healOperation.setStartTime(OffsetDateTime.now().plusDays(1));
74 recievedLcn.setVnfInstanceId(VNF_ID);
75 when(vnfApi.vnfsVnfInstanceIdOperationExecutionsGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(operationExecutions));
76 prepOperation(instantiationOperation);
77 prepOperation(scaleOperation);
78 prepOperation(healOperation);
79 prepOperation(terminationOperation);
80 doNothing().when(notificationSender).processNotification(eq(recievedLcn), currentOperationExecution.capture(), affectedConnectionPoints.capture(), eq(VIM_ID));
81 InstantiateVnfRequest instantiateVnfRequest = new InstantiateVnfRequest();
82 VimInfo vimInfo = new VimInfo();
83 vimInfo.setId(VIM_ID);
84 instantiateVnfRequest.getVims().add(vimInfo);
85 when(operationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(instantiationOperation.getId(), NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(new Gson().toJsonTree(instantiateVnfRequest)));
86 when(vnfApi.vnfsGet(NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(vnfs));
89 VnfProperty prop = new VnfProperty();
90 prop.setName(LifecycleManager.EXTERNAL_VNFM_ID);
91 prop.setValue(VNFM_ID);
92 vnf.setExtensions(new ArrayList<>());
93 vnf.getExtensions().add(prop);
94 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(vnf));
97 private void prepOperation(OperationExecution operationExecution) {
98 addEmptyModifiedConnectionPoints(operationExecution);
99 JsonElement root = new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + JOB_ID + "\"}}");
100 operationExecution.setOperationParams(root);
101 switch (operationExecution.getOperationType()) {
103 root.getAsJsonObject().addProperty("terminationType", "GRACEFULL");
105 when(operationExecutionApi.operationExecutionsOperationExecutionIdGet(operationExecution.getId(), NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(operationExecution));
106 operationExecutions.add(operationExecution);
109 private void addEmptyModifiedConnectionPoints(OperationExecution operationExecution) {
110 OperationResult operationResult = new OperationResult();
111 operationResult.operationResult = new ReportedAffectedConnectionPoints();
112 JsonElement additionalData = new Gson().toJsonTree(operationResult);
113 operationExecution.setAdditionalData(additionalData);
117 * The first instantiation before the current operation is selected
120 public void testLastInstantiationSelection() {
121 List<OperationExecution> operations = new ArrayList<>();
123 OperationExecution operation = buildOperation(OffsetDateTime.now(), TERMINATE);
124 OperationExecution operationScale = buildOperation(OffsetDateTime.now().minusDays(1), SCALE);
125 OperationExecution operationClosestInstantiate = buildOperation(OffsetDateTime.now().minusDays(2), INSTANTIATE);
126 OperationExecution operationFurthers = buildOperation(OffsetDateTime.now().minusDays(3), INSTANTIATE);
128 operations.add(operation);
129 operations.add(operationScale);
130 operations.add(operationClosestInstantiate);
131 operations.add(operationFurthers);
132 assertEquals(operationClosestInstantiate, LifecycleChangeNotificationManager.findLastInstantiationBefore(operations, operation));
136 * The instantiation operation itself is valid as the last instantiation operation
139 public void testInstantiationSufficesTheLastInstantiation() {
140 OffsetDateTime baseTime = OffsetDateTime.now();
141 List<OperationExecution> operations = new ArrayList<>();
143 OperationExecution operation = buildOperation(OffsetDateTime.now(), INSTANTIATE);
144 OperationExecution operationScale = buildOperation(OffsetDateTime.now().minusDays(1), SCALE);
145 OperationExecution operationFurthers = buildOperation(OffsetDateTime.now().minusDays(2), INSTANTIATE);
147 operations.add(operation);
148 operations.add(operationScale);
149 operations.add(operationFurthers);
150 assertEquals(operation, LifecycleChangeNotificationManager.findLastInstantiationBefore(operations, operation));
154 * If no instantiation operation is found for before the selected operation
157 public void testNoInstantiation() {
158 OffsetDateTime baseTime = OffsetDateTime.now();
159 List<OperationExecution> operations = new ArrayList<>();
161 OperationExecution operation = buildOperation(OffsetDateTime.now(), TERMINATE);
162 OperationExecution operationScale = buildOperation(OffsetDateTime.now().minusDays(1), SCALE);
164 operations.add(operation);
165 operations.add(operationScale);
167 LifecycleChangeNotificationManager.findLastInstantiationBefore(operations, operation);
169 } catch (NoSuchElementException e) {
175 * the operations are ordered from newest (first) to oldest (last)
178 public void testOperationOrdering() {
179 List<OperationExecution> operationExecutions = new ArrayList<>();
180 OperationExecution before = buildOperation(OffsetDateTime.now(), OperationType.INSTANTIATE);
181 operationExecutions.add(before);
182 OperationExecution after = buildOperation(OffsetDateTime.now().plusDays(1), OperationType.SCALE);
183 operationExecutions.add(after);
184 List<OperationExecution> sorted1 = LifecycleChangeNotificationManager.NEWEST_OPERATIONS_FIRST.sortedCopy(operationExecutions);
185 assertEquals(after, sorted1.get(0));
186 assertEquals(before, sorted1.get(1));
190 * if VNF listing fails the processing of the notifications is aborted
193 public void testUnableToListVnfs() throws Exception {
194 RuntimeException expectedException = new RuntimeException();
195 when(vnfApi.vnfsGet(NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
198 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
200 } catch (Exception e) {
201 assertEquals(expectedException, e.getCause());
202 verify(logger).error("Unable to list VNFs / query VNF", expectedException);
207 * if VNF query fails the processing of the notifications is aborted
210 public void testUnableToQueryVnf() throws Exception {
211 RuntimeException expectedException = new RuntimeException();
212 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
215 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
217 } catch (Exception e) {
218 assertEquals(expectedException, e.getCause());
219 verify(logger).error("Unable to list VNFs / query VNF", expectedException);
224 * if the VNF is not managed by this VNFM the LCN is dropped
227 public void testNonManagedVnf() throws Exception {
228 vnf.getExtensions().clear();
230 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
232 Mockito.verifyZeroInteractions(operationExecutionApi);
233 verify(logger).warn("The VNF with " + VNF_ID + " identifier is not a managed VNF");
237 * LCN is not logged in case of non info log level
240 public void testNoLogging() throws Exception {
241 vnf.getExtensions().clear();
242 when(logger.isInfoEnabled()).thenReturn(false);
244 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
246 verify(logger, never()).info(eq("Received LCN: {}"), anyString());
250 * if the VNF is not managed by this VNFM the LCN is dropped
253 public void testManagedByOtherVnf() throws Exception {
254 vnf.getExtensions().get(0).setValue("unknownVnfmId");
256 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
258 Mockito.verifyZeroInteractions(operationExecutionApi);
259 verify(logger).warn("The VNF with " + VNF_ID + " identifier is not a managed by the VNFM with id unknownVnfmId");
263 * if the VNF disappeared before processing the LCN
266 public void testDisappearedVnf() throws Exception {
269 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
271 Mockito.verifyZeroInteractions(operationExecutionApi);
272 verify(logger).warn("The VNF with " + VNF_ID + " identifier disappeared before being able to process the LCN");
276 * if the operation parameters of the last instantiation is non querieable error is propagated
279 public void testUnableToQueryOperationParams() throws Exception {
280 recievedLcn.setOperation(OperationType.TERMINATE);
281 recievedLcn.setStatus(OperationStatus.FINISHED);
282 RuntimeException expectedException = new RuntimeException();
283 when(operationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(instantiationOperation.getId(), NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
286 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
288 } catch (Exception e) {
290 Mockito.verifyZeroInteractions(nsLcmApi);
291 assertEquals(expectedException, e.getCause());
292 verify(logger).error("Unable to detect last instantiation operation", e.getCause());
297 * if unable to send LCN to VF-C the error is propagated
300 public void testUnableToQueryCurrentOperation() throws Exception {
301 recievedLcn.setOperation(OperationType.TERMINATE);
302 recievedLcn.setStatus(OperationStatus.FINISHED);
303 RuntimeException expectedException = new RuntimeException();
304 when(vnfApi.vnfsVnfInstanceIdOperationExecutionsGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
307 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
309 } catch (Exception e) {
311 assertEquals(expectedException, e.getCause());
312 verify(logger).error("Unable to retrieve the operation executions for the VNF myVnfId", e.getCause());
317 * test that waitForTerminationToBeProcessed outwaits the successfull processing of the termination notification
320 public void testWaitForTermination() throws Exception {
322 //add an non processed notification
323 VnfLifecycleChangeNotification nonProcessedEvent = new VnfLifecycleChangeNotification();
324 nonProcessedEvent.setStatus(OperationStatus.FINISHED);
325 nonProcessedEvent.setOperation(OperationType.TERMINATE);
326 OperationExecution secondTerminationOperationExecution = new OperationExecution();
327 secondTerminationOperationExecution.setOperationType(OperationType.TERMINATE);
328 secondTerminationOperationExecution.setId("secondId");
329 secondTerminationOperationExecution.setOperationParams(buildTerminationParams());
330 nonProcessedEvent.setLifecycleOperationOccurrenceId(secondTerminationOperationExecution.getId());
331 lifecycleChangeNotificationManager.handleLcn(nonProcessedEvent);
332 //add second termination
333 recievedLcn.setOperation(OperationType.TERMINATE);
334 recievedLcn.setStatus(OperationStatus.FINISHED);
335 recievedLcn.setLifecycleOperationOccurrenceId(terminationOperation.getId());
336 ExecutorService executorService = Executors.newCachedThreadPool();
337 Future<Boolean> waitExitedWithSuccess = executorService.submit(new Callable<Boolean>() {
339 public Boolean call() throws Exception {
341 lifecycleChangeNotificationManager.waitForTerminationToBeProcessed(terminationOperation.getId());
343 } catch (Exception e) {
349 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
351 assertTrue(waitExitedWithSuccess.get());
355 * the processing of the start notification does not trigger the deletion of the VNF
358 public void testStartLcnForTerminate() throws Exception {
359 recievedLcn.setOperation(OperationType.TERMINATE);
360 recievedLcn.setStatus(OperationStatus.STARTED);
361 recievedLcn.setLifecycleOperationOccurrenceId(terminationOperation.getId());
362 ExecutorService executorService = Executors.newCachedThreadPool();
363 Future<Boolean> waitExitedWithSuccess = executorService.submit(() -> {
365 lifecycleChangeNotificationManager.waitForTerminationToBeProcessed(terminationOperation.getId());
367 } catch (Exception e) {
371 //processing the start notification
372 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
375 waitExitedWithSuccess.get(10, TimeUnit.MILLISECONDS);
377 } catch (Exception e) {
379 recievedLcn.setStatus(OperationStatus.FINISHED);
381 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
383 assertTrue(waitExitedWithSuccess.get());
387 * Forceful termination results in an empty affected connection points
390 public void testMissingPreResultForForcefullTermination() {
392 recievedLcn.setOperation(OperationType.INSTANTIATE);
393 recievedLcn.setStatus(OperationStatus.FINISHED);
394 recievedLcn.setLifecycleOperationOccurrenceId(terminationOperation.getId());
395 JsonObject additionalData = new JsonObject();
396 additionalData.add("operationResult", new JsonObject());
397 ((JsonObject) terminationOperation.getOperationParams()).addProperty("terminationType", "FORCEFUL");
398 terminationOperation.setAdditionalData(additionalData);
399 terminationOperation.setStatus(OperationStatus.FINISHED);
400 terminationOperation.setOperationType(OperationType.TERMINATE);
402 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
403 assertFalse(affectedConnectionPoints.getValue().isPresent());
404 verify(logger).warn("Unable to send information related to affected connection points during forceful termination");
408 * Failures in affected connection point processing are tolerated for failed operation
409 * (because the POST script was not able to run)
412 public void testFailedOperations() throws Exception {
414 recievedLcn.setOperation(OperationType.INSTANTIATE);
415 recievedLcn.setStatus(OperationStatus.FAILED);
416 recievedLcn.setLifecycleOperationOccurrenceId(instantiationOperation.getId());
417 instantiationOperation.setAdditionalData(null);
418 instantiationOperation.setStatus(OperationStatus.FAILED);
420 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
422 assertFalse(affectedConnectionPoints.getValue().isPresent());
423 verify(logger).warn("The operation failed and the affected connection points were not reported");
427 * Failures in affected connection point processing are tolerated for failed operation
428 * (because the POST script was not able to run)
431 public void testMissingOperationResult() throws Exception {
433 recievedLcn.setOperation(OperationType.INSTANTIATE);
434 recievedLcn.setStatus(OperationStatus.FAILED);
435 recievedLcn.setLifecycleOperationOccurrenceId(instantiationOperation.getId());
436 instantiationOperation.setStatus(OperationStatus.FAILED);
437 JsonObject additionalData = (JsonObject) instantiationOperation.getAdditionalData();
438 additionalData.remove("operationResult");
440 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
442 assertFalse(affectedConnectionPoints.getValue().isPresent());
443 verify(logger).warn("The operation failed and the affected connection points were not reported");
447 * test end notification scenario for failed scale-out
448 * - LCN is sent to VF-C, but the
451 public void testMissingPreResultForFailedOperation() {
453 recievedLcn.setOperation(OperationType.SCALE);
454 recievedLcn.setStatus(OperationStatus.FAILED);
455 recievedLcn.setLifecycleOperationOccurrenceId(scaleOperation.getId());
456 ScaleVnfRequest request = new ScaleVnfRequest();
457 request.setAdditionalParams(new JsonParser().parse("{ \"type\" : \"IN\", \"jobId\" : \"" + JOB_ID + "\" }"));
458 request.setType(ScaleDirection.OUT);
459 scaleOperation.setOperationParams(request);
460 scaleOperation.setAdditionalData(null);
461 scaleOperation.setStatus(OperationStatus.FAILED);
462 scaleOperation.setOperationType(OperationType.SCALE);
464 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
465 assertFalse(affectedConnectionPoints.getValue().isPresent());
466 verify(logger).warn("The operation failed and the affected connection points were not reported");
470 * if the cbam_post is missing error handling should be applied
473 public void testMissingPostResultForFailedOperation() {
475 recievedLcn.setOperation(OperationType.SCALE);
476 recievedLcn.setStatus(OperationStatus.FAILED);
477 recievedLcn.setLifecycleOperationOccurrenceId(scaleOperation.getId());
478 ScaleVnfRequest request = new ScaleVnfRequest();
479 request.setAdditionalParams(new JsonParser().parse("{ \"type\" : \"IN\", \"jobId\" : \"" + JOB_ID + "\" }"));
480 request.setType(ScaleDirection.OUT);
481 scaleOperation.setOperationParams(request);
482 scaleOperation.setStatus(OperationStatus.FAILED);
483 ((JsonObject) scaleOperation.getAdditionalData()).get("operationResult").getAsJsonObject().remove("cbam_post");
484 scaleOperation.setOperationType(OperationType.SCALE);
486 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
487 assertFalse(affectedConnectionPoints.getValue().isPresent());
488 verify(logger).warn("The operation failed and the affected connection points were not reported");
492 * if invalid type is specified for cbam_post error handling should be applied
495 public void testInvalidPost() {
497 recievedLcn.setOperation(OperationType.SCALE);
498 recievedLcn.setStatus(OperationStatus.FAILED);
499 recievedLcn.setLifecycleOperationOccurrenceId(scaleOperation.getId());
500 ScaleVnfRequest request = new ScaleVnfRequest();
501 request.setAdditionalParams(new JsonParser().parse("{ \"type\" : \"IN\", \"jobId\" : \"" + JOB_ID + "\" }"));
502 request.setType(ScaleDirection.OUT);
503 scaleOperation.setOperationParams(request);
504 scaleOperation.setStatus(OperationStatus.FAILED);
505 JsonObject operationResult = ((JsonObject) scaleOperation.getAdditionalData()).get("operationResult").getAsJsonObject();
506 operationResult.remove("cbam_post");
507 operationResult.addProperty("cbam_post", "");
508 scaleOperation.setOperationType(OperationType.SCALE);
510 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
511 assertFalse(affectedConnectionPoints.getValue().isPresent());
512 verify(logger).warn("The operation failed and the affected connection points were not reported");
517 * test end notification success scenario for scale-out
518 * - LCN is sent to VF-C
521 public void testMissingPreResult() {
523 recievedLcn.setOperation(OperationType.SCALE);
524 recievedLcn.setStatus(OperationStatus.FINISHED);
525 recievedLcn.setLifecycleOperationOccurrenceId(scaleOperation.getId());
526 ScaleVnfRequest request = new ScaleVnfRequest();
527 request.setAdditionalParams(new JsonParser().parse("{ \"type\" : \"IN\", \"jobId\" : \"" + JOB_ID + "\" }"));
528 request.setType(ScaleDirection.OUT);
529 scaleOperation.setOperationParams(request);
530 JsonObject additionalData = new JsonObject();
531 additionalData.add("operationResult", new JsonObject());
532 scaleOperation.setAdditionalData(additionalData);
533 scaleOperation.setStatus(OperationStatus.FINISHED);
534 scaleOperation.setOperationType(OperationType.SCALE);
535 JsonElement root = new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + JOB_ID + "\"}}");
536 JsonObject operationParams = new JsonObject();
539 lifecycleChangeNotificationManager.handleLcn(recievedLcn);
541 } catch (Exception e) {
542 assertEquals("All operations must return the { \"operationResult\" : { \"cbam_pre\" : [<fillMeOut>], \"cbam_post\" : [<fillMeOut>] } } structure", e.getMessage());
546 private JsonObject buildTerminationParams() {
547 JsonObject root = new JsonObject();
548 root.add("terminationType", new JsonPrimitive("GRACEFULL"));
552 private OperationExecution buildOperation(OffsetDateTime baseTime, OperationType operationType) {
553 OperationExecution operation = new OperationExecution();
554 operation.setStartTime(baseTime);
555 operation.setOperationType(operationType);
559 class OperationResult {
560 ReportedAffectedConnectionPoints operationResult;