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 + " 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;