df8e92d66bbcacae8db2857b6bc2d4c843be7e12
[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 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2018 Nokia. All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.vid.services;
23
24 import com.google.common.collect.ImmutableMap;
25 import io.joshworks.restclient.http.HttpResponse;
26 import java.io.IOException;
27 import org.apache.commons.collections.CollectionUtils;
28 import org.apache.commons.io.IOUtils;
29 import org.apache.commons.lang3.StringUtils;
30 import org.hibernate.SessionFactory;
31 import org.onap.vid.aai.AaiClientInterface;
32 import org.onap.vid.aai.AaiOverTLSClientInterface;
33 import org.onap.vid.aai.AaiResponse;
34 import org.onap.vid.aai.exceptions.InvalidAAIResponseException;
35 import org.onap.vid.aai.model.AaiNodeQueryResponse;
36 import org.onap.vid.aai.model.ResourceType;
37 import org.onap.vid.changeManagement.RequestDetailsWrapper;
38 import org.onap.vid.domain.mso.CloudConfiguration;
39 import org.onap.vid.domain.mso.SubscriberInfo;
40 import org.onap.vid.exceptions.DbFailureUncheckedException;
41 import org.onap.vid.exceptions.GenericUncheckedException;
42 import org.onap.vid.exceptions.MaxRetriesException;
43 import org.onap.vid.exceptions.OperationNotAllowedException;
44 import org.onap.vid.job.Job;
45 import org.onap.vid.job.Job.JobStatus;
46 import org.onap.vid.job.JobAdapter;
47 import org.onap.vid.job.JobType;
48 import org.onap.vid.job.JobsBrokerService;
49 import org.onap.vid.model.JobAuditStatus;
50 import org.onap.vid.model.NameCounter;
51 import org.onap.vid.model.ServiceInfo;
52 import org.onap.vid.model.serviceInstantiation.ServiceInstantiation;
53 import org.onap.vid.model.serviceInstantiation.VfModule;
54 import org.onap.vid.model.serviceInstantiation.Vnf;
55 import org.onap.vid.mso.MsoBusinessLogicImpl;
56 import org.onap.vid.mso.MsoProperties;
57 import org.onap.vid.mso.model.ServiceInstantiationRequestDetails;
58 import org.onap.vid.mso.rest.AsyncRequestStatus;
59 import org.onap.vid.utils.DaoUtils;
60 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
61 import org.onap.portalsdk.core.service.DataAccessService;
62 import org.springframework.beans.factory.annotation.Autowired;
63 import org.springframework.beans.factory.annotation.Qualifier;
64 import org.springframework.stereotype.Service;
65
66 import java.sql.Timestamp;
67 import java.time.LocalDateTime;
68 import java.util.*;
69 import java.util.function.Consumer;
70 import java.util.stream.Collectors;
71
72 import static org.onap.vid.utils.Logging.debugRequestDetails;
73
74 @Service
75 public class AsyncInstantiationBusinessLogicImpl implements AsyncInstantiationBusinessLogic {
76
77     private static final int MAX_RETRIES_GETTING_COUNTER = 100;
78     private static final int MAX_RETRIES_GETTING_FREE_NAME_FROM_AAI = 10000;
79     private static final String NAME_FOR_CHECK_AAI_STATUS = "NAME_FOR_CHECK_AAI_STATUS";
80
81     private final DataAccessService dataAccessService;
82
83     private final JobAdapter jobAdapter;
84
85     private final JobsBrokerService jobService;
86
87     private SessionFactory sessionFactory;
88
89     private AaiOverTLSClientInterface aaiOverTLSClient;
90
91     private int maxRetriesGettingFreeNameFromAai = MAX_RETRIES_GETTING_FREE_NAME_FROM_AAI;
92
93     private static final EELFLoggerDelegate logger = EELFLoggerDelegate
94         .getLogger(AsyncInstantiationBusinessLogicImpl.class);
95     private Map<String, JobStatus> msoStateToJobStatusMap = ImmutableMap.<String, JobStatus>builder()
96         .put("inprogress", JobStatus.IN_PROGRESS)
97         .put("failed", JobStatus.FAILED)
98         .put("pause", JobStatus.PAUSE)
99         .put("paused", JobStatus.PAUSE)
100         .put("complete", JobStatus.COMPLETED)
101         .put("pending", JobStatus.IN_PROGRESS)
102         .put("pendingmanualtask", JobStatus.PAUSE)
103         .put("unlocked", JobStatus.IN_PROGRESS)
104         .build();
105
106
107     @Autowired
108     public AsyncInstantiationBusinessLogicImpl(DataAccessService dataAccessService,
109         JobAdapter jobAdapter,
110         JobsBrokerService jobService,
111         SessionFactory sessionFactory,
112         @Qualifier("aaiClientForFasterXmlMapping")  AaiOverTLSClientInterface aaiOverTLSClient) {
113         this.dataAccessService = dataAccessService;
114         this.jobAdapter = jobAdapter;
115         this.jobService = jobService;
116         this.sessionFactory = sessionFactory;
117         this.aaiOverTLSClient = aaiOverTLSClient;
118     }
119
120     @Override
121     public List<ServiceInfo> getAllServicesInfo() {
122         return dataAccessService
123             .getList(ServiceInfo.class, filterByCreationDateAndNotDeleted(), orderByCreatedDateAndStatus(), null);
124     }
125
126     private String filterByCreationDateAndNotDeleted() {
127         LocalDateTime minus3Months = LocalDateTime.now().minusMonths(3);
128         Timestamp filterDate = Timestamp.valueOf(minus3Months);
129         return " where" +
130             "   hidden = false" +
131             "   and deleted_at is null" +  // don't fetch deleted
132             "   and created >= '" + filterDate + "' ";
133     }
134
135     private String orderByCreatedDateAndStatus() {
136         return " createdBulkDate DESC ,\n" +
137             "  (CASE jobStatus\n" +
138             "   WHEN 'COMPLETED' THEN 0\n" +
139             "   WHEN 'FAILED' THEN 0\n" +
140             "   WHEN 'IN_PROGRESS' THEN 1\n" +
141             "   WHEN 'PAUSE' THEN 2\n" +
142             "   WHEN 'PENDING' THEN 3\n" +
143             "   WHEN 'STOPPED' THEN 3 END),\n" +
144             "  statusModifiedDate ";
145     }
146
147     @Override
148     public List<UUID> pushBulkJob(ServiceInstantiation request, String userId) {
149         List<UUID> uuids = new ArrayList<>();
150         Date createdBulkDate = Calendar.getInstance().getTime();
151         int bulkSize = request.getBulkSize();
152         UUID templateId = UUID.randomUUID();
153         for (int i = 0; i < bulkSize; i++) {
154             Job job = jobAdapter.createJob(JobType.ServiceInstantiation, request, templateId, userId, i);
155             UUID jobId = jobService.add(job);
156             auditVidStatus(jobId, job.getStatus());
157             uuids.add(jobId);
158             dataAccessService.saveDomainObject(createServiceInfo(userId, request, jobId, templateId, createdBulkDate),
159                 DaoUtils.getPropsMap());
160         }
161         return uuids;
162     }
163
164     private ServiceInfo createServiceInfo(String userId, ServiceInstantiation serviceInstantiation, UUID jobId,
165         UUID templateId, Date createdBulkDate) {
166         return new ServiceInfo(
167             userId, Job.JobStatus.PENDING, serviceInstantiation.isPause(), jobId, templateId,
168             serviceInstantiation.getOwningEntityId(),
169             serviceInstantiation.getOwningEntityName(),
170             serviceInstantiation.getProjectName(),
171             serviceInstantiation.getAicZoneId(),
172             serviceInstantiation.getAicZoneName(),
173             serviceInstantiation.getTenantId(),
174             serviceInstantiation.getTenantName(),
175             serviceInstantiation.getLcpCloudRegionId(),
176             null,
177             serviceInstantiation.getSubscriptionServiceType(),
178             serviceInstantiation.getSubscriberName(),
179             null,
180             serviceInstantiation.getInstanceName(),
181             serviceInstantiation.getModelInfo().getModelInvariantId(),
182             serviceInstantiation.getModelInfo().getModelName(),
183             serviceInstantiation.getModelInfo().getModelVersion(),
184             createdBulkDate
185         );
186     }
187
188
189     @Override
190     public RequestDetailsWrapper<ServiceInstantiationRequestDetails> generateServiceInstantiationRequest(UUID jobId,
191         ServiceInstantiation payload, String userId) {
192
193         ServiceInstantiationRequestDetails.ServiceInstantiationOwningEntity owningEntity = new ServiceInstantiationRequestDetails.ServiceInstantiationOwningEntity(
194             payload.getOwningEntityId(), payload.getOwningEntityName());
195
196         SubscriberInfo subscriberInfo = new SubscriberInfo();
197         subscriberInfo.setGlobalSubscriberId(payload.getGlobalSubscriberId());
198
199         String serviceInstanceName = null;
200         if (payload.isUserProvidedNaming()) {
201             serviceInstanceName = getUniqueName(payload.getInstanceName(), ResourceType.SERVICE_INSTANCE);
202             String finalServiceInstanceName = serviceInstanceName;
203             updateServiceInfo(jobId, x -> x.setServiceInstanceName(finalServiceInstanceName));
204         }
205         ServiceInstantiationRequestDetails.RequestInfo requestInfo = new ServiceInstantiationRequestDetails.RequestInfo(
206             serviceInstanceName,
207             payload.getProductFamilyId(),
208             "VID",
209             payload.isRollbackOnFailure(),
210             userId);
211
212         List<ServiceInstantiationRequestDetails.ServiceInstantiationService> serviceInstantiationService = new LinkedList<>();
213         List<Map<String, String>> unFilteredInstanceParams =
214             payload.getInstanceParams() != null ? payload.getInstanceParams() : new LinkedList<>();
215         List<Map<String, String>> filteredInstanceParams = removeUnNeededParams(unFilteredInstanceParams);
216         ServiceInstantiationRequestDetails.ServiceInstantiationService serviceInstantiationService1 = new ServiceInstantiationRequestDetails.ServiceInstantiationService(
217             payload.getModelInfo(),
218             serviceInstanceName,
219             filteredInstanceParams,
220             createServiceInstantiationVnfList(payload)
221         );
222         serviceInstantiationService.add(serviceInstantiationService1);
223
224         ServiceInstantiationRequestDetails.RequestParameters requestParameters = new ServiceInstantiationRequestDetails.RequestParameters(
225             payload.getSubscriptionServiceType(), false, serviceInstantiationService);
226
227         ServiceInstantiationRequestDetails.Project project =
228             payload.getProjectName() != null ? new ServiceInstantiationRequestDetails.Project(payload.getProjectName())
229                 : null;
230
231         ServiceInstantiationRequestDetails requestDetails = new ServiceInstantiationRequestDetails(
232             payload.getModelInfo(), owningEntity, subscriberInfo,
233             project, requestInfo, requestParameters);
234
235         RequestDetailsWrapper<ServiceInstantiationRequestDetails> requestDetailsWrapper = new RequestDetailsWrapper(
236             requestDetails);
237         debugRequestDetails(requestDetailsWrapper, logger);
238         return requestDetailsWrapper;
239     }
240
241     private List<Map<String, String>> removeUnNeededParams(List<Map<String, String>> instanceParams) {
242         List<String> keysToRemove = new ArrayList<>();
243         if (instanceParams != null && !instanceParams.isEmpty()) {
244             for (String key : instanceParams.get(0).keySet()) {
245                 for (String paramToIgnore : PARAMS_TO_IGNORE) {
246                     if ((key.equalsIgnoreCase(paramToIgnore))) {
247                         keysToRemove.add(key);
248                     }
249                 }
250             }
251             for (String key : keysToRemove) {
252                 instanceParams.get(0).remove(key);
253             }
254             //TODO will be removed on once we stop using List<Map<String, String>>
255             if (instanceParams.get(0).isEmpty()) {
256                 return Collections.emptyList();
257             }
258         }
259         return instanceParams;
260     }
261
262     private ServiceInstantiationRequestDetails.ServiceInstantiationVnfList createServiceInstantiationVnfList(
263         ServiceInstantiation payload) {
264         CloudConfiguration cloudConfiguration = new CloudConfiguration();
265         cloudConfiguration.setTenantId(payload.getTenantId());
266         cloudConfiguration.setLcpCloudRegionId(payload.getLcpCloudRegionId());
267
268         Map<String, Vnf> vnfs = payload.getVnfs();
269         List<ServiceInstantiationRequestDetails.ServiceInstantiationVnf> vnfList = new ArrayList<>();
270         for (Vnf vnf : vnfs.values()) {
271             Map<String, Map<String, VfModule>> vfModules = vnf.getVfModules();
272             List<VfModule> convertedUnFilteredVfModules = convertVfModuleMapToList(vfModules);
273             List<VfModule> filteredVfModules = filterInstanceParamsFromVfModuleAndUniqueNames(
274                 convertedUnFilteredVfModules, vnf.isUserProvidedNaming());
275             ServiceInstantiationRequestDetails.ServiceInstantiationVnf serviceInstantiationVnf = new ServiceInstantiationRequestDetails.ServiceInstantiationVnf(
276                 vnf.getModelInfo(),
277                 cloudConfiguration,
278                 vnf.getPlatformName(),
279                 vnf.getLineOfBusiness(),
280                 payload.getProductFamilyId(),
281                 removeUnNeededParams(vnf.getInstanceParams()),
282                 filteredVfModules,
283                 vnf.isUserProvidedNaming() ? getUniqueName(vnf.getInstanceName(), ResourceType.GENERIC_VNF) : null
284             );
285             vnfList.add(serviceInstantiationVnf);
286         }
287
288         return new ServiceInstantiationRequestDetails.ServiceInstantiationVnfList(vnfList);
289     }
290
291     private List<VfModule> convertVfModuleMapToList(Map<String, Map<String, VfModule>> vfModules) {
292         return vfModules.values().stream().flatMap(vfModule -> vfModule.values().stream()).collect(Collectors.toList());
293     }
294
295     private List<VfModule> filterInstanceParamsFromVfModuleAndUniqueNames(List<VfModule> unFilteredVfModules,
296         boolean isUserProvidedNaming) {
297         return unFilteredVfModules.stream().map(vfModule ->
298             new VfModule(
299                 vfModule.getModelInfo(),
300                 getUniqueNameIfNeeded(isUserProvidedNaming, vfModule.getInstanceName(), ResourceType.VF_MODULE),
301                 getUniqueNameIfNeeded(isUserProvidedNaming, vfModule.getVolumeGroupInstanceName(),
302                     ResourceType.VOLUME_GROUP),
303                 removeUnNeededParams(vfModule.getInstanceParams())))
304             .collect(Collectors.toList());
305     }
306
307     private String getUniqueNameIfNeeded(boolean isUserProvidedNaming, String name, ResourceType resourceType) {
308         return isUserProvidedNaming && !StringUtils.isEmpty(name) ?
309             getUniqueName(name, resourceType) : null;
310     }
311
312     @Override
313     public String getServiceInstantiationPath(ServiceInstantiation serviceInstantiationRequest) {
314         //in case pause flag is true - use assign , else - use create.
315         return MsoBusinessLogicImpl.validateEndpointPath(
316             serviceInstantiationRequest.isPause() ?
317                 "mso.restapi.serviceInstanceAssign" : "mso.restapi.serviceInstanceCreate"
318         );
319     }
320
321     @Override
322     public String getOrchestrationRequestsPath() {
323         return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_GET_ORC_REQ);
324     }
325
326     @Override
327     public ServiceInfo updateServiceInfo(UUID jobUUID, Consumer<ServiceInfo> serviceUpdater) {
328         ServiceInfo serviceInfo = getServiceInfoByJobId(jobUUID);
329         serviceUpdater.accept(serviceInfo);
330         dataAccessService.saveDomainObject(serviceInfo, DaoUtils.getPropsMap());
331         return serviceInfo;
332     }
333
334     @Override
335     public ServiceInfo updateServiceInfoAndAuditStatus(UUID jobUuid, JobStatus jobStatus) {
336         auditVidStatus(jobUuid, jobStatus);
337         return updateServiceInfo(jobUuid, x -> setServiceInfoStatus(x, jobStatus));
338     }
339
340     private void setServiceInfoStatus(ServiceInfo serviceInfo, JobStatus jobStatus) {
341         serviceInfo.setJobStatus(jobStatus);
342         serviceInfo.setStatusModifiedDate(new Date());
343     }
344
345     public ServiceInfo getServiceInfoByJobId(UUID jobUUID) {
346         List<ServiceInfo> serviceInfoList = dataAccessService
347             .getList(ServiceInfo.class, String.format(" where jobId = '%s' ", jobUUID), null, null);
348         if (serviceInfoList.size() != 1) {
349             throw new GenericUncheckedException(
350                 "Failed to retrieve job with uuid " + jobUUID + " from ServiceInfo table. Instances found: "
351                     + serviceInfoList.size());
352         }
353         return serviceInfoList.get(0);
354     }
355
356     public List<JobAuditStatus> getAuditStatuses(UUID jobUUID, JobAuditStatus.SourceStatus source) {
357         return dataAccessService.getList(
358             JobAuditStatus.class,
359             String.format(" where SOURCE = '%s' and JOB_ID = '%s'", source, jobUUID),
360             " CREATED_DATE ", null);
361     }
362
363     private JobAuditStatus getLatestAuditStatus(UUID jobUUID, JobAuditStatus.SourceStatus source) {
364         List<JobAuditStatus> list = getAuditStatuses(jobUUID, source);
365         return !list.isEmpty() ? list.get(list.size() - 1) : null;
366     }
367
368     @Override
369     public void auditVidStatus(UUID jobUUID, JobStatus jobStatus) {
370         JobAuditStatus vidStatus = new JobAuditStatus(jobUUID, jobStatus.toString(), JobAuditStatus.SourceStatus.VID);
371         auditStatus(vidStatus);
372     }
373
374     @Override
375     public void auditMsoStatus(UUID jobUUID, AsyncRequestStatus.Request msoRequestStatus) {
376         auditMsoStatus(jobUUID, msoRequestStatus.requestStatus.getRequestState(), msoRequestStatus.requestId,
377             msoRequestStatus.requestStatus.getStatusMessage());
378     }
379
380     @Override
381     public void auditMsoStatus(UUID jobUUID, String jobStatus, String requestId, String additionalInfo) {
382         JobAuditStatus msoStatus = new JobAuditStatus(jobUUID, jobStatus, JobAuditStatus.SourceStatus.MSO,
383             requestId != null ? UUID.fromString(requestId) : null,
384             additionalInfo);
385         auditStatus(msoStatus);
386     }
387
388     private void auditStatus(JobAuditStatus jobAuditStatus) {
389         JobAuditStatus latestStatus = getLatestAuditStatus(jobAuditStatus.getJobId(), jobAuditStatus.getSource());
390         if (latestStatus == null || !latestStatus.equals(jobAuditStatus)) {
391             dataAccessService.saveDomainObject(jobAuditStatus, DaoUtils.getPropsMap());
392         }
393
394     }
395
396     public Job.JobStatus calcStatus(AsyncRequestStatus asyncRequestStatus) {
397         String msoRequestState = asyncRequestStatus.request.requestStatus.getRequestState().toLowerCase()
398             .replaceAll("[^a-z]+", "");
399         JobStatus jobStatus = msoStateToJobStatusMap.get(msoRequestState);
400         return (jobStatus != null ? jobStatus : JobStatus.IN_PROGRESS);
401     }
402
403     @Override
404     public void handleFailedInstantiation(UUID jobUUID) {
405         ServiceInfo serviceInfo = updateServiceInfoAndAuditStatus(jobUUID, JobStatus.FAILED);
406         List<ServiceInfo> serviceInfoList = dataAccessService.getList(
407             ServiceInfo.class,
408             String.format(" where templateId = '%s' and jobStatus = '%s'",
409                 serviceInfo.getTemplateId(),
410                 JobStatus.PENDING),
411             null, null);
412         serviceInfoList.forEach(si -> updateServiceInfoAndAuditStatus(si.getJobId(), JobStatus.STOPPED));
413     }
414
415     @Override
416     public void deleteJob(UUID jobId) {
417         jobService.delete(jobId);
418         Date now = new Date();
419         updateServiceInfo(jobId, x -> x.setDeletedAt(now));
420     }
421
422     @Override
423     public void hideServiceInfo(UUID jobUUID) {
424         ServiceInfo serviceInfo = getServiceInfoByJobId(jobUUID);
425         if (!serviceInfo.getJobStatus().isFinal()) {
426             String message = String.format("jobId %s: Service status does not allow hide service, status = %s",
427                 serviceInfo.getJobId(),
428                 serviceInfo.getJobStatus());
429             logger.error(EELFLoggerDelegate.errorLogger, message);
430             throw new OperationNotAllowedException(message);
431         }
432         serviceInfo.setHidden(true);
433         dataAccessService.saveDomainObject(serviceInfo, DaoUtils.getPropsMap());
434     }
435
436     @Override
437     public int
438
439     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             } catch (GenericUncheckedException exception) {
452                 lastException = exception; //do nothing, we will try again in the loop
453             }
454         }
455
456         if (counter != null) {
457             return counter;
458         }
459
460         throw lastException != null ? new DbFailureUncheckedException(lastException) :
461             new DbFailureUncheckedException("Failed to get counter for " + name + " due to unknown error");
462
463     }
464
465     private Integer calcCounter(String name, String hqlSelectNC, String hqlUpdateCounter) {
466         Integer counter;
467         counter = DaoUtils.tryWithSessionAndTransaction(sessionFactory, session -> {
468             NameCounter nameCounter = (NameCounter) session.createQuery(hqlSelectNC)
469                 .setText("name", name)
470                 .uniqueResult();
471             if (nameCounter != null) {
472                 int updatedRows = session.createQuery(hqlUpdateCounter)
473                     .setText("name", nameCounter.getName())
474                     .setInteger("prevCounter", nameCounter.getCounter())
475                     .setInteger("newCounter", nameCounter.getCounter() + 1)
476                     .executeUpdate();
477                 if (updatedRows == 1) {
478                     return nameCounter.getCounter() + 1;
479                 }
480             } else {
481                 Object nameAsId = session.save(new NameCounter(name));
482                 //if save success
483                 if (nameAsId != null) {
484                     return 1;
485                 }
486             }
487             //in case of failure return null, in order to continue the loop
488             return null;
489         });
490         return counter;
491     }
492
493     @Override
494     public int getMaxRetriesGettingFreeNameFromAai() {
495         return maxRetriesGettingFreeNameFromAai;
496     }
497
498     @Override
499     public void setMaxRetriesGettingFreeNameFromAai(int maxRetriesGettingFreeNameFromAai) {
500         this.maxRetriesGettingFreeNameFromAai = maxRetriesGettingFreeNameFromAai;
501     }
502
503     @Override
504     public String getUniqueName(String name, ResourceType resourceType) {
505         //check that name aai response well before increasing counter from DB
506         //Prevents unnecessary increasing of the counter while AAI doesn't response
507         isNameFreeInAai(NAME_FOR_CHECK_AAI_STATUS, resourceType);
508
509         for (int i = 0; i < getMaxRetriesGettingFreeNameFromAai(); i++) {
510             int counter = getCounterForName(name);
511             String newName = formatNameAndCounter(name, counter);
512             if (isNameFreeInAai(newName, resourceType)) {
513                 return newName;
514             }
515         }
516
517         throw new MaxRetriesException("find unused name for " + name, getMaxRetriesGettingFreeNameFromAai());
518     }
519
520     //the method is protected so we can call it in the UT
521     protected String formatNameAndCounter(String name, int counter) {
522         return name + "_" + String.format("%03d", counter);
523     }
524
525     private boolean isNameFreeInAai(String name, ResourceType resourceType) throws InvalidAAIResponseException {
526         HttpResponse<AaiNodeQueryResponse> aaiResponse = aaiOverTLSClient
527             .searchNodeTypeByName(name, resourceType);
528         if (aaiResponse.getStatus() > 399 || aaiResponse.getBody() == null) {
529             try {
530                 String message = IOUtils.toString(aaiResponse.getRawBody(), "UTF-8");
531                 throw new InvalidAAIResponseException(aaiResponse.getStatus(), message);
532             } catch (IOException e) {
533                 throw new InvalidAAIResponseException(aaiResponse.getStatus(), aaiResponse.getStatusText());
534             }
535         }
536         return CollectionUtils.isEmpty(aaiResponse.getBody().resultData);
537     }
538
539 }