Add job handling 87/83987/1
authorMichaelMorris <michael.morris@est.tech>
Tue, 2 Apr 2019 17:14:42 +0000 (17:14 +0000)
committerMichaelMorris <michael.morris@est.tech>
Tue, 2 Apr 2019 17:14:42 +0000 (17:14 +0000)
Issue-ID: SO-1624
Change-Id: I84c089d7b0ad610f0fe38be220a81a1f0762f394
Signed-off-by: MichaelMorris <michael.morris@est.tech>
adapters/mso-vnfm-adapter/mso-vnfm-adapter-api/src/main/resources/vnfmadapter.yaml
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProvider.java
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderConfiguration.java
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmServiceProviderImpl.java
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/extclients/vnfm/VnfmUrlProvider.java [new file with mode: 0644]
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/JobManager.java [new file with mode: 0644]
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/jobmanagement/VnfmOperation.java [new file with mode: 0644]
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/lifecycle/LifecycleManager.java
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/main/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterController.java
adapters/mso-vnfm-adapter/mso-vnfm-etsi-adapter/src/test/java/org/onap/so/adapters/vnfmadapter/rest/VnfmAdapterControllerTest.java

index dc5f85e..9d0a528 100644 (file)
@@ -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
index 0b5b65e..f0646f3 100644 (file)
@@ -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<InlineResponse201> 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<InlineResponse200> getOperation(final String vnfmId, final String operationId);
+
 }
index 28b0b14..88008c6 100644 (file)
@@ -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<HttpMessageConverter<?>> iterator = restTemplate.getMessageConverters().iterator();
+        while (iterator.hasNext()) {
+            if (iterator.next() instanceof MappingJackson2HttpMessageConverter) {
+                iterator.remove();
+            }
+        }
+        restTemplate.getMessageConverters().add(new GsonHttpMessageConverter());
+    }
+
 }
index 4a141ed..43d4f1e 100644 (file)
@@ -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<InlineResponse200> 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 (file)
index 0000000..f5a99b1
--- /dev/null
@@ -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 (file)
index 0000000..ac11bce
--- /dev/null
@@ -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<String, VnfmOperation> 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 <code>true</code> 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 <code>null</code> 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<InlineResponse200> 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 (file)
index 0000000..916c9e4
--- /dev/null
@@ -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 <code>true></code> if a notification must be processed before the operation is considered
+     *         successfully completed, <code>false</code> 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 <code>true</code> of the required notification has been processed, <code>false</code>
+     *         otherwise
+     */
+    public boolean isNotificationProcessed() {
+        return isNotificationProcessed;
+    }
+
+}
index 4c54ded..5c944ca 100644 (file)
@@ -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;
+    }
 }
index 65d5478..055b8e0 100644 (file)
 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<QueryJobResponse> 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);
index e307a25..29bab9d 100644 (file)
@@ -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<CreateVnfResponse> 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> 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<GenericVnf> genericVnfArgument = ArgumentCaptor.forClass(GenericVnf.class);
         final ArgumentCaptor<AAIResourceUri> 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<QueryJobResponse> 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<QueryJobResponse> 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<DeleteVnfResponse> response = restTemplate.exchange(request, DeleteVnfResponse.class);
-        assertEquals(202, response.getStatusCode().value());
-        assertNotNull(response.getBody().getJobId());
+        final ResponseEntity<DeleteVnfResponse> 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<QueryJobResponse> 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<QueryJobResponse> 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<AAIResourceUri> {
@@ -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;