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