67f3a86664d9662212dbaa1ad060338bba545c0c
[externalapi/nbi.git] / src / main / java / org / onap / nbi / apis / serviceorder / workflow / SOTaskProcessor.java
1 /**
2  * Copyright (c) 2018 Orange
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5  * the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11  * specific language governing permissions and limitations under the License.
12  */
13 package org.onap.nbi.apis.serviceorder.workflow;
14
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.Date;
19 import java.util.List;
20 import java.util.Optional;
21 import org.onap.nbi.apis.serviceorder.model.ActionType;
22 import org.onap.nbi.apis.serviceorder.model.ServiceOrder;
23 import org.onap.nbi.apis.serviceorder.model.ServiceOrderItem;
24 import org.onap.nbi.apis.serviceorder.model.StateType;
25 import org.onap.nbi.apis.serviceorder.model.consumer.CreateE2EServiceInstanceResponse;
26 import org.onap.nbi.apis.serviceorder.model.consumer.CreateServiceInstanceResponse;
27 import org.onap.nbi.apis.serviceorder.model.orchestrator.ExecutionTask;
28 import org.onap.nbi.apis.serviceorder.model.orchestrator.ServiceOrderInfo;
29 import org.onap.nbi.apis.serviceorder.repositories.ExecutionTaskRepository;
30 import org.onap.nbi.apis.serviceorder.service.ServiceOrderService;
31 import org.onap.nbi.apis.serviceorder.utils.E2EServiceUtils;
32 import org.onap.nbi.apis.serviceorder.utils.JsonEntityConverter;
33 import org.onap.nbi.exceptions.TechnicalException;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.springframework.beans.factory.annotation.Autowired;
37 import org.springframework.beans.factory.annotation.Value;
38 import org.springframework.http.HttpStatus;
39 import org.springframework.http.ResponseEntity;
40 import org.springframework.stereotype.Service;
41 import org.springframework.util.CollectionUtils;
42
43 @Service
44 public class SOTaskProcessor {
45
46
47     @Autowired
48     private ServiceOrderService serviceOrderService;
49
50     @Autowired
51     private ExecutionTaskRepository executionTaskRepository;
52
53     @Autowired
54     private PostSoProcessor postSoProcessor;
55
56     @Autowired
57     private SOGetStatusManager sOGetStatusManager;
58
59     @Value("${scheduler.pollingDurationInMins}")
60     private float pollingDurationInMins;
61
62     private static final Logger LOGGER = LoggerFactory.getLogger(SOTaskProcessor.class);
63
64     /**
65      * Run the ServiceOrchestrator processing for a serviceOrderItem which with any sub relations
66      */
67     public void processOrderItem(ExecutionTask executionTask) {
68
69         executionTask.setLastAttemptDate(new Date());
70         executionTaskRepository.save(executionTask);
71
72         ServiceOrderInfo serviceOrderInfo = getServiceOrderInfo(executionTask);
73
74         Optional<ServiceOrder> optionalServiceOrder = serviceOrderService
75             .findServiceOrderById(serviceOrderInfo.getServiceOrderId());
76         if (!optionalServiceOrder.isPresent()) {
77             throw new TechnicalException(
78                 "Unable to retrieve service order for id " + serviceOrderInfo.getServiceOrderId());
79         }
80         ServiceOrder serviceOrder = optionalServiceOrder.get();
81         ServiceOrderItem serviceOrderItem = getServiceOrderItem(executionTask, serviceOrder);
82         boolean e2eService = E2EServiceUtils
83             .isE2EService(serviceOrderInfo.getServiceOrderItemInfos().get(serviceOrderItem.getId()));
84
85         if (shouldPostSo(serviceOrderItem)) {
86             if (e2eService) {
87                 ResponseEntity<CreateE2EServiceInstanceResponse> response = postSoProcessor
88                     .postE2EServiceOrderItem(serviceOrderInfo,
89                         serviceOrderItem, serviceOrder);
90                 updateE2EServiceOrderItem(response, serviceOrderItem, serviceOrder);
91             } else {
92
93                 ResponseEntity<CreateServiceInstanceResponse> response = postSoProcessor
94                     .postServiceOrderItem(serviceOrderInfo, serviceOrderItem);
95                 updateServiceOrderItem(response, serviceOrderItem, serviceOrder);
96             }
97         }
98
99         boolean shouldStopPolling = shouldStopPolling(executionTask);
100         if (!shouldStopPolling && StateType.FAILED != serviceOrderItem.getState()
101             ) {
102             // TODO lancer en asynchrone
103             sOGetStatusManager.pollRequestStatus(serviceOrder, serviceOrderItem, e2eService);
104
105             if (serviceOrderItem.getState().equals(StateType.COMPLETED)) {
106                 updateSuccessTask(executionTask);
107             }
108         } else if (shouldStopPolling && StateType.FAILED != serviceOrderItem.getState()) {
109             serviceOrderService.addOrderItemMessage(serviceOrder, serviceOrderItem, "504");
110             updateFailedTask(executionTask, serviceOrder);
111         } else {
112             updateFailedTask(executionTask, serviceOrder);
113         }
114
115         updateServiceOrder(serviceOrder);
116     }
117
118     private boolean shouldPostSo(ServiceOrderItem serviceOrderItem) {
119         return StateType.ACKNOWLEDGED == serviceOrderItem.getState()
120             || StateType.INPROGRESS_MODIFY_ITEM_TO_CREATE == serviceOrderItem.getState();
121     }
122
123     private ServiceOrderItem getServiceOrderItem(ExecutionTask executionTask, ServiceOrder serviceOrder) {
124         for (ServiceOrderItem item : serviceOrder.getOrderItem()) {
125             if (item.getId().equals(executionTask.getOrderItemId())) {
126                 return item;
127             }
128         }
129         throw new TechnicalException(
130             "Unable to retrieve serviceOrderItem for executionTaskId " + executionTask.getInternalId());
131     }
132
133     private ServiceOrderInfo getServiceOrderInfo(ExecutionTask executionTask) {
134         String serviceOrderInfoJson = executionTask.getServiceOrderInfoJson();
135         ServiceOrderInfo serviceOrderInfo = null;
136         try {
137             serviceOrderInfo =
138                 JsonEntityConverter.convertJsonToServiceOrderInfo(serviceOrderInfoJson);
139         } catch (IOException e) {
140             LOGGER
141                 .error("Unable to read ServiceOrderInfo Json for executionTaskId " + executionTask.getInternalId(), e);
142             throw new TechnicalException(
143                 "Unable to read ServiceOrderInfo Json for executionTaskId " + executionTask.getInternalId());
144         }
145         return serviceOrderInfo;
146     }
147
148
149     private void updateServiceOrder(ServiceOrder serviceOrder) {
150         boolean atLeastOneCompleted = false;
151         boolean atLeastOneNotFinished = false;
152         boolean atLeastOneFailed = false;
153
154         for (ServiceOrderItem serviceOrderItem : serviceOrder.getOrderItem()) {
155             switch (serviceOrderItem.getState()) {
156                 case COMPLETED:
157                     atLeastOneCompleted = true;
158                     break;
159                 case INPROGRESS:
160                 case ACKNOWLEDGED:
161                     atLeastOneNotFinished = true;
162                     break;
163                 case FAILED:
164                     atLeastOneFailed = true;
165                     break;
166                 default:
167                     break;
168
169             }
170         }
171         if (atLeastOneNotFinished) {
172             serviceOrderService.updateOrderState(serviceOrder, StateType.INPROGRESS);
173         } else {
174             StateType finalState;
175             if (atLeastOneFailed) {
176                 if (!atLeastOneCompleted) {
177                     finalState = StateType.FAILED;
178                 } else {
179                     finalState = StateType.PARTIAL;
180                 }
181             } else {
182                 finalState = StateType.COMPLETED;
183             }
184             serviceOrderService.updateOrderState(serviceOrder, finalState);
185         }
186     }
187
188
189     /**
190      * Update ServiceOrderItem with SO response by using serviceOrderRepository with the serviceOrderId
191      */
192     private void updateServiceOrderItem(ResponseEntity<CreateServiceInstanceResponse> response,
193         ServiceOrderItem orderItem, ServiceOrder serviceOrder) {
194
195         if (response == null || !response.getStatusCode().is2xxSuccessful()) {
196             LOGGER.warn("response ko for serviceOrderItem.id=" + orderItem.getId());
197             serviceOrderService.updateOrderItemState(serviceOrder, orderItem, StateType.FAILED);
198             buildOrderMessageIfNeeded(orderItem, serviceOrder, response);
199         } else {
200             CreateServiceInstanceResponse createServiceInstanceResponse = response.getBody();
201             if (createServiceInstanceResponse != null && !orderItem.getState().equals(StateType.FAILED)) {
202                 orderItem.getService().setId(createServiceInstanceResponse.getRequestReferences().getInstanceId());
203                 orderItem.setRequestId(createServiceInstanceResponse.getRequestReferences().getRequestId());
204             }
205
206             if (!response.getStatusCode().is2xxSuccessful() || response.getBody() == null
207                 || response.getBody().getRequestReferences() == null) {
208                 serviceOrderService.updateOrderItemState(serviceOrder, orderItem, StateType.FAILED);
209                 LOGGER
210                     .warn("order item {} failed , status {} , response {}", orderItem.getId(), response.getStatusCode(),
211                         response.getBody());
212             } else {
213                 updateOrderItemToInProgress(serviceOrder, orderItem);
214             }
215         }
216     }
217
218
219     private void updateOrderItemToInProgress(ServiceOrder serviceOrder, ServiceOrderItem serviceOrderItem) {
220         if (serviceOrderItem.getAction() != ActionType.MODIFY) {
221             serviceOrderService.updateOrderItemState(serviceOrder, serviceOrderItem, StateType.INPROGRESS);
222         } else {
223             if (StateType.ACKNOWLEDGED == serviceOrderItem.getState()) {
224                 serviceOrderService.updateOrderItemState(serviceOrder, serviceOrderItem,
225                     StateType.INPROGRESS_MODIFY_REQUEST_DELETE_SEND);
226             } else {
227                 serviceOrderService.updateOrderItemState(serviceOrder, serviceOrderItem,
228                     StateType.INPROGRESS_MODIFY_REQUEST_CREATE_SEND);
229             }
230         }
231     }
232
233
234     private void buildOrderMessageIfNeeded(ServiceOrderItem serviceOrderItem, ServiceOrder serviceOrder,
235         ResponseEntity<?> response) {
236         if (response != null) {
237             if (response.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) {
238                 serviceOrderService.addOrderMessage(serviceOrder, "502");
239             } else if (response.getStatusCode() == HttpStatus.BAD_REQUEST) {
240                 ResponseEntity<?> messageError = response;
241                 if (messageError.getBody().toString().toLowerCase().contains("serviceinstance already exists")) {
242                     serviceOrderService.addOrderItemMessage(serviceOrder, serviceOrderItem, "105");
243                 } else {
244                     serviceOrderService.addOrderItemMessageRequestSo(serviceOrder, serviceOrderItem,
245                         messageError.getBody().toString());
246                 }
247             }
248         }
249     }
250
251     /**
252      * Update E2EServiceOrderItem with SO response by using serviceOrderRepository with the serviceOrderId
253      */
254     private void updateE2EServiceOrderItem(ResponseEntity<CreateE2EServiceInstanceResponse> response,
255         ServiceOrderItem orderItem, ServiceOrder serviceOrder) {
256
257         if (response == null || !response.getStatusCode().is2xxSuccessful()) {
258             LOGGER.warn("response ko for serviceOrderItem.id=" + orderItem.getId());
259             serviceOrderService.updateOrderItemState(serviceOrder, orderItem, StateType.FAILED);
260         } else {
261             CreateE2EServiceInstanceResponse createE2EServiceInstanceResponse = response.getBody();
262             if (createE2EServiceInstanceResponse != null && !orderItem.getState().equals(StateType.FAILED)) {
263                 orderItem.getService().setId(createE2EServiceInstanceResponse.getService().getServiceId());
264                 orderItem.setRequestId(createE2EServiceInstanceResponse.getService().getOperationId());
265             }
266
267             if (!response.getStatusCode().is2xxSuccessful() || response.getBody() == null
268                 || response.getBody().getService().getOperationId() == null
269                 || response.getBody().getService().getServiceId() == null) {
270                 serviceOrderService.updateOrderItemState(serviceOrder, orderItem, StateType.FAILED);
271                 LOGGER
272                     .warn("order item {} failed , status {} , response {}", orderItem.getId(), response.getStatusCode(),
273                         response.getBody());
274             } else {
275                 serviceOrderService.updateOrderItemState(serviceOrder, orderItem, StateType.INPROGRESS);
276             }
277         }
278     }
279
280     /**
281      * Update an executionTask in database when it's process with a success
282      */
283     private void updateSuccessTask(ExecutionTask executionTask) {
284         executionTaskRepository.deleteById(executionTask.getInternalId());
285         executionTaskRepository.updateReliedTaskAfterDelete(executionTask.getInternalId());
286
287     }
288
289     /**
290      * @param executionTask
291      * @param serviceOrder
292      */
293     private void updateFailedTask(ExecutionTask executionTask, ServiceOrder serviceOrder) {
294         List<ExecutionTask> executionTasksToDelete = findExecutionTasksRecursively(executionTask);
295         for (ExecutionTask taskId : executionTasksToDelete) {
296             executionTaskRepository.delete(taskId);
297             LOGGER.warn("task {} with orderitem id {} deleted cause orderitem id {} failed ", taskId.getInternalId(),
298                 taskId.getOrderItemId(), executionTask.getOrderItemId());
299         }
300         for (ServiceOrderItem item : serviceOrder.getOrderItem()) {
301             for (ExecutionTask taskToDelete : executionTasksToDelete) {
302                 if (taskToDelete.getOrderItemId().equals(item.getId())) {
303                     serviceOrderService.updateOrderItemState(serviceOrder, item, StateType.FAILED);
304                     LOGGER.warn("task {} with orderitem id {} failed cause orderitem id {} failed ",
305                         taskToDelete.getInternalId(), taskToDelete.getOrderItemId(), executionTask.getOrderItemId());
306
307                 }
308             }
309         }
310     }
311
312     /**
313      * @param executionTask
314      * @return
315      */
316     private List<ExecutionTask> findExecutionTasksRecursively(ExecutionTask executionTask) {
317
318         List<ExecutionTask> executionTasks = new ArrayList<>();
319
320         List<ExecutionTask> tasksReliedToAnOrderItemId =
321             executionTaskRepository.findTasksReliedToAnOrderItemId(executionTask.getInternalId());
322
323         if (CollectionUtils.isEmpty(tasksReliedToAnOrderItemId)) {
324             return Arrays.asList(executionTask);
325         } else {
326             for (ExecutionTask task : tasksReliedToAnOrderItemId) {
327                 executionTasks.addAll(findExecutionTasksRecursively(task));
328             }
329         }
330         executionTasks.add(executionTask);
331         return executionTasks;
332     }
333
334     private boolean shouldStopPolling(ExecutionTask executionTask) {
335         long createTimeinMillis = executionTask.getCreateDate().getTime();
336         long lastAttemptTimeInMillis = executionTask.getLastAttemptDate().getTime();
337         long differenceInMillis = lastAttemptTimeInMillis - createTimeinMillis;
338         float pollingDurationInMillis = pollingDurationInMins * 60000;
339         LOGGER.debug("Task {} with orderitem id {}: Task create date: {} Task last attempt date: {}",
340             executionTask.getInternalId(), executionTask.getOrderItemId(), createTimeinMillis,
341             lastAttemptTimeInMillis);
342         LOGGER.debug("Difference {} and Polling Duration {}",
343             differenceInMillis, pollingDurationInMins);
344         return (differenceInMillis > pollingDurationInMillis);
345     }
346 }