e26eab1aa66a1c0ecc2d0dcf9d4adb97e112a4e8
[vfc/nfvo/driver/vnfm/svnfm.git] / nokiav2 / driver / src / test / java / org / onap / vfc / nfvo / driver / vnfm / svnfm / nokia / vnfm / notification / TestLifecycleChangeNotificationManager.java
1 /*
2  * Copyright 2016-2017, Nokia Corporation
3  *
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.notification;
17
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;
30
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;
38
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;
44
45 public class TestLifecycleChangeNotificationManager extends TestBase {
46
47     public static final String OPERATION_EXECUTION_ID = "myOperationExecutionId";
48
49     @InjectMocks
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();
57
58     private ArgumentCaptor<OperationExecution> currentOperationExecution = ArgumentCaptor.forClass(OperationExecution.class);
59     private ArgumentCaptor<ReportedAffectedConnectionPoints> affectedConnectionPoints = ArgumentCaptor.forClass(ReportedAffectedConnectionPoints.class);
60
61     private List<VnfInfo> vnfs = new ArrayList<>();
62     private VnfInfo vnf = new VnfInfo();
63
64     @Before
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);
93         vnfs.add(vnf);
94         vnf.setId(VNF_ID);
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);
101     }
102
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()) {
108             case TERMINATE:
109                 root.getAsJsonObject().addProperty("terminationType", "GRACEFULL");
110         }
111         when(operationExecutionApi.operationExecutionsOperationExecutionIdGet(operationExecution.getId(), NOKIA_LCM_API_VERSION)).thenReturn(operationExecution);
112         operationExecutions.add(operationExecution);
113     }
114
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);
120     }
121
122     /**
123      * The first instantiation before the current operation is selected
124      */
125     @Test
126     public void testLastInstantiationSelection() {
127         List<OperationExecution> operations = new ArrayList<>();
128
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);
133
134         operations.add(operation);
135         operations.add(operationScale);
136         operations.add(operationClosestInstantiate);
137         operations.add(operationFurthers);
138         assertEquals(operationClosestInstantiate, LifecycleChangeNotificationManager.findLastInstantiationBefore(operations, operation));
139     }
140
141     /**
142      * The instantiation operation itself is valid as the last instantiation operation
143      */
144     @Test
145     public void testInstantiationSufficesTheLastInstantiation() {
146         DateTime baseTime = DateTime.now();
147         List<OperationExecution> operations = new ArrayList<>();
148
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);
152
153         operations.add(operation);
154         operations.add(operationScale);
155         operations.add(operationFurthers);
156         assertEquals(operation, LifecycleChangeNotificationManager.findLastInstantiationBefore(operations, operation));
157     }
158
159     /**
160      * If no instantiation operation is found for before the selected operation
161      */
162     @Test
163     public void testNoInstantiation() {
164         DateTime baseTime = DateTime.now();
165         List<OperationExecution> operations = new ArrayList<>();
166
167         OperationExecution operation = buildOperation(OffsetDateTime.now(), TERMINATE);
168         OperationExecution operationScale = buildOperation(OffsetDateTime.now().minusDays(1), SCALE);
169
170         operations.add(operation);
171         operations.add(operationScale);
172         try {
173             LifecycleChangeNotificationManager.findLastInstantiationBefore(operations, operation);
174             fail();
175         } catch (NoSuchElementException e) {
176
177         }
178     }
179
180     /**
181      * the operations are ordered from newest (first) to oldest (last)
182      */
183     @Test
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));
193     }
194
195     /**
196      * if VNF listing fails the processing of the notifications is aborted
197      */
198     @Test
199     public void testUnableToListVnfs() throws Exception {
200         ApiException expectedException = new ApiException();
201         when(vnfApi.vnfsGet(NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
202         //when
203         try {
204             lifecycleChangeNotificationManager.handleLcn(recievedLcn);
205             fail();
206         } catch (Exception e) {
207             assertEquals(expectedException, e.getCause());
208             verify(logger).error("Unable to list VNFs / query VNF", expectedException);
209         }
210     }
211
212     /**
213      * if VNF query fails the processing of the notifications is aborted
214      */
215     @Test
216     public void testUnableToQueryVnf() throws Exception {
217         ApiException expectedException = new ApiException();
218         when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
219         //when
220         try {
221             lifecycleChangeNotificationManager.handleLcn(recievedLcn);
222             fail();
223         } catch (Exception e) {
224             assertEquals(expectedException, e.getCause());
225             verify(logger).error("Unable to list VNFs / query VNF", expectedException);
226         }
227     }
228
229     /**
230      * if the VNF is not managed by this VNFM the LCN is dropped
231      */
232     @Test
233     public void testNonManagedVnf() throws Exception {
234         vnf.getExtensions().clear();
235         //when
236         lifecycleChangeNotificationManager.handleLcn(recievedLcn);
237         //verify
238         Mockito.verifyZeroInteractions(operationExecutionApi);
239         verify(logger).warn("The VNF with " + VNF_ID + " identifier is not a managed VNF");
240     }
241
242     /**
243      * if the VNF is not managed by this VNFM the LCN is dropped
244      */
245     @Test
246     public void testManagedByOtherVnf() throws Exception {
247         vnf.getExtensions().get(0).setValue("unknownVnfmId");
248         //when
249         lifecycleChangeNotificationManager.handleLcn(recievedLcn);
250         //verify
251         Mockito.verifyZeroInteractions(operationExecutionApi);
252         verify(logger).warn("The VNF with " + VNF_ID + " identifier is not a managed by the VNFM with id unknownVnfmId");
253     }
254
255     /**
256      * if the VNF disappeared before processing the LCN
257      */
258     @Test
259     public void testDisappearedVnf() throws Exception {
260         vnfs.clear();
261         //when
262         lifecycleChangeNotificationManager.handleLcn(recievedLcn);
263         //verify
264         Mockito.verifyZeroInteractions(operationExecutionApi);
265         verify(logger).warn("The VNF with " + VNF_ID + " disappeared before being able to process the LCN");
266     }
267
268     /**
269      * if the operation parameters of the last instantiation is non querieable error is propagated
270      */
271     @Test
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);
277         //when
278         try {
279             lifecycleChangeNotificationManager.handleLcn(recievedLcn);
280             fail();
281         } catch (Exception e) {
282             //verify
283             Mockito.verifyZeroInteractions(nsLcmApi);
284             assertEquals(expectedException, e.getCause());
285             verify(logger).error("Unable to detect last instantiation operation", e.getCause());
286         }
287     }
288
289     /**
290      * if unable to send LCN to VF-C the error is propagated
291      */
292     @Test
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);
298         //when
299         try {
300             lifecycleChangeNotificationManager.handleLcn(recievedLcn);
301             fail();
302         } catch (Exception e) {
303             //verify
304             assertEquals(expectedException, e.getCause());
305             verify(logger).error("Unable to retrieve the current VNF myVnfId", e.getCause());
306         }
307     }
308
309     /**
310      * test that waitForTerminationToBeProcessed outwaits the successfull processing of the termination notification
311      */
312     @Test
313     public void testWaitForTermination() throws Exception {
314         //given
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>() {
331             @Override
332             public Boolean call() throws Exception {
333                 try {
334                     lifecycleChangeNotificationManager.waitForTerminationToBeProcessed(terminationOperation.getId());
335                     return true;
336                 } catch (Exception e) {
337                     return false;
338                 }
339             }
340         });
341         //when
342         lifecycleChangeNotificationManager.handleLcn(recievedLcn);
343         //verify
344         assertTrue(waitExitedWithSuccess.get());
345     }
346
347     /**
348      * Forceful termination results in an empty affected connection points
349      */
350     @Test
351     public void testMissingPreResultForForcefullTermination() {
352         //given
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);
362         //when
363         lifecycleChangeNotificationManager.handleLcn(recievedLcn);
364         assertNull(affectedConnectionPoints.getValue());
365         verify(logger).warn("Unable to send information related to affected connection points during forceful termination");
366     }
367
368     /**
369      * test end notification scenario for failed scale-out
370      * - LCN is sent to VF-C, but the
371      */
372     @Test
373     public void testMissingPreResultForFailedOperation() {
374         //given
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);
385         //when
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");
390     }
391
392     /**
393      * test end notification success scenario for scale-out
394      * - LCN is sent to VF-C
395      */
396     @Test
397     public void testMissingPreResult() {
398         //given
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();
413         //when
414         try {
415             lifecycleChangeNotificationManager.handleLcn(recievedLcn);
416             fail();
417         } catch (Exception e) {
418             assertEquals("All operations must return the { \"operationResult\" : { \"cbam_pre\" : [<fillMeOut>], \"cbam_post\" : [<fillMeOut>] } } structure", e.getMessage());
419         }
420     }
421
422     /**
423      * missing connection points are tolerated in case of failed operations
424      */
425     @Test
426     public void testMissingConnectionPoints() {
427         //given
428         recievedLcn.setOperation(OperationType.INSTANTIATE);
429         recievedLcn.setStatus(OperationStatus.FAILED);
430         recievedLcn.setLifecycleOperationOccurrenceId(instantiationOperation.getId());
431         instantiationOperation.setAdditionalData(null);
432         instantiationOperation.setStatus(OperationStatus.FAILED);
433         //when
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");
438     }
439
440     private JsonObject buildTerminationParams() {
441         JsonObject root = new JsonObject();
442         root.add("terminationType", new JsonPrimitive("GRACEFULL"));
443         return root;
444     }
445
446     private OperationExecution buildOperation(OffsetDateTime baseTime, OperationType operationType) {
447         OperationExecution operation = new OperationExecution();
448         operation.setStartTime(baseTime);
449         operation.setOperationType(operationType);
450         return operation;
451     }
452
453     class OperationResult {
454         ReportedAffectedConnectionPoints operationResult;
455     }
456
457 }