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