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