From 173664d486788099829e40c1c741445100354db3 Mon Sep 17 00:00:00 2001 From: MichaelMorris Date: Tue, 2 Apr 2019 17:14:42 +0000 Subject: Add job handling Issue-ID: SO-1624 Change-Id: I84c089d7b0ad610f0fe38be220a81a1f0762f394 Signed-off-by: MichaelMorris --- .../src/main/resources/vnfmadapter.yaml | 2 +- .../extclients/vnfm/VnfmServiceProvider.java | 10 ++ .../vnfm/VnfmServiceProviderConfiguration.java | 15 +++ .../extclients/vnfm/VnfmServiceProviderImpl.java | 11 +- .../extclients/vnfm/VnfmUrlProvider.java | 73 +++++++++++++ .../vnfmadapter/jobmanagement/JobManager.java | 119 +++++++++++++++++++++ .../vnfmadapter/jobmanagement/VnfmOperation.java | 85 +++++++++++++++ .../vnfmadapter/lifecycle/LifecycleManager.java | 36 ++++++- .../vnfmadapter/rest/VnfmAdapterController.java | 38 ++++++- .../rest/VnfmAdapterControllerTest.java | 112 +++++++++++++++++-- 10 files changed, 485 insertions(+), 16 deletions(-) create mode 100644 adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmUrlProvider.java create mode 100644 adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/JobManager.java create mode 100644 adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/VnfmOperation.java diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-adapter-api/src/main/resources/vnfmadapter.yaml b/adapters/mso-vnfm-adapter/mso-vnfm-adapter-api/src/main/resources/vnfmadapter.yaml index dc5f85e5fe..9d0a5283af 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-adapter-api/src/main/resources/vnfmadapter.yaml +++ b/adapters/mso-vnfm-adapter/mso-vnfm-adapter-api/src/main/resources/vnfmadapter.yaml @@ -241,7 +241,7 @@ definitions: required: - operationStatusRetrievalStatus OperationStatusRetrievalStatusEnum: - description: The status of the attempt to retrrieve the operation from the VNFM + description: The status of the attempt to retrieve the operation from the VNFM type: string enum: - STATUS_FOUND diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProvider.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProvider.java index 0b5b65e7c3..f0646f3cf2 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProvider.java +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProvider.java @@ -21,6 +21,7 @@ package org.onap.so.adapters.vnfmadapter.extclients.vnfm; import com.google.common.base.Optional; +import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200; import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201; /** @@ -36,4 +37,13 @@ public interface VnfmServiceProvider { */ Optional getVnf(final String vnfSelfLink); + /** + * Invoke a get request for a VNFM operation. + * + * @param vnfmId the id of the VNFM in AAI + * @param operationId the id of the operation on the VNFM + * @return the operation from the VNFM + */ + Optional getOperation(final String vnfmId, final String operationId); + } diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java index 28b0b14e4c..88008c6a3f 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java @@ -21,6 +21,7 @@ package org.onap.so.adapters.vnfmadapter.extclients.vnfm; import static org.onap.so.client.RestTemplateConfig.CONFIGURABLE_REST_TEMPLATE; +import java.util.Iterator; import org.onap.so.configuration.rest.BasicHttpHeadersProvider; import org.onap.so.configuration.rest.HttpHeadersProvider; import org.onap.so.rest.service.HttpRestServiceProvider; @@ -29,6 +30,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.GsonHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.client.RestTemplate; /** @@ -45,7 +49,18 @@ public class VnfmServiceProviderConfiguration { private HttpRestServiceProvider getHttpRestServiceProvider(final RestTemplate restTemplate, final HttpHeadersProvider httpHeadersProvider) { + setGsonMessageConverter(restTemplate); return new HttpRestServiceProviderImpl(restTemplate, httpHeadersProvider); } + private void setGsonMessageConverter(final RestTemplate restTemplate) { + final Iterator> iterator = restTemplate.getMessageConverters().iterator(); + while (iterator.hasNext()) { + if (iterator.next() instanceof MappingJackson2HttpMessageConverter) { + iterator.remove(); + } + } + restTemplate.getMessageConverters().add(new GsonHttpMessageConverter()); + } + } diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java index 4a141ede4c..43d4f1e0dd 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java @@ -21,6 +21,7 @@ package org.onap.so.adapters.vnfmadapter.extclients.vnfm; import com.google.common.base.Optional; +import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200; import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201; import org.onap.so.rest.service.HttpRestServiceProvider; import org.springframework.beans.factory.annotation.Autowired; @@ -31,11 +32,13 @@ import org.springframework.stereotype.Service; public class VnfmServiceProviderImpl implements VnfmServiceProvider { private final HttpRestServiceProvider httpServiceProvider; + private final VnfmUrlProvider urlProvider; @Autowired - public VnfmServiceProviderImpl( + public VnfmServiceProviderImpl(final VnfmUrlProvider urlProvider, @Qualifier("vnfmServiceProvider") final HttpRestServiceProvider httpServiceProvider) { this.httpServiceProvider = httpServiceProvider; + this.urlProvider = urlProvider; } @Override @@ -43,5 +46,9 @@ public class VnfmServiceProviderImpl implements VnfmServiceProvider { return httpServiceProvider.get(vnfSelfLink, InlineResponse201.class); } - + @Override + public Optional getOperation(final String vnfmId, final String operationId) { + final String url = urlProvider.getOperationUrl(vnfmId, operationId); + return httpServiceProvider.get(url, InlineResponse200.class); + } } diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmUrlProvider.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmUrlProvider.java new file mode 100644 index 0000000000..f5a99b1d95 --- /dev/null +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmUrlProvider.java @@ -0,0 +1,73 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.so.adapters.vnfmadapter.extclients.vnfm; + +import static org.slf4j.LoggerFactory.getLogger; +import java.net.URI; +import org.onap.aai.domain.yang.EsrSystemInfo; +import org.onap.aai.domain.yang.EsrSystemInfoList; +import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiServiceProvider; +import org.onap.so.adapters.vnfmadapter.rest.exceptions.VnfmNotFoundException; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Provides URLs for REST calls to a VNFM. + */ +@Service +public class VnfmUrlProvider { + + private static Logger logger = getLogger(VnfmUrlProvider.class); + private final AaiServiceProvider aaiServiceProvider; + + @Autowired + public VnfmUrlProvider(final AaiServiceProvider aaiServiceProvider) { + this.aaiServiceProvider = aaiServiceProvider; + } + + /** + * Get the URL for a generic VNF in AAI. + * + * @param vnfId The identifier of the VNF + * @return the URL of the VNF + */ + public String getOperationUrl(final String vnfmId, final String operationId) { + final String url = UriComponentsBuilder.fromUri(getBaseUri(vnfmId)).pathSegment("/vnf_lcm_op_occs/") + .pathSegment(operationId).build().toString(); + logger.debug("getOperationUrl:" + url); + + return url; + } + + private URI getBaseUri(final String vnfmId) { + final EsrSystemInfoList vnfmEsrSystemInfoList = aaiServiceProvider.invokeGetVnfmEsrSystemInfoList(vnfmId); + + if (vnfmEsrSystemInfoList != null) { + for (final EsrSystemInfo esrSystemInfo : vnfmEsrSystemInfoList.getEsrSystemInfo()) { + return UriComponentsBuilder.fromHttpUrl(esrSystemInfo.getServiceUrl()).build().toUri(); + } + } + + throw new VnfmNotFoundException("VNFM, or Service URL for VNFM, not found for VNFM " + vnfmId); + } +} diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/JobManager.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/JobManager.java new file mode 100644 index 0000000000..ac11bcee4b --- /dev/null +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/JobManager.java @@ -0,0 +1,119 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.so.adapters.vnfmadapter.jobmanagement; + +import static org.slf4j.LoggerFactory.getLogger; +import com.google.common.base.Optional; +import com.google.common.collect.Maps; +import java.util.Map; +import java.util.UUID; +import org.onap.so.adapters.vnfmadapter.extclients.vnfm.VnfmServiceProvider; +import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200; +import org.onap.vnfmadapter.v1.model.OperationEnum; +import org.onap.vnfmadapter.v1.model.OperationStateEnum; +import org.onap.vnfmadapter.v1.model.OperationStatusRetrievalStatusEnum; +import org.onap.vnfmadapter.v1.model.QueryJobResponse; +import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Manages jobs enabling the status of jobs to be queried. A job is associated with an operation on + * a VNFM. + */ +@Component +public class JobManager { + private static final String SEPARATOR = "_"; + private static Logger logger = getLogger(JobManager.class); + private final Map mapOfJobIdToVnfmOperation = Maps.newConcurrentMap(); + private final VnfmServiceProvider vnfmServiceProvider; + + @Autowired + JobManager(final VnfmServiceProvider vnfmServiceProvider) { + this.vnfmServiceProvider = vnfmServiceProvider; + } + + /** + * Create a job associated with an operation on a VNFM. + * + * @param vnfmId the VNFM the operation relates to + * @param operationId the ID of the associated VNFM operation + * @param waitForNotificationForSuccess if set to true the + * {@link QueryJobResponse#getOperationState()} shall not return + * {@link org.onap.vnfmadapter.v1.model.OperationStateEnum#COMPLETED} unless a required + * notification has been processed + * @return the ID of the job. Can be used to query the job using {@link #getVnfmOperation(String)} + */ + public String createJob(final String vnfmId, final String operationId, + final boolean waitForNotificationForSuccess) { + final String jobId = vnfmId + SEPARATOR + UUID.randomUUID().toString(); + final VnfmOperation vnfmOperation = new VnfmOperation(vnfmId, operationId, waitForNotificationForSuccess); + mapOfJobIdToVnfmOperation.put(jobId, vnfmOperation); + return jobId; + } + + /** + * Get the operation, associated with the given job ID, from the VNFM. + * + * @param jobId the job ID + * @return the associated operation from the VNFM, or null of no operation is + * associated with the given job ID + */ + public QueryJobResponse getVnfmOperation(final String jobId) { + final VnfmOperation vnfmOperation = mapOfJobIdToVnfmOperation.get(jobId); + final QueryJobResponse response = new QueryJobResponse(); + + if (vnfmOperation == null) { + return null; + } + + final Optional operationOptional = + vnfmServiceProvider.getOperation(vnfmOperation.getVnfmId(), vnfmOperation.getOperationId()); + if (!operationOptional.isPresent()) { + return response.operationStatusRetrievalStatus(OperationStatusRetrievalStatusEnum.OPERATION_NOT_FOUND); + } + final InlineResponse200 operation = operationOptional.get(); + + logger.debug("Job Id: " + jobId + ", operationId: " + operation.getId() + ", operation details: " + operation); + + response.setOperationStatusRetrievalStatus(OperationStatusRetrievalStatusEnum.STATUS_FOUND); + response.setId(operation.getId()); + response.setOperation(OperationEnum.fromValue(operation.getOperation().getValue())); + response.setOperationState(getOperationState(vnfmOperation, operation)); + response.setStartTime(operation.getStartTime()); + response.setStateEnteredTime(operation.getStateEnteredTime()); + response.setVnfInstanceId(operation.getVnfInstanceId()); + + return response; + } + + private OperationStateEnum getOperationState(final VnfmOperation vnfmOperation, + final InlineResponse200 operationResponse) { + final OperationStateEnum operationState = + OperationStateEnum.fromValue(operationResponse.getOperationState().getValue()); + if (operationState == OperationStateEnum.COMPLETED && vnfmOperation.isWaitForNotificationForSuccess() + && !vnfmOperation.isNotificationProcessed()) { + return org.onap.vnfmadapter.v1.model.OperationStateEnum.PROCESSING; + } + return operationState; + } + +} diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/VnfmOperation.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/VnfmOperation.java new file mode 100644 index 0000000000..916c9e4011 --- /dev/null +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/VnfmOperation.java @@ -0,0 +1,85 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2019 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.so.adapters.vnfmadapter.jobmanagement; + +/** + * Represents an operation on a VNFM. + */ +public class VnfmOperation { + + private final String vnfmId; + private final String operationId; + private boolean waitForNotificationForSuccess = false; + private boolean isNotificationProcessed = false; + + public VnfmOperation(final String vnfmId, final String operationId, final boolean waitForNotificationForSuccess) { + this.vnfmId = vnfmId; + this.operationId = operationId; + this.waitForNotificationForSuccess = waitForNotificationForSuccess; + } + + /** + * Get the ID of the operation on the VNFM. + * + * @return the ID of the operation on the VNFM + */ + public String getOperationId() { + return operationId; + } + + /** + * Get the ID of the VNFM the operation is carried out by. + * + * @return the ID of the VNFM + */ + public String getVnfmId() { + return vnfmId; + } + + /** + * Check if a notification should be processed before the operation is considered successfully + * completed. + * + * @return true> if a notification must be processed before the operation is considered + * successfully completed, false otherwise + */ + public boolean isWaitForNotificationForSuccess() { + return waitForNotificationForSuccess; + } + + /** + * Set the required notification has been processed for the operation. + */ + public void setNotificationProcessed() { + this.isNotificationProcessed = true; + } + + /** + * Check if the required notification has been processed. + * + * @return true of the required notification has been processed, false + * otherwise + */ + public boolean isNotificationProcessed() { + return isNotificationProcessed; + } + +} diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java index 4c54ded4ae..5c944ca38b 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java @@ -28,31 +28,45 @@ import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiClientProvider; import org.onap.so.adapters.vnfmadapter.extclients.aai.AaiHelper; import org.onap.so.adapters.vnfmadapter.extclients.vnfm.VnfmServiceProvider; import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201; +import org.onap.so.adapters.vnfmadapter.jobmanagement.JobManager; import org.onap.so.client.aai.AAIObjectType; import org.onap.so.client.aai.entities.uri.AAIUriFactory; import org.onap.so.client.graphinventory.entities.uri.Depth; import org.onap.vnfmadapter.v1.model.CreateVnfRequest; import org.onap.vnfmadapter.v1.model.CreateVnfResponse; +import org.onap.vnfmadapter.v1.model.DeleteVnfResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +/** + * Manages lifecycle operations towards the VNFMs. + */ @Component public class LifecycleManager { private static final Logger logger = LoggerFactory.getLogger(LifecycleManager.class); private final AaiClientProvider aaiClientProvider; private final VnfmServiceProvider vnfmServiceProvider; private final AaiHelper aaiHelper; + private final JobManager jobManager; @Autowired LifecycleManager(final AaiClientProvider aaiClientProvider, final AaiHelper aaiHelper, - final VnfmServiceProvider vnfmServiceProvider) { + final VnfmServiceProvider vnfmServiceProvider, final JobManager jobManager) { this.aaiClientProvider = aaiClientProvider; this.vnfmServiceProvider = vnfmServiceProvider; this.aaiHelper = aaiHelper; + this.jobManager = jobManager; } + /** + * Create a VNF on a VNFM. + * + * @param vnfIdInAai the ID of the VNF in AAI + * @param request the create request + * @return the response to the request + */ public CreateVnfResponse createVnf(final String vnfIdInAai, final CreateVnfRequest request) { final GenericVnf genericVnf = getGenericVnfFromAai(vnfIdInAai); checkIfVnfAlreadyExistsInVnfm(genericVnf); @@ -63,8 +77,11 @@ public class LifecycleManager { aaiHelper.addRelationshipFromGenericVnfToVnfm(genericVnf, vnfm.getVnfmId()); } + // operation ID set to random value for now, will be set correctly once we implement instantiate + // call towards the VNFM + final String jobId = jobManager.createJob(vnfm.getVnfmId(), UUID.randomUUID().toString(), false); final CreateVnfResponse response = new CreateVnfResponse(); - response.setJobId(UUID.randomUUID().toString()); + response.setJobId(jobId); return response; } @@ -94,4 +111,19 @@ public class LifecycleManager { } } } + + /** + * Delete a VNF on a VNFM. + * + * @param vnfIdInAai the ID of the VNF in AAI + * @return the response to the request + */ + public DeleteVnfResponse deleteVnf(final String vnfIdInAai) { + // vnfm ID and operation ID set to random value for now, will be set correctly once we implement + // terminate call towards the VNFM + final String jobId = jobManager.createJob(UUID.randomUUID().toString(), UUID.randomUUID().toString(), true); + final DeleteVnfResponse response = new DeleteVnfResponse(); + response.setJobId(jobId); + return response; + } } diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterController.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterController.java index 65d5478a57..055b8e0450 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterController.java +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterController.java @@ -21,14 +21,15 @@ package org.onap.so.adapters.vnfmadapter.rest; import static org.onap.so.adapters.vnfmadapter.Constants.BASE_URL; -import java.util.UUID; import javax.validation.Valid; import javax.ws.rs.core.MediaType; import org.onap.logging.ref.slf4j.ONAPLogConstants; +import org.onap.so.adapters.vnfmadapter.jobmanagement.JobManager; import org.onap.so.adapters.vnfmadapter.lifecycle.LifecycleManager; import org.onap.vnfmadapter.v1.model.CreateVnfRequest; import org.onap.vnfmadapter.v1.model.CreateVnfResponse; import org.onap.vnfmadapter.v1.model.DeleteVnfResponse; +import org.onap.vnfmadapter.v1.model.QueryJobResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; @@ -37,6 +38,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -54,10 +56,12 @@ public class VnfmAdapterController { private static final Logger logger = LoggerFactory.getLogger(VnfmAdapterController.class); private final LifecycleManager lifecycleManager; + private final JobManager jobManager; @Autowired - VnfmAdapterController(final LifecycleManager lifecycleManager) { + VnfmAdapterController(final LifecycleManager lifecycleManager, final JobManager jobManager) { this.lifecycleManager = lifecycleManager; + this.jobManager = jobManager; } @PostMapping(value = "/vnfs/{vnfId}") @@ -109,12 +113,38 @@ public class VnfmAdapterController { logger.info("REST request vnfDelete for VNF: {}", vnfId); - final DeleteVnfResponse response = new DeleteVnfResponse(); - response.setJobId(UUID.randomUUID().toString()); + final DeleteVnfResponse response = lifecycleManager.deleteVnf(vnfId); clearLoggingMDCs(); return new ResponseEntity<>(response, HttpStatus.ACCEPTED); } + @GetMapping(value = "/jobs/{jobId}") + public ResponseEntity jobQuery( + @ApiParam(value = "The identifier of the Job.", required = true) @PathVariable("jobId") final String jobId, + @ApiParam( + value = "Used to track REST requests for logging purposes. Identifies a single top level invocation of ONAP", + required = false) @RequestHeader(value = ONAPLogConstants.Headers.REQUEST_ID, + required = false) final String requestId, + @ApiParam( + value = "Used to track REST requests for logging purposes. Identifies the client application user agent or user invoking the API", + required = false) @RequestHeader(value = ONAPLogConstants.Headers.PARTNER_NAME, + required = false) final String partnerName, + @ApiParam( + value = "Used to track REST requests for logging purposes. Identifies a single invocation of a single component", + required = false) @RequestHeader(value = ONAPLogConstants.Headers.INVOCATION_ID, + required = false) final String invocationId) { + + setLoggingMDCs(requestId, partnerName, invocationId); + + final QueryJobResponse response = jobManager.getVnfmOperation(jobId); + if (response == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + return new ResponseEntity<>(response, HttpStatus.OK); + + } + + private void setLoggingMDCs(final String requestId, final String partnerName, final String invocationId) { MDC.put(ONAPLogConstants.MDCs.REQUEST_ID, requestId); MDC.put(ONAPLogConstants.MDCs.PARTNER_NAME, partnerName); diff --git a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterControllerTest.java b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterControllerTest.java index e307a253f8..29bab9dc6a 100644 --- a/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterControllerTest.java +++ b/adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterControllerTest.java @@ -33,6 +33,7 @@ import java.net.URI; import java.util.Optional; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; +import org.hamcrest.core.StringStartsWith; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -47,6 +48,7 @@ import org.onap.aai.domain.yang.Relationship; import org.onap.aai.domain.yang.RelationshipData; import org.onap.aai.domain.yang.RelationshipList; import org.onap.so.adapters.vnfmadapter.VnfmAdapterApplication; +import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200; import org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse201; import org.onap.so.adapters.vnfmadapter.rest.exceptions.VnfmNotFoundException; import org.onap.so.client.aai.AAIResourcesClient; @@ -54,6 +56,9 @@ import org.onap.so.client.aai.entities.uri.AAIResourceUri; import org.onap.vnfmadapter.v1.model.CreateVnfRequest; import org.onap.vnfmadapter.v1.model.CreateVnfResponse; import org.onap.vnfmadapter.v1.model.DeleteVnfResponse; +import org.onap.vnfmadapter.v1.model.OperationEnum; +import org.onap.vnfmadapter.v1.model.OperationStateEnum; +import org.onap.vnfmadapter.v1.model.QueryJobResponse; import org.onap.vnfmadapter.v1.model.Tenant; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -70,6 +75,9 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.web.client.RestTemplate; +import org.threeten.bp.LocalDateTime; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.ZoneOffset; @RunWith(SpringRunner.class) @@ -78,6 +86,11 @@ import org.springframework.web.client.RestTemplate; public class VnfmAdapterControllerTest { + private static final OffsetDateTime JAN_1_2019_12_00 = + OffsetDateTime.of(LocalDateTime.of(2019, 1, 1, 12, 0), ZoneOffset.UTC); + private static final OffsetDateTime JAN_1_2019_1_00 = + OffsetDateTime.of(LocalDateTime.of(2019, 1, 1, 1, 0), ZoneOffset.UTC); + @LocalServerPort private int port; @Autowired @@ -144,16 +157,28 @@ public class VnfmAdapterControllerTest { doReturn(Optional.of(esrSystemInfoList1)).when(aaiResourcesClient).get(eq(EsrSystemInfoList.class), MockitoHamcrest.argThat(new AaiResourceUriMatcher( "/external-system/esr-vnfm-list/esr-vnfm/vnfm1/esr-system-info-list"))); - doReturn(Optional.of(esrSystemInfoList2)).when(aaiResourcesClient).get(eq(EsrSystemInfoList.class), MockitoHamcrest.argThat(new AaiResourceUriMatcher( "/external-system/esr-vnfm-list/esr-vnfm/vnfm2/esr-system-info-list"))); + final InlineResponse200 firstOperationQueryResponse = createOperationQueryResponse( + org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationEnum.INSTANTIATE, + org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationStateEnum.PROCESSING); + mockRestServer.expect(requestTo(new StringStartsWith("http://vnfm2:8080/vnf_lcm_op_occs"))) + .andRespond(withSuccess(gson.toJson(firstOperationQueryResponse), MediaType.APPLICATION_JSON)); - final ResponseEntity response = + final InlineResponse200 secondOperationQueryReponse = createOperationQueryResponse( + org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationEnum.INSTANTIATE, + org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationStateEnum.COMPLETED); + mockRestServer.expect(requestTo(new StringStartsWith("http://vnfm2:8080/vnf_lcm_op_occs"))) + .andRespond(withSuccess(gson.toJson(secondOperationQueryReponse), MediaType.APPLICATION_JSON)); + + // Invoke the create request + + final ResponseEntity createVnfResponse = controller.vnfCreate("myTestVnfId", createVnfRequest, "asadas", "so", "1213"); - assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); - assertNotNull(response.getBody().getJobId()); + assertEquals(HttpStatus.ACCEPTED, createVnfResponse.getStatusCode()); + assertNotNull(createVnfResponse.getBody().getJobId()); final ArgumentCaptor genericVnfArgument = ArgumentCaptor.forClass(GenericVnf.class); final ArgumentCaptor uriArgument = ArgumentCaptor.forClass(AAIResourceUri.class); @@ -169,6 +194,22 @@ public class VnfmAdapterControllerTest { assertEquals("esr-vnfm", createdRelationship.getRelatedTo()); assertEquals("tosca.relationships.DependsOn", createdRelationship.getRelationshipLabel()); assertEquals("/aai/v15/external-system/esr-vnfm-list/esr-vnfm/vnfm2", createdRelationship.getRelatedLink()); + + // check the job status + + final ResponseEntity firstJobQueryResponse = + controller.jobQuery(createVnfResponse.getBody().getJobId(), "", "so", "1213"); + assertEquals(OperationEnum.INSTANTIATE, firstJobQueryResponse.getBody().getOperation()); + assertEquals(OperationStateEnum.PROCESSING, firstJobQueryResponse.getBody().getOperationState()); + assertEquals(JAN_1_2019_12_00, firstJobQueryResponse.getBody().getStartTime()); + assertEquals(JAN_1_2019_1_00, firstJobQueryResponse.getBody().getStateEnteredTime()); + + final ResponseEntity secondJobQueryResponse = + controller.jobQuery(createVnfResponse.getBody().getJobId(), "", "so", "1213"); + assertEquals(OperationEnum.INSTANTIATE, secondJobQueryResponse.getBody().getOperation()); + assertEquals(OperationStateEnum.COMPLETED, secondJobQueryResponse.getBody().getOperationState()); + assertEquals(JAN_1_2019_12_00, secondJobQueryResponse.getBody().getStartTime()); + assertEquals(JAN_1_2019_1_00, secondJobQueryResponse.getBody().getStateEnteredTime()); } @Test(expected = IllegalArgumentException.class) @@ -336,9 +377,62 @@ public class VnfmAdapterControllerTest { .delete(new URI("http://localhost:" + port + "/so/vnfm-adapter/v1/vnfs/myVnfId")) .accept(MediaType.APPLICATION_JSON).header("X-ONAP-RequestId", "myRequestId") .header("X-ONAP-InvocationID", "myInvocationId").header("Content-Type", "application/json").build(); - final ResponseEntity response = restTemplate.exchange(request, DeleteVnfResponse.class); - assertEquals(202, response.getStatusCode().value()); - assertNotNull(response.getBody().getJobId()); + final ResponseEntity deleteVnfResponse = + restTemplate.exchange(request, DeleteVnfResponse.class); + assertEquals(202, deleteVnfResponse.getStatusCode().value()); + assertNotNull(deleteVnfResponse.getBody().getJobId()); + + + final EsrSystemInfo esrSystemInfo = new EsrSystemInfo(); + esrSystemInfo.setServiceUrl("http://vnfm:8080"); + esrSystemInfo.setType("vnfmType"); + esrSystemInfo.setSystemType("VNFM"); + final EsrSystemInfoList esrSystemInfoList = new EsrSystemInfoList(); + esrSystemInfoList.getEsrSystemInfo().add(esrSystemInfo); + + doReturn(Optional.of(esrSystemInfoList)).when(aaiResourcesClient).get(eq(EsrSystemInfoList.class), + MockitoHamcrest.argThat(new AaiResourceUriMatcher("/external-system/esr-vnfm-list/esr-vnfm/..."))); + + final InlineResponse200 firstOperationQueryResponse = createOperationQueryResponse( + org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationEnum.TERMINATE, + org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationStateEnum.PROCESSING); + mockRestServer.expect(requestTo(new StringStartsWith("http://vnfm:8080/vnf_lcm_op_occs"))) + .andRespond(withSuccess(gson.toJson(firstOperationQueryResponse), MediaType.APPLICATION_JSON)); + + + final InlineResponse200 secondOperationQueryReponse = createOperationQueryResponse( + org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationEnum.TERMINATE, + org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationStateEnum.COMPLETED); + mockRestServer.expect(requestTo(new StringStartsWith("http://vnfm:8080/vnf_lcm_op_occs"))) + .andRespond(withSuccess(gson.toJson(secondOperationQueryReponse), MediaType.APPLICATION_JSON)); + + + final ResponseEntity firstJobQueryResponse = + controller.jobQuery(deleteVnfResponse.getBody().getJobId(), "", "so", "1213"); + assertEquals(OperationEnum.TERMINATE, firstJobQueryResponse.getBody().getOperation()); + assertEquals(OperationStateEnum.PROCESSING, firstJobQueryResponse.getBody().getOperationState()); + assertEquals(JAN_1_2019_12_00, firstJobQueryResponse.getBody().getStartTime()); + assertEquals(JAN_1_2019_1_00, firstJobQueryResponse.getBody().getStateEnteredTime()); + + final ResponseEntity secondJobQueryResponse = + controller.jobQuery(deleteVnfResponse.getBody().getJobId(), "", "so", "1213"); + assertEquals(OperationEnum.TERMINATE, secondJobQueryResponse.getBody().getOperation()); + assertEquals(OperationStateEnum.PROCESSING, secondJobQueryResponse.getBody().getOperationState()); + assertEquals(JAN_1_2019_12_00, secondJobQueryResponse.getBody().getStartTime()); + assertEquals(JAN_1_2019_1_00, secondJobQueryResponse.getBody().getStateEnteredTime()); + } + + private InlineResponse200 createOperationQueryResponse( + final org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationEnum operation, + final org.onap.so.adapters.vnfmadapter.extclients.vnfm.model.InlineResponse200.OperationStateEnum operationState) { + final InlineResponse200 response = new InlineResponse200(); + response.setId("9876"); + response.setOperation(operation); + response.setOperationState(operationState); + response.setStartTime(JAN_1_2019_12_00); + response.setStateEnteredTime(JAN_1_2019_1_00); + response.setVnfInstanceId("myVnfInstanceId"); + return response; } private class AaiResourceUriMatcher extends BaseMatcher { @@ -352,6 +446,10 @@ public class VnfmAdapterControllerTest { @Override public boolean matches(final Object item) { if (item instanceof AAIResourceUri) { + if (uriAsString.endsWith("...")) { + return ((AAIResourceUri) item).build().toString() + .startsWith(uriAsString.substring(0, uriAsString.indexOf("..."))); + } return ((AAIResourceUri) item).build().toString().equals(uriAsString); } return false; -- cgit 1.2.3-korg