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;
18 import com.google.common.collect.Lists;
19 import com.google.gson.JsonElement;
20 import com.google.gson.JsonParser;
21 import com.nokia.cbam.lcm.v32.model.*;
22 import io.reactivex.Observable;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.NoSuchElementException;
26 import java.util.UUID;
27 import java.util.concurrent.ExecutorService;
28 import java.util.concurrent.Executors;
29 import java.util.concurrent.Future;
30 import javax.servlet.http.HttpServletResponse;
31 import org.junit.Before;
32 import org.junit.Test;
33 import org.mockito.ArgumentCaptor;
34 import org.mockito.InjectMocks;
35 import org.mockito.Mock;
36 import org.mockito.Mockito;
37 import org.mockito.invocation.InvocationOnMock;
38 import org.mockito.stubbing.Answer;
39 import org.onap.vnfmdriver.model.JobDetailInfo;
40 import org.onap.vnfmdriver.model.JobResponseInfo;
41 import org.onap.vnfmdriver.model.JobStatus;
42 import org.springframework.test.util.ReflectionTestUtils;
43 import org.threeten.bp.OffsetDateTime;
45 import static junit.framework.TestCase.*;
46 import static org.mockito.Mockito.*;
47 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.SEPARATOR;
48 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.CbamRestApiProvider.NOKIA_LCM_API_VERSION;
49 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.JobManager.extractOnapJobId;
50 import static org.onap.vnfmdriver.model.JobStatus.*;
52 public class TestJobManager extends TestBase {
55 private HttpServletResponse httpResponse;
58 private JobManager jobManager;
59 private List<VnfInfo> vnfs = new ArrayList<>();
62 public void initMocks() throws Exception {
63 ReflectionTestUtils.setField(JobManager.class, "logger", logger);
64 when(vnfApi.vnfsGet(NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(vnfs));
65 when(selfRegistrationManager.isReady()).thenReturn(true);
69 * Only the _ can be used as separator
70 * . / % & handled specially in URLs
71 * - used in CBAM for separation
74 public void testSeparator() {
75 assertEquals("_", SEPARATOR);
79 * The operation result must contain the ONAP job identifier under the jobId field
82 public void testJobIdExtractionFromOperationResult() {
83 assertEquals("1234", extractOnapJobId(new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"1234\"}}")));
85 extractOnapJobId(new JsonParser().parse("{ }"));
87 } catch (NoSuchElementException e) {
88 assertEquals("The operation result {} does not contain the mandatory additionalParams structure", e.getMessage());
91 extractOnapJobId(new JsonParser().parse("{ \"additionalParams\" : { } }"));
93 } catch (NoSuchElementException e) {
94 assertEquals("The operation result {\"additionalParams\":{}} does not contain the mandatory jobId in the additionalParams structure", e.getMessage());
99 * If the VNF does not exists but the job manager still runs the VNF manipulation process the job is reported to be running
102 public void testJobForNonExistingVnfReportedRunningIfJobIsOngoing() throws Exception {
103 String jobId = jobManager.spawnJob(VNF_ID, httpResponse);
105 JobDetailInfo job = jobManager.getJob(VNFM_ID, jobId);
107 assertResult(jobId, job, STARTED, "50", "Operation started");
111 * If the VNF does not exists and the internal job is not running the job is reported to be finished
114 public void testJobForExistingVnfReportedRunningIfJobIsFinished() throws Exception {
115 String jobId = jobManager.spawnJob(VNF_ID, httpResponse);
116 jobManager.jobFinished(jobId);
118 JobDetailInfo job = jobManager.getJob(VNFM_ID, jobId);
120 assertResult(jobId, job, JobStatus.FINISHED, "100", "Operation finished");
124 * Spawning jobs after preparing for shutdown results in error
127 public void testNoMoreJobsAreAllowedAfterPrepareForShutdown() throws Exception {
128 jobManager.prepareForShutdown();
131 jobManager.spawnJob(JOB_ID, httpResponse);
133 } catch (Exception e) {
134 verify(logger).error("The service is preparing to shut down");
139 * Verify if the jobId has valid format
142 public void testJobIdValidation() throws Exception {
145 jobManager.getJob(VNFM_ID, "bad");
148 } catch (IllegalArgumentException e) {
149 assertEquals("The jobId should be in the <vnfId>_<UUID> format, but was bad", e.getMessage());
153 jobManager.getJob(VNFM_ID, "vnfId_");
156 } catch (IllegalArgumentException e) {
157 assertEquals("The UUID in the jobId (vnfId_) can not be empty", e.getMessage());
161 jobManager.getJob(VNFM_ID, "_UUID");
164 } catch (IllegalArgumentException e) {
165 assertEquals("The vnfId in the jobId (_UUID) can not be empty", e.getMessage());
170 * If the VNF exists but no operation execution is present with given internalJobId, than the state of the
171 * job is ongoing if the internal job is ongoing
174 public void testExistingVnfWithNotYetStartedOperation() throws Exception {
175 String jobId = jobManager.spawnJob(VNF_ID, httpResponse);
176 VnfInfo vnf = new VnfInfo();
179 VnfInfo detailedVnf = new VnfInfo();
180 detailedVnf.setId(VNF_ID);
181 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(detailedVnf));
182 JobDetailInfo job = jobManager.getJob(VNFM_ID, jobId);
184 assertResult(jobId, job, STARTED, "50", "Operation started");
185 assertTrue(jobManager.hasOngoingJobs());
189 * If the VNF exists but no operation execution is present with given internalJobId, than the state of the
190 * job is failed if the internal job is finished (the operation on CBAM was not able to start)
193 public void testExistingVnfWithNotUnableToStartOperation() throws Exception {
194 String jobId = jobManager.spawnJob(VNF_ID, httpResponse);
195 VnfInfo vnf = new VnfInfo();
198 VnfInfo detailedVnf = new VnfInfo();
199 detailedVnf.setId(VNF_ID);
200 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(detailedVnf));
201 jobManager.jobFinished(jobId);
202 JobDetailInfo job = jobManager.getJob(VNFM_ID, jobId);
204 assertResult(jobId, job, ERROR, "100", "Operation failed due to The requested operation was not able to start on CBAM");
205 assertFalse(jobManager.hasOngoingJobs());
209 * If the VNF exists but and the operation execution is present with given internalJobId, than the state of the
210 * job is ongoing if the operation is ongoing
213 public void testExistingVnfWithStartedOperation() throws Exception {
214 String jobId = jobManager.spawnJob(VNF_ID, httpResponse);
215 VnfInfo vnf = new VnfInfo();
218 VnfInfo detailedVnf = new VnfInfo();
219 detailedVnf.setId(VNF_ID);
220 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(detailedVnf));
221 OperationExecution operation = new OperationExecution();
222 operation.setId(UUID.randomUUID().toString());
223 operation.setStartTime(OffsetDateTime.now());
224 operation.setStatus(OperationStatus.STARTED);
225 detailedVnf.setOperationExecutions(new ArrayList<>());
226 detailedVnf.getOperationExecutions().add(operation);
227 JsonElement operationParams = new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + jobId + "\"}}");
228 when(operationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(operation.getId(), NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(operationParams));
229 JobDetailInfo job = jobManager.getJob(VNFM_ID, jobId);
231 assertResult(jobId, job, STARTED, "50", "Operation started");
232 assertTrue(jobManager.hasOngoingJobs());
236 * If the VNF does not exists till the time the job queries the status of the operation
239 public void testTerminatedVnf() throws Exception {
241 String jobId = jobManager.spawnJob(VNF_ID, httpResponse);
242 VnfInfo vnf = new VnfInfo();
245 VnfInfo detailedVnf = new VnfInfo();
246 detailedVnf.setId(VNF_ID);
247 List<Integer> vnfQueryCallCounter = new ArrayList<>();
248 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenAnswer(new Answer<Observable<VnfInfo>>() {
250 public Observable<VnfInfo> answer(InvocationOnMock invocation) throws Throwable {
252 return buildObservable(detailedVnf);
256 jobManager.jobFinished(jobId);
258 OperationExecution operation = new OperationExecution();
259 operation.setId(UUID.randomUUID().toString());
260 operation.setStartTime(OffsetDateTime.now());
261 operation.setStatus(OperationStatus.FINISHED);
262 operation.setOperationType(OperationType.TERMINATE);
263 detailedVnf.setOperationExecutions(new ArrayList<>());
264 detailedVnf.getOperationExecutions().add(operation);
266 JsonElement operationParams = new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + jobId + "\"}}");
267 when(operationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(operation.getId(), NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(operationParams));
269 JobDetailInfo job = jobManager.getJob(VNFM_ID, jobId);
271 assertResult(jobId, job, FINISHED, "100", "Operation finished");
275 * If the VNF exists but and the operation execution is present with given internalJobId, than the state of the
276 * job is error if the operation is failed
279 public void testExistingVnfWithFailedOperation() throws Exception {
280 String jobId = jobManager.spawnJob(VNF_ID, httpResponse);
281 VnfInfo vnf = new VnfInfo();
284 VnfInfo detailedVnf = new VnfInfo();
285 detailedVnf.setId(VNF_ID);
286 List<Integer> vnfCounter = new ArrayList<>();
287 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(detailedVnf));
288 OperationExecution operation = new OperationExecution();
289 operation.setId(UUID.randomUUID().toString());
290 operation.setStartTime(OffsetDateTime.now());
291 operation.setStatus(OperationStatus.FAILED);
292 ProblemDetails errorDetails = new ProblemDetails();
293 errorDetails.setTitle("Title");
294 errorDetails.setDetail("detail");
295 operation.setError(errorDetails);
296 detailedVnf.setOperationExecutions(new ArrayList<>());
297 detailedVnf.getOperationExecutions().add(operation);
298 JsonElement operationParams = new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + jobId + "\"}}");
299 when(operationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(operation.getId(), NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(operationParams));
301 JobDetailInfo job = jobManager.getJob(VNFM_ID, jobId);
303 assertResult(jobId, job, ERROR, "100", "Operation failed due to Title: detail");
304 assertTrue(jobManager.hasOngoingJobs());
307 private void assertResult(String jobId, JobDetailInfo job, JobStatus status, String progress, String descriptor) {
308 assertEquals(jobId, job.getJobId());
309 assertEquals(status, job.getResponseDescriptor().getStatus());
310 assertEquals(progress, job.getResponseDescriptor().getProgress());
311 assertNull(job.getResponseDescriptor().getErrorCode());
312 boolean finalState = JobStatus.ERROR.equals(status) || JobStatus.FINISHED.equals(status);
314 assertEquals(2, job.getResponseDescriptor().getResponseHistoryList().size());
315 JobResponseInfo startEvent = job.getResponseDescriptor().getResponseHistoryList().get(0);
316 JobResponseInfo endEvent = job.getResponseDescriptor().getResponseHistoryList().get(1);
317 assertNull(startEvent.getErrorCode());
318 assertEquals("50", startEvent.getProgress());
319 assertEquals(JobStatus.STARTED.name(), startEvent.getStatus());
320 assertEquals("1", startEvent.getResponseId());
321 assertEquals("Operation started", startEvent.getStatusDescription());
323 assertNull(endEvent.getErrorCode());
324 assertEquals("100", endEvent.getProgress());
325 assertEquals(job.getResponseDescriptor().getStatus().name(), endEvent.getStatus());
326 assertEquals("2", endEvent.getResponseId());
327 assertEquals(descriptor, endEvent.getStatusDescription());
329 assertEquals(1, job.getResponseDescriptor().getResponseHistoryList().size());
330 assertNull(job.getResponseDescriptor().getResponseHistoryList().get(0).getErrorCode());
331 assertEquals(progress, job.getResponseDescriptor().getResponseHistoryList().get(0).getProgress());
332 assertEquals(job.getResponseDescriptor().getStatus().name(), job.getResponseDescriptor().getResponseHistoryList().get(0).getStatus());
333 assertEquals("1", job.getResponseDescriptor().getResponseHistoryList().get(0).getResponseId());
334 assertEquals(descriptor, job.getResponseDescriptor().getResponseHistoryList().get(0).getStatusDescription());
336 assertEquals(Integer.toString(job.getResponseDescriptor().getResponseHistoryList().size()), job.getResponseDescriptor().getResponseId());
337 assertEquals(descriptor, job.getResponseDescriptor().getStatusDescription());
341 * If the VNF exists but and the operation execution is present with given internalJobId, than the state of the
342 * job is finished if the operation is finished, but is not a termination
345 public void testExistingVnfWithFinishedOperation() throws Exception {
346 String jobId = jobManager.spawnJob(VNF_ID, httpResponse);
347 VnfInfo vnf = new VnfInfo();
350 VnfInfo detailedVnf = new VnfInfo();
351 detailedVnf.setId(VNF_ID);
352 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(detailedVnf));
353 OperationExecution operation = new OperationExecution();
354 operation.setId(UUID.randomUUID().toString());
355 operation.setStartTime(OffsetDateTime.now());
356 operation.setStatus(OperationStatus.FINISHED);
357 operation.setOperationType(OperationType.SCALE);
358 detailedVnf.setOperationExecutions(new ArrayList<>());
359 detailedVnf.getOperationExecutions().add(operation);
360 JsonElement operationParams = new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + jobId + "\"}}");
361 when(operationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(operation.getId(), NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(operationParams));
362 JobDetailInfo job = jobManager.getJob(VNFM_ID, jobId);
364 assertResult(jobId, job, JobStatus.FINISHED, "100", "Operation finished");
365 assertTrue(jobManager.hasOngoingJobs());
369 * If the VNF exists but and the operation execution is present with given internalJobId, than the state of the
370 * job is ongoing if the termination operation is finished. In ONAP terminology the termination includes
371 * delete, so the ONAP operation is ongoing since the VNF is not yet deleted
374 public void testExistingVnfWithFinishedTerminationOperation() throws Exception {
375 String jobId = jobManager.spawnJob(VNF_ID, httpResponse);
376 VnfInfo vnf = new VnfInfo();
379 VnfInfo detailedVnf = new VnfInfo();
380 detailedVnf.setId(VNF_ID);
381 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(detailedVnf));
382 OperationExecution operation = new OperationExecution();
383 operation.setId(UUID.randomUUID().toString());
384 operation.setStartTime(OffsetDateTime.now());
385 operation.setStatus(OperationStatus.FINISHED);
386 operation.setOperationType(OperationType.TERMINATE);
387 detailedVnf.setOperationExecutions(new ArrayList<>());
388 detailedVnf.getOperationExecutions().add(operation);
389 JsonElement operationParams = new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + jobId + "\"}}");
390 when(operationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(operation.getId(), NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(operationParams));
391 JobDetailInfo job = jobManager.getJob(VNFM_ID, jobId);
393 assertResult(jobId, job, STARTED, "50", "Operation started");
395 jobManager.jobFinished(jobId);
396 job = jobManager.getJob(VNFM_ID, jobId);
398 assertResult(jobId, job, ERROR, "100", "Operation failed due to unable to delete VNF");
399 assertFalse(jobManager.hasOngoingJobs());
404 * Failuire to retrieve operation parameters (CBAM REST API fail) is logged and propagated
407 public void failuresDuringOperationExecutionRetrievalIsLoggedAndPropagated() throws Exception {
408 String jobId = jobManager.spawnJob(VNF_ID, httpResponse);
409 VnfInfo vnf = new VnfInfo();
412 VnfInfo detailedVnf = new VnfInfo();
413 detailedVnf.setId(VNF_ID);
414 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(detailedVnf));
415 OperationExecution operation = new OperationExecution();
416 operation.setId(UUID.randomUUID().toString());
417 detailedVnf.setOperationExecutions(new ArrayList<>());
418 detailedVnf.getOperationExecutions().add(operation);
419 RuntimeException expectedException = new RuntimeException();
420 when(operationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(operation.getId(), NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
423 JobDetailInfo job = jobManager.getJob(VNFM_ID, jobId);
425 } catch (RuntimeException e) {
426 assertEquals(expectedException, e.getCause());
427 verify(logger).error("Unable to retrieve operation parameters of operation with " + operation.getId() + " identifier", expectedException);
429 assertTrue(jobManager.hasOngoingJobs());
433 * Failure to retrieve VNF (CBAM REST API fail) is logged and propagated
436 public void failuresDuringVnfRetrievalIsLoggedAndPropagated() throws Exception {
437 String jobId = jobManager.spawnJob(VNF_ID, httpResponse);
438 VnfInfo vnf = new VnfInfo();
441 RuntimeException expectedException = new RuntimeException();
442 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenThrow(expectedException);
445 JobDetailInfo job = jobManager.getJob(VNFM_ID, jobId);
447 } catch (RuntimeException e) {
448 assertEquals(expectedException, e.getCause());
449 verify(logger).error("Unable to retrieve VNF with myVnfId identifier", expectedException);
451 assertTrue(jobManager.hasOngoingJobs());
455 * When searching for the ONAP job by iterating the operation executions. The newest jobs
456 * are inspected first (performance optimalization)
459 public void testNewestOperationAreInspectedFirst() throws Exception {
460 String jobId = jobManager.spawnJob(VNF_ID, httpResponse);
461 VnfInfo vnf = new VnfInfo();
464 VnfInfo detailedVnf = new VnfInfo();
465 detailedVnf.setId(VNF_ID);
466 when(vnfApi.vnfsVnfInstanceIdGet(VNF_ID, NOKIA_LCM_API_VERSION)).thenReturn(buildObservable(detailedVnf));
467 OperationExecution olderOperation = new OperationExecution();
468 olderOperation.setId(UUID.randomUUID().toString());
469 olderOperation.setStartTime(OffsetDateTime.now());
470 olderOperation.setStatus(OperationStatus.FINISHED);
471 olderOperation.setOperationType(OperationType.TERMINATE);
472 OperationExecution newerOperation = new OperationExecution();
473 newerOperation.setId(UUID.randomUUID().toString());
474 newerOperation.setStartTime(OffsetDateTime.now().plusDays(1));
475 newerOperation.setStatus(OperationStatus.FINISHED);
476 newerOperation.setOperationType(OperationType.TERMINATE);
477 detailedVnf.setOperationExecutions(new ArrayList<>());
478 detailedVnf.getOperationExecutions().add(olderOperation);
479 detailedVnf.getOperationExecutions().add(newerOperation);
480 JsonElement operationParams = new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + jobId + "\"}}");
481 List<String> queriedOperaionsInOrder = new ArrayList<>();
482 when(operationExecutionApi.operationExecutionsOperationExecutionIdOperationParamsGet(Mockito.anyString(), Mockito.eq(NOKIA_LCM_API_VERSION)))
483 .then(new Answer<Observable<Object>>() {
485 public Observable<Object> answer(InvocationOnMock invocationOnMock) throws Throwable {
486 queriedOperaionsInOrder.add(invocationOnMock.getArguments()[0].toString());
487 if (invocationOnMock.getArguments()[0].equals(olderOperation.getId())) {
488 return buildObservable(new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + jobId + "\"}}"));
490 return buildObservable(new JsonParser().parse("{ \"additionalParams\" : { \"jobId\" : \"" + "nonMatching" + "\"}}"));
494 JobDetailInfo job = jobManager.getJob(VNFM_ID, jobId);
496 assertEquals(Lists.newArrayList(newerOperation.getId(), olderOperation.getId()), queriedOperaionsInOrder);
497 assertTrue(jobManager.hasOngoingJobs());
501 * if the registration process has not finished it is prevented to spawn jobs
504 public void noJobCanBeStartedIfRegistrationNotFinished() throws Exception {
506 when(selfRegistrationManager.isReady()).thenReturn(false);
509 jobManager.spawnJob(VNF_ID, httpResponse);
511 } catch (RuntimeException e) {
512 assertEquals("The service is not yet ready", e.getMessage());
517 * Ongoing job are out waited during the the preparation for shutdown
520 //need to wait for an asynchronous execution to finish
521 //this is the most optimal way to do it
522 @SuppressWarnings("squid:S2925")
523 public void onGoingJobsAreOutwaitedDuringShutdown() throws Exception {
524 String firstJobId = jobManager.spawnJob(VNF_ID, httpResponse);
525 ExecutorService executorService = Executors.newCachedThreadPool();
526 ArgumentCaptor<Integer> sleeps = ArgumentCaptor.forClass(Integer.class);
527 doNothing().when(systemFunctions).sleep(sleeps.capture());
528 //when prepare job manager for shutdown
529 Future<?> shutDown = executorService.submit(() -> jobManager.prepareForShutdown());
530 while (sleeps.getAllValues().size() == 0) {
533 } catch (InterruptedException e) {
536 assertFalse(shutDown.isDone());
537 jobManager.jobFinished(firstJobId);
540 verify(systemFunctions, times(sleeps.getAllValues().size())).sleep(500L);