1 package org.onap.vid.services;
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;
40 import java.sql.Timestamp;
41 import java.time.LocalDateTime;
43 import java.util.function.Consumer;
44 import java.util.stream.Collectors;
46 import static org.onap.vid.utils.Logging.debugRequestDetails;
49 public class AsyncInstantiationBusinessLogicImpl implements AsyncInstantiationBusinessLogic {
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";
55 private final DataAccessService dataAccessService;
57 private final JobAdapter jobAdapter;
59 private final JobsBrokerService jobService;
61 private SessionFactory sessionFactory;
63 private AaiClientInterface aaiClient;
65 private int maxRetriesGettingFreeNameFromAai = MAX_RETRIES_GETTING_FREE_NAME_FROM_AAI;
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)
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;
94 public List<ServiceInfo> getAllServicesInfo() {
95 return dataAccessService.getList(ServiceInfo.class, filterByCreationDateAndNotDeleted(), orderByCreatedDateAndStatus(), null);
98 private String filterByCreationDateAndNotDeleted() {
99 LocalDateTime minus3Months = LocalDateTime.now().minusMonths(3);
100 Timestamp filterDate = Timestamp.valueOf(minus3Months);
103 " and deleted_at is null" + // don't fetch deleted
104 " and created >= '" + filterDate + "' ";
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 ";
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());
130 dataAccessService.saveDomainObject(createServiceInfo(userId, request, jobId, templateId, createdBulkDate), DaoUtils.getPropsMap());
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(),
147 serviceInstantiation.getSubscriptionServiceType(),
148 serviceInstantiation.getSubscriberName(),
150 serviceInstantiation.getInstanceName(),
151 serviceInstantiation.getModelInfo().getModelInvariantId(),
152 serviceInstantiation.getModelInfo().getModelName(),
153 serviceInstantiation.getModelInfo().getModelVersion(),
160 public RequestDetailsWrapper<ServiceInstantiationRequestDetails> generateServiceInstantiationRequest(UUID jobId, ServiceInstantiation payload, String userId) {
162 ServiceInstantiationRequestDetails.ServiceInstantiationOwningEntity owningEntity = new ServiceInstantiationRequestDetails.ServiceInstantiationOwningEntity(payload.getOwningEntityId(), payload.getOwningEntityName());
164 SubscriberInfo subscriberInfo = new SubscriberInfo();
165 subscriberInfo.setGlobalSubscriberId(payload.getGlobalSubscriberId());
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));
173 ServiceInstantiationRequestDetails.RequestInfo requestInfo = new ServiceInstantiationRequestDetails.RequestInfo(
175 payload.getProductFamilyId(),
177 payload.isRollbackOnFailure(),
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(),
186 filteredInstanceParams,
187 createServiceInstantiationVnfList(payload)
189 serviceInstantiationService.add(serviceInstantiationService1);
191 ServiceInstantiationRequestDetails.RequestParameters requestParameters = new ServiceInstantiationRequestDetails.RequestParameters(payload.getSubscriptionServiceType(), false, serviceInstantiationService);
193 ServiceInstantiationRequestDetails.Project project = payload.getProjectName() != null ? new ServiceInstantiationRequestDetails.Project(payload.getProjectName()) : null;
195 ServiceInstantiationRequestDetails requestDetails = new ServiceInstantiationRequestDetails(payload.getModelInfo(), owningEntity, subscriberInfo,
196 project, requestInfo, requestParameters);
198 RequestDetailsWrapper<ServiceInstantiationRequestDetails> requestDetailsWrapper = new RequestDetailsWrapper(requestDetails);
199 debugRequestDetails(requestDetailsWrapper, logger);
200 return requestDetailsWrapper;
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);
212 for (String key : keysToRemove) {
213 instanceParams.get(0).remove(key);
215 //TODO will be removed on once we stop using List<Map<String, String>>
216 if (instanceParams.get(0).isEmpty()) {
217 return Collections.emptyList();
220 return instanceParams;
223 private ServiceInstantiationRequestDetails.ServiceInstantiationVnfList createServiceInstantiationVnfList(ServiceInstantiation payload) {
224 CloudConfiguration cloudConfiguration = new CloudConfiguration();
225 cloudConfiguration.setTenantId(payload.getTenantId());
226 cloudConfiguration.setLcpCloudRegionId(payload.getLcpCloudRegionId());
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(
237 vnf.getPlatformName(),
238 vnf.getLineOfBusiness(),
239 payload.getProductFamilyId(),
240 removeUnNeededParams(vnf.getInstanceParams()),
242 vnf.isUserProvidedNaming() ? getUniqueName(vnf.getInstanceName(), ResourceType.GENERIC_VNF) : null
244 vnfList.add(serviceInstantiationVnf);
247 return new ServiceInstantiationRequestDetails.ServiceInstantiationVnfList(vnfList);
250 private List<VfModule> convertVfModuleMapToList(Map<String, Map<String, VfModule>> vfModules) {
251 return vfModules.values().stream().flatMap(vfModule -> vfModule.values().stream()).collect(Collectors.toList());
254 private List<VfModule> filterInstanceParamsFromVfModuleAndUniqueNames(List<VfModule> unFilteredVfModules, boolean isUserProvidedNaming) {
255 return unFilteredVfModules.stream().map(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());
264 private String getUniqueNameIfNeeded(boolean isUserProvidedNaming, String name, ResourceType resourceType) {
265 return isUserProvidedNaming && !StringUtils.isEmpty(name) ?
266 getUniqueName(name, resourceType) : null;
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"
279 public String getOrchestrationRequestsPath() {
280 return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_GET_ORC_REQ);
284 public ServiceInfo updateServiceInfo(UUID jobUUID, Consumer<ServiceInfo> serviceUpdater) {
285 ServiceInfo serviceInfo = getServiceInfoByJobId(jobUUID);
286 serviceUpdater.accept(serviceInfo);
287 dataAccessService.saveDomainObject(serviceInfo, DaoUtils.getPropsMap());
292 public ServiceInfo updateServiceInfoAndAuditStatus(UUID jobUuid, JobStatus jobStatus) {
293 auditVidStatus(jobUuid,jobStatus);
294 return updateServiceInfo(jobUuid, x -> setServiceInfoStatus(x, jobStatus));
297 private void setServiceInfoStatus(ServiceInfo serviceInfo, JobStatus jobStatus) {
298 serviceInfo.setJobStatus(jobStatus);
299 serviceInfo.setStatusModifiedDate(new Date());
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());
307 return serviceInfoList.get(0);
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);
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;
323 public void auditVidStatus(UUID jobUUID, JobStatus jobStatus){
324 JobAuditStatus vidStatus = new JobAuditStatus(jobUUID, jobStatus.toString(), JobAuditStatus.SourceStatus.VID);
325 auditStatus(vidStatus);
329 public void auditMsoStatus(UUID jobUUID, AsyncRequestStatus.Request msoRequestStatus){
330 auditMsoStatus(jobUUID, msoRequestStatus.requestStatus.getRequestState(), msoRequestStatus.requestId, msoRequestStatus.requestStatus.getStatusMessage());
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,
338 auditStatus(msoStatus);
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());
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);
355 public void handleFailedInstantiation(UUID jobUUID) {
356 ServiceInfo serviceInfo = updateServiceInfoAndAuditStatus(jobUUID, JobStatus.FAILED);
357 List<ServiceInfo> serviceInfoList = dataAccessService.getList(
359 String.format(" where templateId = '%s' and jobStatus = '%s'",
360 serviceInfo.getTemplateId(),
363 serviceInfoList.forEach(si -> updateServiceInfoAndAuditStatus(si.getJobId(), JobStatus.STOPPED));
367 public void deleteJob(UUID jobId) {
368 jobService.delete(jobId);
369 Date now = new Date();
370 updateServiceInfo(jobId, x -> x.setDeletedAt(now));
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);
383 serviceInfo.setHidden(true);
384 dataAccessService.saveDomainObject(serviceInfo, DaoUtils.getPropsMap());
391 getCounterForName(String name) {
393 String hqlSelectNC = "from NameCounter where name = :name";
394 String hqlUpdateCounter = "update NameCounter set counter = :newCounter " +
395 "where name= :name " +
396 "and counter= :prevCounter";
398 Integer counter = null;
399 GenericUncheckedException lastException = null;
400 for (int i = 0; i< MAX_RETRIES_GETTING_COUNTER && counter==null; i++) {
402 counter = calcCounter(name, hqlSelectNC, hqlUpdateCounter);
404 catch (GenericUncheckedException exception) {
405 lastException = exception; //do nothing, we will try again in the loop
413 throw lastException!=null ? new DbFailureUncheckedException(lastException) :
414 new DbFailureUncheckedException("Failed to get counter for "+name+" due to unknown error");
418 private Integer calcCounter(String name, String hqlSelectNC, String hqlUpdateCounter) {
420 counter = DaoUtils.tryWithSessionAndTransaction(sessionFactory, session -> {
421 NameCounter nameCounter = (NameCounter) session.createQuery(hqlSelectNC)
422 .setText("name", name)
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)
430 if (updatedRows == 1) {
431 return nameCounter.getCounter() + 1;
434 Object nameAsId = session.save(new NameCounter(name));
436 if (nameAsId != null) {
440 //in case of failure return null, in order to continue the loop
447 public int getMaxRetriesGettingFreeNameFromAai() {
448 return maxRetriesGettingFreeNameFromAai;
452 public void setMaxRetriesGettingFreeNameFromAai(int maxRetriesGettingFreeNameFromAai) {
453 this.maxRetriesGettingFreeNameFromAai = maxRetriesGettingFreeNameFromAai;
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);
462 for (int i=0; i<getMaxRetriesGettingFreeNameFromAai(); i++) {
463 int counter = getCounterForName(name);
464 String newName = formatNameAndCounter(name, counter);
465 if (isNameFreeInAai(newName, resourceType)) {
470 throw new MaxRetriesException("find unused name for "+name, getMaxRetriesGettingFreeNameFromAai());
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);
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);
483 return CollectionUtils.isEmpty(aaiResponse.getT().resultData);