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