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.stereotype.Service;
65 import java.sql.Timestamp;
66 import java.time.LocalDateTime;
68 import java.util.function.Consumer;
69 import java.util.stream.Collectors;
71 import static org.onap.vid.utils.Logging.debugRequestDetails;
74 public class AsyncInstantiationBusinessLogicImpl implements AsyncInstantiationBusinessLogic {
76 private static final int MAX_RETRIES_GETTING_COUNTER = 100;
77 private static final int MAX_RETRIES_GETTING_FREE_NAME_FROM_AAI = 10000;
78 private static final String NAME_FOR_CHECK_AAI_STATUS = "NAME_FOR_CHECK_AAI_STATUS";
80 private final DataAccessService dataAccessService;
82 private final JobAdapter jobAdapter;
84 private final JobsBrokerService jobService;
86 private SessionFactory sessionFactory;
88 private AaiClientInterface aaiClient;
90 private AaiOverTLSClientInterface aaiOverTLSClient;
92 private int maxRetriesGettingFreeNameFromAai = MAX_RETRIES_GETTING_FREE_NAME_FROM_AAI;
94 private static final EELFLoggerDelegate logger = EELFLoggerDelegate
95 .getLogger(AsyncInstantiationBusinessLogicImpl.class);
96 private Map<String, JobStatus> msoStateToJobStatusMap = ImmutableMap.<String, JobStatus>builder()
97 .put("inprogress", JobStatus.IN_PROGRESS)
98 .put("failed", JobStatus.FAILED)
99 .put("pause", JobStatus.PAUSE)
100 .put("paused", JobStatus.PAUSE)
101 .put("complete", JobStatus.COMPLETED)
102 .put("pending", JobStatus.IN_PROGRESS)
103 .put("pendingmanualtask", JobStatus.PAUSE)
104 .put("unlocked", JobStatus.IN_PROGRESS)
109 public AsyncInstantiationBusinessLogicImpl(DataAccessService dataAccessService,
110 JobAdapter jobAdapter,
111 JobsBrokerService jobService,
112 SessionFactory sessionFactory,
113 AaiClientInterface aaiClient,
114 AaiOverTLSClientInterface aaiOverTLSClient) {
115 this.dataAccessService = dataAccessService;
116 this.jobAdapter = jobAdapter;
117 this.jobService = jobService;
118 this.sessionFactory = sessionFactory;
119 this.aaiClient = aaiClient;
120 this.aaiOverTLSClient = aaiOverTLSClient;
124 public List<ServiceInfo> getAllServicesInfo() {
125 return dataAccessService
126 .getList(ServiceInfo.class, filterByCreationDateAndNotDeleted(), orderByCreatedDateAndStatus(), null);
129 private String filterByCreationDateAndNotDeleted() {
130 LocalDateTime minus3Months = LocalDateTime.now().minusMonths(3);
131 Timestamp filterDate = Timestamp.valueOf(minus3Months);
134 " and deleted_at is null" + // don't fetch deleted
135 " and created >= '" + filterDate + "' ";
138 private String orderByCreatedDateAndStatus() {
139 return " createdBulkDate DESC ,\n" +
140 " (CASE jobStatus\n" +
141 " WHEN 'COMPLETED' THEN 0\n" +
142 " WHEN 'FAILED' THEN 0\n" +
143 " WHEN 'IN_PROGRESS' THEN 1\n" +
144 " WHEN 'PAUSE' THEN 2\n" +
145 " WHEN 'PENDING' THEN 3\n" +
146 " WHEN 'STOPPED' THEN 3 END),\n" +
147 " statusModifiedDate ";
151 public List<UUID> pushBulkJob(ServiceInstantiation request, String userId) {
152 List<UUID> uuids = new ArrayList<>();
153 Date createdBulkDate = Calendar.getInstance().getTime();
154 int bulkSize = request.getBulkSize();
155 UUID templateId = UUID.randomUUID();
156 for (int i = 0; i < bulkSize; i++) {
157 Job job = jobAdapter.createJob(JobType.ServiceInstantiation, request, templateId, userId, i);
158 UUID jobId = jobService.add(job);
159 auditVidStatus(jobId, job.getStatus());
161 dataAccessService.saveDomainObject(createServiceInfo(userId, request, jobId, templateId, createdBulkDate),
162 DaoUtils.getPropsMap());
167 private ServiceInfo createServiceInfo(String userId, ServiceInstantiation serviceInstantiation, UUID jobId,
168 UUID templateId, Date createdBulkDate) {
169 return new ServiceInfo(
170 userId, Job.JobStatus.PENDING, serviceInstantiation.isPause(), jobId, templateId,
171 serviceInstantiation.getOwningEntityId(),
172 serviceInstantiation.getOwningEntityName(),
173 serviceInstantiation.getProjectName(),
174 serviceInstantiation.getAicZoneId(),
175 serviceInstantiation.getAicZoneName(),
176 serviceInstantiation.getTenantId(),
177 serviceInstantiation.getTenantName(),
178 serviceInstantiation.getLcpCloudRegionId(),
180 serviceInstantiation.getSubscriptionServiceType(),
181 serviceInstantiation.getSubscriberName(),
183 serviceInstantiation.getInstanceName(),
184 serviceInstantiation.getModelInfo().getModelInvariantId(),
185 serviceInstantiation.getModelInfo().getModelName(),
186 serviceInstantiation.getModelInfo().getModelVersion(),
193 public RequestDetailsWrapper<ServiceInstantiationRequestDetails> generateServiceInstantiationRequest(UUID jobId,
194 ServiceInstantiation payload, String userId) {
196 ServiceInstantiationRequestDetails.ServiceInstantiationOwningEntity owningEntity = new ServiceInstantiationRequestDetails.ServiceInstantiationOwningEntity(
197 payload.getOwningEntityId(), payload.getOwningEntityName());
199 SubscriberInfo subscriberInfo = new SubscriberInfo();
200 subscriberInfo.setGlobalSubscriberId(payload.getGlobalSubscriberId());
202 String serviceInstanceName = null;
203 if (payload.isUserProvidedNaming()) {
204 serviceInstanceName = getUniqueName(payload.getInstanceName(), ResourceType.SERVICE_INSTANCE);
205 String finalServiceInstanceName = serviceInstanceName;
206 updateServiceInfo(jobId, x -> x.setServiceInstanceName(finalServiceInstanceName));
208 ServiceInstantiationRequestDetails.RequestInfo requestInfo = new ServiceInstantiationRequestDetails.RequestInfo(
210 payload.getProductFamilyId(),
212 payload.isRollbackOnFailure(),
215 List<ServiceInstantiationRequestDetails.ServiceInstantiationService> serviceInstantiationService = new LinkedList<>();
216 List<Map<String, String>> unFilteredInstanceParams =
217 payload.getInstanceParams() != null ? payload.getInstanceParams() : new LinkedList<>();
218 List<Map<String, String>> filteredInstanceParams = removeUnNeededParams(unFilteredInstanceParams);
219 ServiceInstantiationRequestDetails.ServiceInstantiationService serviceInstantiationService1 = new ServiceInstantiationRequestDetails.ServiceInstantiationService(
220 payload.getModelInfo(),
222 filteredInstanceParams,
223 createServiceInstantiationVnfList(payload)
225 serviceInstantiationService.add(serviceInstantiationService1);
227 ServiceInstantiationRequestDetails.RequestParameters requestParameters = new ServiceInstantiationRequestDetails.RequestParameters(
228 payload.getSubscriptionServiceType(), false, serviceInstantiationService);
230 ServiceInstantiationRequestDetails.Project project =
231 payload.getProjectName() != null ? new ServiceInstantiationRequestDetails.Project(payload.getProjectName())
234 ServiceInstantiationRequestDetails requestDetails = new ServiceInstantiationRequestDetails(
235 payload.getModelInfo(), owningEntity, subscriberInfo,
236 project, requestInfo, requestParameters);
238 RequestDetailsWrapper<ServiceInstantiationRequestDetails> requestDetailsWrapper = new RequestDetailsWrapper(
240 debugRequestDetails(requestDetailsWrapper, logger);
241 return requestDetailsWrapper;
244 private List<Map<String, String>> removeUnNeededParams(List<Map<String, String>> instanceParams) {
245 List<String> keysToRemove = new ArrayList<>();
246 if (instanceParams != null && !instanceParams.isEmpty()) {
247 for (String key : instanceParams.get(0).keySet()) {
248 for (String paramToIgnore : PARAMS_TO_IGNORE) {
249 if ((key.equalsIgnoreCase(paramToIgnore))) {
250 keysToRemove.add(key);
254 for (String key : keysToRemove) {
255 instanceParams.get(0).remove(key);
257 //TODO will be removed on once we stop using List<Map<String, String>>
258 if (instanceParams.get(0).isEmpty()) {
259 return Collections.emptyList();
262 return instanceParams;
265 private ServiceInstantiationRequestDetails.ServiceInstantiationVnfList createServiceInstantiationVnfList(
266 ServiceInstantiation payload) {
267 CloudConfiguration cloudConfiguration = new CloudConfiguration();
268 cloudConfiguration.setTenantId(payload.getTenantId());
269 cloudConfiguration.setLcpCloudRegionId(payload.getLcpCloudRegionId());
271 Map<String, Vnf> vnfs = payload.getVnfs();
272 List<ServiceInstantiationRequestDetails.ServiceInstantiationVnf> vnfList = new ArrayList<>();
273 for (Vnf vnf : vnfs.values()) {
274 Map<String, Map<String, VfModule>> vfModules = vnf.getVfModules();
275 List<VfModule> convertedUnFilteredVfModules = convertVfModuleMapToList(vfModules);
276 List<VfModule> filteredVfModules = filterInstanceParamsFromVfModuleAndUniqueNames(
277 convertedUnFilteredVfModules, vnf.isUserProvidedNaming());
278 ServiceInstantiationRequestDetails.ServiceInstantiationVnf serviceInstantiationVnf = new ServiceInstantiationRequestDetails.ServiceInstantiationVnf(
281 vnf.getPlatformName(),
282 vnf.getLineOfBusiness(),
283 payload.getProductFamilyId(),
284 removeUnNeededParams(vnf.getInstanceParams()),
286 vnf.isUserProvidedNaming() ? getUniqueName(vnf.getInstanceName(), ResourceType.GENERIC_VNF) : null
288 vnfList.add(serviceInstantiationVnf);
291 return new ServiceInstantiationRequestDetails.ServiceInstantiationVnfList(vnfList);
294 private List<VfModule> convertVfModuleMapToList(Map<String, Map<String, VfModule>> vfModules) {
295 return vfModules.values().stream().flatMap(vfModule -> vfModule.values().stream()).collect(Collectors.toList());
298 private List<VfModule> filterInstanceParamsFromVfModuleAndUniqueNames(List<VfModule> unFilteredVfModules,
299 boolean isUserProvidedNaming) {
300 return unFilteredVfModules.stream().map(vfModule ->
302 vfModule.getModelInfo(),
303 getUniqueNameIfNeeded(isUserProvidedNaming, vfModule.getInstanceName(), ResourceType.VF_MODULE),
304 getUniqueNameIfNeeded(isUserProvidedNaming, vfModule.getVolumeGroupInstanceName(),
305 ResourceType.VOLUME_GROUP),
306 removeUnNeededParams(vfModule.getInstanceParams())))
307 .collect(Collectors.toList());
310 private String getUniqueNameIfNeeded(boolean isUserProvidedNaming, String name, ResourceType resourceType) {
311 return isUserProvidedNaming && !StringUtils.isEmpty(name) ?
312 getUniqueName(name, resourceType) : null;
316 public String getServiceInstantiationPath(ServiceInstantiation serviceInstantiationRequest) {
317 //in case pause flag is true - use assign , else - use create.
318 return MsoBusinessLogicImpl.validateEndpointPath(
319 serviceInstantiationRequest.isPause() ?
320 "mso.restapi.serviceInstanceAssign" : "mso.restapi.serviceInstanceCreate"
325 public String getOrchestrationRequestsPath() {
326 return MsoBusinessLogicImpl.validateEndpointPath(MsoProperties.MSO_REST_API_GET_ORC_REQ);
330 public ServiceInfo updateServiceInfo(UUID jobUUID, Consumer<ServiceInfo> serviceUpdater) {
331 ServiceInfo serviceInfo = getServiceInfoByJobId(jobUUID);
332 serviceUpdater.accept(serviceInfo);
333 dataAccessService.saveDomainObject(serviceInfo, DaoUtils.getPropsMap());
338 public ServiceInfo updateServiceInfoAndAuditStatus(UUID jobUuid, JobStatus jobStatus) {
339 auditVidStatus(jobUuid, jobStatus);
340 return updateServiceInfo(jobUuid, x -> setServiceInfoStatus(x, jobStatus));
343 private void setServiceInfoStatus(ServiceInfo serviceInfo, JobStatus jobStatus) {
344 serviceInfo.setJobStatus(jobStatus);
345 serviceInfo.setStatusModifiedDate(new Date());
348 public ServiceInfo getServiceInfoByJobId(UUID jobUUID) {
349 List<ServiceInfo> serviceInfoList = dataAccessService
350 .getList(ServiceInfo.class, String.format(" where jobId = '%s' ", jobUUID), null, null);
351 if (serviceInfoList.size() != 1) {
352 throw new GenericUncheckedException(
353 "Failed to retrieve job with uuid " + jobUUID + " from ServiceInfo table. Instances found: "
354 + serviceInfoList.size());
356 return serviceInfoList.get(0);
359 public List<JobAuditStatus> getAuditStatuses(UUID jobUUID, JobAuditStatus.SourceStatus source) {
360 return dataAccessService.getList(
361 JobAuditStatus.class,
362 String.format(" where SOURCE = '%s' and JOB_ID = '%s'", source, jobUUID),
363 " CREATED_DATE ", null);
366 private JobAuditStatus getLatestAuditStatus(UUID jobUUID, JobAuditStatus.SourceStatus source) {
367 List<JobAuditStatus> list = getAuditStatuses(jobUUID, source);
368 return !list.isEmpty() ? list.get(list.size() - 1) : null;
372 public void auditVidStatus(UUID jobUUID, JobStatus jobStatus) {
373 JobAuditStatus vidStatus = new JobAuditStatus(jobUUID, jobStatus.toString(), JobAuditStatus.SourceStatus.VID);
374 auditStatus(vidStatus);
378 public void auditMsoStatus(UUID jobUUID, AsyncRequestStatus.Request msoRequestStatus) {
379 auditMsoStatus(jobUUID, msoRequestStatus.requestStatus.getRequestState(), msoRequestStatus.requestId,
380 msoRequestStatus.requestStatus.getStatusMessage());
384 public void auditMsoStatus(UUID jobUUID, String jobStatus, String requestId, String additionalInfo) {
385 JobAuditStatus msoStatus = new JobAuditStatus(jobUUID, jobStatus, JobAuditStatus.SourceStatus.MSO,
386 requestId != null ? UUID.fromString(requestId) : null,
388 auditStatus(msoStatus);
391 private void auditStatus(JobAuditStatus jobAuditStatus) {
392 JobAuditStatus latestStatus = getLatestAuditStatus(jobAuditStatus.getJobId(), jobAuditStatus.getSource());
393 if (latestStatus == null || !latestStatus.equals(jobAuditStatus)) {
394 dataAccessService.saveDomainObject(jobAuditStatus, DaoUtils.getPropsMap());
399 public Job.JobStatus calcStatus(AsyncRequestStatus asyncRequestStatus) {
400 String msoRequestState = asyncRequestStatus.request.requestStatus.getRequestState().toLowerCase()
401 .replaceAll("[^a-z]+", "");
402 JobStatus jobStatus = msoStateToJobStatusMap.get(msoRequestState);
403 return (jobStatus != null ? jobStatus : JobStatus.IN_PROGRESS);
407 public void handleFailedInstantiation(UUID jobUUID) {
408 ServiceInfo serviceInfo = updateServiceInfoAndAuditStatus(jobUUID, JobStatus.FAILED);
409 List<ServiceInfo> serviceInfoList = dataAccessService.getList(
411 String.format(" where templateId = '%s' and jobStatus = '%s'",
412 serviceInfo.getTemplateId(),
415 serviceInfoList.forEach(si -> updateServiceInfoAndAuditStatus(si.getJobId(), JobStatus.STOPPED));
419 public void deleteJob(UUID jobId) {
420 jobService.delete(jobId);
421 Date now = new Date();
422 updateServiceInfo(jobId, x -> x.setDeletedAt(now));
426 public void hideServiceInfo(UUID jobUUID) {
427 ServiceInfo serviceInfo = getServiceInfoByJobId(jobUUID);
428 if (!serviceInfo.getJobStatus().isFinal()) {
429 String message = String.format("jobId %s: Service status does not allow hide service, status = %s",
430 serviceInfo.getJobId(),
431 serviceInfo.getJobStatus());
432 logger.error(EELFLoggerDelegate.errorLogger, message);
433 throw new OperationNotAllowedException(message);
435 serviceInfo.setHidden(true);
436 dataAccessService.saveDomainObject(serviceInfo, DaoUtils.getPropsMap());
442 getCounterForName(String name) {
444 String hqlSelectNC = "from NameCounter where name = :name";
445 String hqlUpdateCounter = "update NameCounter set counter = :newCounter " +
446 "where name= :name " +
447 "and counter= :prevCounter";
449 Integer counter = null;
450 GenericUncheckedException lastException = null;
451 for (int i = 0; i < MAX_RETRIES_GETTING_COUNTER && counter == null; i++) {
453 counter = calcCounter(name, hqlSelectNC, hqlUpdateCounter);
454 } catch (GenericUncheckedException exception) {
455 lastException = exception; //do nothing, we will try again in the loop
459 if (counter != null) {
463 throw lastException != null ? new DbFailureUncheckedException(lastException) :
464 new DbFailureUncheckedException("Failed to get counter for " + name + " due to unknown error");
468 private Integer calcCounter(String name, String hqlSelectNC, String hqlUpdateCounter) {
470 counter = DaoUtils.tryWithSessionAndTransaction(sessionFactory, session -> {
471 NameCounter nameCounter = (NameCounter) session.createQuery(hqlSelectNC)
472 .setText("name", name)
474 if (nameCounter != null) {
475 int updatedRows = session.createQuery(hqlUpdateCounter)
476 .setText("name", nameCounter.getName())
477 .setInteger("prevCounter", nameCounter.getCounter())
478 .setInteger("newCounter", nameCounter.getCounter() + 1)
480 if (updatedRows == 1) {
481 return nameCounter.getCounter() + 1;
484 Object nameAsId = session.save(new NameCounter(name));
486 if (nameAsId != null) {
490 //in case of failure return null, in order to continue the loop
497 public int getMaxRetriesGettingFreeNameFromAai() {
498 return maxRetriesGettingFreeNameFromAai;
502 public void setMaxRetriesGettingFreeNameFromAai(int maxRetriesGettingFreeNameFromAai) {
503 this.maxRetriesGettingFreeNameFromAai = maxRetriesGettingFreeNameFromAai;
507 public String getUniqueName(String name, ResourceType resourceType) {
508 //check that name aai response well before increasing counter from DB
509 //Prevents unnecessary increasing of the counter while AAI doesn't response
510 isNameFreeInAai(NAME_FOR_CHECK_AAI_STATUS, resourceType);
512 for (int i = 0; i < getMaxRetriesGettingFreeNameFromAai(); i++) {
513 int counter = getCounterForName(name);
514 String newName = formatNameAndCounter(name, counter);
515 if (isNameFreeInAai(newName, resourceType)) {
520 throw new MaxRetriesException("find unused name for " + name, getMaxRetriesGettingFreeNameFromAai());
523 //the method is protected so we can call it in the UT
524 protected String formatNameAndCounter(String name, int counter) {
525 return name + "_" + String.format("%03d", counter);
528 private boolean isNameFreeInAai(String name, ResourceType resourceType) throws InvalidAAIResponseException {
529 HttpResponse<AaiNodeQueryResponse> aaiResponse = aaiOverTLSClient
530 .searchNodeTypeByName(name, resourceType);
531 if (aaiResponse.getStatus() > 399 || aaiResponse.getBody() == null) {
533 String message = IOUtils.toString(aaiResponse.getRawBody(), "UTF-8");
534 throw new InvalidAAIResponseException(aaiResponse.getStatus(), message);
535 } catch (IOException e) {
536 throw new InvalidAAIResponseException(aaiResponse.getStatus(), aaiResponse.getStatusText());
539 return CollectionUtils.isEmpty(aaiResponse.getBody().resultData);