2 * ============LICENSE_START=======================================================
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
22 package org.onap.vid.services;
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;
66 import java.sql.Timestamp;
67 import java.time.LocalDateTime;
69 import java.util.function.Consumer;
70 import java.util.stream.Collectors;
72 import static org.onap.vid.utils.Logging.debugRequestDetails;
75 public class AsyncInstantiationBusinessLogicImpl implements AsyncInstantiationBusinessLogic {
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";
81 private final DataAccessService dataAccessService;
83 private final JobAdapter jobAdapter;
85 private final JobsBrokerService jobService;
87 private SessionFactory sessionFactory;
89 private AaiOverTLSClientInterface aaiOverTLSClient;
91 private int maxRetriesGettingFreeNameFromAai = MAX_RETRIES_GETTING_FREE_NAME_FROM_AAI;
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)
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;
121 public List<ServiceInfo> getAllServicesInfo() {
122 return dataAccessService
123 .getList(ServiceInfo.class, filterByCreationDateAndNotDeleted(), orderByCreatedDateAndStatus(), null);
126 private String filterByCreationDateAndNotDeleted() {
127 LocalDateTime minus3Months = LocalDateTime.now().minusMonths(3);
128 Timestamp filterDate = Timestamp.valueOf(minus3Months);
131 " and deleted_at is null" + // don't fetch deleted
132 " and created >= '" + filterDate + "' ";
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 ";
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());
158 dataAccessService.saveDomainObject(createServiceInfo(userId, request, jobId, templateId, createdBulkDate),
159 DaoUtils.getPropsMap());
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(),
177 serviceInstantiation.getSubscriptionServiceType(),
178 serviceInstantiation.getSubscriberName(),
180 serviceInstantiation.getInstanceName(),
181 serviceInstantiation.getModelInfo().getModelInvariantId(),
182 serviceInstantiation.getModelInfo().getModelName(),
183 serviceInstantiation.getModelInfo().getModelVersion(),
190 public RequestDetailsWrapper<ServiceInstantiationRequestDetails> generateServiceInstantiationRequest(UUID jobId,
191 ServiceInstantiation payload, String userId) {
193 ServiceInstantiationRequestDetails.ServiceInstantiationOwningEntity owningEntity = new ServiceInstantiationRequestDetails.ServiceInstantiationOwningEntity(
194 payload.getOwningEntityId(), payload.getOwningEntityName());
196 SubscriberInfo subscriberInfo = new SubscriberInfo();
197 subscriberInfo.setGlobalSubscriberId(payload.getGlobalSubscriberId());
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));
205 ServiceInstantiationRequestDetails.RequestInfo requestInfo = new ServiceInstantiationRequestDetails.RequestInfo(
207 payload.getProductFamilyId(),
209 payload.isRollbackOnFailure(),
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(),
219 filteredInstanceParams,
220 createServiceInstantiationVnfList(payload)
222 serviceInstantiationService.add(serviceInstantiationService1);
224 ServiceInstantiationRequestDetails.RequestParameters requestParameters = new ServiceInstantiationRequestDetails.RequestParameters(
225 payload.getSubscriptionServiceType(), false, serviceInstantiationService);
227 ServiceInstantiationRequestDetails.Project project =
228 payload.getProjectName() != null ? new ServiceInstantiationRequestDetails.Project(payload.getProjectName())
231 ServiceInstantiationRequestDetails requestDetails = new ServiceInstantiationRequestDetails(
232 payload.getModelInfo(), owningEntity, subscriberInfo,
233 project, requestInfo, requestParameters);
235 RequestDetailsWrapper<ServiceInstantiationRequestDetails> requestDetailsWrapper = new RequestDetailsWrapper(
237 debugRequestDetails(requestDetailsWrapper, logger);
238 return requestDetailsWrapper;
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);
251 for (String key : keysToRemove) {
252 instanceParams.get(0).remove(key);
254 //TODO will be removed on once we stop using List<Map<String, String>>
255 if (instanceParams.get(0).isEmpty()) {
256 return Collections.emptyList();
259 return instanceParams;
262 private ServiceInstantiationRequestDetails.ServiceInstantiationVnfList createServiceInstantiationVnfList(
263 ServiceInstantiation payload) {
264 CloudConfiguration cloudConfiguration = new CloudConfiguration();
265 cloudConfiguration.setTenantId(payload.getTenantId());
266 cloudConfiguration.setLcpCloudRegionId(payload.getLcpCloudRegionId());
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(
278 vnf.getPlatformName(),
279 vnf.getLineOfBusiness(),
280 payload.getProductFamilyId(),
281 removeUnNeededParams(vnf.getInstanceParams()),
283 vnf.isUserProvidedNaming() ? getUniqueName(vnf.getInstanceName(), ResourceType.GENERIC_VNF) : null
285 vnfList.add(serviceInstantiationVnf);
288 return new ServiceInstantiationRequestDetails.ServiceInstantiationVnfList(vnfList);
291 private List<VfModule> convertVfModuleMapToList(Map<String, Map<String, VfModule>> vfModules) {
292 return vfModules.values().stream().flatMap(vfModule -> vfModule.values().stream()).collect(Collectors.toList());
295 private List<VfModule> filterInstanceParamsFromVfModuleAndUniqueNames(List<VfModule> unFilteredVfModules,
296 boolean isUserProvidedNaming) {
297 return unFilteredVfModules.stream().map(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());
307 private String getUniqueNameIfNeeded(boolean isUserProvidedNaming, String name, ResourceType resourceType) {
308 return isUserProvidedNaming && !StringUtils.isEmpty(name) ?
309 getUniqueName(name, resourceType) : null;
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"
322 public String getOrchestrationRequestsPath() {
323 return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_GET_ORC_REQ);
327 public ServiceInfo updateServiceInfo(UUID jobUUID, Consumer<ServiceInfo> serviceUpdater) {
328 ServiceInfo serviceInfo = getServiceInfoByJobId(jobUUID);
329 serviceUpdater.accept(serviceInfo);
330 dataAccessService.saveDomainObject(serviceInfo, DaoUtils.getPropsMap());
335 public ServiceInfo updateServiceInfoAndAuditStatus(UUID jobUuid, JobStatus jobStatus) {
336 auditVidStatus(jobUuid, jobStatus);
337 return updateServiceInfo(jobUuid, x -> setServiceInfoStatus(x, jobStatus));
340 private void setServiceInfoStatus(ServiceInfo serviceInfo, JobStatus jobStatus) {
341 serviceInfo.setJobStatus(jobStatus);
342 serviceInfo.setStatusModifiedDate(new Date());
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());
353 return serviceInfoList.get(0);
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);
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;
369 public void auditVidStatus(UUID jobUUID, JobStatus jobStatus) {
370 JobAuditStatus vidStatus = new JobAuditStatus(jobUUID, jobStatus.toString(), JobAuditStatus.SourceStatus.VID);
371 auditStatus(vidStatus);
375 public void auditMsoStatus(UUID jobUUID, AsyncRequestStatus.Request msoRequestStatus) {
376 auditMsoStatus(jobUUID, msoRequestStatus.requestStatus.getRequestState(), msoRequestStatus.requestId,
377 msoRequestStatus.requestStatus.getStatusMessage());
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,
385 auditStatus(msoStatus);
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());
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);
404 public void handleFailedInstantiation(UUID jobUUID) {
405 ServiceInfo serviceInfo = updateServiceInfoAndAuditStatus(jobUUID, JobStatus.FAILED);
406 List<ServiceInfo> serviceInfoList = dataAccessService.getList(
408 String.format(" where templateId = '%s' and jobStatus = '%s'",
409 serviceInfo.getTemplateId(),
412 serviceInfoList.forEach(si -> updateServiceInfoAndAuditStatus(si.getJobId(), JobStatus.STOPPED));
416 public void deleteJob(UUID jobId) {
417 jobService.delete(jobId);
418 Date now = new Date();
419 updateServiceInfo(jobId, x -> x.setDeletedAt(now));
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);
432 serviceInfo.setHidden(true);
433 dataAccessService.saveDomainObject(serviceInfo, DaoUtils.getPropsMap());
439 getCounterForName(String name) {
441 String hqlSelectNC = "from NameCounter where name = :name";
442 String hqlUpdateCounter = "update NameCounter set counter = :newCounter " +
443 "where name= :name " +
444 "and counter= :prevCounter";
446 Integer counter = null;
447 GenericUncheckedException lastException = null;
448 for (int i = 0; i < MAX_RETRIES_GETTING_COUNTER && counter == null; i++) {
450 counter = calcCounter(name, hqlSelectNC, hqlUpdateCounter);
451 } catch (GenericUncheckedException exception) {
452 lastException = exception; //do nothing, we will try again in the loop
456 if (counter != null) {
460 throw lastException != null ? new DbFailureUncheckedException(lastException) :
461 new DbFailureUncheckedException("Failed to get counter for " + name + " due to unknown error");
465 private Integer calcCounter(String name, String hqlSelectNC, String hqlUpdateCounter) {
467 counter = DaoUtils.tryWithSessionAndTransaction(sessionFactory, session -> {
468 NameCounter nameCounter = (NameCounter) session.createQuery(hqlSelectNC)
469 .setText("name", name)
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)
477 if (updatedRows == 1) {
478 return nameCounter.getCounter() + 1;
481 Object nameAsId = session.save(new NameCounter(name));
483 if (nameAsId != null) {
487 //in case of failure return null, in order to continue the loop
494 public int getMaxRetriesGettingFreeNameFromAai() {
495 return maxRetriesGettingFreeNameFromAai;
499 public void setMaxRetriesGettingFreeNameFromAai(int maxRetriesGettingFreeNameFromAai) {
500 this.maxRetriesGettingFreeNameFromAai = maxRetriesGettingFreeNameFromAai;
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);
509 for (int i = 0; i < getMaxRetriesGettingFreeNameFromAai(); i++) {
510 int counter = getCounterForName(name);
511 String newName = formatNameAndCounter(name, counter);
512 if (isNameFreeInAai(newName, resourceType)) {
517 throw new MaxRetriesException("find unused name for " + name, getMaxRetriesGettingFreeNameFromAai());
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);
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) {
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());
536 return CollectionUtils.isEmpty(aaiResponse.getBody().resultData);