Add resources summarizing function in service instance.
[vid.git] / vid-app-common / src / main / java / org / onap / vid / services / AsyncInstantiationBusinessLogicImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * VID
4  * ================================================================================
5  * Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.vid.services;
22
23 import static java.util.stream.Collectors.counting;
24 import static java.util.function.Function.identity;
25 import static java.util.stream.Collectors.groupingBy;
26 import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
27 import static org.onap.vid.controller.MsoController.SVC_INSTANCE_ID;
28 import static org.onap.vid.controller.MsoController.VNF_INSTANCE_ID;
29 import static org.onap.vid.utils.KotlinUtilsKt.JACKSON_OBJECT_MAPPER;
30
31 import com.google.common.collect.ImmutableMap;
32 import java.io.IOException;
33 import java.time.ZonedDateTime;
34 import java.util.ArrayList;
35 import java.util.Calendar;
36 import java.util.Collection;
37 import java.util.Date;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.UUID;
42 import java.util.function.Consumer;
43 import java.util.function.Function;
44 import java.util.stream.Collectors;
45 import org.apache.commons.collections.CollectionUtils;
46 import org.apache.commons.lang3.StringUtils;
47 import org.hibernate.SessionFactory;
48 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
49 import org.onap.vid.aai.AaiClientInterface;
50 import org.onap.vid.aai.model.ResourceType;
51 import org.onap.vid.dal.AsyncInstantiationRepository;
52 import org.onap.vid.exceptions.DbFailureUncheckedException;
53 import org.onap.vid.exceptions.GenericUncheckedException;
54 import org.onap.vid.exceptions.MaxRetriesException;
55 import org.onap.vid.exceptions.OperationNotAllowedException;
56 import org.onap.vid.job.Job;
57 import org.onap.vid.job.Job.JobStatus;
58 import org.onap.vid.job.JobAdapter;
59 import org.onap.vid.job.JobType;
60 import org.onap.vid.job.JobsBrokerService;
61 import org.onap.vid.job.impl.JobSharedData;
62 import org.onap.vid.model.Action;
63 import org.onap.vid.model.NameCounter;
64 import org.onap.vid.model.ResourceInfo;
65 import org.onap.vid.model.ServiceInfo;
66 import org.onap.vid.model.serviceInstantiation.BaseResource;
67 import org.onap.vid.model.serviceInstantiation.ServiceInstantiation;
68 import org.onap.vid.mso.MsoBusinessLogicImpl;
69 import org.onap.vid.mso.MsoProperties;
70 import org.onap.vid.mso.MsoUtil;
71 import org.onap.vid.mso.RestObject;
72 import org.onap.vid.mso.rest.AsyncRequestStatus;
73 import org.onap.vid.mso.rest.RequestStatus;
74 import org.onap.vid.properties.Features;
75 import org.onap.vid.utils.DaoUtils;
76 import org.onap.vid.utils.TimeUtils;
77 import org.springframework.beans.factory.annotation.Autowired;
78 import org.springframework.stereotype.Service;
79 import org.togglz.core.manager.FeatureManager;
80
81 @Service
82 public class AsyncInstantiationBusinessLogicImpl implements
83         AsyncInstantiationBusinessLogic {
84
85     private static final int MAX_RETRIES_GETTING_COUNTER = 100;
86     private static final int MAX_RETRIES_GETTING_FREE_NAME_FROM_AAI = 10000;
87     public static final String NAME_FOR_CHECK_AAI_STATUS = "NAME_FOR_CHECK_AAI_STATUS";
88
89     private final JobAdapter jobAdapter;
90
91     private final JobsBrokerService jobService;
92
93     private final CloudOwnerService cloudOwnerService;
94
95     private final AsyncInstantiationRepository asyncInstantiationRepository;
96
97     private SessionFactory sessionFactory;
98
99     private AaiClientInterface aaiClient;
100
101     private FeatureManager featureManager;
102
103     private AuditService auditService;
104
105
106     private int maxRetriesGettingFreeNameFromAai = MAX_RETRIES_GETTING_FREE_NAME_FROM_AAI;
107
108     private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AsyncInstantiationBusinessLogicImpl.class);
109     private Map<String, JobStatus> msoStateToJobStatusMap = ImmutableMap.<String, JobStatus>builder()
110             .put("inprogress", JobStatus.IN_PROGRESS)
111             .put("failed", JobStatus.FAILED)
112             .put("pause", JobStatus.PAUSE)
113             .put("paused", JobStatus.PAUSE)
114             .put("complete", JobStatus.COMPLETED)
115             .put("pending", JobStatus.IN_PROGRESS)
116             .put("pendingmanualtask", JobStatus.PAUSE)
117             .put("unlocked", JobStatus.IN_PROGRESS)
118             .put("aborted", JobStatus.COMPLETED_WITH_ERRORS)
119             .put("rolledback", JobStatus.FAILED)
120             .put("rolledbacktoassigned", JobStatus.FAILED)
121             .put("rolledbacktocreated", JobStatus.FAILED)
122             .build();
123
124
125     @Autowired
126     public AsyncInstantiationBusinessLogicImpl(JobAdapter jobAdapter,
127                                                JobsBrokerService jobService,
128                                                SessionFactory sessionFactory,
129                                                AaiClientInterface aaiClient,
130                                                FeatureManager featureManager,
131                                                CloudOwnerService cloudOwnerService, AsyncInstantiationRepository asyncInstantiationRepository,
132                                                AuditService auditService) {
133         this.jobAdapter = jobAdapter;
134         this.jobService = jobService;
135         this.sessionFactory = sessionFactory;
136         this.aaiClient = aaiClient;
137         this.featureManager = featureManager;
138         this.cloudOwnerService = cloudOwnerService;
139         this.asyncInstantiationRepository = asyncInstantiationRepository;
140         this.auditService = auditService;
141     }
142
143     @Override
144     public List<ServiceInfo> getAllServicesInfo() {
145         return asyncInstantiationRepository.getAllServicesInfo();
146     }
147
148     JobType getJobType(ServiceInstantiation request) {
149         if (request.isALaCarte()) {
150             switch (defaultIfNull(request.getAction(), Action.Create)) {
151                 case Delete:
152                     return JobType.ALaCarteService;
153                 case None:
154                     return JobType.ALaCarteService;
155                 default:
156                     return JobType.ALaCarteServiceInstantiation;
157             }
158         } else {
159             return JobType.MacroServiceInstantiation;
160         }
161     }
162
163     @Override
164     public List<UUID> pushBulkJob(ServiceInstantiation request, String userId) {
165
166         List<UUID> uuids = new ArrayList<>();
167         Date createdBulkDate = Calendar.getInstance().getTime();
168         int bulkSize = request.getBulkSize();
169         UUID templateId = UUID.randomUUID();
170         for (int i = 0; i < bulkSize; i++) {
171             ServiceInstantiation requestPerJob = prepareServiceToBeUnique(request);
172             ServiceInfo.ServiceAction serviceAction = getAction(requestPerJob);
173             JobType jobType = getJobType(requestPerJob);
174             final String optimisticUniqueServiceInstanceName = bulkSize>1 ? //only bulk with more than 1 service need to get multiple names
175                     getOptimisticUniqueServiceInstanceName(requestPerJob.getInstanceName()) : requestPerJob.getInstanceName();
176             Job job = jobAdapter.createServiceInstantiationJob(jobType, requestPerJob, templateId, userId, request.getTestApi(), optimisticUniqueServiceInstanceName, i);
177             UUID jobId = job.getUuid();
178
179             asyncInstantiationRepository.saveServiceInfo(createServiceInfo(userId, requestPerJob, jobId, templateId, createdBulkDate, optimisticUniqueServiceInstanceName, serviceAction));
180             asyncInstantiationRepository.addJobRequest(jobId, requestPerJob);
181             auditService.auditVidStatus(jobId, job.getStatus());
182             uuids.add(jobId);
183
184             jobService.add(job);
185         }
186         return uuids;
187     }
188
189     public  Map<String, Long> getSummarizedChildrenMap(ServiceInstantiation serviceInstantiation){
190         List<String> existingTypesList = new ArrayList<>();
191         Map<String, Long> existingTypesCounters;
192
193         existingTypesList = getChildrenList(serviceInstantiation, existingTypesList);
194         existingTypesCounters =  existingTypesList.stream().collect(groupingBy(identity(), counting()));
195
196         return existingTypesCounters;
197     }
198
199     private List<String> getChildrenList(BaseResource resource, List<String> list){
200         Collection<? extends BaseResource> children =  resource.getChildren();
201         if (CollectionUtils.isNotEmpty(children)){
202             children.forEach( child -> {
203                 String childType = child.getModelInfo().getModelType();
204                 getChildrenList(child, list);
205                 list.add(childType);
206             });
207         }
208         return list;
209     }
210
211     private ServiceInfo.ServiceAction getAction(ServiceInstantiation request) {
212         if (request.getAction() == null) {
213             //throw new GenericUncheckedException("Required 'action' field not provided at service level");
214             return Action.Create.getServiceAction();
215         }
216         return request.getAction().getServiceAction();
217     }
218
219
220     private String getOptimisticUniqueServiceInstanceName(String instanceName) {
221         return StringUtils.isNotEmpty(instanceName) ? getUniqueNameFromDbOnly(instanceName) : instanceName;
222     }
223
224     protected ServiceInfo createServiceInfo(String userId, ServiceInstantiation serviceInstantiation, UUID jobId, UUID templateId, Date createdBulkDate, String optimisticUniqueServiceInstanceName, ServiceInfo.ServiceAction serviceAction) {
225         return new ServiceInfo(
226                 userId,
227                 serviceInstantiation.isALaCarte(),
228                 Job.JobStatus.PENDING, serviceInstantiation.isPause(), jobId, templateId,
229                 serviceInstantiation.getOwningEntityId(),
230                 serviceInstantiation.getOwningEntityName(),
231                 serviceInstantiation.getProjectName(),
232                 serviceInstantiation.getAicZoneId(),
233                 serviceInstantiation.getAicZoneName(),
234                 serviceInstantiation.getTenantId(),
235                 serviceInstantiation.getTenantName(),
236                 serviceInstantiation.getLcpCloudRegionId(),
237                 null,
238                 serviceInstantiation.getSubscriptionServiceType(),
239                 serviceInstantiation.getSubscriberName(),
240                 serviceInstantiation.getGlobalSubscriberId(),
241                 serviceInstantiation.getInstanceId(),
242                 optimisticUniqueServiceInstanceName,
243                 serviceInstantiation.getModelInfo().getModelVersionId(),
244                 serviceInstantiation.getModelInfo().getModelName(),
245                 serviceInstantiation.getModelInfo().getModelVersion(),
246                 createdBulkDate,
247                 serviceAction,
248                 false);
249     }
250
251     @Override
252     public boolean isPartOfBulk(UUID jobId) {
253         if (jobId == null) {
254             return false;
255     }
256         ServiceInfo serviceInfo = asyncInstantiationRepository.getServiceInfoByJobId(jobId);
257         UUID templateId = serviceInfo.getTemplateId();
258         if (templateId != null) {
259             return getNumberOfJobsInBulk(templateId) > 1;
260     }
261         return false;
262
263     }
264
265     private int getNumberOfJobsInBulk(UUID templateId) {
266         String hqlSelectJob = "from JobDaoImpl where templateId = :templateId";
267         return DaoUtils.tryWithSessionAndTransaction(sessionFactory, session ->
268             session.createQuery(hqlSelectJob)
269                     .setText("templateId", templateId.toString())
270                     .list()
271                     .size()
272         );
273     }
274
275     @Override
276     public String getServiceInstantiationPath(ServiceInstantiation serviceInstantiationRequest) {
277         //in case pause flag is true - use assign , else - use create.
278         return MsoBusinessLogicImpl.validateEndpointPath(
279                 serviceInstantiationRequest.isPause() ?
280                         MsoProperties.MSO_REST_API_SERVICE_INSTANCE_ASSIGN : MsoProperties.MSO_RESTAPI_SERVICE_INSTANCE
281         );
282     }
283
284     @Override
285     public String getServiceDeletionPath(String serviceInstanceId) {
286         return MsoBusinessLogicImpl.validateEndpointPath( MsoProperties.MSO_RESTAPI_SERVICE_INSTANCE)  + "/" + serviceInstanceId;
287     }
288
289     @Override
290     public String getVnfInstantiationPath(String serviceInstanceId) {
291         return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_VNF_INSTANCE).
292                 replaceFirst(SVC_INSTANCE_ID, serviceInstanceId);
293     }
294
295     @Override
296     public String getVnfDeletionPath(String serviceInstanceId, String vnfInstanceId) {
297         return (MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_VNF_INSTANCE)
298                 + '/' + vnfInstanceId)
299                 .replaceFirst(SVC_INSTANCE_ID, serviceInstanceId).replaceFirst(VNF_INSTANCE_ID, vnfInstanceId);
300     }
301
302     @Override
303     public String getNetworkInstantiationPath(String serviceInstanceId) {
304         return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_NETWORK_INSTANCE).
305                 replaceFirst(SVC_INSTANCE_ID, serviceInstanceId);
306     }
307
308     @Override
309     public String getVfmoduleInstantiationPath(String serviceInstanceId, String vnfInstanceId) {
310         return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_VF_MODULE_INSTANCE)
311                 .replaceFirst(SVC_INSTANCE_ID, serviceInstanceId)
312                 .replaceFirst(VNF_INSTANCE_ID, vnfInstanceId);
313     }
314
315     @Override
316     public String getVfModuleReplacePath(String serviceInstanceId, String vnfInstanceId, String vfModuleInstanceId)
317     {
318         return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_VF_MODULE_INSTANCE)
319                 .replaceFirst(SVC_INSTANCE_ID, serviceInstanceId)
320                 .replaceFirst(VNF_INSTANCE_ID, vnfInstanceId)
321                 + "/" + vfModuleInstanceId
322                 + "/replace";
323     }
324
325     @Override
326     public String getVfModuleDeletePath(String serviceInstanceId, String vnfInstanceId, String vfModuleInstanceId) {
327         return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_VF_MODULE_INSTANCE)
328                 .replaceFirst(SVC_INSTANCE_ID, serviceInstanceId)
329                 .replaceFirst(VNF_INSTANCE_ID, vnfInstanceId)
330                 + "/" + vfModuleInstanceId;
331     }
332
333     @Override
334     public String getVolumeGroupInstantiationPath(String serviceInstanceId, String vnfInstanceId) {
335         return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_VOLUME_GROUP_INSTANCE)
336                 .replaceFirst(SVC_INSTANCE_ID, serviceInstanceId)
337                 .replaceFirst(VNF_INSTANCE_ID, vnfInstanceId);
338     }
339
340     @Override
341     public String getInstanceGroupInstantiationPath() {
342         return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_INSTANCE_GROUP);
343     }
344
345     @Override
346     public String getInstanceGroupMemberInstantiationPath(String vnfGroupInstanceId) {
347         return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_INSTANCE_GROUP)
348                 + '/' + vnfGroupInstanceId + "/addMembers";
349     }
350
351     @Override
352     public String getInstanceGroupDeletePath(String instanceGroupId) {
353         return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_INSTANCE_GROUP)
354                 + '/' + instanceGroupId;
355     }
356
357     @Override
358     public String getInstanceGroupMemberDeletePath(String vnfGroupInstanceId){
359         return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_INSTANCE_GROUP)
360                 + '/' + vnfGroupInstanceId + "/removeMembers";
361     }
362
363     @Override
364     public String getNetworkDeletePath(String serviceInstanceId, String networkInstanceId) {
365         return (MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_NETWORK_INSTANCE)
366                 + "/" + networkInstanceId)
367                 .replaceFirst(SVC_INSTANCE_ID, serviceInstanceId);
368     }
369
370     @Override
371     public String getResumeRequestPath(String requestId) {
372         return MsoBusinessLogicImpl.validateEndpointPath("mso.restapi.resume.orc.req")
373                 .replaceFirst("<request_id>", requestId);
374     }
375
376     @Override
377     public String getOrchestrationRequestsPath() {
378         return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_GET_ORC_REQ);
379     }
380
381     @Override
382     public ServiceInfo updateServiceInfo(UUID jobUUID, Consumer<ServiceInfo> serviceUpdater) {
383         ServiceInfo serviceInfo = asyncInstantiationRepository.getServiceInfoByJobId(jobUUID);
384         serviceUpdater.accept(serviceInfo);
385         asyncInstantiationRepository.saveServiceInfo(serviceInfo);
386         return serviceInfo;
387     }
388
389     @Override
390     public ServiceInfo updateServiceInfoAndAuditStatus(UUID jobUuid, JobStatus jobStatus) {
391         auditService.auditVidStatus(jobUuid, jobStatus);
392         return updateServiceInfo(jobUuid, x -> setServiceInfoStatus(x, jobStatus));
393     }
394
395     private boolean isRetryEnabledForStatus(JobStatus jobStatus) {
396         return featureManager.isActive(Features.FLAG_1902_RETRY_JOB) &&
397                 (jobStatus==JobStatus.COMPLETED_WITH_ERRORS || jobStatus==JobStatus.FAILED);
398     }
399
400     private void setServiceInfoStatus(ServiceInfo serviceInfo, JobStatus jobStatus) {
401         serviceInfo.setJobStatus(jobStatus);
402         serviceInfo.setStatusModifiedDate(new Date());
403         serviceInfo.setRetryEnabled(isRetryEnabledForStatus(jobStatus));
404     }
405
406     public Job.JobStatus calcStatus(AsyncRequestStatus asyncRequestStatus) {
407         String msoRequestState = asyncRequestStatus.request.requestStatus.getRequestState().toLowerCase().replaceAll("[^a-z]+", "");
408         JobStatus jobStatus = msoStateToJobStatusMap.get(msoRequestState);
409         return (jobStatus != null ? jobStatus : JobStatus.IN_PROGRESS);
410     }
411
412     @Override
413     public void handleFailedInstantiation(UUID jobUUID) {
414         ServiceInfo serviceInfo = asyncInstantiationRepository.getServiceInfoByJobId(jobUUID);
415         List<ServiceInfo> serviceInfoList = asyncInstantiationRepository.getServiceInfoByTemplateIdAndJobStatus(serviceInfo.getTemplateId(), JobStatus.PENDING);
416         serviceInfoList.forEach(si -> updateServiceInfoAndAuditStatus(si.getJobId(), JobStatus.STOPPED));
417     }
418
419     @Override
420     public void deleteJob(UUID jobId) {
421         jobService.delete(jobId);
422         Date now = new Date();
423         updateServiceInfo(jobId, x -> x.setDeletedAt(now));
424     }
425
426     @Override
427     public void hideServiceInfo(UUID jobUUID) {
428         ServiceInfo serviceInfo = asyncInstantiationRepository.getServiceInfoByJobId(jobUUID);
429         if (!serviceInfo.getJobStatus().isFinal()) {
430             String message = String.format("jobId %s: Service status does not allow hide service, status = %s",
431                     serviceInfo.getJobId(),
432                     serviceInfo.getJobStatus());
433             logger.error(EELFLoggerDelegate.errorLogger, message);
434             throw new OperationNotAllowedException(message);
435         }
436         serviceInfo.setHidden(true);
437         asyncInstantiationRepository.saveServiceInfo(serviceInfo);
438     }
439
440     @Override
441     public int getCounterForName(String name) {
442
443         String hqlSelectNC = "from NameCounter where name = :name";
444         String hqlUpdateCounter = "update NameCounter set counter = :newCounter " +
445                 "where name= :name " +
446                 "and counter= :prevCounter";
447
448         Integer counter = null;
449         GenericUncheckedException lastException = null;
450         for (int i = 0; i< MAX_RETRIES_GETTING_COUNTER && counter==null; i++) {
451             try {
452                 counter = calcCounter(name, hqlSelectNC, hqlUpdateCounter);
453             }
454             catch (GenericUncheckedException exception) {
455                 lastException = exception; //do nothing, we will try again in the loop
456             }
457         }
458
459         if (counter!=null) {
460             return counter;
461         }
462
463         throw lastException!=null ? new DbFailureUncheckedException(lastException) :
464                 new DbFailureUncheckedException("Failed to get counter for "+name+" due to unknown error");
465
466     }
467
468     private Integer calcCounter(String name, String hqlSelectNC, String hqlUpdateCounter) {
469         Integer counter;
470         counter = DaoUtils.tryWithSessionAndTransaction(sessionFactory, session -> {
471             NameCounter nameCounter = (NameCounter) session.createQuery(hqlSelectNC)
472                     .setText("name", name)
473                     .uniqueResult();
474             if (nameCounter != null) {
475                 int updatedRows = session.createQuery(hqlUpdateCounter)
476                         .setText("name", nameCounter.getName())
477                         .setInteger("prevCounter", nameCounter.getCounter())
478                         .setInteger("newCounter", nameCounter.getCounter() + 1)
479                         .executeUpdate();
480                 if (updatedRows == 1) {
481                     return nameCounter.getCounter() + 1;
482                 }
483             } else {
484                 Object nameAsId = session.save(new NameCounter(name));
485                 //if save success
486                 if (nameAsId != null) {
487                     return 0;
488                 }
489             }
490             //in case of failure return null, in order to continue the loop
491             return null;
492         });
493         return counter;
494     }
495
496     @Override
497     public int getMaxRetriesGettingFreeNameFromAai() {
498         return maxRetriesGettingFreeNameFromAai;
499     }
500
501     @Override
502     public void setMaxRetriesGettingFreeNameFromAai(int maxRetriesGettingFreeNameFromAai) {
503         this.maxRetriesGettingFreeNameFromAai = maxRetriesGettingFreeNameFromAai;
504     }
505
506     @Override
507     public String getUniqueName(String name, ResourceType resourceType) {
508         //check that name aai response well before increasing counter from DB
509         //Prevents unnecessary increasing of the counter while AAI doesn't response
510         isNameFreeInAai(NAME_FOR_CHECK_AAI_STATUS, resourceType);
511
512         for (int i=0; i<getMaxRetriesGettingFreeNameFromAai(); i++) {
513             String newName = getUniqueNameFromDbOnly(name);
514             if (isNameFreeInAai(newName, resourceType)) {
515                 return newName;
516             }
517         }
518
519         throw new MaxRetriesException("can't find unused name for "+name, getMaxRetriesGettingFreeNameFromAai());
520     }
521
522     @Override
523     public ServiceInstantiation prepareServiceToBeUnique(ServiceInstantiation serviceInstantiation) {
524         try {
525             ServiceInstantiation clonedServiceInstantiation = JACKSON_OBJECT_MAPPER.readValue(
526                     JACKSON_OBJECT_MAPPER.writeValueAsBytes(serviceInstantiation), ServiceInstantiation.class);
527             clonedServiceInstantiation.setBulkSize(1);
528             return replaceAllTrackById(clonedServiceInstantiation);
529         } catch (IOException e) {
530             throw new GenericUncheckedException(e);
531         }
532
533     }
534
535     private<T extends BaseResource> T replaceAllTrackById(T resource) {
536         resource.setTrackById(UUID.randomUUID().toString());
537         resource.getChildren().forEach(this::replaceAllTrackById);
538         return resource;
539     }
540
541     @Override
542     public List<UUID> retryJob(ServiceInstantiation request, UUID jobId, String userId ) {
543         updateServiceInfo(jobId, si->si.setRetryEnabled(false));
544         return pushBulkJob(request, userId);
545     }
546
547     @Override
548     public List<UUID> retryJob(UUID jobId, String userId) {
549         ServiceInstantiation serviceInstantiationRequest = asyncInstantiationRepository.getJobRequest(jobId);
550         enrichBulkForRetry(serviceInstantiationRequest, jobId);
551
552         try {
553             logger.debug(EELFLoggerDelegate.debugLogger, "retry ServiceInstantiation request: "+
554                     JACKSON_OBJECT_MAPPER.writeValueAsString(serviceInstantiationRequest));
555         } catch (Exception e) {
556             logger.error(EELFLoggerDelegate.errorLogger, "failed to log retry of ServiceInstantiation request ", e);
557         }
558         return retryJob(serviceInstantiationRequest, jobId, userId);
559     }
560
561     @Override
562     public ServiceInstantiation getBulkForRetry(UUID jobId) {
563          return enrichBulkForRetry( asyncInstantiationRepository.getJobRequest(jobId), jobId);
564     }
565
566     @Override
567     public void addResourceInfo(JobSharedData sharedData, Job.JobStatus jobStatus, String instanceId) {
568         String trackById = ((BaseResource) sharedData.getRequest()).getTrackById();
569         ResourceInfo resourceInfo = new ResourceInfo(trackById, sharedData.getRootJobId(), instanceId, jobStatus, null);
570         asyncInstantiationRepository.saveResourceInfo(resourceInfo);
571     }
572
573     @Override
574     public void addFailedResourceInfo(JobSharedData sharedData, RestObject msoResponse) {
575         String trackById = ((BaseResource) sharedData.getRequest()).getTrackById();
576         String errorMessage = MsoUtil.formatExceptionAdditionalInfo(msoResponse.getStatusCode(), msoResponse.getRaw());
577         AsyncRequestStatus asyncRequestStatus = convertMessageToAsyncRequestStatus(errorMessage);
578         ResourceInfo resourceInfo = new ResourceInfo(trackById, sharedData.getRootJobId(), null, JobStatus.FAILED, asyncRequestStatus);
579
580         asyncInstantiationRepository.saveResourceInfo(resourceInfo);
581     }
582
583     @Override
584     public void updateResourceInfo(JobSharedData sharedData, JobStatus jobStatus, AsyncRequestStatus message) {
585         ResourceInfo resourceInfo = asyncInstantiationRepository.getResourceInfoByTrackId(((BaseResource) sharedData.getRequest()).getTrackById());
586         resourceInfo.setJobStatus(jobStatus);
587         if (jobStatus.isFailure()) {
588             resourceInfo.setErrorMessage(message);
589         }
590         asyncInstantiationRepository.saveResourceInfo(resourceInfo);
591     }
592
593     public AsyncRequestStatus convertMessageToAsyncRequestStatus(String message) {
594         RequestStatus requestStatus = new RequestStatus("FAILED", message, TimeUtils.zonedDateTimeToString(ZonedDateTime.now()));
595         AsyncRequestStatus.Request request = new AsyncRequestStatus.Request(requestStatus);
596         return new AsyncRequestStatus(request);
597     }
598
599     protected String getUniqueNameFromDbOnly(String name) {
600         int counter = getCounterForName(name);
601         return formatNameAndCounter(name, counter);
602     }
603
604     //the method is protected so we can call it in the UT
605     protected String formatNameAndCounter(String name, int counter) {
606         return counter==0 ? name : name + "_" + String.format("%03d", counter);
607     }
608
609     private boolean isNameFreeInAai(String name, ResourceType resourceType){
610         return !aaiClient.isNodeTypeExistsByName(name, resourceType);
611     }
612
613     @Override
614     public ServiceInstantiation enrichBulkForRetry(ServiceInstantiation serviceInstantiation, UUID jobId){
615         Map<String, ResourceInfo> resourceInfoByTrackId = asyncInstantiationRepository.getResourceInfoByRootJobId(jobId);
616
617         return setResourceStatus(resourceInfoByTrackId, serviceInstantiation);
618     }
619
620     protected String readStatusMsg(ResourceInfo resourceInfo){
621         if(resourceInfo!=null && resourceInfo.getErrorMessage()!=null && resourceInfo.getErrorMessage().request != null &&resourceInfo.getErrorMessage().request.requestStatus != null ) {
622             return resourceInfo.getErrorMessage().request.requestStatus.getStatusMessage();
623         }
624         return null;
625     }
626
627     private<T extends BaseResource> T setResourceStatus(Map<String, ResourceInfo> resourceInfoByTrackId, T resource) {
628         ResourceInfo resourceInfo = resourceInfoByTrackId.get(resource.getTrackById());
629         if(resourceInfo != null) {
630             boolean failed = resourceInfo.getJobStatus().isFailure();
631             resource.setIsFailed(failed);
632             resource.setStatusMessage(readStatusMsg(resourceInfo));
633             if (!failed) {
634                 // if(resource.getAction().equals(Action.Delete)){
635                 // TODO not yet implemented- completed after delete should remove the node
636                 resource.setAction(Action.None);
637                 resource.setInstanceId(resourceInfo.getInstanceId());
638             }
639         }
640         resource.getChildren().forEach(child -> setResourceStatus(resourceInfoByTrackId, child));
641         return resource;
642     }
643
644 }