f240fa33efcde1b5037fafb1321d612f92cdb849
[so.git] / so-etsi-nfvo / so-etsi-nfvo-ns-lcm / so-etsi-nfvo-ns-lcm-bpmn-flows / src / main / java / org / onap / so / etsi / nfvo / ns / lcm / bpmn / flows / service / JobExecutorService.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2020 Nordix Foundation.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20 package org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.service;
21
22 import static org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.CamundaVariableNameConstants.CREATE_NS_REQUEST_PARAM_NAME;
23 import static org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.CamundaVariableNameConstants.GLOBAL_CUSTOMER_ID_PARAM_NAME;
24 import static org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.CamundaVariableNameConstants.INSTANTIATE_NS_REQUEST_PARAM_NAME;
25 import static org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.CamundaVariableNameConstants.JOB_ID_PARAM_NAME;
26 import static org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.CamundaVariableNameConstants.NS_INSTANCE_ID_PARAM_NAME;
27 import static org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.CamundaVariableNameConstants.OCC_ID_PARAM_NAME;
28 import static org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.CamundaVariableNameConstants.SERVICE_TYPE_PARAM_NAME;
29 import static org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.CamundaVariableNameConstants.TERMINATE_NS_REQUEST_PARAM_NAME;
30 import static org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.Constants.CREATE_NS_WORKFLOW_NAME;
31 import static org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.Constants.INSTANTIATE_NS_WORKFLOW_NAME;
32 import static org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.Constants.TERMINATE_NS_WORKFLOW_NAME;
33 import static org.onap.so.etsi.nfvo.ns.lcm.database.beans.JobAction.INSTANTIATE;
34 import static org.onap.so.etsi.nfvo.ns.lcm.database.beans.JobAction.TERMINATE;
35 import static org.onap.so.etsi.nfvo.ns.lcm.database.beans.JobStatusEnum.ERROR;
36 import static org.onap.so.etsi.nfvo.ns.lcm.database.beans.JobStatusEnum.FINISHED;
37 import static org.onap.so.etsi.nfvo.ns.lcm.database.beans.JobStatusEnum.FINISHED_WITH_ERROR;
38 import static org.onap.so.etsi.nfvo.ns.lcm.database.beans.JobStatusEnum.IN_PROGRESS;
39 import static org.onap.so.etsi.nfvo.ns.lcm.database.beans.JobStatusEnum.STARTING;
40 import static org.slf4j.LoggerFactory.getLogger;
41 import java.time.Instant;
42 import java.time.LocalDateTime;
43 import java.util.HashMap;
44 import java.util.Map;
45 import java.util.Optional;
46 import java.util.concurrent.TimeUnit;
47 import org.apache.commons.lang3.tuple.ImmutablePair;
48 import org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.GsonProvider;
49 import org.onap.so.etsi.nfvo.ns.lcm.bpmn.flows.exceptions.NsRequestProcessingException;
50 import org.onap.so.etsi.nfvo.ns.lcm.database.beans.JobAction;
51 import org.onap.so.etsi.nfvo.ns.lcm.database.beans.JobStatusEnum;
52 import org.onap.so.etsi.nfvo.ns.lcm.database.beans.NfvoJob;
53 import org.onap.so.etsi.nfvo.ns.lcm.database.beans.NfvoNsInst;
54 import org.onap.so.etsi.nfvo.ns.lcm.database.beans.NsLcmOpOcc;
55 import org.onap.so.etsi.nfvo.ns.lcm.database.beans.NsLcmOpType;
56 import org.onap.so.etsi.nfvo.ns.lcm.database.beans.OperationStateEnum;
57 import org.onap.so.etsi.nfvo.ns.lcm.database.beans.State;
58 import org.onap.so.etsi.nfvo.ns.lcm.database.service.DatabaseServiceProvider;
59 import org.onap.so.etsi.nfvo.ns.lcm.model.CreateNsRequest;
60 import org.onap.so.etsi.nfvo.ns.lcm.model.InlineResponse400;
61 import org.onap.so.etsi.nfvo.ns.lcm.model.InstantiateNsRequest;
62 import org.onap.so.etsi.nfvo.ns.lcm.model.NsInstancesNsInstance;
63 import org.onap.so.etsi.nfvo.ns.lcm.model.TerminateNsRequest;
64 import org.slf4j.Logger;
65 import org.springframework.beans.factory.annotation.Autowired;
66 import org.springframework.beans.factory.annotation.Value;
67 import org.springframework.stereotype.Service;
68 import com.google.common.collect.ImmutableSet;
69 import com.google.gson.Gson;
70
71 /**
72  * @author Waqas Ikram (waqas.ikram@est.tech)
73  *
74  */
75 @Service
76 public class JobExecutorService {
77
78     private static final Logger logger = getLogger(JobExecutorService.class);
79
80     private static final ImmutableSet<JobStatusEnum> JOB_FINISHED_STATES =
81             ImmutableSet.of(FINISHED, ERROR, FINISHED_WITH_ERROR);
82
83     private static final int SLEEP_TIME_IN_SECONDS = 5;
84
85     @Value("${so-etsi-ns-lcm-workflow-engine.requesttimeout.timeoutInSeconds:300}")
86     private int timeOutInSeconds;
87
88     private final DatabaseServiceProvider databaseServiceProvider;
89     private final WorkflowExecutorService workflowExecutorService;
90     private final WorkflowQueryService workflowQueryService;
91     private Gson gson;
92
93     @Autowired
94     public JobExecutorService(final DatabaseServiceProvider databaseServiceProvider,
95             final WorkflowExecutorService workflowExecutorService, final WorkflowQueryService workflowQueryService,
96             final GsonProvider gsonProvider) {
97         this.databaseServiceProvider = databaseServiceProvider;
98         this.workflowExecutorService = workflowExecutorService;
99         this.workflowQueryService = workflowQueryService;
100         gson = gsonProvider.getGson();
101     }
102
103     public NsInstancesNsInstance runCreateNsJob(final CreateNsRequest createNsRequest, final String globalCustomerId,
104             final String serviceType) {
105         logger.info("Starting 'Create NS' workflow job for request:\n{}", createNsRequest);
106         final NfvoJob newJob = new NfvoJob().startTime(LocalDateTime.now()).jobType("NS").jobAction(JobAction.CREATE)
107                 .resourceId(createNsRequest.getNsdId()).resourceName(createNsRequest.getNsName())
108                 .status(JobStatusEnum.STARTING).progress(5);
109         databaseServiceProvider.addJob(newJob);
110
111         logger.info("New job created in database :\n{}", newJob);
112
113         workflowExecutorService.executeWorkflow(newJob.getJobId(), CREATE_NS_WORKFLOW_NAME,
114                 getVariables(newJob.getJobId(), createNsRequest, globalCustomerId, serviceType));
115
116         final ImmutablePair<String, JobStatusEnum> immutablePair =
117                 waitForJobToFinish(newJob.getJobId(), JOB_FINISHED_STATES);
118
119         if (immutablePair.getRight() == null) {
120             final String message = "Failed to create NS for request: \n" + createNsRequest;
121             logger.error(message);
122             throw new NsRequestProcessingException(message);
123         }
124         final JobStatusEnum finalJobStatus = immutablePair.getRight();
125         final String processInstanceId = immutablePair.getLeft();
126
127         if (!FINISHED.equals(finalJobStatus)) {
128
129             final Optional<InlineResponse400> optional = workflowQueryService.getProblemDetails(processInstanceId);
130             if (optional.isPresent()) {
131                 final InlineResponse400 problemDetails = optional.get();
132                 final String message =
133                         "Failed to create NS for request: \n" + createNsRequest + " due to \n" + problemDetails;
134                 logger.error(message);
135                 throw new NsRequestProcessingException(message, problemDetails);
136             }
137
138             final String message = "Received unexpected Job Status: " + finalJobStatus
139                     + " Failed to Create NS for request: \n" + createNsRequest;
140             logger.error(message);
141             throw new NsRequestProcessingException(message);
142         }
143
144         logger.debug("Will query for CreateNsResponse using processInstanceId:{}", processInstanceId);
145         final Optional<NsInstancesNsInstance> optional = workflowQueryService.getCreateNsResponse(processInstanceId);
146         if (optional.isEmpty()) {
147             final String message =
148                     "Unable to find CreateNsReponse in Camunda History for process instance: " + processInstanceId;
149             logger.error(message);
150             throw new NsRequestProcessingException(message);
151         }
152         return optional.get();
153     }
154
155     public String runInstantiateNsJob(final String nsInstanceId, final InstantiateNsRequest instantiateNsRequest) {
156
157         final NfvoJob newJob = new NfvoJob().startTime(LocalDateTime.now()).jobType("NS").jobAction(INSTANTIATE)
158                 .resourceId(nsInstanceId).status(STARTING).progress(0);
159         databaseServiceProvider.addJob(newJob);
160         logger.info("New job created in database :\n{}", newJob);
161
162         final LocalDateTime currentDateTime = LocalDateTime.now();
163         final NsLcmOpOcc newNsLcmOpOcc = new NsLcmOpOcc().id(nsInstanceId).operation(NsLcmOpType.INSTANTIATE)
164                 .operationState(OperationStateEnum.PROCESSING).stateEnteredTime(currentDateTime)
165                 .startTime(currentDateTime).nfvoNsInst(getNfvoNsInst(nsInstanceId)).isAutoInnovation(false)
166                 .isCancelPending(false).operationParams(gson.toJson(instantiateNsRequest));
167         databaseServiceProvider.addNSLcmOpOcc(newNsLcmOpOcc);
168         logger.info("New NSLcmOpOcc created in database :\n{}", newNsLcmOpOcc);
169
170         workflowExecutorService.executeWorkflow(newJob.getJobId(), INSTANTIATE_NS_WORKFLOW_NAME,
171                 getVariables(nsInstanceId, newJob.getJobId(), newNsLcmOpOcc.getId(), instantiateNsRequest));
172
173         final ImmutableSet<JobStatusEnum> jobFinishedStates =
174                 ImmutableSet.of(FINISHED, ERROR, FINISHED_WITH_ERROR, IN_PROGRESS);
175         final ImmutablePair<String, JobStatusEnum> immutablePair =
176                 waitForJobToFinish(newJob.getJobId(), jobFinishedStates);
177
178         if (immutablePair.getRight() == null) {
179             final String message = "Failed to Instantiate NS for request: \n" + instantiateNsRequest;
180             logger.error(message);
181             throw new NsRequestProcessingException(message);
182         }
183
184         final JobStatusEnum finalJobStatus = immutablePair.getRight();
185
186         if (IN_PROGRESS.equals(finalJobStatus) || FINISHED.equals(finalJobStatus)) {
187             logger.info("Instantiation Job status: {}", finalJobStatus);
188
189
190             return newNsLcmOpOcc.getId();
191         }
192
193         final String message = "Received unexpected Job Status: " + finalJobStatus
194                 + " Failed to instantiate NS for request: \n" + instantiateNsRequest;
195         logger.error(message);
196         throw new NsRequestProcessingException(message);
197     }
198
199     public String runTerminateNsJob(final String nsInstanceId, final TerminateNsRequest terminateNsRequest) {
200         doInitialTerminateChecks(nsInstanceId, terminateNsRequest);
201
202         final NfvoJob nfvoJob = new NfvoJob().startTime(LocalDateTime.now()).jobType("NS").jobAction(TERMINATE)
203                 .resourceId(nsInstanceId).status(STARTING).progress(0);
204         databaseServiceProvider.addJob(nfvoJob);
205         logger.info("New job created in database :\n{}", nfvoJob);
206
207         final LocalDateTime currentDateTime = LocalDateTime.now();
208         final NsLcmOpOcc nsLcmOpOcc = new NsLcmOpOcc().id(nsInstanceId).operation(NsLcmOpType.TERMINATE)
209                 .operationState(OperationStateEnum.PROCESSING).stateEnteredTime(currentDateTime)
210                 .startTime(currentDateTime).nfvoNsInst(getNfvoNsInst(nsInstanceId)).isAutoInnovation(false)
211                 .isCancelPending(false).operationParams(gson.toJson(terminateNsRequest));
212         databaseServiceProvider.addNSLcmOpOcc(nsLcmOpOcc);
213         logger.info("New NSLcmOpOcc created in database :\n{}", nsLcmOpOcc);
214
215         workflowExecutorService.executeWorkflow(nfvoJob.getJobId(), TERMINATE_NS_WORKFLOW_NAME,
216                 getVariables(nsInstanceId, nfvoJob.getJobId(), nsLcmOpOcc.getId(), terminateNsRequest));
217
218         final ImmutableSet<JobStatusEnum> jobFinishedStates =
219                 ImmutableSet.of(FINISHED, ERROR, FINISHED_WITH_ERROR, IN_PROGRESS);
220         final ImmutablePair<String, JobStatusEnum> immutablePair =
221                 waitForJobToFinish(nfvoJob.getJobId(), jobFinishedStates);
222
223         if (immutablePair.getRight() == null) {
224             final String message =
225                     "Failed to Terminate NS with id: " + nsInstanceId + " for request: \n" + terminateNsRequest;
226             logger.error(message);
227             throw new NsRequestProcessingException(message);
228         }
229
230         final JobStatusEnum finalJobStatus = immutablePair.getRight();
231
232         if (IN_PROGRESS.equals(finalJobStatus) || FINISHED.equals(finalJobStatus)) {
233             logger.info("Termination Job status: {}", finalJobStatus);
234             return nsLcmOpOcc.getId();
235         }
236
237         final String message = "Received unexpected Job Status: " + finalJobStatus + " Failed to Terminate NS with id: "
238                 + nsInstanceId + " for request: \n" + terminateNsRequest;
239         logger.error(message);
240         throw new NsRequestProcessingException(message);
241     }
242
243     private void doInitialTerminateChecks(final String nsInstanceId, final TerminateNsRequest terminateNsRequest) {
244         if (isNotImmediateTerminateRequest(terminateNsRequest)) {
245             final String message = "TerminateNsRequest received with terminateTime: "
246                     + terminateNsRequest.getTerminationTime()
247                     + "\nOnly immediate Terminate requests are currently supported \n(i.e., terminateTime field must not be set).";
248             logger.error(message);
249             throw new NsRequestProcessingException(message);
250         }
251
252         final NfvoNsInst nfvoNsInst = getNfvoNsInst(nsInstanceId);
253         if (isNotInstantiated(nfvoNsInst)) {
254             final String message = "TerminateNsRequest received: " + terminateNsRequest + " for nsInstanceId: "
255                     + nsInstanceId + "\nUnable to terminate.  NS Instance is already in NOT_INSTANTIATED state."
256                     + "\nThis method can only be used with an NS instance in the INSTANTIATED state.";
257             logger.error(message);
258             throw new NsRequestProcessingException(message);
259         }
260     }
261
262     private boolean isNotImmediateTerminateRequest(final TerminateNsRequest terminateNsRequest) {
263         return terminateNsRequest.getTerminationTime() != null;
264     }
265
266     private boolean isNotInstantiated(final NfvoNsInst nfvoNsInst) {
267         return State.NOT_INSTANTIATED.equals(nfvoNsInst.getStatus());
268     }
269
270     private NfvoNsInst getNfvoNsInst(final String nsInstId) {
271         logger.info("Getting NfvoNsInst with nsInstId: {}", nsInstId);
272         final Optional<NfvoNsInst> optionalNfvoNsInst = databaseServiceProvider.getNfvoNsInst(nsInstId);
273
274         if (optionalNfvoNsInst.isEmpty()) {
275             final String message = "No matching NS Instance for id: " + nsInstId + " found in database.";
276             throw new NsRequestProcessingException(message);
277         }
278
279         return optionalNfvoNsInst.get();
280     }
281
282     private ImmutablePair<String, JobStatusEnum> waitForJobToFinish(final String jobId,
283             final ImmutableSet<JobStatusEnum> jobFinishedStates) {
284         try {
285             final long startTimeInMillis = System.currentTimeMillis();
286             final long timeOutTime = startTimeInMillis + TimeUnit.SECONDS.toMillis(timeOutInSeconds);
287
288             logger.info("Will wait till {} for {} job to finish", Instant.ofEpochMilli(timeOutTime).toString(), jobId);
289             JobStatusEnum currentJobStatus = null;
290             while (timeOutTime > System.currentTimeMillis()) {
291
292                 final Optional<NfvoJob> optional = databaseServiceProvider.getJob(jobId);
293
294                 if (optional.isEmpty()) {
295                     logger.error("Unable to find Job using jobId: {}", jobId);
296                     return ImmutablePair.nullPair();
297                 }
298
299                 final NfvoJob nfvoJob = optional.get();
300                 currentJobStatus = nfvoJob.getStatus();
301                 logger.debug("Received job status response: \n {}", nfvoJob);
302                 if (jobFinishedStates.contains(nfvoJob.getStatus())) {
303                     logger.info("Job finished \n {}", currentJobStatus);
304                     return ImmutablePair.of(nfvoJob.getProcessInstanceId(), currentJobStatus);
305                 }
306
307                 logger.info("Haven't received one of finish state {} yet, will try again in {} seconds",
308                         jobFinishedStates, SLEEP_TIME_IN_SECONDS);
309                 TimeUnit.SECONDS.sleep(SLEEP_TIME_IN_SECONDS);
310
311             }
312             logger.warn("Timeout current job status: {}", currentJobStatus);
313             return ImmutablePair.nullPair();
314         } catch (final InterruptedException interruptedException) {
315             Thread.currentThread().interrupt();
316             logger.error("Sleep was interrupted", interruptedException);
317             return ImmutablePair.nullPair();
318         }
319     }
320
321     private Map<String, Object> getVariables(final String jobId, final CreateNsRequest createNsRequest,
322             final String globalCustomerId, final String serviceType) {
323         final Map<String, Object> variables = new HashMap<>();
324         variables.put(JOB_ID_PARAM_NAME, jobId);
325         variables.put(CREATE_NS_REQUEST_PARAM_NAME, createNsRequest);
326         variables.put(GLOBAL_CUSTOMER_ID_PARAM_NAME, globalCustomerId);
327         variables.put(SERVICE_TYPE_PARAM_NAME, serviceType);
328         return variables;
329     }
330
331     private Map<String, Object> getVariables(final String nsInstanceId, final String jobId, final String occId,
332             final InstantiateNsRequest instantiateNsRequest) {
333         final Map<String, Object> variables = new HashMap<>();
334         variables.put(NS_INSTANCE_ID_PARAM_NAME, nsInstanceId);
335         variables.put(JOB_ID_PARAM_NAME, jobId);
336         variables.put(OCC_ID_PARAM_NAME, occId);
337         variables.put(INSTANTIATE_NS_REQUEST_PARAM_NAME, instantiateNsRequest);
338         return variables;
339     }
340
341     private Map<String, Object> getVariables(final String nsInstanceId, final String jobId, final String occId,
342             final TerminateNsRequest terminateNsRequest) {
343         final Map<String, Object> variables = new HashMap<>();
344         variables.put(NS_INSTANCE_ID_PARAM_NAME, nsInstanceId);
345         variables.put(JOB_ID_PARAM_NAME, jobId);
346         variables.put(OCC_ID_PARAM_NAME, occId);
347         variables.put(TERMINATE_NS_REQUEST_PARAM_NAME, terminateNsRequest);
348         return variables;
349     }
350 }