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