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