def4fe0975244fd26a149bba092c39640ce3f1a2
[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.Map;
21 import org.onap.nbi.apis.serviceorder.SoClient;
22 import org.onap.nbi.apis.serviceorder.model.ServiceCharacteristic;
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.CloudConfiguration;
27 import org.onap.nbi.apis.serviceorder.model.consumer.CreateServiceInstanceResponse;
28 import org.onap.nbi.apis.serviceorder.model.consumer.GetRequestStatusResponse;
29 import org.onap.nbi.apis.serviceorder.model.consumer.MSOPayload;
30 import org.onap.nbi.apis.serviceorder.model.consumer.ModelInfo;
31 import org.onap.nbi.apis.serviceorder.model.consumer.OwningEntity;
32 import org.onap.nbi.apis.serviceorder.model.consumer.Project;
33 import org.onap.nbi.apis.serviceorder.model.consumer.RequestDetails;
34 import org.onap.nbi.apis.serviceorder.model.consumer.RequestInfo;
35 import org.onap.nbi.apis.serviceorder.model.consumer.RequestParameters;
36 import org.onap.nbi.apis.serviceorder.model.consumer.RequestState;
37 import org.onap.nbi.apis.serviceorder.model.consumer.SubscriberInfo;
38 import org.onap.nbi.apis.serviceorder.model.consumer.UserParams;
39 import org.onap.nbi.apis.serviceorder.model.orchestrator.ExecutionTask;
40 import org.onap.nbi.apis.serviceorder.model.orchestrator.ServiceOrderInfo;
41 import org.onap.nbi.apis.serviceorder.repositories.ExecutionTaskRepository;
42 import org.onap.nbi.apis.serviceorder.service.ServiceOrderService;
43 import org.onap.nbi.apis.serviceorder.utils.JsonEntityConverter;
44 import org.onap.nbi.exceptions.TechnicalException;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47 import org.springframework.beans.factory.annotation.Autowired;
48 import org.springframework.beans.factory.annotation.Value;
49 import org.springframework.http.ResponseEntity;
50 import org.springframework.stereotype.Service;
51 import org.springframework.util.CollectionUtils;
52
53 @Service
54 public class SOTaskProcessor {
55
56     @Value("${nbi.callForVNF}")
57     private boolean enableCallForVNF;
58
59     @Value("${onap.lcpCloudRegionId}")
60     private String lcpCloudRegionId;
61
62     @Value("${onap.tenantId}")
63     private String tenantId;
64
65     @Value("${so.owning.entity.id}")
66     private String soOwningEntityId;
67
68     @Value("${so.owning.entity.name}")
69     private String soOwningEntityName;
70
71     @Value("${so.project.name}")
72     private String soProjectName;
73
74
75     @Autowired
76     private ServiceOrderService serviceOrderService;
77
78     @Autowired
79     private ExecutionTaskRepository executionTaskRepository;
80
81     @Autowired
82     private SoClient soClient;
83
84
85     private static final Logger LOGGER = LoggerFactory.getLogger(SOTaskProcessor.class);
86
87     /**
88      * Run the ServiceOrchestrator processing for a serviceOrderItem which with any sub relations
89      *
90      * @throws InterruptedException
91      */
92     public void processOrderItem(ExecutionTask executionTask) throws InterruptedException {
93
94         ServiceOrderInfo serviceOrderInfo = getServiceOrderInfo(executionTask);
95
96         ServiceOrder serviceOrder = serviceOrderService.findServiceOrderById(serviceOrderInfo.getServiceOrderId());
97         ServiceOrderItem serviceOrderItem = getServiceOrderItem(executionTask, serviceOrder);
98
99         if (StateType.ACKNOWLEDGED == serviceOrderItem.getState()) {
100
101             ResponseEntity<CreateServiceInstanceResponse> response = postServiceOrderItem(serviceOrderInfo,
102                 serviceOrderItem);
103             updateServiceOrderItem(response, serviceOrderItem,serviceOrder);
104         }
105
106         if (executionTask.getNbRetries() > 0 && StateType.FAILED != serviceOrderItem.getState()
107             ) {
108             // TODO lancer en asynchrone
109             pollSoRequestStatus(serviceOrder,serviceOrderItem);
110             if (serviceOrderItem.getState().equals(StateType.COMPLETED)) {
111                 updateSuccessTask(executionTask);
112             } else {
113                 int nbRetries = executionTask.getNbRetries();
114                 executionTask.setNbRetries(--nbRetries);
115                 executionTask.setLastAttemptDate(new Date());
116                 executionTaskRepository.save(executionTask);
117             }
118         } else {
119             updateFailedTask(executionTask, serviceOrder);
120         }
121
122         updateServiceOrder(serviceOrder);
123     }
124
125     private ResponseEntity<CreateServiceInstanceResponse> postServiceOrderItem(ServiceOrderInfo serviceOrderInfo,
126         ServiceOrderItem serviceOrderItem) {
127         ResponseEntity<CreateServiceInstanceResponse> response = null;
128         try {
129             response = postSORequest(serviceOrderItem, serviceOrderInfo);
130         } catch (NullPointerException e) {
131             LOGGER.error("Unable to create service instance for serviceOrderItem.id=" + serviceOrderItem.getId(), e);
132             response = null;
133         }
134         return response;
135     }
136
137     private ServiceOrderItem getServiceOrderItem(ExecutionTask executionTask, ServiceOrder serviceOrder) {
138         for (ServiceOrderItem item : serviceOrder.getOrderItem()) {
139             if (item.getId().equals(executionTask.getOrderItemId())) {
140                 return item;
141             }
142         }
143         throw new TechnicalException(
144             "Unable to retrieve serviceOrderItem forexecutionTaskId " + executionTask.getInternalId());
145     }
146
147     private ServiceOrderInfo getServiceOrderInfo(ExecutionTask executionTask) {
148         String serviceOrderInfoJson = executionTask.getServiceOrderInfoJson();
149         ServiceOrderInfo serviceOrderInfo = null;
150         try {
151             serviceOrderInfo =
152                 JsonEntityConverter.convertJsonToServiceOrderInfo(serviceOrderInfoJson);
153         } catch (IOException e) {
154             LOGGER
155                 .error("Unable to read ServiceOrderInfo Json for executionTaskId " + executionTask.getInternalId(), e);
156             throw new TechnicalException(
157                 "Unable to read ServiceOrderInfo Json for executionTaskId " + executionTask.getInternalId());
158         }
159         return serviceOrderInfo;
160     }
161
162     private ResponseEntity<CreateServiceInstanceResponse> postSORequest(ServiceOrderItem serviceOrderItem,
163         ServiceOrderInfo serviceOrderInfo) {
164         RequestDetails requestDetails = buildSoRequest(serviceOrderItem,
165             serviceOrderInfo.getServiceOrderItemInfos().get(serviceOrderItem.getId()).getCatalogResponse(),
166             serviceOrderInfo.getSubscriberInfo());
167         MSOPayload msoPayload = new MSOPayload(requestDetails);
168         ResponseEntity<CreateServiceInstanceResponse> response = null;
169
170         switch (serviceOrderItem.getAction()) {
171             case ADD:
172                 response = soClient.callCreateServiceInstance(msoPayload);
173                 break;
174             case DELETE:
175                 response = soClient.callDeleteServiceInstance(msoPayload, serviceOrderItem.getService().getId());
176                 break;
177             default:
178                 break;
179         }
180         return response;
181     }
182
183     private void updateServiceOrder(ServiceOrder serviceOrder) {
184         boolean atLeastOneCompleted = false;
185         boolean atLeastOneNotFinished = false;
186         boolean atLeastOneFailed = false;
187
188         for (ServiceOrderItem serviceOrderItem : serviceOrder.getOrderItem()) {
189             switch (serviceOrderItem.getState()) {
190                 case COMPLETED:
191                     atLeastOneCompleted = true;
192                     break;
193                 case INPROGRESS:
194                 case ACKNOWLEDGED:
195                     atLeastOneNotFinished = true;
196                     break;
197                 case FAILED:
198                     atLeastOneFailed = true;
199                     break;
200                 default:
201                     break;
202
203             }
204         }
205
206         if (atLeastOneNotFinished) {
207             serviceOrderService.updateOrderState(serviceOrder,StateType.INPROGRESS);
208         } else {
209             StateType finalState;
210             if (atLeastOneFailed) {
211                 if (!atLeastOneCompleted) {
212                     finalState=StateType.FAILED;
213                 } else {
214                     finalState=StateType.PARTIAL;
215                 }
216             } else {
217                 finalState=StateType.COMPLETED;
218             }
219             serviceOrderService.updateOrderFinalState(serviceOrder,finalState);
220         }
221     }
222
223
224     /**
225      * * @param orderItem
226      */
227     private void pollSoRequestStatus(ServiceOrder serviceOrder,
228         ServiceOrderItem orderItem) throws InterruptedException {
229         boolean stopPolling = false;
230         String requestId = orderItem.getRequestId();
231         GetRequestStatusResponse response = null;
232         int nbRetries = 0;
233
234         while (!stopPolling) {
235             response = soClient.callGetRequestStatus(requestId);
236             if (response != null) {
237                 if (response.getRequest().getRequestStatus().getPercentProgress() != 100) {
238                     nbRetries++;
239                     serviceOrderService.updateOrderItemState(serviceOrder,orderItem,StateType.INPROGRESS);
240                     Thread.sleep(1000);
241                     LOGGER.debug("orderitem id {} still in progress from so",orderItem.getId());
242                 } else if (RequestState.COMPLETE != response.getRequest().getRequestStatus().getRequestState()) {
243                     serviceOrderService.updateOrderItemState(serviceOrder,orderItem,StateType.FAILED);
244                     stopPolling = true;
245                     LOGGER.debug("orderitem id {} failed, response from request status {}",orderItem.getId(),response.getRequest().getRequestStatus().getRequestState());
246                 } else {
247                     serviceOrderService.updateOrderItemState(serviceOrder,orderItem,StateType.COMPLETED);
248                     stopPolling = true;
249                     LOGGER.debug("orderitem id {} completed");
250                 }
251             } else {
252                 serviceOrderService.updateOrderItemState(serviceOrder,orderItem,StateType.INPROGRESS);
253                 stopPolling = true;
254                 LOGGER.debug("orderitem id {} still in progress from so",orderItem.getId());
255             }
256             if (nbRetries == 3) {
257                 stopPolling = true;
258                 LOGGER.debug("orderitem id {} stop polling from getrequeststatus, 3 retries done",orderItem.getId());
259
260             }
261         }
262     }
263
264     /**
265      * Build SO CREATE request from the ServiceOrder and catalog informations from SDC
266      *
267      * @param orderItem
268      * @param sdcInfos
269      * @param subscriberInfo
270      * @return
271      */
272     private RequestDetails buildSoRequest(ServiceOrderItem orderItem, Map<String, Object> sdcInfos,
273         SubscriberInfo subscriberInfo) {
274         RequestDetails requestDetails = new RequestDetails();
275
276         requestDetails.setSubscriberInfo(subscriberInfo);
277
278         ModelInfo modelInfo = new ModelInfo();
279         modelInfo.setModelType("service");
280         modelInfo.setModelInvariantId((String) sdcInfos.get("invariantUUID"));
281         modelInfo.setModelNameVersionId(orderItem.getService().getServiceSpecification().getId());
282         modelInfo.setModelVersionId(orderItem.getService().getServiceSpecification().getId());
283         modelInfo.setModelName((String) sdcInfos.get("name"));
284         modelInfo.setModelVersion((String) sdcInfos.get("version"));
285         requestDetails.setModelInfo(modelInfo);
286
287         RequestInfo requestInfo = new RequestInfo();
288         requestInfo.setInstanceName(orderItem.getService().getName());
289         requestInfo.setSource("VID");
290         requestInfo.setSuppressRollback(false);
291         requestInfo.setRequestorId("NBI");
292         requestDetails.setRequestInfo(requestInfo);
293
294         RequestParameters requestParameters = new RequestParameters();
295         requestParameters.setSubscriptionServiceType((String) sdcInfos.get("name"));
296         requestParameters.setUserParams(
297             retrieveUserParamsFromServiceCharacteristics(orderItem.getService().getServiceCharacteristic()));
298         requestParameters.setaLaCarte(true);
299         requestDetails.setRequestParameters(requestParameters);
300
301         CloudConfiguration cloudConfiguration = new CloudConfiguration(lcpCloudRegionId, tenantId);
302         requestDetails.setCloudConfiguration(cloudConfiguration);
303
304         OwningEntity owningEntity = new OwningEntity();
305         owningEntity.setOwningEntityId(soOwningEntityId);
306         owningEntity.setOwningEntityName(soOwningEntityName);
307         requestDetails.setOwningEntity(owningEntity);
308
309         Project project = new Project();
310         project.setProjectName(soProjectName);
311
312         requestDetails.setProject(project);
313
314         return requestDetails;
315     }
316
317     /**
318      * Build a list of UserParams for the SO request by browsing a list of ServiceCharacteristics from SDC
319      */
320     private List<UserParams> retrieveUserParamsFromServiceCharacteristics(List<ServiceCharacteristic> characteristics) {
321         List<UserParams> userParams = new ArrayList<>();
322
323         if (!CollectionUtils.isEmpty(characteristics)) {
324             for (ServiceCharacteristic characteristic : characteristics) {
325                 UserParams userParam = new UserParams(characteristic.getName(),
326                     characteristic.getValue().getServiceCharacteristicValue());
327                 userParams.add(userParam);
328             }
329         }
330
331         return userParams;
332     }
333
334
335     /**
336      * Update ServiceOrderItem with SO response by using serviceOrderRepository with the serviceOrderId
337      *  @param response
338      * @param orderItem
339      * @param serviceOrder
340      */
341     private void updateServiceOrderItem(ResponseEntity<CreateServiceInstanceResponse> response,
342         ServiceOrderItem orderItem, ServiceOrder serviceOrder) {
343
344         if (response == null) {
345             LOGGER.warn("response=null for serviceOrderItem.id=" + orderItem.getId());
346             serviceOrderService.updateOrderItemState(serviceOrder,orderItem,StateType.FAILED);
347         }
348         else {
349             CreateServiceInstanceResponse createServiceInstanceResponse = response.getBody();
350             if (createServiceInstanceResponse != null && !orderItem.getState().equals(StateType.FAILED)) {
351                 orderItem.getService().setId(createServiceInstanceResponse.getRequestReferences().getInstanceId());
352                 orderItem.setRequestId(createServiceInstanceResponse.getRequestReferences().getRequestId());
353             }
354
355             if (!response.getStatusCode().is2xxSuccessful() || response.getBody() == null
356                 || response.getBody().getRequestReferences() == null) {
357                 serviceOrderService.updateOrderItemState(serviceOrder,orderItem,StateType.FAILED);
358                 LOGGER.warn("order item {} failed , status {} , response {}",orderItem.getId(),response.getStatusCode(),response.getBody());
359             } else {
360                 serviceOrderService.updateOrderItemState(serviceOrder,orderItem,StateType.INPROGRESS);
361             }
362         }
363     }
364
365     /**
366      * Update an executionTask in database when it's process with a success
367      *
368      * @param executionTask
369      */
370     private void updateSuccessTask(ExecutionTask executionTask) {
371         executionTaskRepository.delete(executionTask.getInternalId());
372         executionTaskRepository.updateReliedTaskAfterDelete(executionTask.getInternalId());
373
374     }
375
376     /**
377      * @param executionTask
378      * @param serviceOrder
379      */
380     private void updateFailedTask(ExecutionTask executionTask, ServiceOrder serviceOrder) {
381         List<ExecutionTask> executionTasksToDelete = findExecutionTasksRecursively(executionTask);
382         for (ExecutionTask taskId : executionTasksToDelete) {
383             executionTaskRepository.delete(taskId);
384             LOGGER.warn("task {} with orderitem id {} deleted cause orderitem id {} failed ",taskId.getInternalId(),taskId.getOrderItemId(),executionTask.getOrderItemId());
385         }
386         for (ServiceOrderItem item : serviceOrder.getOrderItem()) {
387             for (ExecutionTask taskToDelete : executionTasksToDelete) {
388                 if (taskToDelete.getOrderItemId().equals(item.getId())) {
389                     serviceOrderService.updateOrderItemState(serviceOrder,item,StateType.FAILED);
390                     LOGGER.warn("task {} with orderitem id {}  to failed cause orderitem id {} failed ",taskToDelete.getInternalId(),taskToDelete.getOrderItemId(),executionTask.getOrderItemId());
391
392                 }
393             }
394         }
395     }
396
397     /**
398      * @param executionTask
399      * @return
400      */
401     private List<ExecutionTask> findExecutionTasksRecursively(ExecutionTask executionTask) {
402
403         List<ExecutionTask> executionTasks = new ArrayList<>();
404
405         List<ExecutionTask> tasksReliedToAnOrderItemId =
406             executionTaskRepository.findTasksReliedToAnOrderItemId(executionTask.getInternalId());
407
408         if (CollectionUtils.isEmpty(tasksReliedToAnOrderItemId)) {
409             return Arrays.asList(executionTask);
410         } else {
411             for (ExecutionTask task : tasksReliedToAnOrderItemId) {
412                 executionTasks.addAll(findExecutionTasksRecursively(task));
413             }
414         }
415         executionTasks.add(executionTask);
416         return executionTasks;
417     }
418
419
420 }